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

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>

Related

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>

Make Button on click expand by animation over the other elements in the uniformgrid

I'am developing an WPF application while following the MVVM pattern. But I'am having som trouble with animation. I have this uniformGrid filled wih buttons, and when i press one of these buttons I want it to expand by animation all over the other elements in the uniformgrid, until it is the only one in the grid.
Illustration of the Uniform grid with the buttons
Illustration of the Uniform grid just after one of the buttons have been clicked. It will Grow to cover all of the elements
Here is some of the code if you are curious:
The uniformgrid
<Canvas Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="7" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" x:Name="CanvasContainer">
<ScrollViewer x:Name="TheScrollViewer" Uid="212" Height="{Binding ActualHeight, RelativeSource={RelativeSource AncestorType=Canvas}}"
Width="{Binding ActualWidth, RelativeSource={RelativeSource AncestorType=Canvas}}"
VerticalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Hidden"
>
<ItemsControl HorizontalAlignment="Stretch" ItemsSource="{Binding PageList}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" IsItemsHost="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding}" Height="{Binding ActualHeight, RelativeSource={RelativeSource AncestorType=Canvas}}"
Width="{Binding ActualWidth, RelativeSource={RelativeSource AncestorType=Canvas}}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid
Rows="{Binding KeyboardModel.GridRows, Source={StaticResource Locator}}"
Columns="{Binding KeyboardModel.GridColumns, Source={StaticResource Locator}}" >
</UniformGrid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<navigationControls:SymbolControl></navigationControls:SymbolControl>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Canvas>
Button
<Grid>
<Button x:Name="SymbolButton" wpf:WindowBoundInteraction.Command="{Binding KeyboardModel.UpdateSymbolsCommand, Source={StaticResource Locator}}"
CommandParameter="{Binding}" Width="200" Style="{StaticResource SymbolButtonStyle}" >
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<Image Source="{Binding Path=Image}" Height="100"/>
<TextBlock Text="{Binding Name}" HorizontalAlignment="Center" FontSize="20" />
</StackPanel>
<Button.RenderTransform>
<TranslateTransform></TranslateTransform>
</Button.RenderTransform>
<Button.Triggers>
<!-- <EventTrigger RoutedEvent="Button.Loaded">
<BeginStoryboard Storyboard="{StaticResource FadeInStoryboard}"></BeginStoryboard>
</EventTrigger>-->
<EventTrigger RoutedEvent="wpf:WindowBoundInteraction.Activate">
<BeginStoryboard Storyboard="{StaticResource Minimize}"></BeginStoryboard>
</EventTrigger >
</Button.Triggers>
</Button>
</Grid>
Don't necessairly need a long answer, if someone could pinpoint me in the right direction I would be really happy.

WPF - How to make ItemsControl single item content larger than ItemsControl itself

Is it possible to make a datatemplate for ItemsControl, which would 'enlarge' selected item's content beyond ItemsControl height, overlapping other ItemsControl items - but without altering ItemsControl height/width, without shifting adjacent ItemsControl items.
I have interaction triggers and bindings etc, just need UI elements capable of that.
Tried Popup element - but it doesn't look to be the best solution.
EDIT:
Thanks again for your replies, I tried to post a simplier description of my problem - and that was my mistake, sorry. Heres the EXACT problem:
(tried to apply ScaleTransform - it makes my ScrollViewer larger)
<s:SurfaceScrollViewer MaxWidth="1920" Margin="50" ClipToBounds="False"
VerticalAlignment="Center" HorizontalAlignment="Center"
Visibility="{Binding Path=ScrollViewerYearVisibility}">
<ItemsControl HorizontalAlignment="Center" BorderBrush="Red" BorderThickness="3"
ItemsSource="{Binding Path=YearItemsSource}"
ItemTemplate="{StaticResource DataTemplateYearItem}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</s:SurfaceScrollViewer>
<DataTemplate x:Key="DataTemplateYearItem">
<Grid VerticalAlignment="Center" HorizontalAlignment="Center" ClipToBounds="False">
<Grid.InputBindings>
<MouseBinding MouseAction="LeftDoubleClick" Command="{Binding Path=CommandDeviceDoubleClick}"/>
</Grid.InputBindings>
<Image VerticalAlignment="Center" HorizontalAlignment="Center"
Width="200" Height="200" Stretch="None"
Source="{Binding Path=ContentImagePathCurrent}">
</Image>
<Image VerticalAlignment="Center" HorizontalAlignment="Center" ClipToBounds="False"
Margin="-248,-210,0,0"
Stretch="None"
Source="{Binding Path=SlideCurrent.ContentImagePath}">
<Image.RenderTransform>
<ScaleTransform ScaleX="3" ScaleY="3"/>
</Image.RenderTransform>
</Image>
</Grid>
</DataTemplate>

ScrollViewer content is scrolling over other parts of window

I have the following as part of my XAML:
<DockPanel>
<ToolBar Name="toolbar1" DockPanel.Dock="Top" Height="41" Background="#FFA5D95A">
//other controls
</ToolBar>
<ScrollViewer>
<ListBox Name="listBox1" ItemsSource="{Binding ElementName=This, Path=Items}"
ItemTemplateSelector="{StaticResource entryItemTemplateSelector}">
<ListBox.Template>
<ControlTemplate>
<WrapPanel IsItemsHost="True" />
</ControlTemplate>
</ListBox.Template>
</ListBox>
</ScrollViewer>
</DockPanel>
I want the scrool bar to move the items in the ListBox just to the top of the WrapPanel, and then to stop showing them when they get to the top; in stead, these items scroll into the ToolBar at the top.
What am I doing wrong?
Note: Haing been asked, here are the DataTemplates I currently have; EntryItemHost is a type derived from WindowsFormsHost.
<DataTemplate x:Key="folderTemplate">
<my:EntryItemHost Item="{Binding}"
Background="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=my:MainWindow, AncestorLevel=1}, Path=Background}"
DoubleClick="FolderDoubleClick" />
</DataTemplate>
<DataTemplate x:Key="imageTemplate">
<my:EntryItemHost Item="{Binding}"
Background="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=my:MainWindow, AncestorLevel=1}, Path=Background}" />
</DataTemplate>
Instead of setting ListBox Template set the ItemsPanel and remove the ScrollViewer
sample
<DockPanel>
<ToolBar Name="toolbar1"
DockPanel.Dock="Top"
Height="41"
Background="#FFA5D95A">
//other controls
</ToolBar>
<ListBox Name="listBox1" ItemsSource="{Binding ElementName=This, Path=Items}"
ItemTemplateSelector="{StaticResource entryItemTemplateSelector}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Width="{Binding ActualWidth, RelativeSource={RelativeSource FindAncestor, AncestorType=ContentPresenter}}" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</DockPanel>
notice that I have replaced the template with ItemsPanel and restricted the width to the listbox
I made this based on the assumptions, let me know is this not what you are looking for

ListView: define ItemsPanelTemplate in resource dictionary

I have a ListView which layout looks like a Windows Explorer view (icon + some details), bound to a list somewhere in the ViewModel.
My aim here is to be able to switch between explorer view or classic view whenever we want.
I could define an ItemsPanelTemplate doing exactly the work to display correctly the layout, directly in the ListView.ItemsPanel field. Now, I'd like to define it in the resources so that I'll be able to use it in different views, and especially in one control, the user should have the choice between Explorer view or classic list view (the default rendering for a list)
How'd you do that? I cannot define any ItemsPanelTemplate in my ResourceDictionary, and if I define a DataTemplate it is not compatible (while I thought that following pure logic, ItemsPanelTemplate should inherit from DataTemplate, but it actually doesn't look like so).
Code snippet for the actual list:
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel
Width="{Binding (FrameworkElement.ActualWidth),
RelativeSource={RelativeSource
AncestorType=ScrollContentPresenter}}"
ItemWidth="{Binding (ListView.View).ItemWidth,
RelativeSource={RelativeSource AncestorType=ListView}}"
ItemHeight="{Binding (ListView.View).ItemHeight,
RelativeSource={RelativeSource AncestorType=ListView}}"
/>
<!--
MinWidth="{Binding ItemWidth, RelativeSource={RelativeSource Self}}"
-->
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel
Orientation="Horizontal"
Height="Auto"
Width="150" >
<Image
Source="{Binding Path=Appli.AppType, Converter={StaticResource TypeToIconConverter}}"
Margin="5"
Height="50"
Width="50" />
<StackPanel
VerticalAlignment="Center"
Width="90" >
<TextBlock
Text="{Binding Path=Appli.AppName}"
FontSize="13"
HorizontalAlignment="Left"
TextWrapping="WrapWithOverflow"
Margin="0,0,0,1" />
<TextBlock
Text="{Binding Path=Appli.AppType}"
FontSize="9"
HorizontalAlignment="Left"
Margin="0,0,0,1" />
</StackPanel>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
Keeping the ItemTemplate in a static resource was easy to do, but now I can't do anything with the ItemsPanelTemplate...
Any ideas? I'm using MVVM so I'm trying ideally not to use code-behind if possible
You would use a style for the whole ListView for that. So you would do:
<Grid.Resources>
<Style x:Key="ListViewStyle" TargetType="ListView">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<WrapPanel
Width="{Binding (FrameworkElement.ActualWidth), RelativeSource={RelativeSource AncestorType=ScrollContentPresenter}}"
ItemWidth="{Binding (ListView.View).ItemWidth, RelativeSource={RelativeSource AncestorType=ListView}}"
ItemHeight="{Binding (ListView.View).ItemHeight, RelativeSource={RelativeSource AncestorType=ListView}}" />
<!--
MinWidth="{Binding ItemWidth, RelativeSource={RelativeSource Self}}"
-->
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<ListView
SelectionMode="Single"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Bottom"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ItemsSource="{Binding ListUserApps, UpdateSourceTrigger=PropertyChanged}"
SelectedIndex="{Binding SelectedUserApp, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Background="White"
Style="{StaticResource ListViewStyle}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel
Orientation="Horizontal"
Height="Auto"
Width="150">
<Image
Source="{Binding Path=Appli.AppType, Converter={StaticResource TypeToIconConverter}}"
Margin="5"
Height="50"
Width="50"/>
<StackPanel
VerticalAlignment="Center"
Width="90">
<TextBlock
Text="{Binding Path=Appli.AppName}"
FontSize="13"
HorizontalAlignment="Left"
TextWrapping="WrapWithOverflow"
Margin="0,0,0,1" />
<TextBlock
Text="{Binding Path=Appli.AppType}"
FontSize="9"
HorizontalAlignment="Left"
Margin="0,0,0,1" />
</StackPanel>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
If you want the user then be able to switch between explorer and classic view, just define a second Style and switch the style of the listview. This can be done for example with some VisualStates and a 'DataStateBehavior'.
Alternatively you could create a style with some DataTriggers and Setters for the individual ItemsPanels.

Resources