为什么我的WPF复选框绑定不工作
本文关键字:工作 绑定 复选框 我的 WPF 为什么 | 更新日期: 2023-09-27 18:01:18
我使用的是MVVM, VS 2008和。net 3.5 SP1。我有一个项目列表,每个项目都公开一个IsSelected属性。我添加了一个CheckBox来管理列表中所有项目的选择/取消选择(更新每个项目的IsSelected属性)。当CheckBox的绑定控件触发PropertyChanged事件时,除了IsChecked属性没有在视图中更新之外,其他一切都在工作。
<CheckBox
Command="{Binding SelectAllCommand}"
IsChecked="{Binding Path=AreAllSelected, Mode=OneWay}"
Content="Select/deselect all identified duplicates"
IsThreeState="True" />
我的虚拟机:
public class MainViewModel : BaseViewModel
{
public MainViewModel(ListViewModel listVM)
{
ListVM = listVM;
ListVM.PropertyChanged += OnListVmChanged;
}
public ListViewModel ListVM { get; private set; }
public ICommand SelectAllCommand { get { return ListVM.SelectAllCommand; } }
public bool? AreAllSelected
{
get
{
if (ListVM == null)
return false;
return ListVM.AreAllSelected;
}
}
private void OnListVmChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "AreAllSelected")
OnPropertyChanged("AreAllSelected");
}
}
我没有在这里展示SelectAllCommand或单个项目选择的实现,但它似乎不相关。当用户选择列表中的单个项目(或单击问题CheckBox以选择/取消选择所有项目)时,我已经验证了OnPropertyChanged("AreAllSelected")行代码的执行,并且在调试器中进行跟踪,可以看到PropertyChanged事件被订阅并按预期触发。但是AreAllSelected属性的get只执行一次——当视图实际呈现的时候。Visual Studio的输出窗口没有报告任何数据绑定错误,所以从我可以看出,CheckBox的IsSelected属性是正确绑定的。
如果我将CheckBox替换为Button:
<Button Content="{Binding SelectAllText}" Command="{Binding SelectAllCommand}"/>
和更新VM:
...
public string SelectAllText
{
get
{
var msg = "Select All";
if (ListVM != null && ListVM.AreAllSelected != null && ListVM.AreAllSelected.Value)
msg = "Deselect All";
return msg;
}
}
...
private void OnListVmChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "AreAllSelected")
OnPropertyChanged("SelectAllText");
}
一切工作如预期-按钮的文本更新,因为所有项目被选中/取消。是否有一些我错过了对CheckBox的IsSelected属性的绑定?
谢谢你的帮助!
我找到问题了。似乎WPF 3.0中存在一个错误,在IsChecked上有单向绑定,导致绑定被移除。感谢这篇文章的帮助,听起来这个bug在WPF 4.0中已经修复了
要复制,请创建一个新的WPF项目。
添加一个FooViewModel.cs:
using System;
using System.ComponentModel;
using System.Windows.Input;
namespace Foo
{
public class FooViewModel : INotifyPropertyChanged
{
private bool? _isCheckedState = true;
public FooViewModel()
{
ChangeStateCommand = new MyCmd(ChangeState);
}
public bool? IsCheckedState
{
get { return _isCheckedState; }
}
public ICommand ChangeStateCommand { get; private set; }
private void ChangeState()
{
switch (_isCheckedState)
{
case null:
_isCheckedState = true;
break;
default:
_isCheckedState = null;
break;
}
OnPropertyChanged("IsCheckedState");
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
var changed = PropertyChanged;
if (changed != null)
changed(this, new PropertyChangedEventArgs(propertyName));
}
}
public class MyCmd : ICommand
{
private readonly Action _execute;
public event EventHandler CanExecuteChanged;
public MyCmd(Action execute)
{
_execute = execute;
}
public void Execute(object parameter)
{
_execute();
}
public bool CanExecute(object parameter)
{
return true;
}
}
}
修改Window1.xaml.cs:
using System.Windows;
using System.Windows.Controls.Primitives;
namespace Foo
{
public partial class Window1
{
public Window1()
{
InitializeComponent();
}
private void OnClick(object sender, RoutedEventArgs e)
{
var bindingExpression = MyCheckBox.GetBindingExpression(ToggleButton.IsCheckedProperty);
if (bindingExpression == null)
MessageBox.Show("IsChecked property is not bound!");
}
}
}
修改Window1.xaml:
<Window
x:Class="Foo.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:Foo"
Title="Window1"
Height="200"
Width="200"
>
<Window.DataContext>
<vm:FooViewModel />
</Window.DataContext>
<StackPanel>
<CheckBox
x:Name="MyCheckBox"
Command="{Binding ChangeStateCommand}"
IsChecked="{Binding Path=IsCheckedState, Mode=OneWay}"
Content="Foo"
IsThreeState="True"
Click="OnClick"/>
<Button Command="{Binding ChangeStateCommand}" Click="OnClick" Content="Change State"/>
</StackPanel>
</Window>
点击按钮几次,看到CheckBox的状态在true和null(不是false)之间切换。但是点击CheckBox,你会看到Binding已经从IsChecked属性中移除了。
方法:
将IsChecked绑定更新为TwoWay,并将其UpdateSourceTrigger设置为显式:
IsChecked="{Binding Path=IsCheckedState, Mode=TwoWay, UpdateSourceTrigger=Explicit}"
并更新bound属性,使其不再是只读的:
public bool? IsCheckedState
{
get { return _isCheckedState; }
set { }
}