Click or drag to resize
Page 6: React to Changes

This topic contains the following sections:

Property Changes

All element interfaces where properties may be changed by the provider (IElement, IParameter, INode) derive from INotifyPropertyChanged. Their PropertyChanged event occurs whenever a provider change has been applied.

The PropertyChanged event also occurs when IParameterValue (or one of its strongly typed variants) is modified locally.

In a generic application like e.g. a viewer it would be straightforward to subscribe to PropertyChanged. In the code below however, for demonstration purposes we're going to subscribe to event of a specific parameter. As mentioned earlier, the dynamic interface is not very well suited for tasks that make assumptions about the contents of the database:

C#
AsyncPump.Run(
    async () =>
    {
        using (var client = await ConnectAsync("localhost", 9000))
        using (var consumer = await Consumer<MyRoot>.CreateAsync(client))
        {
            INode root = consumer.Root;

            // Navigate to the parameter we're interested in.
            var sapphire = (INode)root.Children.First(c => c.Identifier == "Sapphire");
            var sources = (INode)sapphire.Children.First(c => c.Identifier == "Sources");
            var fpgm1 = (INode)sources.Children.First(c => c.Identifier == "FPGM 1");
            var fader = (INode)fpgm1.Children.First(c => c.Identifier == "Fader");
            var positionParameter = fader.Children.First(c => c.Identifier == "Position");

            var valueChanged = new TaskCompletionSource<string>();
            positionParameter.PropertyChanged += (s, e) => valueChanged.SetResult(((IElement)s).GetPath());
            Console.WriteLine("Waiting for the parameter to change...");
            Console.WriteLine("A value of the element with the path {0} has been changed.", await valueChanged.Task);
        }
    });

Specifically, the code above has the following problems:

  • No attempt is made to handle exceptions that might result from incorrect assumptions. Such exceptions could be thrown when an expected element is not present (e.g. the parameter is named Pos rather than Position), the actual element has a different type (e.g. Position is of type INode rather than IParameter) or Position is really a double rather than a long. Robust code would have to handle these exceptions which would make the process even more tedious than it already is.

  • The interface offers no way of getting an element by name. The code above has to use LINQ to search for the desired elements, which is cumbersome and inefficient.

We will see later how the static interface is a much better fit for this scenario.

We can test this by running the above program and then modfiying a value of the parameter in Tiny Ember+.

Collection Changes

All exposed collections implement the INotifyCollectionChanged interface.

Note Note

Throughout its lifetime, a consumer automatically sends getDirectory commands to retrieve children client code has declared interest in. This is done recursively down to leaf elements and new nodes are only announced through INotifyCollectionChanged once all children (including grandchildren, great-grandchildren, etc.) have been received from the provider.

Proceed to Page 7: Send Local Changes to the Provider.