Я понимаю, что IObservable<T>
и IObserver<T>
являются реализациями шаблона наблюдателя и могут использоваться в ситуациях, аналогичных событиям .Net.
Мне было интересно, есть ли какое-либо отношение к INotifyPropertyChanged
?
В настоящее время я использую INotifyPropertyChanged
для привязки данных в приложениях winforms и WPF, и мне было интересно, смогу ли я использовать IObservable в сценариях привязки данных пользовательского интерфейса?
ура
AWC
Ответ 1
Из того, что я могу собрать, нет никаких отношений. Наблюдения/события .NET - это два способа достижения стиля стиля Observer/Notification.
Ответ Microsoft должен был быть построен поверх шаблона событий .NET, а не обесценивать его в пользу зарегистрированных вручную объектов Observer.
Один из моих самых больших моментов с событиями - неспособность очистить цепочку делегатов по требованию, что приводит к довольно большому количеству сценариев утечки с управляемой памятью. С этой целью Microsoft представила концепцию слабых событий, то есть решить проблему несогласованных сроков наблюдения/наблюдателей.
Подробнее о шаблоне WeakEvent здесь.
Джош Смит выпустил внедрение WeakEventManager для INotifyPropertyChanged здесь. Это обеспечивает более безопасный (с точки зрения памяти) способ подключения объектов, которые изменяют свойства, и их наблюдателей.
Ответ 2
Если WinForms и привязки WPF не поддерживают IObservable
, это не поможет обновить пользовательский интерфейс с изменениями в модели. Причина, по которой вы можете использовать INotifyPropertyChanged
, состоит в том, что код привязки в WinForms и WPF ищет этот интерфейс, а когда он реализован, использует свое событие, чтобы обновить интерфейс.
Ответ 3
Во-первых, я немного новичок в Rx, поэтому сделайте мои комментарии соответственно.
Тем не менее, я думаю, что есть прекрасная возможность для сотрудничества между INotifyPropertyChanged и Rx IObservable. Я думаю, что относительно очевидно, что пользовательский интерфейс построен вокруг INPC на данный момент. Тем не менее, INPC также является основным способом обнаружения изменений и управления сценариями, когда модель домена или модель представления имеют взаимозависимости между объектами и свойствами. Именно эти взаимозависимости кажутся хорошими кандидатами на Rx.
Работа с INPC напрямую немного сложна и несколько болезненна. Множество магических струн, с которыми нужно иметь дело. Также немного больно наблюдать за событием на объекте на нескольких уровнях в дереве объектов.
Но если я смогу моделировать эти взаимодействия "реактивно", то мои модели взглядов и модели домена начинают чувствовать себя немного более элегантными. Это видно из элегантности проектов, таких как Bindable Linq, Непрерывный Linq, Obtics и т.д. Эти библиотеки упрощают создание "живых значений" или "живых коллекций", которые автоматически обновляются (смею сказать "реактивно" ) на изменения. Непрерывный Linq даже имеет "реактивный объект" framework для выполнения реактивного программирования, хотя без Rx.
Мне кажется, что синергии возникают, если мы можем использовать Rx, чтобы модель и модель были согласованы. Затем мы можем сделать "связующую поверхность" модели/модели зрения честью INPC, продолжая при необходимости увеличивать PropertyChanged. Я видел пару изящных методов расширения, которые создадут видимый из INotifyPropertyChanged. Похоже, что другой половине этого может быть создание некоторой инфраструктуры, которая переводится с Rx обратно в INPC.
Ответ 4
Если вы имеете в виду IObserver/IObservable, как определено расширениями Rx:
http://channel9.msdn.com/shows/Going+Deep/Kim-Hamilton-and-Wes-Dyer-Inside-NET-Rx-and-IObservableIObserver-in-the-BCL-VS-2010/
и
http://themechanicalbride.blogspot.com/2009/07/introducing-rx-linq-to-events.html
Затем его, как яблоки и апельсины.
INotifyPropertyChanged просто предоставляет общую привязку к событиям для привязки данных /etc, чтобы позволить элементам управления знать, когда обновлять связанные значения.
IObservable/IObserver больше похож на "запрос с последовательностями событий", но даже это плохое описание.
Хм... хорошо, так что вы знаете, как вы можете поместить вещи в эту "сумку", называемую коллекцией, и затем запросить эту коллекцию (вручную или с помощью оператора LINQ), чтобы вытащить значения, правильно? Это вроде как, но вместо того, чтобы "вытаскивать" данные из "мешка", вы делаете события "толкаемыми" к вам.
Бесстыдный плагин, который может помочь или еще больше запутать:
http://blog.lab49.com/archives/3252
Ответ 5
Часто самый простой способ представить IObservable<T>
с MVVM - это создать обычный объект модели представления, подобный приведенному ниже, и вручную подписать его на наблюдаемый. Подписка должна быть выполнена с использованием .ObserveOn(SynchronizationContext.Current)
для отправки всех уведомлений в потоке пользовательского интерфейса. В свою очередь, контекст синхронизации должен существовать в этот момент (SynchronizationContext.Current
равен null перед new Application().Run(mainWindow)
). Смотрите образец ниже:
public class ViewModel : INotifyPropertyChanged
{
private int _property1;
public int Property1
{
get => _property1;
set
{
_property1 = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Property1)));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Вид:
<Window x:Class="ConsoleApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TextBox Text="{Binding Property1}" />
</Window>
Метод вызова:
[STAThread]
static void Main()
{
var model = Observable.Timer(TimeSpan.Zero, TimeSpan.FromSeconds(1)).Take(10);
var viewModel = new ViewModel();
var mainWindow = new MainWindow
{
DataContext = viewModel
};
IDisposable subscription = null;
mainWindow.Loaded += (sender, e) =>
subscription = model
.ObserveOn(SynchronizationContext.Current) // Dispatch all the events in the UI thread
.Subscribe(item => viewModel.Property1 = item);
new Application().Run(mainWindow);
subscription?.Dispose();
}
Обновить
Другой вариант - использовать ReactiveUI.WPF, а еще более лаконично - ReactiveUI.Fody для генерации авто-свойств в модели представления.
View-модель:
public class ViewModel : ReactiveObject, IDisposable
{
private readonly IDisposable subscription;
public ViewModel(IObservable<long> source)
{
subscription = source
.ObserveOnDispatcher()
.ToPropertyEx(this, x => x.Property1);
}
// To be weaved by Fody
public extern long Property1 { [ObservableAsProperty]get; }
// Unsubscribe from the source observable
public void Dispose() => subscription.Dispose();
}
Здесь ObserveOnDispatcher()
работает, даже если диспетчер не был запущен, а SynchronizationContext.Current
имеет значение null. Метод вызова:
[STAThread]
static void Main()
{
new Application().Run(
new MainWindow
{
DataContext = new ViewModel(
Observable.Timer(
TimeSpan.Zero,
TimeSpan.FromSeconds(1))
.Take(20))
});
}
FodyWeavers.xml рядом с файлом решения:
<?xml version="1.0" encoding="utf-8" ?>
<Weavers>
<ReactiveUI />
</Weavers>