【问题标题】:MVVM WPF databound checkbox won't firing events to the ViewModel for Checked and Unchecked statesMVVM WPF 数据绑定复选框不会针对选中和未选中状态向 ViewModel 触发事件
【发布时间】:2012-11-08 02:02:15
【问题描述】:

我在 DataGrid 中有一个数据绑定 CheckBox,使用 WPF 和 MVVM;

<DataGridTemplateColumn Width="80" Header="Enabled">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <CheckBox IsChecked="{Binding Path=IsEnabled, UpdateSourceTrigger=PropertyChanged,  Mode=TwoWay}" Name="theCheckbox" HorizontalAlignment="Center"/>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

这可以正常工作,并且在设置 IsEnabled 时正在检查 CheckBoxIsEnabled 是我绑定到DataGrid 的对象集合中的一个属性。我想要做的是能够验证当用户选择它时是否应该允许检查DataGrid 中的特定行CheckBox,如果不删除他们的检查并显示类似“第 1 行”的警告消息如果不检查第 5 行和第 9 行,则无法检查”。我发现了如何使用CheckBoxCheckedUnchecked 属性背后的代码来执行此操作,但是我使用的是MVVM,因此想要处理与查看DataGrid 关联的ViewModel 中的事情和CheckBox 在里面。我该怎么做?我也需要一种传递 DataRow 的 Id 字段的方法,以便确定我正在处理哪一行,为了论证,假设 Id 字段称为BorderId

【问题讨论】:

    标签: wpf mvvm checkbox


    【解决方案1】:

    在您的对象上实现IDataErrorInfo,这是 WPF 的默认验证接口,并设置您的验证代码以验证是否可以选中复选框

    这实际上比正常情况要棘手一些,因为您正在使用数据项上不存在的数据验证数据项上的属性,这意味着您需要提供某种方式在运行时将不相关的验证附加到您的对象时间。

    我通常使用的解决方案是从我的对象中公开一个 ValidationDelegate,其他对象可以使用它来为该对象附加额外的验证。它的代码发布在on my blog,它允许您将验证附加到您的对象,如下所示:

    public class MyViewModel
    {
        // Keeping this generic to reduce code here, but it
        // should be a full property with PropertyChange notification
        public ObservableCollection<SomeObject> SomeCollection { get; set; }
    
        public MyViewModel()
        {
            SomeCollection = LoadDataGridObjects();
    
            // Add the validation delegate to each object
            foreach(var item in SomeCollection)
                item.AddValidationDelegate(ValidateObject);
        }
    
        // Validation Delegate to verify the right checkboxes are checked
        private string ValidateObject(object sender, string propertyName)
        {
            if (propertyName == "IsChecked")
            {
                var item = (SomeObject)sender;
                if (item.Id == 1
                    && !SomeCollection.First(p => p.Id == 5).IsChecked
                    && !SomeCollection.First(p => p.Id == 9).IsChecked)
                {
                    return "Row 1 cannot be checked without rows 5 and 9 being checked";
                }
            }
            return null;
        }
    }
    

    【讨论】:

      【解决方案2】:

      您可以添加到列表中每个项目的 PropertyChanged 事件处理程序吗?像这样的:

      foreach (Object x in List)
      {
          x.PropertyChanged += OnItemChanged;
      }
      

      然后在更改侦听器中,您可以对发送者对象执行任何您喜欢的操作,包括更改 IsEnabled 属性。

      void OnItemChanged(object sender, PropertyChangedEventArgs e)
      {
          // change sender object properties or set bound label text here
      }
      

      【讨论】:

      • 抱歉,我不太明白您要指出的是什么。您的意思是每当有人选中复选框时都会触发 OnPropertyChange 事件?如果是这样,这可能可行,但我想要一种更直接的方法,而不是遍历集合中可能有数千行的内容来检查已更新的内容。在 ViewModel 中,我确实需要使用传回的 BorderId 的 switch 语句,然后我可以编写验证代码来检查是否选择了其他行中所需的复选框。
      • 这不是一个很好的方法,因为它仍然允许 CheckBox 被选中,然后引发错误并再次取消选中该属性。如果属性在选中和未选中时都无效,它也有陷入循环的风险。此外,您不需要运行代码来从 MVVM 中的 ViewModel 更新您的 UI 对象 :)
      • 关于循环问题 Rachel 的要点。您的解决方案肯定更优雅。只是为了向 OP 澄清,在设置列表时,foreach 循环只会运行一次。 OnItemChanged 代码将在给定项目发生更改时触发,将该对象作为发送者变量传递。
      【解决方案3】:

      如果我理解你的问题,你有一个复选框,你想控制它是否可以被选中。与其让用户一直检查它并在您不想允许时回推错误消息,不如在您不希望用户检查时首先使您的复选框处于非活动状态。

      要通过您的 ViewModel(当然是您的 DataContext)执行此操作,您需要将 Checkbox 的“命令”绑定到您的 ViewModel 中的自定义命令。

      WPF Command 将提供以下功能:

      • 根据自定义逻辑自动启用/禁用控件
      • 处理控件的“动作”事件(在复选框按钮上,它会是“单击”)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-02-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多