IObservable <T>и INotifyPropertyChanged - есть ли связь - PullRequest
11 голосов
/ 29 января 2010

Я понимаю, что IObservable<T> & IObserver<T> являются реализациями шаблона наблюдателя и могут использоваться при обстоятельствах, аналогичных событиям .Net.

Мне было интересно, есть ли какое-либо отношение к INotifyPropertyChanged?

В настоящее время я использую INotifyPropertyChanged для привязки данных в приложениях winforms и WPF, и мне было интересно, смогу ли я использовать IObservable в сценариях привязки данных пользовательского интерфейса?

Приветствия

AWC

Ответы [ 5 ]

5 голосов
/ 09 февраля 2010

Из того, что я могу собрать, нет никаких отношений. Наблюдатели / .NET Eventing - это два способа достижения поведения в стиле Observer / Notification.

Ответ Microsoft состоял в том, чтобы построить поверх шаблона событий .NET, а не использовать его в пользу зарегистрированных вручную объектов Observer.

Одна из моих самых больших проблем с событиями - это невозможность очистить цепочку делегатов по требованию, что приводит к нескольким сценариям утечки управляемой памяти. С этой целью Microsoft представила концепцию слабых событий, то есть для решения проблемы несоответствующих сроков для наблюдаемых / наблюдателей.

Подробнее о шаблоне WeakEvent можно прочитать здесь .

Джош Смит выпустил реализацию WeakEventManager для INotifyPropertyChanged здесь . Это обеспечивает более безопасный (с точки зрения памяти) способ подключения объектов, которые изменяют свойства, и их наблюдателей.

3 голосов
/ 29 января 2010

Если привязки WinForms и WPF также не поддерживают IObservable, обновление пользовательского интерфейса с изменениями в модели не поможет. Причина, по которой вы можете использовать INotifyPropertyChanged, заключается в том, что код привязок в WinForms и WPF ищет этот интерфейс, а при реализации использует свое событие для обновления пользовательского интерфейса.

2 голосов
/ 18 июня 2010

Прежде всего, я немного новичок в Rx, поэтому примите мои комментарии соответственно.

Тем не менее, я думаю, что есть прекрасная возможность для сотрудничества между INotifyPropertyChanged и Rx IObservable. Я думаю, что это относительно очевидно, что пользовательский интерфейс построен вокруг INPC на данный момент. Однако INPC также является основным способом обнаружения изменений и управления сценариями, в которых модель предметной области или модель представления имеют взаимозависимости между объектами и свойствами. Именно эти взаимозависимости кажутся хорошими кандидатами на прием.

Работа с INPC напрямую немного сложна и несколько болезненна. Множество волшебных струн для решения. Также немного больно наблюдать за событием на объекте, расположенном на нескольких уровнях в дереве объектов.

Но если я смогу смоделировать эти взаимодействия "реактивно", то мои модели представлений и модели предметной области начнут казаться более элегантными. Это проявляется в элегантности таких проектов, как Bindable Linq , Continuous Linq , Obtics и т. Д. Эти библиотеки упрощают создание "живых значений" или " Живые коллекции », которые автоматически обновляются (смею сказать« реагирующе ») на изменения. Непрерывный Linq даже имеет структуру "реактивного объекта" для реактивного программирования, хотя и без Rx.

Мне кажется, что синергизм наступит, если мы сможем использовать Rx для сохранения согласованности модели и представления модели. Затем мы можем сделать так, чтобы «привязываемая поверхность» модели модель / представление учитывала INPC, продолжая поднимать PropertyChanged по мере необходимости. Я видел пару элегантных методов расширения , которые будут создавать наблюдаемые из INotifyPropertyChanged. Похоже, что другая половина этого может заключаться в создании некоторой инфраструктуры, которая переводит с Rx обратно в INPC.

1 голос
/ 16 февраля 2010

Если вы имеете в виду 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

0 голосов
/ 31 декабря 2018

Часто самый простой способ представить IObservable<T> с MVVM - это создать обычный объект модели представления, подобный представленному ниже, и вручную подписать его на наблюдаемый. Подписка должна быть выполнена с использованием .ObserveOn(SynchronizationContext.Current) для отправки всех уведомлений в потоке пользовательского интерфейса. В свою очередь, контекст синхронизации должен существовать в этот момент (SynchronizationContext.Current равен нулю до 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 равен нулю. Метод вызова:

[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>
...