简体   繁体   中英

Trying to do two way binding INotifyPropertyChanged null

What's supposed to happen is the checkbox I have will flicker back and forth due to a timer. The variable changes, but the form does not. The name of the dataMember is Checked on both sides.

Class that's meant to be similar to implementation of INotifyPropertyChanged from here: Implementing INotifyPropertyChanged - does a better way exist?

using System;
using System.Collections.Generic;
using System.ComponentModel;

namespace Notice
{
    public class Notifier : INotifyPropertyChanged
    {
        //used to prevent infinite loops
        private bool controllerChanged;
        public event PropertyChangedEventHandler PropertyChanged;
        public Notifier()
        {
            controllerChanged = false;
        }
        public bool SetField<T>(ref T field, T value, string propertyName)
        {
            if (!controllerChanged)
            {
                controllerChanged = true;
                if (EqualityComparer<T>.Default.Equals(field, value))
                {
                    controllerChanged = false;
                    return false;
                }
                field = value;
                OnPropertyChanged(propertyName);
                controllerChanged = false;
                return true;

            }
            return false;
        }

        protected virtual void OnPropertyChanged(string property)
        {
            //PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));

            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(property));
            }

        }

    }
}

Class variable, just one for the example

using System;
using Notice;

namespace Vars
{
    public class Checks
    {

        private bool checkbox;

        Notifier note = new Notifier();
        public Checks()
        {
            checkbox = true;

        }

        public bool Checkbox 
        { 
            get { return checkbox; }
            //set { WindowsFormsApp1.Program.note.SetField(ref checkbox, value, "Checkbox"); }
            set { note.SetField(ref checkbox, value, "Checkbox"); }
        }


    }
}

Most of the form code:

public Form1()
        {
            InitializeComponent();
            //If you bind it using properties for the item in the form, don't need this line
            checkBox1.DataBindings.Add("Checked", WindowsFormsApp1.Program.Ch, "Checkbox", true, DataSourceUpdateMode.OnPropertyChanged);
        }

        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            WindowsFormsApp1.Program.Ch.Checkbox = !WindowsFormsApp1.Program.Ch.Checkbox;
            Console.WriteLine(WindowsFormsApp1.Program.Ch.Checkbox);
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            WindowsFormsApp1.Program.Ch.Checkbox = !WindowsFormsApp1.Program.Ch.Checkbox;
            Console.WriteLine(WindowsFormsApp1.Program.Ch.Checkbox);
        }

While the phrase "prefer composition over inheritance" comes up a lot, you've taken it too far here.

The INotifyPropertyChanged interface relies on the sender for the event being the same object where the property itself exists. In your implementation, you've delegated the event to an object that is not exposed to the public at all, and even if the XAML could receive notifications from that object, that's not the object where the property exists.

You should:

  1. Make Checks inherit the Notifier class instead of composing one
  2. Make the SetField() method protected instead of public , and call it from the property setter directly, rather than going through an intermediate instance of Notifier

For what it's worth, one would not normally require the controllerChanged functionality you've added to the canonical implementation. Recursion doesn't happen, because no notification is given when the value being set is the same as the current value. If you have a situation where you're running into recursion, you're probably doing something else incorrect, such as trying to toggle a value in response to notification. Short-circuiting the recursion avoids the stack overflow in that case, but hides the real bug in your code that was causing it.

I'd advise removing the controllerChanged functionality as well. If you have trouble with the code without it, post a new question, make sure you include a good [mcve] that reliably reproduces the stack overflow, explain what you've already tried in order to fix it, and what specifically you need help with.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM