multiple items in ItemsControl on a Canvas - wpf

In the XAML below, please fill in the "WhatGoesHere" node and explain to me how that won't mess up the coordinates on the Canvas.
<ItemsControl ItemsSource="{Binding ViewModels}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<WhatGoesHere?>
<Path Stroke="CornflowerBlue" StrokeThickness="2">
<Path.Data>
<PathGeometry Figures="{Binding Figures}"/>
</Path.Data>
</Path>
<Path Stroke="Red" StrokeThickness="2">
<Path.Data>
<PathGeometry Figures="{Binding Figures2}"/>
</Path.Data>
</Path>
</WhatGoesHere?>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
My example has two of the same type of object in the template, but I will have several other control types in there as well.

You have more than one element within a DataTemplate. You would have to put your two Path objects into some kind of Panel, like for example a Grid. The question is where are all your the canvas coordinates computed so that you can bind the Grid to it? In your view model? Then you could bind to it and it could look somewhat like this:
<ItemsControl ItemsSource="{Binding ViewModels}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Canvas.Top="{Binding Y}" Canvas.Left="{Binding X}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Path Stroke="CornflowerBlue" StrokeThickness="2">
<Path.Data>
<PathGeometry Figures="{Binding Figures}"/>
</Path.Data>
</Path>
<Path Stroke="Red" StrokeThickness="2" Grid.Row="1">
<Path.Data>
<PathGeometry Figures="{Binding Figures2}"/>
</Path.Data>
</Path>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
In case you do not have these coordinates, then I would recommend to use another value for ItemsPanelTemplate like a VirtualizedStackPanel. This might look like this:
<ItemsControl ItemsSource="{Binding ViewModels}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizedStackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Path Stroke="CornflowerBlue" StrokeThickness="2">
<Path.Data>
<PathGeometry Figures="{Binding Figures}"/>
</Path.Data>
</Path>
<Path Stroke="Red" StrokeThickness="2" Grid.Row="1">
<Path.Data>
<PathGeometry Figures="{Binding Figures2}"/>
</Path.Data>
</Path>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
If you could tell me what your actually trying to achieve, I'm sure I can help you in a better way.

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>

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>

Scrollviewer and ItemsControl VerticalScrolling into nirvana

Hi i've got a complex ItemsControl wich is used to display news (variable height!) with a slide/fade in effect. (like google currents) my problem now is that the scrollviewer will calculate the available scrollingsize left based on whatever.. that will end up in a very ugly way of scrolling if the user scrolls fast! sometimes the scrollview scrolls into the nirvana. i thought this might be the cause of virtualization but i'm not able to deactivate it. as you can see i ve already replaced the ItemsPanel.
<ScrollViewer
ManipulationMode="Control">
<ItemsControl
Name="TickerItemList"
ItemsSource="{Binding TickerItems, Source={StaticResource TickerViewModel}}"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Right">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
<!--<VirtualizingStackPanel CleanUpVirtualizedItemEvent="CleanUpVirtualizedItem" VirtualizingStackPanel.VirtualizationMode="Recycling" />-->
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Template>
<ControlTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<Image Grid.Row="0"
Source="{Binding TitleImage, Source={StaticResource TickerViewModel}}"
Name="TitleImage"
Height="240"
Stretch="UniformToFill"
Loaded="TitleImageLoaded"
CacheMode="BitmapCache"/>
<Grid Grid.Row="1">
<Border Height="15" Margin="0,-15,0,0" VerticalAlignment="Top" >
<Border.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#00000000" Offset="0"/>
<GradientStop Color="#44000000" Offset="1"/>
</LinearGradientBrush>
</Border.Background>
</Border>
<ItemsPresenter />
</Grid>
</Grid>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid RenderTransformOrigin="0.5,0.5" Tap="ItemTapped" Background="{StaticResource TickerPageBackgroundBrush}">
<!-- RenderTransform definition for animating each item -->
<Grid.RenderTransform>
<TransformGroup>
<ScaleTransform />
<TranslateTransform />
<RotateTransform />
</TransformGroup>
</Grid.RenderTransform>
<!-- Selector for ticker type -->
<local:NinePatch Margin="10,10,10,0" Image="{StaticResource TickerPaperImage}" CacheMode="BitmapCache">
<local:TickerListItemLayoutSelector Content="{Binding}">
too many lines... ;)
</local:TickerListItemLayoutSelector>
</local:NinePatch>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>

Bind the height of a Polygon to the StackPanel height

I can't figure how to bind the height of a polygon to the height of my stack panel.
If I wanted to add a rectangle, all I had to do is something like that:
<Rectangle Width="75" >
<Rectangle.Fill>
<SolidColorBrush Color="Red" />
</Rectangle.Fill>
</Rectangle>
This one won't brake the height of the panel. but with the polygon it seems like I can't leave some of the points as blank so that will scale with the parent panel.
Thanks
Wrap your polygon with a <Viewbox>.
The Viewbox automatically scales its content to its size. Exactly how it does so can be tweaked with the Stretch and StretchDirection properties.
this solution works too
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal">
<Border BorderBrush="Black" BorderThickness="1,1,0,1">
<StackPanel Orientation="Horizontal">
<TextBlock Text="TextBlock1" Margin="2" />
<TextBlock Text="TextBlock2" Margin="2" />
<TextBlock Text="TextBlock3" Margin="2" />
<TextBlock Text="TextBlock4" Margin="2" />
<TextBlock Text="TextBlock5" Margin="2" />
</StackPanel>
</Border>
<Path Fill="Yellow" Stroke="Black" StrokeThickness="1"
Width="50" Stretch="Fill">
<Path.Data>
<PathGeometry>
<PathFigure IsClosed="True" StartPoint="1,0.5">
<LineSegment Point="0,0" IsSmoothJoin="True" />
<LineSegment Point="0,1" IsSmoothJoin="True" />
</PathFigure>
</PathGeometry>
</Path.Data>
</Path>
</StackPanel>
</Grid>

SL 4: Strange behavior with templated control

We have some xaml:
<Style TargetType="local:V_RelLine">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:V_RelLine">
<Grid x:Name="LayoutRoot">
<VisualStateManager.VisualStateGroups>
</VisualStateManager.VisualStateGroups>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Ellipse x:Name="startArrow" Height="20" Width="60" Fill="Green" Stroke="Blue" Visibility="Visible" />
<Path x:Name="LinePathPart" Visibility="Visible" Stroke="Red" StrokeDashArray="2 2" StrokeThickness="2"
>
<Path.Data>
<PathGeometry x:Name="LinePathGeometry" >
<PathFigure x:Name="linePathBezierFigure" >
<BezierSegment x:Name="linePathBezierSegment" />
</PathFigure>
</PathGeometry>
</Path.Data>
</Path>
<Ellipse x:Name="endArrow" Height="20" Width="20" Fill="Red" Stroke="Red" Visibility="Visible" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And in the code behind:
LinePathBezierFigure.StartPoint = startPoint;
Canvas.SetLeft(startArrow, startPoint.X);
Canvas.SetTop(startArrow, startPoint.Y);
/* similar for endArrow */
At runtime, startArrow and endArrow end up at the same point (even though they were set to different locations), as though they ended up at 0,0.
In fact, a subsequent call to Canvas.GetLeft(startArrow) show that it is at 0,0.
What is going on? Why are different objects in the same template, assigned the same coordinates, ending up in different locations?
Thanks for any insight in to this....
Just a thought but Canvas.Left and Canvas.Top normally only work well when the elements are placed in a Canvas rather than a Grid like you are using currently.

Resources