如何为我的bool属性正确实现INotifyPropertyChanged并绑定到CheckBox.IsChecked? [英] How do I correctly implement INotifyPropertyChanged for my bool property and bind to CheckBox.IsChecked?

查看:53
本文介绍了如何为我的bool属性正确实现INotifyPropertyChanged并绑定到CheckBox.IsChecked?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

新手在这里.我一直试图绕过数据绑定,并想尝试将视图中的复选框双向绑定到我称为状态"的单独类中的布尔值.关键是要确保它们始终保持同步.

Novice here. I've been trying to wrap my head around databinding, and wanted to do try out two-way binding of a checkbox in the view to a boolean in a separate class that I've called "State". The point is to ensure that they are always in sync.

因此,我在视图中创建了一个复选框,并将其绑定到State-class中的上述boolean属性,并带有一个绕过该复选框并直接切换boolean属性的按钮(恰当地标记为"Ninja!").重点是测试属性更改时复选框的数据绑定是否响应.但是,我无法尽全力弄清楚应该如何在属性更改时调用OnPropertyChanged方法.

So I've made a checkbox in the view and bound it to the aforementioned boolean property in the State-class, accompanied by a button that bypasses the checkbox and toggles the boolean property directly (aptly labeled 'Ninja!'). The point was to test that the checkbox' databinding reacts when the property changes. However, I can't for the best of me figure out how the OnPropertyChanged-method is supposed to be invoked when the property changes.

这是我到目前为止所拥有的:

Here's what I have so far:

<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"/>

还有我编写的状态"类的代码:

And the code for the "State"-class I've made:

namespace TestTwoWayBinding
{
    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(Checked));
            }
        }
    }
}

视图中用于初始化和处理事件的代码:

And the code-behind on the view for initialization and handling the events:

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();
        }
    }
}

从我收集到的信息来看,OnPropertyChanged需要一个String propertyName,但是我不知道在这里需要什么.当我输入属性名称(选中)时,它自然是指布尔值,而不是字符串.我没有得到什么?还有什么我做错的,因为通过按钮更改属性时,复选框未注册属性更改?

From what I gather, OnPropertyChanged expects a String propertyName, but I don't know what that would entail here. When I put in the name of the property (Checked), then that naturally refers to a boolean, not a string. What am I not getting? And what else am I doing wrong, as the checkbox doesn't register the property change when I change it through the button?

推荐答案

建议您传递字符串文字"Checked" 的两个答案都可以,但是恕我直言并不是最好的方法它.相反,我更喜欢在实现 OnPropertyChanged()方法时使用 [CallerMemberName] .(我不知道第三个答案是关于hellip的;它似乎与这个问题无关,我想它只是从其他地方复制/粘贴的.)

The two answers which suggest you pass the string literal "Checked" will work, but IMHO aren't the best way to do it. Instead, I prefer using [CallerMemberName] when implementing the OnPropertyChanged() method. (I have no idea what that third answer is all about…it doesn't appear to have anything to do with this question, and I'd guess it was just copy/pasted from somewhere else).

以下是我如何编写您的 State 类的示例:

Here's an example of how I'd write your State class:

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 的默认值在那里,只是这样编译器将允许调用者不传递值.

The key here is that the parameter marked with [CallerMemberName] will automatically be filled in with the correct value from the caller, simply by not passing any value. The default value of null is there just so the compiler will allow the caller to not pass a value.

请注意,我还简化了 Toggle()方法.无需使用 if 语句将一个 bool 值转换为另一个值;这就是布尔运算符的用途.

Note that I also simplified the Toggle() method. There's no need to use an if statement to transform one bool value into another; that's what the Boolean operators are there for.

我还更改了 OnPropertyChanged()方法,使其具有线程安全性,即,如果某些代码取消了两次之间的 PropertyChanged 事件中的最后一个处理程序的预订,则不会崩溃.将事件字段与 null 进行比较的时间,以及实际引发事件的时间.通常,这不是问题,因为几乎总是只能从单个线程访问这些属性,但是它很容易防止受到攻击,并且是一种很好的习惯.

I also changed the OnPropertyChanged() method so that it's thread-safe, i.e. won't crash if some code unsubscribes the last handler from the PropertyChanged event between the time the event field is compared to null and the time the event is actually raised. Typically, this is a non-issue as these properties are nearly always accessed only from a single thread, but it's easy enough to protect against and is a good habit to get into.

请注意,在C#6中,您可以选择只为方法主体编写 PropertyChanged?.Invoke(this,new PropertyChangedEventArgs(propertyName)); .并不是所有人都100%地在使用新的编译器,所以我只是将其作为您的可选选择.

Note that in C# 6, you have the option of just writing PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); for the method body. Not everyone is using the new compiler 100% of the time yet, so I just mention that as an optional choice for you.

自然,您还需要正确设置 DataContext ,如其他答案之一:

Naturally, you also need to set the DataContext correctly, as shown in one of the other answers:

public MainWindow()
{
    InitializeComponent();
    _state = new State((bool)checkBox.IsChecked);
    this.DataContext = _state;
}

尽管如此,就我个人而言,我不确定我是否会为构造函数烦恼.您似乎没有其他代码可以设置 checkBox.IsChecked ,因此在我看来,无论如何,您总是会得到默认值.此外,如果它没有参数化的构造函数,则无法在XAML中创建视图模型类.将来,您可能希望像这样配置 DataContext .例如:

Though, personally, I'm not sure I'd bother with the constructor. You appear to have no other code that would set checkBox.IsChecked, so it seems to me that you're always going to get the default value anyway. Besides, you can't create your view model class in XAML if it doesn't have a parameterized constructor; in the future, you may prefer to configure your DataContext like that. E.g.:

<Window.DataContext>
  <l:State Checked="True"/>
</Window.DataContext>

并在窗口的构造函数中:

And in the window's constructor:

public MainWindow()
{
    InitializeComponent();
    _state = (State)this.DataContext;
}


另请参阅相关的问题与解答
自动INotifyPropertyChanged .这里的问题确实是关于不同的东西—他们希望实现该接口,而不必在属性设置器中显式编写任何内容.但是无论好坏,他们得到的答案实际上更多地是关于您的情况的,这只是简化属性设置器实现的问题,而不是使其完全自动化.


See also the related Q&A Automatically INotifyPropertyChanged. The question there is really about something different — they want to implement the interface without having to explicitly write anything in the property setter — but for better or worse, the answers they got are really more about your scenario, where it's just a question of simplifying the property setter implementation rather than making it completely automatic.

我必须承认,我以为已经有另一个问题将您的重复标记为.我确实发现了许多与相关的问题.但是,没有什么可以直接关注我如何实现和使用实现 INotifyPropertyChanged 的视图模型?"的问题,而这实际上就是您的问题所在.

I have to admit, I would've thought there would have been another question already with which to mark yours as a duplicate. And I did find lots of related questions. But nothing that focuses directly on just "how do I implement and use a view model that implements INotifyPropertyChanged?", which is really what your question seems to be about.


附录:

我做了一些进一步的搜索,尽管它们似乎都没有被认为是精确的重复项,但是它们都有很好的信息,可以帮助解决有关实现 INotifyPropertyChanged 的问题:

I did some more searching, and while none of these seem like they would be considered exact duplicates per se, they all have good information that help address the question about implementing INotifyPropertyChanged:

属性的使用…INotifyPropertyChanged
更改了模型和视图模型的INotifyProperty
BindableBase与INotifyChanged
如何在MVVM(WPF)中编写"ViewModelBase"

Use of Attributes… INotifyPropertyChanged
INotifyPropertyChanged for model and viewmodel
BindableBase vs INotifyChanged
How to write "ViewModelBase" in MVVM (WPF)

这篇关于如何为我的bool属性正确实现INotifyPropertyChanged并绑定到CheckBox.IsChecked?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆