arrow-left arrow-right brightness-2 chevron-left chevron-right circle-half-full dots-horizontal facebook-box facebook loader magnify menu-down RSS star Twitter twitter GitHub white-balance-sunny window-close
Observal
2 min read

Observal

This is an old post and doesn't necessarily reflect my current thinking on a topic, and some links or images may not work. The text is preserved here for posterity.

I just released some source on Google Code called Observal:

http://code.google.com/p/observal/

Observal was extracted from work on a recent WPF project. In our application, we had a deep hierarchy of view model objects, with some very complicated interrelationships - setting one property over here means adding or removing items from a collection over there - and since WPF applications are so stateful, we had to do it all reactively.

The project home page gives a simple example of how Observal might be used. I'll use the remainder of this post for a deeper example.

Example

Suppose we have an object model to represent an organization chart:

An employee class, with a collection of direct reports

We'll build a view to show and edit a hierarchy of employees, and provide a filter to show a list of items from the hierarchy:

A window with a treeview of employees, their details, and a list of employees with under $100,000 salary

Working with the hierarchy in WPF is easy - we just build a hierarchical object model and bind it to the tree view. We could build that view model using code like this:

public partial class OrgChartWindow : Window
{
    public OrgChartWindow()
    {
        InitializeComponent();

        var sampleEmployees =
            new Employee("Ryan Howard", 200000,
                new Employee("Michael Scott", 130000,
                    new Employee("Dwight Schrute", 80000),
                    new Employee("Jim Halpert", 80000,
                        new Employee("Andy Bernard", 75000,
                            new Employee("Stanley Hudson", 70000),
                            new Employee("Phyllis Lapin", 70000)))));

        DataContext = new OrgChartViewModel(new[] { sampleEmployees });
    }
}

That gives us the tree view, ability to add new employees and editing support. But how to we manage the list of employees earning under $100,000?

Enter Observal

The "Employees with salary < $100,000" panel is effectively a flattened view of the employee hierarchy. To build it, we'd need to subscribe to the CollectionChanged event on every employee's DirectReports collection, and to subscribe to the PropertyChanged event on every employee.

Observal makes this trivial. We can make the following addition to our view model:

public OrgChartViewModel(IEnumerable<Employee> employees)
{
    _rootEmployees = new ObservableCollection<Employee>(employees);

    var observer = new Observer();
    observer.Extend(new TraverseExtension()).Follow<Employee>(e => e.DirectReports);
    observer.Extend(new CollectionExpansionExtension());
    observer.Extend(new PropertyChangedExtension()).WhenPropertyChanges<Employee>(x => FilterEmployee(x.Source));
    observer.Extend(new ItemsChangedExtension()).WhenAdded<Employee>(FilterEmployee);
    observer.Add(_rootEmployees);
}

private void FilterEmployee(Employee employee)
{
    if (employee.Salary < 100000)
    {
        if (!FilteredEmployees.Contains(employee))
            FilteredEmployees.Add(employee);
    }
    else
    {
        FilteredEmployees.Remove(employee);
    }
}

The idea behind observal is that there is an Observer, which keeps a list of items being observed. Observers can accept IObserverExtensions, which are notified when items are added or removed. In the example above, we make use of four different extensions:

  • TraverseExtension - any time an employee is added to the collection, we'll add the DirectReports collection too.
  • CollectionExpansionExtension - when the DirectReports collection is added, we'll add all items in the collection to the observer.
  • PropertyChangedExtension - this is called any time a property on an existing object changes
  • ItemsChangedExtension - this notifies us whenever an item is added or removed

Each extension is useful by itself, but they become very powerful when combined together. In this example, we were able to monitor an entire hierarchy of objects, and to react whenever parts of the hierarchy changes. I'd urge you to check out Observal on Google Code and let me know what you think.

Paul Stovell's Blog

Hello, I'm Paul Stovell

I'm a Brisbane-based software developer, and founder of Octopus Deploy, a DevOps automation software company. This is my personal blog where I write about my journey with Octopus and software development.

I write new blog posts about once a month. Subscribe and I'll send you an email when I publish something new.

Subscribe

Comments