Quantcast
Channel: Windows Presentation Foundation (WPF) forum
Viewing all articles
Browse latest Browse all 18858

Artificial inheritance context for 3D list

$
0
0

My goal is to build a list (inherited from "ItemsControl"), which shows its data in 3D (using a proper "Viewport3D" element). The list should be ideally as flexible as an ordinary "ItemsControl".

I created "ItemsControl3d" class, base on code from pelebyte.net. It demands a template part of type "ModelVisual3D", uses a dummy panel and adds panel's children (to be precise, their "Model" property) to that template part. Looks very much like hacking, but works.

Now the children are expected to be of type "Template3d", which is a custom type, inherited from Control and containing Model property (marked as content property) of type "ModelVisual3D". So a data template for such list looks like:

<DataTemplate x:Key="GrowingCylinderItem" DataType="res:Item"><res:Template3d><res:Model3dCylinder.../></res:Template3d></DataTemplate>

So far so good. But now I need to bind properties inside that Cylinder to (1) DataContext and (2) parent control.
Apparently, all Visual3D elements lack DataContext concept entirely. There is no property to set. And, when not in the logical tree (as is the case here), RelativeParent bindings do not work either.

I hacked it by using DataContextSpy and ElementSpy, which need to be specified in Template3d.Resources:

<res:Template3d.Resources><res:DataContextSpy x:Key="dc"/><res:ElementSpy x:Key="color" Element="{Binding RelativeSource={RelativeSource AncestorType={x:Type ctr:ColorStateContainer}}}"/></res:Template3d.Resources>

Both spies are derived from "Freezable", which injects them into the logical tree.
Then the bindings in the model look like this:

<res:Model3dCylinder.StartPoint><MultiBinding Converter="{StaticResource Point3dConverter}"><Binding Path="DataContext.Location.Y" Source="{StaticResource dc}"/>
...<DiffuseMaterial Brush="{Binding Element.ActualColor, Source={StaticResource color}, Converter={StaticResource ColorConverter}}"/>

But I don't think this is very user-friendly. The additional "Source" properties and "DataContext" / "Element" path elements are disturbing.

So I tried to inject the created Model into the logical tree by using the inheritance context hack inside Template3d:

Private Shared Sub ModelChanged(obj As DependencyObject, args As DependencyPropertyChangedEventArgs)
  Dim this = DirectCast(obj, Template3d)
  Dim model = DirectCast(args.NewValue, ModelVisual3D)
  Dim methSet = GetType(DependencyObject).GetMethod("SynchronizeInheritanceParent", BindingFlags.NonPublic Or BindingFlags.Instance)
  methSet.Invoke(model, New Object() {this})
End Sub

Apparently, this didn't help. I found, that Visual3D has its own InheritanceContext read-only property, but I'm not sure how to use it. Ideally, I'd like to get such bindings without any additional resources, just like in a 2D DataTemplate:

<res:Model3dCylinder.StartPoint><MultiBinding Converter="{StaticResource Point3dConverter}"><Binding Path="Location.Y"/>
...<DiffuseMaterial Brush="{Binding ActualColor, RelativeSource={RelativeSource AncestorType=...}, Converter={StaticResource ColorConverter}"/>
Is this at all possible?
I guess I'm asking the Microsoft guys, who know the internals of the framework and can find a way around its limitations... Fellows?..

Mikhail


Viewing all articles
Browse latest Browse all 18858

Trending Articles