Попытка сделать двустороннее связывание INotifyPropertyChanged null - PullRequest
1 голос
/ 27 марта 2020

То, что должно произойти, это флажок, который у меня будет мигать назад и вперед из-за таймера. Переменная меняется, а форма - нет. Имя dataMember проверено с обеих сторон.

Класс, который должен быть похож на реализацию INotifyPropertyChanged отсюда: Реализация INotifyPropertyChanged - существует ли лучший способ?

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

        }

    }
}

Переменная класса, только одна для пример

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


    }
}

Большая часть кода формы:

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

1 Ответ

0 голосов
/ 27 марта 2020

Хотя фраза «предпочесть композицию наследованию» встречается довольно часто, вы слишком далеко зашли здесь.

Интерфейс INotifyPropertyChanged использует sender для события, являющегося тем же объектом где сама собственность существует. В вашей реализации вы делегировали событие объекту, который вообще не доступен для публикации c, и даже если XAML мог получать уведомления от этого объекта, это не тот объект, где существует свойство.

Вы должны:

  1. Сделать Checks наследующим класс Notifier вместо того, чтобы составлять один
  2. Сделать метод SetField() protected вместо public, и вызывать его непосредственно из установщика свойств, а не проходить через промежуточный экземпляр Notifier

. Для чего бы это ни стоило, обычно не требуется функциональность controllerChanged, которую вы добавили в каноническая реализация. Рекурсия не происходит, потому что уведомление не выдается, когда устанавливаемое значение совпадает с текущим значением. Если у вас возникла ситуация, когда вы сталкиваетесь с рекурсией, вы, вероятно, делаете что-то еще неправильно, например, пытаетесь переключить значение в ответ на уведомление. Короткое замыкание рекурсии позволяет избежать переполнения стека в этом случае, но скрывает реальную ошибку в коде, которая его вызывала.

Я бы посоветовал также удалить функциональность controllerChanged. Если у вас возникли проблемы с кодом без него, опубликуйте новый вопрос, убедитесь, что вы включили хороший [mcve], который надежно воспроизводит переполнение стека, объясните, что вы уже пытались исправить, и что конкретно вам требуется помощь с.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...