ElementFlow is now a Panel !!!
The ElementFlow control was something I was working on a while back and I even posted about it here. The control originally derived from a FrameworkElement and had its own properties for enabling binding to a data-source, namely ElementsSource and ElementTemplate. These properties behave similar to the ItemsSource and ItemTemplate of ItemsControl.
One could argue that instead of deriving from FrameworkElement, I should derive from ItemsControl. That way I would get the Databinding support free of cost. But making ElementFlow an ItemsControl means that I could use any Panel as my ItemsPanel. This ofcourse is NOT true. ElementFlow is primarily meant to visualize items in 3D. This means I would be using Viewport3D and GeometryModels to represent my items. Using any other kind of panel doesn’t really work well and is simply not correct. Also I think it violates the Liskov Substitution Principle (LSP).
However, ItemsControl does take care of data-binding by automatically generating the ContentPresenters for the items in the data-source. That idea led to a philosophical debate with myself, where I was arguing (with myself) that ElementFlow should really be handling layout of the items in 3D and should not worry about the data-source and deciphering DataTemplates. Its primary job is really to layout items in 3D.
The component in WPF that does pure layout is a Panel. Making ElementFlow a Panel was “definitely” the way to go.
Deriving directly from Panel required me to make couple different changes to the control and I also ran into a few issues along the way:
- Previously I was handling the part where I would generate the ContentPresenters from ElementsSource and ElementTemplate. That all had to go away, since none of it is needed any more.
- Next I had to hook into the stage where the Panel makes a change to its visual-children collection. This can be done by overriding
1 protected override void OnVisualChildrenChanged(DependencyObject visualAdded, 2 DependencyObject visualRemoved) 3
- I also ran into an issue when setting ElementFlow as the ItemsPanel inside an ItemsControl. It had to do with the way ItemsPresenter uses the ItemsPanel property of ItemsControl. ItemsPresenter requires that the count of the visual children of the Panel should be 0 at the beginning (ie before populating the Panel). However inside my VisualChildrenCount override, I was always returning “1”, indicating that I only have one visual child, which is my Viewport3D. Obviously I am not supposed to return “1” all the time. To circumvent this issue I call into the base-class to get me the VisualChildrenCount. If the count is zero, I return zero, else I return one.
Once past those issues, I had ElementFlow working in collaboration with ItemsControl. I can now get all my UI elements from the ItemsControl and not have to worry about data-binding and its related issues (children add/remove/update). This also saves me lot of development and maintenance time, as I rely more on the framework for things its really good at, namely DataBinding!
Note that ElementFlow also works independently of ItemsControl, just like any other Panel :)