powered by simpleCommunicator - 2.0.51     © 2025 Programmizd 02
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / OnPropertyChanged
25 сообщений из 31, страница 1 из 2
OnPropertyChanged
    #39762860
256k
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Как прикрутить в WinForms приложении INotifyPropertyChanged и реакцию на OnPropertyChanged?

Вот МС пример:
https://docs.microsoft.com/en-us/dotnet/framework/wpf/data/how-to-implement-property-change-notification

Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
using System.ComponentModel;

namespace SDKSample
{
  // This class implements INotifyPropertyChanged
  // to support one-way and two-way bindings
  // (such that the UI element updates when the source
  // has been changed dynamically)
  public class Person : INotifyPropertyChanged
  {
      private string name;
      // Declare the event
      public event PropertyChangedEventHandler PropertyChanged;

      public Person()
      {
      }

      public Person(string value)
      {
          this.name = value;
      }
      
      public string PersonName
      {
          get { return name; }
          set
          {
              name = value;
              // Call OnPropertyChanged whenever the property is updated
              OnPropertyChanged("PersonName");
          }
      }

      // Create the OnPropertyChanged method to raise the event
      protected void OnPropertyChanged(string name)
      {
          PropertyChangedEventHandler handler = PropertyChanged;
          if (handler != null)
          {
              handler(this, new PropertyChangedEventArgs(name));
          }
      }
  }
}



Этот пример понятен, но здесь обработчик события в том же классе, а мне надо, чтобы при изменении значения переменной в одном объекте реагировали объекты другого класса. Как такое прикрутить?
...
Рейтинг: 0 / 0
OnPropertyChanged
    #39762861
Roman Mejtes
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Объект который зависит от Person подпишется на его событие и когда событие будет возбуждено выполнится тот метод, который вы добавили к делегату события.
...
Рейтинг: 0 / 0
OnPropertyChanged
    #39762888
256k
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Roman MejtesОбъект который зависит от Person подпишется на его событие и когда событие будет возбуждено выполнится тот метод, который вы добавили к делегату события.

ок, это как реализовать:
"Объект который зависит от Person подпишется на его событие" ?
(в уже существующем приложении)


Есть объект другого класса, как сделать его зависимым от Person?

Реально у меня в приложении есть класс апп сеттингов, и когда юзер менятем значение какой-то настройки надо чтобы код в другом объекте понимал, что есть изменения и свежее значение перечитывалось
...
Рейтинг: 0 / 0
OnPropertyChanged
    #39762900
Фотография Konst_One
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Код: c#
1.
_obj.UserEvent += eventWorker;
...
Рейтинг: 0 / 0
OnPropertyChanged
    #39762902
Фотография Konst_One
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
События C# по-человечески:
https://habr.com/ru/post/213809/
...
Рейтинг: 0 / 0
OnPropertyChanged
    #39762907
Фотография Shocker.Pro
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
256kнадо чтобы код в другом объекте понималНу так этот другой объект и должен подписываться на события в классе аппсеттингов
...
Рейтинг: 0 / 0
OnPropertyChanged
    #39762909
Фотография Shocker.Pro
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Shocker.ProНу так этот другой объект и должен подписываться на события в классе аппсеттинговТолько надо не забывать отписываться при уничтожении этого "другого объекта", иначе будет утечка памяти. Ну или использовать подходы WeakEvents
...
Рейтинг: 0 / 0
OnPropertyChanged
    #39762959
256k
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Shocker.Pro256kнадо чтобы код в другом объекте понималНу так этот другой объект и должен подписываться на события в классе аппсеттингов


Не совсем понятно зачем в примере выше нужно выводиться из INotifyPropertyChanged, можно и без него создать событие и подписаться на него извне.
...
Рейтинг: 0 / 0
OnPropertyChanged
    #39762978
Roman Mejtes
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
главное помнить один момент

когда объект с коротким сроком жизни (объекта А) подписывается на объект срок жизни которого длительный (объект B)
К примеру, ваш конфиг, скорее всего статический, по этому все порожденные экземпляры объектов связанные с конфигурацией будут иметь срок жизни, пока ваш процесс выполняется.
Подписавшись на событие объекта (B) из объекта (А) в экземпляр делегата объекта (B), сохранится указатель на объект (A).
Следовательно, если ваша ссылка на объект (А) будет удалена (вами), она всё еще будет в экземпляре делегата класса (B) и GC не удалит этот объект (A) из памяти. Если таких объектов будет 100500 и они будут то загружаться, то "удаляться", то количество этих объектов будет увеличиваться, а скорость обратного вызова через делегат деградировать.
Возникнет "утечка" памяти, так как объект вам уже не нужен, но ссылка на него всё еще существует. Более того вызов события будет приводить к выполнению этого события в класс (А).

То есть когда мы из объекта (А) подписываемся на событие объекта (B), мы просто сохраняем в поле типа делегат объекта (B) ссылку на метод который необходимо запустить и ссылку на сам этот объект (чтоб можно было передать аргумент this).
...
Рейтинг: 0 / 0
OnPropertyChanged
    #39762986
256k
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Roman Mejtesглавное помнить один момент

когда объект с коротким сроком жизни (объекта А) подписывается на объект срок жизни которого длительный (объект B)
К примеру, ваш конфиг, скорее всего статический, по этому все порожденные экземпляры объектов связанные с конфигурацией будут иметь срок жизни, пока ваш процесс выполняется.
Подписавшись на событие объекта (B) из объекта (А) в экземпляр делегата объекта (B), сохранится указатель на объект (A).
Следовательно, если ваша ссылка на объект (А) будет удалена (вами), она всё еще будет в экземпляре делегата класса (B) и GC не удалит этот объект (A) из памяти. Если таких объектов будет 100500 и они будут то загружаться, то "удаляться", то количество этих объектов будет увеличиваться, а скорость обратного вызова через делегат деградировать.
Возникнет "утечка" памяти, так как объект вам уже не нужен, но ссылка на него всё еще существует. Более того вызов события будет приводить к выполнению этого события в класс (А).

То есть когда мы из объекта (А) подписываемся на событие объекта (B), мы просто сохраняем в поле типа делегат объекта (B) ссылку на метод который необходимо запустить и ссылку на сам этот объект (чтоб можно было передать аргумент this).

Лайкнул, спасибо
...
Рейтинг: 0 / 0
OnPropertyChanged
    #39762990
Фотография Shocker.Pro
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
256kНе совсем понятно зачем в примере выше нужно выводиться из INotifyPropertyChanged, можно и без него создать событие и подписаться на него извне.Хм. Перепроверил автора топика - так это ж ты и предложил INotifyPropertyChanged.

Просто INotifyPropertyChanged - это паттерн. Если он тебе не нужен - ну значит не нужен. Смысл есть, когда один обработчик обрабатывает много разных событий многих разнотипных объектов.
...
Рейтинг: 0 / 0
OnPropertyChanged
    #39762993
Фотография Petro123
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
256kINotifyPropertyChangedэто событие для биндинга обычно.
А для своих дел можно использовать хоть MyEvent.
...
Рейтинг: 0 / 0
OnPropertyChanged
    #39762995
Фотография Petro123
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Roman Mejtes,
Если коротко, то нужно отписываться, т.к.сборщик мусора это не умеет.
Что отписываться выше говорилось.
...
Рейтинг: 0 / 0
OnPropertyChanged
    #39763007
Фотография Shocker.Pro
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Лично я сделал себе централизованный уведомитель для подобных целей, уведомления уровня приложения. Уведомитель-синглтон принимает от желающих подписки на определенные события, другие желающие могут их возбуждать. Соответственно, реализовано через слабосвязанные события, так что отписка не требуется
...
Рейтинг: 0 / 0
OnPropertyChanged
    #39763010
Фотография Petro123
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Shocker.ProУведомитель-синглтона если в главной форме? И достать всегда можно.
...
Рейтинг: 0 / 0
OnPropertyChanged
    #39763011
Фотография Shocker.Pro
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Petro123а если в главной форме? И достать всегда можно.да без разницы
...
Рейтинг: 0 / 0
OnPropertyChanged
    #39763033
256k
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Shocker.Pro256kНе совсем понятно зачем в примере выше нужно выводиться из INotifyPropertyChanged, можно и без него создать событие и подписаться на него извне.Хм. Перепроверил автора топика - так это ж ты и предложил INotifyPropertyChanged.

Просто INotifyPropertyChanged - это паттерн. Если он тебе не нужен - ну значит не нужен. Смысл есть, когда один обработчик обрабатывает много разных событий многих разнотипных объектов.

Да не я, это у майкрософта написано
...
Рейтинг: 0 / 0
OnPropertyChanged
    #39763034
256k
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Petro123256kINotifyPropertyChangedэто событие для биндинга обычно.
А для своих дел можно использовать хоть MyEvent.

Спасибо, Пэдро, теперь понял
...
Рейтинг: 0 / 0
OnPropertyChanged
    #39763038
Фотография LR
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
256kНе совсем понятно зачем в примере выше нужно выводиться из INotifyPropertyChanged, можно и без него создать событие и подписаться на него извне.
256k
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
using System.ComponentModel;

namespace SDKSample
{
  // This class implements INotifyPropertyChanged
  // to support one-way and two-way bindings
  // (such that the UI element updates when the source
  // has been changed dynamically)
  public class Person : INotifyPropertyChanged



Большинство стандартных контролов умеют "правильно реагировать" на соответствующее событие подвязанного источника данных (см. binding). Т.е., разработчику не нужно "вручную" разруливать событие из другого потока, выставлять свойства контрола в соответствие новому значению, отписываться от события и т.п. Типа, создал связь (binding) с источником данных - и забыл, примерно для этого.
...
Рейтинг: 0 / 0
OnPropertyChanged
    #39763374
256k
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Roman Mejtesглавное помнить один момент

когда объект с коротким сроком жизни (объекта А) подписывается на объект срок жизни которого длительный (объект B)
К примеру, ваш конфиг, скорее всего статический, по этому все порожденные экземпляры объектов связанные с конфигурацией будут иметь срок жизни, пока ваш процесс выполняется.
Подписавшись на событие объекта (B) из объекта (А) в экземпляр делегата объекта (B), сохранится указатель на объект (A).
Следовательно, если ваша ссылка на объект (А) будет удалена (вами), она всё еще будет в экземпляре делегата класса (B) и GC не удалит этот объект (A) из памяти. Если таких объектов будет 100500 и они будут то загружаться, то "удаляться", то количество этих объектов будет увеличиваться, а скорость обратного вызова через делегат деградировать.
Возникнет "утечка" памяти, так как объект вам уже не нужен, но ссылка на него всё еще существует. Более того вызов события будет приводить к выполнению этого события в класс (А).

То есть когда мы из объекта (А) подписываемся на событие объекта (B), мы просто сохраняем в поле типа делегат объекта (B) ссылку на метод который необходимо запустить и ссылку на сам этот объект (чтоб можно было передать аргумент this).

Кстати, а как бы отписать всех обработчиков? т.е. я могу не знать кто уже подписался и сколько подписчиков, но знаю, что пора "делать ноги".
...
Рейтинг: 0 / 0
OnPropertyChanged
    #39763387
Фотография Petro123
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
256k,
При закрытии приложения не надо отписывать.
...
Рейтинг: 0 / 0
OnPropertyChanged
    #39763396
Фотография Shocker.Pro
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
256kКстати, а как бы отписать всех обработчиков? т.е. я могу не знать кто уже подписался и сколько подписчиков, но знаю, что пора "делать ноги".Если предполагается уничтожить экземпляр класса, НА события которого кто-то подписался, то ничего страшного, он уничтожится, так как на него ссылок нет, это наоборот - он содержит ссылки на подписчиков.
...
Рейтинг: 0 / 0
OnPropertyChanged
    #39763469
Roman Mejtes
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
256k,

Для начала, стоит задать вопрос, что такое событие?
Событие по своей сути очень похоже на Свойство класса, это надстройка над полем класса, позволяющая добавить уровень абстракции.
Свойство реализуется 2 методами Get и Set над полем класса заданного типа.
Событие реализуется 2 методами Add и Remove, а в качество поля используется делегат заданного типа.
В обоих случая мы можем не определять реализацию методов свойства или события явно, за нас это делает компилятор.
по этому будет достаточно написать:
Код: c#
1.
public event MyDelegate MyEvent

Всё остальное за нас сделает компилятор, в результате его работы получится, нечто похожее на то, что написано ниже.
Но если реализация объявлена явно, мы можем добавить свой код, к примеру, код проверки значения свойства или проверки объекта который пытается подписаться на наше событие. По факту мы можем сделать так, что подписываясь на событие, объекту будет в этом отказано и ссылка которую он передает на метод и объект не будет добавлена в делегат, или будет добавлена сразу в 2, а возможно я создам таблицу, в которой буду хранить все объекты, которые добавлены в делегате. Вариантов море.
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
class MyClass
{
	public delegate void MyDelegate(object sender, object arg);

	private MyDelegate _myDelegate;

	public event MyDelegate MyEvent
	{
		add { _myDelegate += value; }
		remove { _myDelegate -= value; }
	}

	private object _myFields;
	public object MyProperty
	{
		set { _myFields = value; }
		get { return _myFields; }
	}
}


Поле типа делегат это объект ссылочный тип похожий на список, в нём хранятся ссылки на методы и если мы добавляем в делегат метод экземпляра объекта, ссылка на этот объект.
Удалив делегат, обнулив его, мы потеряем все указатели на эти объекты и методы, а через какое то время GC грохнет этот объект и все те объекты, на которые он ссылается, так как все они больше не имеют корневого объекта
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
class Program
{
	static void Main(string[] args)
	{
		//создаем класс PersistentObject и сохраняем его указатель в переменной old
		var old = new PersistentObject();
		//создаем 3 объекта TemporaryObject, но указатели на эти объекты мы не сохраняем
		//вместо этого передаем ссылку на объект в конструктор класс, где и совершится 
		//подписка насобытие объекта old
		new TemporaryObject(old);
		new TemporaryObject(old);
		new TemporaryObject(old);
		//так как мы не сохранили ссылки на наши объекты TemporaryObject, они должны существовать
		//до первого выполнения GC, но как мы видим в примере, этого не происходит, так как указатели
		//на эти объекте хранятся в делегате, а делегат хранится в поле объекта old
		Console.WriteLine("GC.Collect()");
		GC.Collect();
		//Возбудим событие класса old, это приведет к выполнению всех обработчкиов события MyEvent
		//которые подписались на обработку
		Console.WriteLine("Raise event");
		old.Raise();
		//Обнулим делегат, в результате чего все ссылки на TemporaryObject будут потеряны
		Console.WriteLine("Clear delegate");
		old.Clear();
		//Вызовем GC.Collect(), этот метод сообщит GC, пора собирать урожай, так как 
		//выше мы не сохраняли ссылки на объекты типа TemporaryObject, и в нашем делегате класса
		//PersistentObject этих ссылок теперь тоже нет, объекты будут уничтожены 
		Console.WriteLine("GC.Collect()");
		GC.Collect();
		//Поторный вызов события не приведет к вызову обработчиков, так делегат не содержит указатели
		Console.WriteLine("Raise event");
		old.Raise();
		//Нажмите любую кнопку
		Console.WriteLine("Press any key for exit");
		Console.ReadKey();
	}

	//класс постоянного объект, ссылка на который мы удалять не будем
	class PersistentObject
	{
		//объявим собственный тип делегата
		public delegate void MyDelegate(object sender, object arg);
		//инкапсулированный делегат
		private MyDelegate _myDelegate;
		//событие
		public event MyDelegate MyEvent
		{
			add
			{
				//Этот код выполняется, когда осуществляется подписка на наше событие
				_myDelegate += value;
				Console.WriteLine($"Object {value.Target} add handler {value.Method.Name}");
			}
			remove
			{
				//Этот код выполняется, когда осуществялется отписка от этого события
				_myDelegate -= value;
				Console.WriteLine($"Object {value.Target} remove handler {value.Method.Name}");
			}
		}

		//Данный мотед возбуждает событие MyEvent, для этого вызывет метод Invoke нашего делегата
		public void Raise() => _myDelegate?.Invoke(this, null);

		//Данный метод "обнуляет" наш делегат
		public void Clear() => _myDelegate = null;
	}

	class TemporaryObject
	{
		//Счетчик объектов, нужен только для того, чтоб инициализировать экзепляры объектов
		private static int _counter = 0;
		//Номер экземпляра объекта (просто для идентификации)
		private readonly int _index;

		public TemporaryObject(PersistentObject old)
		{
			_index = _counter++;
			//Подписываемся на событие PersistentObject.MyEvent
			old.MyEvent += MyEventHandler;
		}

		//Обработчик события PersistentObject.MyEvent
		private void MyEventHandler(object sender, object e)
		{
			Console.WriteLine($"Handler invoked for {this}");
		}

		public override string ToString()
		{
			return $"TempObject #{_index}";
		}

		//Добавим явную реализацию деструктора
		~TemporaryObject()
		{
			Console.WriteLine($"Finallized {this}");
		}
	}
}



Результат выполнения:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
bject TempObject #0 add handler MyEventHandler
Object TempObject #1 add handler MyEventHandler
Object TempObject #2 add handler MyEventHandler
GC.Collect()
Raise event
Handler invoked for TempObject #0
Handler invoked for TempObject #1
Handler invoked for TempObject #2
Clear delegate
GC.Collect()
Raise event
Press any key for exit
Finallized TempObject #2
Finallized TempObject #0
Finallized TempObject #1

Всё это довольно просто и весело, когда вы работаете в 1 потоке, но когда приложение многопоточное, наступает жопа, так как добавление обработчика события операция не атомарная (мягко говоря). По этому, в тот момент, когда объект подписывается или отписывается на событие, оно может быть возбуждено. Если объект отписывается, он может быть уже не готов его обработать, но ссылка в делегате всё еще существует, такая ситуация может привести к исключению или другим неприятным последствиям. Но это тема для другой темы.

если где я не прав, пусть меня поправят, я же не Троелсен :D
...
Рейтинг: 0 / 0
OnPropertyChanged
    #39763540
Фотография LR
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Roman MejtesВсё это довольно просто и весело, когда вы работаете в 1 потоке, но когда приложение многопоточное, наступает жопа, так как добавление обработчика события операция не атомарная (мягко говоря). По этому, в тот момент, когда объект подписывается или отписывается на событие, оно может быть возбуждено. Если объект отписывается, он может быть уже не готов его обработать, но ссылка в делегате всё еще существует, такая ситуация может привести к исключению или другим неприятным последствиям. Но это тема для другой темы.

если где я не прав, пусть меня поправят, я же не Троелсен :D
Эта проблема подписчика ("он может быть уже не готов его обработать"), по-моему, надуманна (сначала отпишись, затем отключай готовность обрабатывать). А вот "возбуждение" события действительно может быть проблемным. Такая конструкция (беспроблемная) возможна лишь начиная с C#6
Код: c#
1.
2.
		//Данный мотед возбуждает событие MyEvent, для этого вызывет метод Invoke нашего делегата
		public void Raise() => _myDelegate?.Invoke(this, null);


а вот такая конструкция очень даже проблемная (потоконезащищенная)
Код: c#
1.
2.
       if(_myDelegate != null)
            _myDelegate.Invoke(this, null);
...
Рейтинг: 0 / 0
OnPropertyChanged
    #39763563
Фотография LR
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
LRRoman MejtesВсё это довольно просто и весело, когда вы работаете в 1 потоке, но когда приложение многопоточное, наступает жопа, так как добавление обработчика события операция не атомарная (мягко говоря). По этому, в тот момент, когда объект подписывается или отписывается на событие, оно может быть возбуждено. Если объект отписывается, он может быть уже не готов его обработать, но ссылка в делегате всё еще существует, такая ситуация может привести к исключению или другим неприятным последствиям. Но это тема для другой темы.

если где я не прав, пусть меня поправят, я же не Троелсен :D
Эта проблема подписчика ("он может быть уже не готов его обработать"), по-моему, надуманна (сначала отпишись, затем отключай готовность обрабатывать).
Наверное, речь шла о проблеме #4 вот в этой статье https://www.codeproject.com/Articles/886223/Csharp-Multithreading-and-Events, тогда да, ее нужно решать...
...
Рейтинг: 0 / 0
25 сообщений из 31, страница 1 из 2
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / OnPropertyChanged
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]