C# 如何正确实现我的bool属性的INotifyPropertyChanged并绑定到CheckBox.IsChecked?
我是新手。我一直在尝试数据绑定,并想尝试将视图中的复选框双向绑定到一个单独的类(我称之为“State”)中的布尔值。关键是要确保它们始终同步 因此,我在视图中创建了一个复选框,并将其绑定到State类中前面提到的boolean属性,同时还有一个绕过复选框并直接切换boolean属性的按钮(贴有“Ninja!”)。重点是测试复选框的数据绑定在属性更改时是否有反应。但是,我无法最好地理解当属性更改时,OnPropertyChanged方法应该如何调用 以下是我目前掌握的情况:C# 如何正确实现我的bool属性的INotifyPropertyChanged并绑定到CheckBox.IsChecked?,c#,wpf,data-binding,C#,Wpf,Data Binding,我是新手。我一直在尝试数据绑定,并想尝试将视图中的复选框双向绑定到一个单独的类(我称之为“State”)中的布尔值。关键是要确保它们始终同步 因此,我在视图中创建了一个复选框,并将其绑定到State类中前面提到的boolean属性,同时还有一个绕过复选框并直接切换boolean属性的按钮(贴有“Ninja!”)。重点是测试复选框的数据绑定在属性更改时是否有反应。但是,我无法最好地理解当属性更改时,OnPropertyChanged方法应该如何调用 以下是我目前掌握的情况: <CheckBo
<CheckBox x:Name="checkBox" Content="CheckBox" HorizontalAlignment="Left" Margin="232,109,0,0" VerticalAlignment="Top" IsChecked="{Binding Checked, Mode=TwoWay}"/>
<Button x:Name="button" Content="Ninja!" HorizontalAlignment="Left" Margin="228,182,0,0" VerticalAlignment="Top" Width="75" Click="button_Click"/>
以及视图上用于初始化和处理事件的代码:
namespace TestTwoWayBinding
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private State _state;
public MainWindow()
{
InitializeComponent();
_state = new State((bool)checkBox.IsChecked);
}
private void button_Click(object sender, RoutedEventArgs e)
{
_state.Toggle();
}
}
}
命名空间TestTwoWayBinding
{
///
///MainWindow.xaml的交互逻辑
///
公共部分类主窗口:窗口
{
私营国家;
公共主窗口()
{
初始化组件();
_状态=新状态((bool)复选框.IsChecked);
}
私有无效按钮\u单击(对象发送者,路由目标e)
{
_state.Toggle();
}
}
}
从我收集的信息来看,OnPropertyChanged需要一个字符串propertyName,但我不知道这需要什么。当我输入属性名(选中)时,它自然是指布尔值,而不是字符串。我没有得到什么?我还做错了什么,因为当我通过按钮更改属性时,复选框没有注册属性更改?OnPropertyChanged方法期望
Checked
属性的名称作为参数-此时,您正在传递它的值
这意味着,将选中的属性声明更改为:
public bool Checked {
get
{
return _checked;
}
set
{
_checked = value;
OnPropertyChanged("Checked");
}
}
你真的很接近。您需要进行2个小更改,然后您的测试工作正常:
将窗口的DataContext
分配给_state变量
将字符串“Checked”放入OnPropertyChanged
中,并在OnPropertyChanged
方法中将propertyName
传递给PropertyChangedEventArgs
因此,您的主窗口将变成:
public MainWindow()
{
InitializeComponent();
_state = new State((bool)checkBox.IsChecked);
this.DataContext = _state;
}
状态类文件如下所示:
class State : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _checked;
public bool Checked
{
get
{
return _checked;
}
set
{
_checked = value;
OnPropertyChanged("Checked");
}
}
public void Toggle()
{
if (!Checked)
{
Checked = true;
}
else
{
Checked = false;
}
}
public State(bool c)
{
this.Checked = c;
}
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
作为新手,我建议您了解更多关于模型视图模型设计模式的知识。这是WPF的一种常见模式,有助于鼓励分离关注点(将业务逻辑排除在用户界面逻辑之外)这两个答案建议您传递字符串literal“Checked”
将起作用,但IMHO不是最好的方法。相反,我更喜欢在实现OnPropertyChanged()
方法时使用[CallerMemberName]
。(我不知道第三个答案是关于什么的……它似乎与这个问题没有任何关系,我猜它只是从其他地方复制/粘贴的)
下面是我如何编写您的State
类的示例:
class State : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _checked;
public bool Checked
{
get { return _checked; }
set { _checked = value; OnPropertyChanged(); }
}
public void Toggle()
{
Checked = !Checked;
}
public State(bool c)
{
this.Checked = c;
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
这里的关键是,标记为[CallerMemberName]
的参数将自动填充来自调用者的正确值,只需不传递任何值即可。默认值null
存在,只是为了让编译器允许调用方不传递值
注意,我还简化了Toggle()
方法。无需使用if
语句将一个bool
值转换为另一个值;这就是布尔运算符的作用
我还更改了OnPropertyChanged()
方法,使其具有线程安全性,即在将事件字段与null
进行比较和实际引发事件之间,如果某些代码取消订阅PropertyChanged
事件的最后一个处理程序,则不会崩溃。通常情况下,这不是问题,因为这些属性几乎总是只能从单个线程访问,但这很容易防范,而且是一个好习惯
注意,在C#6中,您可以选择只编写PropertyChanged?.Invoke(这是新的PropertyChangedEventArgs(propertyName))方法体的代码>。还不是每个人都在100%的时间使用新的编译器,所以我只是把它作为您的可选选择
当然,您还需要正确设置DataContext
,如所示:
不过,就个人而言,我不确定我是否会为构造函数而烦恼。您似乎没有其他代码可以设置复选框。IsChecked
,因此在我看来,您总是会得到默认值。此外,如果没有参数化构造函数,就不能在XAML中创建视图模型类;将来,您可能更喜欢这样配置DataContext
。例如:
<Window.DataContext>
<l:State Checked="True"/>
</Window.DataContext>
另见相关问答。这里的问题实际上是关于一些不同的东西-他们想要实现接口,而不必在属性setter中显式地编写任何内容-但不管是好是坏,他们得到的答案实际上更多地是关于您的场景,这里的问题只是简化属性设置器实现,而不是使其完全自动化
我必须承认,我本以为还有另外一个问题,可以把你的标记为复制品。我发现了很多相关的问题。但是没有什么直接关注于“如何实现和使用实现了INotifyPropertyChanged
”的视图模型,这才是您的问题的真正含义
附录:
我做了更多的搜索,虽然这些看起来都不是完全相同的,但它们都有很好的信息来帮助解决关于实现的问题
public MainWindow()
{
InitializeComponent();
_state = new State((bool)checkBox.IsChecked);
this.DataContext = _state;
}
<Window.DataContext>
<l:State Checked="True"/>
</Window.DataContext>
public MainWindow()
{
InitializeComponent();
_state = (State)this.DataContext;
}