3

I need to subscribe to a property of an object in a list. I found this example (https://stackoverflow.com/a/18770397/3954928), which works perfect, but if I add a new element to the list, it does not work. Any solution? Thank you!

EDIT

IDisposable subscription =
    Observable
        .FromEventPattern
            <NotifyCollectionChangedEventHandler, NotifyCollectionChangedEventArgs>(
                x => MyList.CollectionChanged += x,
                x => MyList.CollectionChanged -= x)
        .Where(x => x.EventArgs.Action == NotifyCollectionChangedAction.Add)
        .SelectMany(x => x.EventArgs.NewItems.Cast<MyCustomClass>())
        .SelectMany(x =>
        {
            CallMethodWhenAddItem(x);
            return x.OnPropertyChange(nameof(x.MyCustomProperty));
        })
        .Subscribe(x =>
            // x is PropertyChangedEventArgs, not MyCustomClass
            if (x.MyCustomProperty == "SomeValue") {
                RunAction();
            }
        });


public static IObservable<PropertyChangedEventArgs> OnPropertyChange<T>(this T currentSource, string propertyName)
    where T : INotifyPropertyChanged
{
    return
        Observable
            .FromEventPattern
                <PropertyChangedEventHandler, PropertyChangedEventArgs>(
                    eventHandler => eventHandler.Invoke,
                    x => currentSource.PropertyChanged += x,
                    x => currentSource.PropertyChanged -= x)
            .Where(x => x.EventArgs.PropertyName == propertyName)
            .Select(x => x.EventArgs);
}

Could you guide me a little with the following questions?

1) What is the difference of using "eventHandler => eventHandler.Invoke" and not using it. Many examples on the internet use it and others do not. And I really do not see the difference.

2) How I unsubscribe from a property that was added "dynamically". Just remove it from the list?

Thx!

avechuche
  • 1,470
  • 6
  • 28
  • 45

1 Answers1

1

The System.Func<EventHandler<TEventArgs>, TDelegate> parameter is used to convert the given event handler to a delegate compatible with the underlying .NET event. If it is not needed - don't use it. It's really used to wire up non-standard event handlers.

You should never do nested subscriptions. That way you never need to worry about disposing the inner subscription. Write your query like this:

IDisposable subscription =
    Observable
        .FromEventPattern
            <NotifyCollectionChangedEventHandler, NotifyCollectionChangedEventArgs>(
                x => MyList.CollectionChanged += x,
                x => MyList.CollectionChanged -= x)
        .Where(x => x.EventArgs.Action == NotifyCollectionChangedAction.Add)
        .SelectMany(x => x.EventArgs.NewItems.Cast<MyCustomClass>())
        .SelectMany(x =>
        {
            CallMethodWhenAddItem(x);
            return x.OnPropertyChange(nameof(x.MyCustomProperty));
        })
        .Subscribe(_ => { });

Updated based on comments:

void Main()
{
    var MyList = new ObservableCollection<int>();

    IDisposable subscription =
        Observable
            .FromEventPattern
                <NotifyCollectionChangedEventHandler, NotifyCollectionChangedEventArgs>(
                    x => MyList.CollectionChanged += x,
                    x => MyList.CollectionChanged -= x)
            .Where(x => x.EventArgs.Action == NotifyCollectionChangedAction.Add)
            .SelectMany(x => x.EventArgs.NewItems.Cast<MyCustomClass>())
            .SelectMany(x =>
            {
                CallMethodWhenAddItem(x);
                return x.OnPropertyChange(nameof(x.MyCustomProperty));
            })
            .Where(x => x.MyCustomProperty == "SomeValue")
            .Subscribe(_ => { });
}

public static class Ex
{
    public static IObservable<T> OnPropertyChange<T>(this T currentSource, string propertyName)
        where T : INotifyPropertyChanged
    {
        return
            Observable
                .FromEventPattern
                    <PropertyChangedEventHandler, PropertyChangedEventArgs>(
                        eventHandler => eventHandler.Invoke,
                        x => currentSource.PropertyChanged += x,
                        x => currentSource.PropertyChanged -= x)
                .Where(x => x.EventArgs.PropertyName == propertyName)
                .Select(x => currentSource);
    }
}

public class MyCustomClass : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    public string  MyCustomProperty { get; set; }
}

public void CallMethodWhenAddItem(MyCustomClass x) { }
Enigmativity
  • 113,464
  • 11
  • 89
  • 172
  • Thanks for the help. I have a question about this implementation. In the example that I wrote, there is something missing that I did not think was relevant. When an element is added to the collection, I make a call to a method (I modified the example). What would be the correct implementation with this change? Thank you! – avechuche Feb 26 '19 at 02:08
  • @avechuche - It's really not very different to your existing code. I've updated my answer. – Enigmativity Feb 26 '19 at 05:45
  • @avechuche - Apart from starting the observable, what's the point of doing `.Subscribe(_ => { })`? i.e. throwing away the value? – Enigmativity Feb 26 '19 at 11:35
  • I found the problem that I think you think. In the subscription I need to have access to the object which changed the property. The variable X in my original implementation, was the complete object (MyCustomClass). With this example, the subscription variable is typeof "PropertyChangedEventArgs". I edited the example for a better understanding. – avechuche Feb 26 '19 at 12:01
  • I think I tried to modify his answer instead of my example. Did not pay attention :) – avechuche Feb 26 '19 at 12:41
  • @avechuche - You're just saying you need to change the return type of `OnPropertyChange` and to add a `.Where` clause to the query, right? – Enigmativity Feb 26 '19 at 21:04
  • Exactly, within the subscription I need to have access to the object that has the property that was modified. – avechuche Feb 26 '19 at 21:28
  • @avechuche - That's trivial change. Can you do it? – Enigmativity Feb 27 '19 at 05:05
  • I could not do it :( I have found an answer from you with a possible solution, but I could not implement it (https://stackoverflow.com/a/11254562/3954928) .. I am a newbie in Subscriptions – avechuche Mar 01 '19 at 14:42
  • Thanks for your time and help! – avechuche Mar 02 '19 at 14:34