I have an ItemsControl
, which I use to draw two different sets of shapes on a canvas. As such, I have two ItemsSource
containing Edge objects and Node objects.
I have two different DataTemplates
for each type. However, I need to set the canvas positioning for the nodes, but not for the edges. There are abundant examples on the internet on how to do this with a single ItemsSource
, but not with multiple as in my case.
I have hacked it like this, but this throws a lot of binding errors in the output window (because only Nodes have a Position
property, not Edges, and thus this 'works'). Also, I want to set the ZIndex
for the Nodes and Edges separately, which is impossible in this way. Does anyone have any suggestions?
<ItemsControl>
<ItemsControl.ItemsSource>
<MultiBinding>
<MultiBinding.Converter>
<p:CompositeCollectionConverter/>
</MultiBinding.Converter>
<Binding Path="Graph.Nodes"/>
<Binding Path="Graph.Edges"/>
</MultiBinding>
</ItemsControl.ItemsSource>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type model:Edge}">
<Path
Stroke="Blue"
Data="{Binding Path=EdgeSegments, Converter={StaticResource EdgeSegmentsConverter}}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type model:Node}">
<Ellipse
Width="8"
Height="8"
Stroke="Black"
Fill="Gray"/>
</DataTemplate>
</ItemsControl.Resources>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left">
<Setter.Value>
<Binding Path="Position.X">
<Binding.Converter>
<p:NodePositionConverter />
</Binding.Converter>
</Binding>
</Setter.Value>
</Setter>
<Setter Property="Canvas.Top">
<Setter.Value>
<Binding Path="Position.Y">
<Binding.Converter>
<p:NodePositionConverter />
</Binding.Converter>
</Binding>
</Setter.Value>
</Setter>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
Why not use ItemContainerStyleSelector ? Add the styles to ItemsControl.Resources
:
<Style TargetType="ContentPresenter" x:Key="{x:Type model:Edge}">
<Setter Property="ZIndex">
...
</Setter>
</Style>
<Style TargetType="ContentPresenter" x:Key="{x:Type model:Node}">
<Setter Property="ZIndex">
...
</Setter>
<Setter Property="Canvas.Top">
...
</Setter>
<Setter Property="Canvas.Left">
...
</Setter>
</Style>
Note the x:Key
is set to the type so we can easily look up by item.GetType()
in the style selector:
public override Style SelectStyle(object item, DependencyObject container) {
var containerElement = (FrameworkElement)container;
var style = containerElement.TryFindResource(item.GetType()) as Style;
if (style != null) {
return style;
}
return base.SelectStyle(item, container);
}
It's a bit weird what you are doing.. I didn't know if your really need to do it with this way...
You can't juste merge your two List into a simple List ? Like this:
List<AChild> a;
List<BChild> b;
List<Mother> ab = a.Concat(b).Cast<Mother>();
And after in your View you can use a TemplateSelector who will help you to choose which DataTemplate is appropriate for the item.
<ItemsControl ItemTemplateSelector="{StaticResource YourTemplateSelector}" ItemsSource="{Binding ab}"/>
The answer is given by Rachel in the following StackOverflow thread: https://stackoverflow.com/a/7931448/970589
So kudos to her. Short summary of the answer: Use a converter to check the type of the calling binding, and return a value according to that type.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.