ItemsControl ignores DataTemplates in Resources - c#

I want to bind a collection of multiple types to an ItemsControl being displayed in a canvas. My code so far is the following:
<ItemsControl ItemsSource="{Binding Path=Objects}">
<ItemsControl.Resources>
<DataTemplate DataType="self:CalibrationRectangle">
<Rectangle Fill="{Binding Path=Color, Converter={StaticResource ColorToBrushConverter}}" Width="{Binding Width}" Height="{Binding Height}" />
</DataTemplate>
<DataTemplate DataType="self:PolygonVM">
<Polygon Fill="{Binding Path=Color, Converter={StaticResource ColorToBrushConverter}}" Points="{Binding Points}" />
</DataTemplate>
</ItemsControl.Resources>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Background="Red" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!--<ItemsControl.ItemTemplate>
<DataTemplate DataType="self:CalibrationRectangle">
<Rectangle Fill="{Binding Path=Color, Converter={StaticResource ColorToBrushConverter}}" Width="{Binding Width}" Height="{Binding Height}" />
</DataTemplate>
</ItemsControl.ItemTemplate>-->
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Top" Value="{Binding Path=Y}" />
<Setter Property="Canvas.Left" Value="{Binding Path=X}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
In this state of the code the DataTemplates in the Resources are completely ignored and instead objects are represented as strings.
If instead I use the commented out lines and define the DataTemplate in ItemsControl.ItemsTemplate it works. However, as I need multiple DataTemplates (one per type) this is no solution.
What is the problem here? Why are the DataTemplates in the resources not working?
Any ideas are appreciated :)

At the moment you target XML element name. If you want to target type you need to use {x:Type ...} and replace
<DataTemplate DataType="self:CalibrationRectangle">
with
<DataTemplate DataType="{x:Type self:CalibrationRectangle}">
From MSDN:
To refer to the type name of the class, use the x:Type Markup Extension. If the template is intended for XML data, this property contains the XML element name.

Related

UniformGrid Grid.Row Grid.Column not being adhered to

Following on from this question (DataTemplateSelector for Uniform Grid binding?), I still am not getting my Buttons/Textblocks to adhere to the row and the column that they are bound to in the view model. I will only post the XAML as I know the row and column bindings are correct (Live Visual Tree tells me Row is 1 and Column is 1 for example, but on the grid it shows otherwise...).
If you need anymore code let me know. This is the small bit of XAML.
<Grid DockPanel.Dock="Left" Background="Transparent" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MinWidth="800" Height="400">
<ItemsControl ItemsSource="{Binding ObjCompositeCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid DockPanel.Dock="Top" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" Name="objGrid" Grid.Row="1"
Rows="{Binding RowCount}"
Columns="{Binding ColumnCount}"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Grid.Row" Value="{Binding Row}"/>
<Setter Property="Grid.Column" Value="{Binding Column}"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type engine:ObjA}">
<Button Content="{Binding Id}" />
</DataTemplate>
<DataTemplate DataType="{x:Type engine:GridLabeller}">
<TextBlock Text="{Binding HeaderName}"/>
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
</Grid>
A UniformGrid in WPF doesn't care about the Grid.Row and Grid.Column attached properties. You need to create a Grid with RowDefinitions and ColumnsDefinitions for setting these properties to have any effect.
You may create a Grid programmatically in the view based on the values of the RowCount and ColumnCount source properties.

How to add a WPF Button to add content to a List inside an ObservableCollection?

From a Database I am receiving a List with multiple Properties. One of those Properties is again a List of an Appendix Object containing ID Information and an Image.
I converted that List into an ObservableCollection and bound it to a WPF ListBox. Using a DataTemplate I show this content. Inside the DataTemplate I now have an ItemControl bound to the Inner-List (my Appendix with none, one or multiple entries):
<ListBox ItemsSource="{Binding Reports}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<Label Content="Report Date:" FontWeight="Bold"/>
<Label Content="{Binding ReportDate.Date, StringFormat=dd.MM.yyyy , ConverterCulture=de-DE}"/>
<Label Content="Comment:" FontWeight="Bold"/>
<Label Content="{Binding Comment}"/>
</StackPanel>
//Here I would like to have a Button to add a Picture into the Appendix List
<ItemsControl x:Name="SelectedOnlyListBox" ItemsSource="{Binding Appendix}" Visibility="Collapsed">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel IsItemsHost="True" Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Picture}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}, AncestorLevel=1}, Path=IsSelected}" Value="True">
<Setter Property="Visibility" Value="Visible" TargetName="SelectedOnlyListBox" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox.ItemTemplate>
My question is, how do I get the current Object via Binding into my Code. Because I will have multiple Buttons for one outer List (Reports) entry. But the Button should only add into the proper Inner List (Appendix).
Additional info: I am using the MVVM-Light Toolkit.
Thank you very much.
Edit: I forgot to mention that the first List is one of many Properties of a Detail class which again comes in a List of all Details. So with my DataContext already got a Property "SelectedDetail". This one I know...
But how do I now match the specific Button of each SelectedDetail.Report to my SelectedDetail.Reports.Appendix?

WPF Application - <Ellipse.InputBindings> - <MouseBinding>

Hi I am new to WPF Development and run into a problem Regarding Mouse Binding of an Ellipse which was created with Item Controls Trough Data Binding. This is the source code. My Problem is that the "CLoadModelFromDisk" Binding is not executed. In another context the Command works without any problems.
<ItemsControl ItemsSource="{Binding JointsModelPartView}" Grid.Row="1" Grid.Column="1">
<ItemsControl.ItemContainerStyle>
<Style TargetType="FrameworkElement">
<Setter Property="Canvas.Left" Value="{Binding posx1}" />
<Setter Property="Canvas.Top" Value="{Binding posy1}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="Point">
<Ellipse Fill="AntiqueWhite"
Stroke="Black"
Width="10"
Height="10"
Margin="-5,-5,5,5"
>
<Ellipse.InputBindings>
<MouseBinding
Command="{Binding CLoadModelFromDisk}"
Gesture="LeftClick"
/>
</Ellipse.InputBindings>
</Ellipse>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas IsItemsHost="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
It would be really nice if somebody could help me with this problem.
This is the code for the context menu which does not work with your solution. Do you have any idea why?
<Ellipse.ContextMenu>
<ContextMenu ItemsSource="{Binding DataContext.ContextActionsView, RelativeSource={RelativeSource AncestorType=ItemsControl}}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Name}"/>
<Setter Property="Command" Value="{Binding RCommand}"/>
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</Ellipse.ContextMenu>
I already tried to set the Relative Source to the Header and Command but that also does not worked.
In your DataTemplate, the DataContext will be the item displayed in the template -- one of the items in JointsModelPartView. Do those items have a property of type ICommand named CLoadModelFromDisk?
My guess is that they don't, and CLoadModelFromDisk is a member of the same viewmodel as JointsModelPartView. In that case, you need to bind not to the list item, but to the parent viewmodel to which the property actually belongs. That'll be the DataContext of the ItemsControl -- it must be, since the ItemsControl is able to bind to JointsModelPartView.
Try this:
<MouseBinding
Command="{Binding DataContext.CLoadModelFromDisk, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
Gesture="LeftClick"
/>

Layer system for images on a canvas

I am trying to figure out how I can have a layer system for my app. I am adding images to an itemscontrol and they are all rendering one on top of the other.
<ItemsControl Name="canvasDataBinding"
HorizontalAlignment="Center"
Height="512"
Width="512"
VerticalAlignment="Center"
ClipToBounds="True">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Background="Transparent"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Image Source="{Binding Name}"
Width="{Binding Width}"
Height="{Binding Height}">
</Image>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
For testing purposes, I tried adding Panel.Zindex="{Binding Zind}" inside the image control, and decrementing Zind every time a new image was added to the collection, but the images still render on top of each other.
I also thought about having multiple collections, each collection would be its own layer. I dont know how I would implement this with my current code though.
Edit:
I added an ItemContainerStyle:
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Panel.ZIndex" Value="{Binding Zindex}" />
</Style>
</ItemsControl.ItemContainerStyle>
Again I am binding the value of ZIndex to an int that I decrement before the image is added to the collection. It is still rendering one on top of the other.
You need to set your Canvas.Top, Canvas.Left, and Panel.ZIndex in the ItemContainerStyle so the property gets applied to the <ContentPresenter> that wraps each Image.
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Top" Value="{Binding Y}" />
<Setter Property="Canvas.Left" Value="{Binding X}" />
<Setter Property="Panel.ZIndex" Value="{Binding Zindex}" />
</Style>
</ItemsControl.ItemContainerStyle>
When your ItemsControl gets rendered, it actually renders like this
<Canvas>
<ContentPresenter>
<Image />
</ContentPresenter>
<ContentPresenter>
<Image />
</ContentPresenter>
<ContentPresenter>
<Image />
</ContentPresenter>
</Canvas>
which is why it doesn't work to set the property on the <Image> itself.

Using A WrapPanel in a TreeView

I want to display data with level of detail, so i use a TreeView, but each detail is quite short, so i would like to use a WrapPanel (horizontal) to have many details per line.
Something like :
This is an unexpanded item
This is The Header of an expanded item
Info 1 Info 2 Info 3 Info 4
Info 5 Info 6 Info 7
So i tried defining TreeViewItem's Template but i could not get the wrappanel to
wrap. I have only one info per line, when info's datatemplate width is 100 and TreeView
is 500. i tried setting Width of WrapPanel, ItemsWidth, are other things with no success.
Any idea ?
EDIT : i finally got this to work with a 'simpler' solution. Still it seems that we
have to define the WrapPanel's Width, which make the solution less generic.
Here's the solution i came to : just defining, in a style, the ItemsPanel used in a
TreeViewItem :
<Style TargetType="TreeViewItem">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"
Width="520"
HorizontalAlignment="Stretch"
Margin="0"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
IsItemsHost="True"
/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
And i still let the not working solution here, for completeness sake.
(Why wouldn't it work ???)
<Style TargetType="TreeViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TreeViewItem">
<Grid Margin="2" Width="500">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ContentPresenter Name="PART_Header"
ContentSource="Header"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
!!!! this is the wrapanel not wrapping
<ListBox Name="AllItems" Grid.Row="1" >
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ItemsPresenter />
</ListBox>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="False">
<Setter
TargetName="AllItems"
Property="Visibility"
Value="Collapsed" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
EDIT : i finally got this to work with a 'simpler' solution. Still it seems that we
have to define the WrapPanel's Width, which make the solution less generic.
(Maybe a binding of the width (but which ?) would solve this)
Here's the solution i came to : just defining, in a style, the ItemsPanel used in a
TreeViewItem :
<Style TargetType="TreeViewItem">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"
Width="520"
HorizontalAlignment="Stretch"
Margin="0"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
IsItemsHost="True"
/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
You must set
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
to your
<TreeView/>
not for
<WrapPanel/>
example:
<TreeView x:Name="fieldTreeView" Grid.Row="1" Margin="5,0,5,0" Background="Beige"
ItemsSource="{Binding Source={StaticResource bla}}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<TreeView.Resources>
<DataTemplate DataType="{x:Type Model:bla}">
<StackPanel Orientation="Vertical">
<Label Content="{Binding Name}"/>
<TextBox Text=""/>
</StackPanel>
</DataTemplate>
</TreeView.Resources>
<TreeView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</TreeView.ItemsPanel>
</TreeView>
This solution works for me.
Try
Disable the scroll bars
Widen the WrapPanel and see the visual impact, is it effected?
Make a color border/background to track the actual size of the WrapPanel
These will help you trace the problem

Categories

Resources