开发者

IObservable<T> and INotifyPropertyChanged - is there a connection

I understand the IObservable<T> & IObserver<T> are implementations of the obser开发者_运维百科ver pattern and can be used in similar circumstances to .Net events.

I was wondering if there is any relationship to INotifyPropertyChanged?

I currently use INotifyPropertyChanged for data binding in winforms & WPF applications and was wondering if I'll be able to use IObservable in UI data binding scenarios?

Cheers

AWC


From what I can gather, there is no relationship. Observers/.NET eventing are two ways of achieving the Observer/Notification style behavior.

Microsoft's answer has been to build on top of the .NET eventing pattern, not deprecate it in favor of manually registered Observer objects.

One of my biggest peeves with events is the inability to clear the delegate chain on demand, which leads to quite a few managed-memory leak scenarios. To this end, Microsoft introduced the concept of weak events, that is, to address the issue of mismatched timelines for Observables/Observers.

You can read more about the WeakEvent pattern here.

Josh Smith has released an implmentation of the WeakEventManager for INotifyPropertyChanged here. This provides a safer (from a memory-standpoint) way of hooking up objects which change properties, and their Observers.


Unless WinForms and WPF bindings also support IObservable, it won't help to keep the UI updated with changes in the model. The reason that you can use INotifyPropertyChanged is because the bindings code in WinForms and WPF looks for this interface, and when implemented uses its event to keep the UI up to date.


First off, I'm a bit new to Rx, so take my comments accordingly.

That said, I think that there is a great opportunity for cooperation between INotifyPropertyChanged and Rx's IObservable. I think it's relatively obvious that the UI is built around INPC at this point. However, INPC is also a primary way of detecting changes and managing scenarios where a domain model or view model has interdependencies between objects and properties. It is these interdependencies that seem like good candidates for Rx.

Working with INPC directly is a bit tricky and somewhat painful. Lots of magic strings to deal with. It is also a bit painful to watch for an event on an object multiple levels away in the object tree.

But if I can model these interactions "reactively", then my view models and domain models start to feel a bit more elegant. This is evident in the elegance of projects like Bindable Linq, Continuous Linq, Obtics, etc. These libraries make it simple to create "live values" or "live collections" that update automatically (dare I say "reactively") to changes. Continuous Linq even has a "reactive object" framework to do reactive programming, albeit without Rx.

It seems to me that the synergies come in if we can use Rx to keep the model and view model coherent. Then we can make the "bindable surface" of the model / view model honor the INPC by continuing to raise PropertyChanged as needed. I've seen a couple elegant extension methods that will create an observable from INotifyPropertyChanged. It seems that the other half of this might be to create some infrastructure that translates from Rx back to INPC.


If you mean IObserver/IObservable as defined by the Rx extensions:

http://channel9.msdn.com/shows/Going+Deep/Kim-Hamilton-and-Wes-Dyer-Inside-NET-Rx-and-IObservableIObserver-in-the-BCL-VS-2010/

and:

http://themechanicalbride.blogspot.com/2009/07/introducing-rx-linq-to-events.html

Then its like apples and oranges.

INotifyPropertyChanged just provides a common event hookup for databinding/etc to let controls know when to update their bound values.

IObservable/IObserver is more like "querying with sequences of events", but even that is a poor description.

Hmm...ok, so you know how you can put stuff in this "bag" called a collection, and then query that collection (manually or with a LINQ statement) to pull out the values, right? It's kind of like that, but instead of "pulling" data out of a "bag", you are getting events "pushed" to you.

Shameless plug that might help or further confuse: http://blog.lab49.com/archives/3252


Often the most straightforward way to present IObservable<T> with MVVM is to make a conventional view-model object like one below and manually subscribe it to the observable. The subscription should be performed using .ObserveOn(SynchronizationContext.Current) to dispatch all the notifications in the UI thread. In turn, the synchronization context should exist at that moment (SynchronizationContext.Current is null before new Application().Run(mainWindow)). See sample below:

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

The view:

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

The caller method:

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

Update

Another option is to use ReactiveUI.WPF and even more concise - ReactiveUI.Fody to generate auto-properties in the view-model.

View-model:

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

Here ObserveOnDispatcher() call works even when the dispatcher has not been started and SynchronizationContext.Current is null. The caller method:

[STAThread]
static void Main()
{
    new Application().Run(
        new MainWindow
        {
            DataContext = new ViewModel(
                Observable.Timer(
                    TimeSpan.Zero,
                    TimeSpan.FromSeconds(1))
                .Take(20))
        });
}

FodyWeavers.xml near the solution file:

<?xml version="1.0" encoding="utf-8" ?>
<Weavers>
    <ReactiveUI />
</Weavers>


I agree with micahtan's and NathanAW's answers. I want just summarize and add. In the strict sense, there is no direct connection beetwen IObservable and INotifyPropertyChanged. Rx is abstracted from event specific: it is framework for processing any types of events. RX handles all types of events in the same way and all specifics are only in user code. If you want to get benefit exactly from INotifyPropertyChanged and INotifyCollectionChanged in your WPF (Xamarin, Blazor) expearence take a look on my ObservableComputations library. This library is a production-ready analogue of the Bindable Linq, Continuous Linq, Optics libraries that NathanAW advised.These libraries can be used themselves or cooperated with Rx. In that sence there is connection beetwen Rx and INotifyPropertyChanged, but again, the same connection exists beetwen Rx and any other event.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜