为什么从 UI 中删除命令源后会调用 CanExecute?

2024-04-21

我试图理解为什么在已从 UI 中删除的命令源上调用 CanExecute。这是一个简化的程序来演示:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Height="350" Width="525">
    <StackPanel>
        <ListBox ItemsSource="{Binding Items}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel>
                        <Button Content="{Binding Txt}" 
                                Command="{Binding Act}" />
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        <Button Content="Remove first item" Click="Button_Click"  />
    </StackPanel>
</Window>

隐藏代码:

public partial class MainWindow : Window
{
    public class Foo
    {
        static int _seq = 0;
        int _txt = _seq++;
        RelayCommand _act;
        public bool Removed = false;

        public string Txt { get { return _txt.ToString(); } }

        public ICommand Act
        {
            get
            {
                if (_act == null) {
                    _act = new RelayCommand(
                        param => { },
                        param => {
                            if (Removed)
                                Console.WriteLine("Why is this happening?");
                            return true;
                        });
                }
                return _act;
            }
        }
    }

    public ObservableCollection<Foo> Items { get; set; }

    public MainWindow()
    {
        Items = new ObservableCollection<Foo>();
        Items.Add(new Foo());
        Items.Add(new Foo());
        Items.CollectionChanged += 
            new NotifyCollectionChangedEventHandler(Items_CollectionChanged);
        DataContext = this;
        InitializeComponent();
    }

    void Items_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.Action == NotifyCollectionChangedAction.Remove)
            foreach (Foo foo in e.OldItems) {
                foo.Removed = true;
                Console.WriteLine("Removed item marked 'Removed'");
            }
    }

    void Button_Click(object sender, RoutedEventArgs e)
    {
        Items.RemoveAt(0);
        Console.WriteLine("Item removed");
    }
}

当我单击“删除第一项”按钮一次时,我得到以下输出:

Removed item marked 'Removed'
Item removed
Why is this happening?
Why is this happening?

“为什么会这样?”每次我单击窗口的某些空白部分时,都会不断打印。

为什么会发生这种情况?我可以或应该做什么来防止在已删除的命令源上调用 CanExecute?

Note:可以找到RelayCommandhere http://msdn.microsoft.com/en-us/magazine/dd419663.aspx#id0090030.

迈克尔·伊登菲尔德问题的答案:

Q1:在删除的按钮上调用 CanExecute 时的调用堆栈:

WpfApplication1.exe!WpfApplication1.MainWindow.Foo.get_Act.AnonymousMethod__1(对象参数) 第 30 行 WpfApplication1.exe!WpfApplication1.RelayCommand.CanExecute(对象参数) 第 41 行 + 0x1a 字节 PresentationFramework.dll!MS.Internal.Commands.CommandHelpers.CanExecuteCommandSource(System.Windows.Input.ICommandSource commandSource) + 0x8a 字节 PresentationFramework.dll!System.Windows.Controls.Primitives.ButtonBase.UpdateCanExecute()+ 0x18字节PresentationFramework.dll!System.Windows.Controls.Primitives.ButtonBase.OnCanExecuteChanged(对象发送者,System.EventArgs e)+ 0x5字节 PresentationCore.dll!System.Windows.Input.CommandManager.CallWeakReferenceHandlers(System.Collections.Generic.List 处理程序) + 0xac 字节 PresentationCore.dll!System.Windows.Input.CommandManager.RaiseRequerySuggested(对象 obj) + 0xf 字节

Q2:另外,如果您从列表中删除所有按钮(而不仅仅是第一个按钮),这种情况还会继续发生吗?

Yes.


问题是命令源(即按钮)没有取消订阅CanExecuteChanged它所绑定的命令的,以便每当CommandManager.RequerySuggested fires, CanExecute在命令源消失很久之后也会触发。

为了解决这个问题我实施了IDisposable on RelayCommand,并添加了必要的代码,以便每当删除模型对象并从 UI 中删除时,都会调用 Dispose() 就其所有的RelayCommand.

这是修改后的RelayCommand(原文是here http://msdn.microsoft.com/en-us/magazine/dd419663.aspx#id0090030):

public class RelayCommand : ICommand, IDisposable
{
    #region Fields

    List<EventHandler> _canExecuteSubscribers = new List<EventHandler>();
    readonly Action<object> _execute;
    readonly Predicate<object> _canExecute;

    #endregion // Fields

    #region Constructors

    public RelayCommand(Action<object> execute)
        : this(execute, null)
    {
    }

    public RelayCommand(Action<object> execute, Predicate<object> canExecute)
    {
        if (execute == null)
            throw new ArgumentNullException("execute");

        _execute = execute;
        _canExecute = canExecute;
    }

    #endregion // Constructors

    #region ICommand

    [DebuggerStepThrough]
    public bool CanExecute(object parameter)
    {
        return _canExecute == null ? true : _canExecute(parameter);
    }

    public event EventHandler CanExecuteChanged
    {
        add
        {
            CommandManager.RequerySuggested += value;
            _canExecuteSubscribers.Add(value);
        }
        remove
        {
            CommandManager.RequerySuggested -= value;
            _canExecuteSubscribers.Remove(value);
        }
    }

    public void Execute(object parameter)
    {
        _execute(parameter);
    }

    #endregion // ICommand

    #region IDisposable

    public void Dispose()
    {
        _canExecuteSubscribers.ForEach(h => CanExecuteChanged -= h);
        _canExecuteSubscribers.Clear();
    }

    #endregion // IDisposable
}

无论我在哪里使用上述内容,我都会跟踪所有实例化的 RelayCommand,以便我可以调用Dispose()在时机成熟时:

Dictionary<string, RelayCommand> _relayCommands 
    = new Dictionary<string, RelayCommand>();

public ICommand SomeCmd
{
    get
    {
        RelayCommand command;
        string commandName = "SomeCmd";
        if (_relayCommands.TryGetValue(commandName, out command))
            return command;
        command = new RelayCommand(
            param => {},
            param => true);
        return _relayCommands[commandName] = command;
    }
}

void Dispose()
{
    foreach (string commandName in _relayCommands.Keys)
        _relayCommands[commandName].Dispose();
    _relayCommands.Clear();
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么从 UI 中删除命令源后会调用 CanExecute? 的相关文章

  • 分段错误(核心转储)错误

    我的程序编译罚款 但在输入文件时出现 分段错误 核心转储 错误 我没有正确处理 ostream 吗 include
  • C# 正则表达式用于查找 中具有特定结尾的链接

    我需要一个正则表达式模式来查找字符串 带有 HTML 代码 中的链接 以获取文件结尾如 gif 或 png 的链接 示例字符串 a href site com folder picture png target blank picture
  • 将字符串中的“奇怪”字符转换为罗马字符

    我需要能够将用户输入仅转换为 a z 罗马字符 不区分大小写 所以 我感兴趣的角色只有26个 然而 用户可以输入他们想要的任何 形式 的字符 西班牙语 n 法语 e 和德语 u 都可以包含用户输入中的重音符号 这些重音符号会被程序删除 我已
  • 从 C 结构生成 C# 结构

    我有几十个 C 结构 我需要在 C 中使用它们 典型的 C 结构如下所示 typedef struct UM EVENT ULONG32 Id ULONG32 Orgin ULONG32 OperationType ULONG32 Size
  • SFINAE 如何使用省略号?

    过去 当使用 SFINAE 选择构造函数重载时 我通常使用以下内容 template
  • 如何使用MySqlCommand和prepare语句进行多行插入?(#C)

    Mysql 给出了如何使用准备语句和 NET 插入行的示例 http dev mysql com doc refman 5 5 en connector net programming prepared html http dev mysq
  • 如何生成 appsettings..json 文件?

    我有一个 ASP NET Core 2 WebAPI 它将部署在以下环境中 INT QA STAGE 生产环境 基于上述 我需要有appsettings
  • 从 Code::Blocks 运行程序时出现空白控制台窗口 [重复]

    这个问题在这里已经有答案了 当我尝试在 Code Blocks 中构建并运行新程序时 控制台窗口弹出空白 我必须单击退出按钮才能停止它 它对我尝试过的任何新项目 包括 Hello world 都执行此操作 奇怪的是 它对于我拥有的任何旧项目
  • C# 委托责任链

    为了我的理解目的 我实现了责任链模式 Abstract Base Type public abstract class CustomerServiceDesk protected CustomerServiceDesk nextHandle
  • 2D morton 码编码/解码 64 位

    如何将给定 x y 的莫顿代码 z 顺序 编码 解码为 32 位无符号整数 生成 64 位莫顿代码 反之亦然 我确实有 xy2d 和 d2xy 但仅适用于 16 位宽的坐标 产生 32 位莫顿数 在网上查了很多 但没有找到 请帮忙 如果您可
  • 从 R 到 C 处理列表并访问它

    我想使用从 R 获得的 C 列表 我意识到这个问题与此非常相似 使用 call 在 R 和 C 之间传递数据帧 https stackoverflow com questions 6658168 passing a data frame f
  • 0-1背包算法

    以下 0 1 背包问题是否可解 浮动 正值和 浮动 权重 可以是正数或负数 背包的 浮动 容量 gt 0 我平均有 这是一个相对简单的二进制程序 我建议用蛮力进行修剪 如果任何时候你超过了允许的重量 你不需要尝试其他物品的组合 你可以丢弃整
  • 从 Delphi 调用 C# dll

    我用单一方法编写了 Net 3 5 dll 由Delphi exe调用 不幸的是它不起作用 步骤 1 使用以下代码创建 C 3 5 dll public class MyDllClass public static int MyDllMet
  • C 中带有指针的结构的内存开销[重复]

    这个问题在这里已经有答案了 我意识到当我的结构包含指针时 它们会产生内存开销 这里有一个例子 typedef struct int num1 int num2 myStruct1 typedef struct int p int num2
  • 如何引用解决方案之外的项目?

    我有一个 Visual Studio C 解决方案 其中包含一些项目 其中一个项目需要引用另一个不属于解决方案的项目 一开始我引用了dll
  • C语言声明数组没有初始大小

    编写一个程序来操纵温度详细信息 如下所示 输入要计算的天数 主功能 输入摄氏度温度 输入功能 将温度从摄氏度转换为华氏度 独立功能 查找华氏度的平均温度 我怎样才能在没有数组初始大小的情况下制作这个程序 include
  • 类中不允许使用不完整类型,但类模板中允许使用不完整类型

    以下为无效代码 struct foo struct bar bar x error field x has incomplete type struct bar int value 42 int main return foo x valu
  • 为什么文件更新时“如果较新则复制”不复制文件?

    我在 Visual Studio Express 中有一个解决方案 如下所示 The LogicSchemaC 中的类 将在运行时解析指定的 XML 文件 以下是在main的方法Program cs LogicSchema ls new L
  • 服务器响应 PASV 命令返回的地址与建立 FTP 连接的地址不同

    System Net WebException 服务器响应 PASV 命令返回的地址与建立 FTP 连接的地址不同 在 System Net FtpWebRequest CheckError 在 System Net FtpWebReque
  • 如何使用 C# 以低分辨率形式提供高分辨率图像

    尝试使用 300dpi tif 图像在网络上显示 目前 当用户上传图像时 我正在动态创建缩略图 如果创建的页面引用宽度为 500x500px 的高分辨率图像 我可以使用相同的功能即时转换为 gif jpg 吗 将创建的 jpg 的即将分辨率

随机推荐

  • October CMS:如何扩展后端用户的角色范围

    我已经能够延长Backend Models User类并添加一个范围查询方法以仅检索超级用户 public function boot User extend function model model gt addDynamicMethod
  • 如何自动链接本地npm包?

    我正在构建两个相互依赖的私有 npm 包 说我有 project my commons package json name my commons version 0 0 1 my server package json dependenci
  • Beaglebone Black 上的 GPIO

    我目前遇到了 Beaglebone black GPIO 引脚的问题 我正在寻找一种正确的方法来读取 C 中的 GPIO 引脚 p8 4 的值 如果我理解正确的话 我尝试使用一个库 该库使用了在引入设备树之前不支持的旧方法 我尝试寻找其他解
  • 如果方法只需要 ajax 调用,会返回什么错误?

    如果操作期望仅通过 AJAX 使用 但在没有正确的 ajax 标头的情况下调用 则操作应返回什么 HTTP 状态 我觉得我应该指出一些错误 但我真的找不到合适的错误 我想最好是 405 Method not allowed 但是如果例如 a
  • PowerShell 中的“net use”不指定驱动器

    通过 net use 您可以执行以下操作 net use server user domian username 然后 它会提示输入密码 并且使用任何程序 cmd Explorer Word 等 与该服务器建立的任何进一步 CIFS 连接都
  • 验证 TextBox 中的文本更改

    我已经在 WinForm 中的文本框上实现了验证规则 并且效果很好 但是 只有当我跳出该字段时 它才会检查验证 我希望它在框中输入任何内容以及每次内容发生变化时立即进行检查 我还希望它在 WinForm 打开后立即检查验证 我记得最近通过设
  • 一页上有多个夏季笔记 div

    我正在尝试获取特定夏季笔记 div 的代码 其中单个页面上有多个笔记 div 我的暑假笔记是用 php 从数据库创建的 如下所示 div class tab content div class tab pane div class summ
  • 如何在大窗口上优化窗口聚合?

    我在 Spark 2 4 4 中使用带有大窗口的窗口函数 例如 Window partitionBy id orderBy timestamp 在我的测试中 我有大约 70 个不同的 ID 但我可能有大约 200 000 行 ID 如果没有
  • 检索 AR 模型的所有关联属性?

    您认为检索 AR 模型所有关联的所有属性的最佳方法是什么 即 假设我们有模型Target class Target lt ActiveRecord Base has many countries has many cities has ma
  • 使用推断的 (?) 类型克隆 std::iter::Map

    我在以紧凑的方式克隆地图时遇到问题 extern crate itertools num use itertools num linspace fn main 440Hz as wave frequency middle A let fre
  • 没有 GameKit 的 iOS 蓝牙

    我已经知道使用 GameKit 我只能连接到运行相同应用程序的其他 iOS 设备 但我想连接到 Parallaz EasyBluetooth 芯片来发送和接收命令 因此我需要不同的解决方案 我想有一些针对越狱设备的库 但我不知道 你能给我一
  • 我什么时候知道 GeoFire 已完成加载,因为它是基于事件的触发器

    因此 我使用 GeoFire 和 AngularFire 来填充我的 ng repeat 项目列表 有一个离子拉动来刷新列表 因此 用户拉动它并获取当前更新的位置并刷新列表 调用 GeoFire 来重新填充列表 scope doRefres
  • 未捕获的类型错误:无法读取未定义的属性(读取“isBatchingLegacy”)

    我正在尝试使用 jest 测试反应打字稿项目 但它给出了一个令人困惑的错误 错误图像 https i stack imgur com gdWQp png 这是我的 package json dependencies testing libr
  • 未捕获的引用错误:谷歌未定义

    我正在尝试将 geoJSON 文件中的标记加载到我的地图上 地图加载正常 但不断出现错误 Uncaught ReferenceError google is not defined 在这一行 google maps event addDom
  • 使用Python修改INI文件

    我有一个 INI 文件需要使用 Python 进行修改 我正在调查ConfigParser模块 但仍然遇到问题 我的代码是这样的 config ConfigParser RawConfigParser config read C itb i
  • 解析,如何向目标用户发送推送通知

    我已成功设置解析推送通知 并且在我的安装表中我同时拥有安装和设备令牌 我真正想做的是将推送通知发送给某些用户 而不是某些设备 如何将安装表绑定到使用表 以便我可以由用户进行查询并返回要推送到的设备ID From https parse co
  • 如何将 Maxmind .MMDB 转换为 .DAT?

    如何将 MaxMinds MMDB GeoIP 转换为 DAT 格式 以便我可以与 modsecurity Apache 一起使用 Modsecurity 仅支持 DAT 格式 截至 2019 年 2 月 以下 Python 脚本是将 Ge
  • 任务之间的气流延迟

    As you can see in the image airflow is making too much time between tasks execution it almost represents 30 of the DAG e
  • 如何使用 Rollup 构建自定义引导程序包

    根据Bootstrap 5官方文档 我们可以从以下位置导入预编译的js文件bootstrap js dist并使用 Webpack rollup 构建自定义捆绑包 https getbootstrap com docs 5 0 gettin
  • 为什么从 UI 中删除命令源后会调用 CanExecute?

    我试图理解为什么在已从 UI 中删除的命令源上调用 CanExecute 这是一个简化的程序来演示