using a datatemplate on Polylines - wpf

i try to Bind a X and Y out of Observablecolletion.Points directly with Datatemplate. Reason is I want to use converter on X and Y later on. I did the Binding like this.
<ItemsControl ItemsSource="{Binding Measurements}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding Points}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Polyline Margin="20">
<Polyline.Points>
<Point X="{Binding X}"/>
<Point Y="{Binding Y }"/>
</Polyline.Points>
</Polyline>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The Probelm is for some reason the datatempalte got totally ignored.
The WPF is shown in my uercontroll with ever polyline i added to Measurements but never throu my datatemplate.

Related

WPF/XAML: Positioning primitives in nested data-structure within a canvas

in my current Project I have a nested data-structure. The root is an ObservableCollection ( terrainModel.terrainElements). Each of the elements in the collection host another ObservableCollection(drawElements) that consists of data to draw primitives in a canvas. Depending on the primitive I provide a DataTemplate so that it is rendered in the Canvas.
Here is the actual XAML:
<ItemsControl ItemsSource="{Binding terrainModel.terrainElements}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Width="1000" Height="500" Background="Aquamarine"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding drawElements}">
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type drawtype:LineElement}">
<Line Stroke="Black" X1="{Binding startPoint.X}" Y1="{Binding startPoint.Y}" X2="{Binding endPoint.X}" Y2="{Binding endPoint.Y}" />
</DataTemplate>
<DataTemplate DataType="{x:Type drawtype:CircleElement}">
<Ellipse Stroke="Black" Width="{Binding radius}" Height="{Binding radius}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type drawtype:RectangleElement}">
<Rectangle Stroke="Blue" Width="{Binding width}" Height="{Binding height}" Canvas.Left="{Binding position.X}" Canvas.Top="{Binding position.Y}"/>
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The problem is however that I am not able to set the position of the primitives correct in the XAML, as Canvas.Left="{Binding position.X}" for example does not work as it is in the inner ItemsControl and not in the outer ItemsControl.
I also tried to transform the primitives like this:
<Rectangle.RenderTransform><TranslateTransform x:Name="myTransform2" X="{Binding position.X}" Y="{Binding position.Y}" /></Rectangle.RenderTransform>
This worked but ruined the position of following elements to draw. Of course I could draw everything in code of the view, but I would like to know if it's also possible to do in xaml.
The solution by Clemens worked:
Set a Canvas as the ItemsPanel for the inner ItemsControl the same way
you did for the outer one. Then use the RenderTransform solution, as
setting Canvas.Left and Canvas.Top would still not work because the
Rectangle will not become a direct child of the Canvas. You would need
to set an ItemsContainerStyle for the Left and Top bindings.
The outer Canvas should still (also) have a Canvas as its ItemsPanel.
Otherwise you will have problems with more than one terrainElement.
I thought that a new Canvas would be generated in the inner ItemsControl for every outer ItemsControl.
Here is the XAML if somebody is interested:
<ItemsControl ItemsSource="{Binding terrainModel.terrainElements}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding drawElements}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Width="1000" Height="500" Background="Aquamarine"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type drawtype:LineElement}">
<Line Stroke="Black" X1="{Binding startPoint.X}" Y1="{Binding startPoint.Y}" X2="{Binding endPoint.X}" Y2="{Binding endPoint.Y}" />
</DataTemplate>
<DataTemplate DataType="{x:Type drawtype:CircleElement}">
<Ellipse Stroke="Black" Width="{Binding radius}" Height="{Binding radius}">
<Ellipse.RenderTransform>
<TranslateTransform x:Name="myTransform" X="{Binding position.X}" Y="{Binding position.Y}" />
</Ellipse.RenderTransform>
</Ellipse>
</DataTemplate>
<DataTemplate DataType="{x:Type drawtype:RectangleElement}">
<Rectangle Stroke="Blue" Width="{Binding width}" Height="{Binding height}">
<Rectangle.RenderTransform>
<TranslateTransform x:Name="myTransform2" X="{Binding position.X}" Y="{Binding position.Y}" />
</Rectangle.RenderTransform>
</Rectangle>
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

Use CollectionViewSource with TabControl

I'm trying to group and display the items of an ObservableCollection, just by using XAML code. It works well using a simple CollectionViewSource and a ListBox[1].
Actually, I would prefer to display the group's content in a tabcontrol. Google led me to the following social.msdn article wich presents a workaround to display the groups as a TabControl using code behind:
https://social.msdn.microsoft.com/Forums/vstudio/en-US/e073f275-0826-4fca-b9da-e310ccf1713e/wpf-grouping?forum=wpf
However, as I'm using MVVM and must rely on xaml only, I can't get it to work. Actually, the CollectionViewSource populates the groups (the TabControl shows the correct tabItemHeaders), but clicking on any of these TabItems freezes the application. Here's what I've tried:
<StackPanel x:Key="ModulSelectInputParameterView">
<StackPanel.Resources>
<CollectionViewSource x:Key="cvs" x:Name="collectionViewSource" Source="{Binding ReferencedLmtItem.ModulInputParameterCollection }">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Category"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</StackPanel.Resources>
<Grid >
<TabControl ItemsSource="{Binding Source={StaticResource cvs}, Path=Groups, Mode=OneWay}" DataContext="{Binding Source={StaticResource cvs}, Mode=OneWay}">
<!-- First Level -->
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<ListBox ItemsSource="{Binding Items}">
Second Level
<ListBox.ItemTemplate>
<DataTemplate>
<Expander Header="{Binding Name}">
<ListBox ItemsSource="{Binding Items}">
The Item of the Collection
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Key}"/>
<TextBlock Text=" - "/>
<TextBlock Text="{Binding Value.Comment}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Expander>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
</StackPanel>
[1]: This peace of xaml does work as expected, but uses a wrappanel to display groups contents:
<StackPanel x:Key="ModulSelectInputParameterView">
<StackPanel.Resources>
<CollectionViewSource x:Key="cvs" x:Name="collectionViewSource" Source="{Binding ReferencedLmtItem.ModulInputParameterCollection }">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Category"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</StackPanel.Resources>
<ListBox ItemsSource="{Binding Source={StaticResource cvs}}" VerticalContentAlignment="Top" ItemContainerStyle="{StaticResource ModulSelectInputParameterListBoxItemContainerStyle}" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Border BorderBrush="DarkGray" BorderThickness="2" Margin="2">
<TextBlock FontWeight="Bold" FontSize="14" Text="{Binding Path=Name}" HorizontalAlignment="Center" MinWidth="100"/>
</Border>
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" Margin="2"/>
</ItemsPanelTemplate>
</GroupStyle.Panel>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Border BorderThickness="2" BorderBrush="DarkGray">
<StackPanel>
<ContentPresenter Content="{TemplateBinding ContentControl.Content}" ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}" ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}" />
<ItemsPresenter Margin="2,0,2,2" />
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListBox.GroupStyle>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" Orientation="Vertical" VerticalAlignment="Top"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</StackPanel>
I think there's something wrong with your binding your code should work.
To get the same items in both ListBoxes try to bind the second ListBox Itemssource to the first ListBox Itemssource like this :
<ListBox ItemsSource="{Binding Items}" Name="ListBox">
<ListBox.ItemTemplate>
<DataTemplate>
<Expander Header="{Binding Key}">
<ListBox ItemsSource="{Binding ItemsSource, ElementName=ListBox}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Key}"/>
<TextBlock Text=" - "/>
<TextBlock Text="{Binding Value}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Expander>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Thanks to Joh who helped with the appropriate DataBinding. However, the reason was totally different, a quick and dirty solution is given below[1]:
Basically, I was missing that the above mentioned tab control is nested within an outer Tab Control in my main window. I am not toally sure if the following description is entirely correct[1], but to my mind the reason is the following:
The outer TabControl uses a style to display its content. This content applies to a ViewModel which holds the above mentioned observable collection, which in turn should be the ItemsSource of the CollectionViewSource that feeds the inner tabControl with the groups.
As I have defined this style only in the outer TabControl.Resources, and missed to define a separate style for the inner tab Control, the inner tabcontrol inherits the outer style and tries to display its data using the same content.
This content is again another inner tabControl, which calls another inner tabControl and so on.
[1] Defining an empty style in the inenr tabControl.Resources solved the problem:
<TabControl.Resources>
<Style TargetType="TabItem">
</Style>
</TabControl.Resources>
I would be happy if someone could confirm this idea or provide some links to well known issues with shared styles in nested controls.

How to fit a list of paths inside a border in WPF

am writing a C# WPF program to view and edit SVGs.
So i have a List of pathes (PathList) and i diplay them inside a border on a canvas here's my XAML:
<ItemsControl ItemsSource="{Binding PathList}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDown" >
<cmd:EventToCommand Command="{Binding Path= OnMouseDownCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Canvas>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Viewbox Stretch="Uniform" Height="500" Width="500">
<Path Stroke="{Binding Stroke}"
StrokeThickness="{Binding StrokeThickness}"
Fill="{Binding Fill}"
Data="{Binding Data}"
Tag="{Binding Tag}"
</Viewbox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
as i said in the title my goal is to fit those paths inside a border and as you can see in my code i used Viewbox to stretch the paths but the problem is it stretches each and every path inside the list to fill the size (500*500) distorting the form of the vector graphics.. i want the scaling to happen to all the list at once as an entity leaving the relative scale and locations of those paths intact.
The following simplified example shows how to put a list of Geometry items in a scalable ItemsControl. It is necessary that you set the Width and Height of the ItemsControl.
<Viewbox>
<ItemsControl Width="500" Height="500">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Path Stroke="Black" StrokeThickness="3" Data="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.Items>
<EllipseGeometry Center="150,150" RadiusX="100" RadiusY="100"/>
<EllipseGeometry Center="350,350" RadiusX="100" RadiusY="100"/>
</ItemsControl.Items>
</ItemsControl>
</Viewbox>

How to make itemtemplate aware of its containing template?

I want this Ellipse to get its coordinates from its corresponding BallViewModel, and to use them to determine its location inside a canvas.
The list of balls is bound to List<BallVM> in the mainviewmodel and thus I chose an itemsControl which has a canvas panel.
Is this approach correct?
If I try to bind to X and Y inside an itemcontainerstyle, then it's not specific to a certain ball.
No matter what I set in the Canvas.bottom or canvas.left properties the ellipse is always at the top left.
<Grid>
<ItemsControl ItemsSource="{Binding Balls}" Background="red">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas MouseMove="Canvas_MouseMove" Background="Blue"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type VM:BallVM}">
<Ellipse Canvas.Bottom="{Binding Y}" Canvas.Left="{Binding X}" Width="100" Height="100" Fill="Red"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
When you use an ItemTemplate with an ItemControls it does not directly put your Elippses on the Canvas but wraps them into an ContentPresenter. So you have to apply your canvas.Bottom/Left properties on the ItemsPresenter. You can can do this with the ItemContainerStyle:
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Bottom" Value="{Binding Y}" />
<Setter Property="Canvas.Left" Value="{Binding X}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type VM:BallVM}">
<Ellipse Width="100" Height="100" Fill="Red"/>
</DataTemplate>
</ItemsControl.ItemTemplate>

XAML ~ Need help with the Binding syntax for collection of objects

I am trying to bind a List of objects to an ItemsControl. The object has only two properties: Movie (a string) and Actors (an array of string). I can get the binding to work fine for the Movie. But I can't figure out the binding for the Actors array.
<ItemsControl x:Name="MovieList">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Width="100">
<Border Margin="2">
<TextBlock Text="{Binding Movie, Mode=OneWay}" />
</Border>
<ListBox ItemsSource="{Binding Actors, Mode=OneWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<controlsToolkit:WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
Any suggestions?
<ListBox ItemsSource="{Binding Actors, Mode=OneWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
this is wrong...you have to tell it what you want to bind to in the actors collection.
{Binding Path=ActorName} for example...since you only have it one way you could use displaymemberpath instead and just go: DisplayMemberPath="ActorName"

Resources