Dependency Property of Style type - wpf

I am wanting to expose a Style as a dependency property. Basically there is a rectangle indicator in the middle and the using control is going to expose a style dependency property for containing controls to set. It would allow those containing controls to provide a style for coloring based on their own knowledge of their items.
<ItemsControl ItemsSource="{Binding Rows}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding RowName}"/>
<ItemsControl ItemsSource="{Binding Statuses}">
<Rectangle Style="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Grid}, Path=RectangleStyle}"/>
</ItemsControl>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The dependency property in the code-behind of the custom control.
public Style RectangleStyle
{
get { return (Style)GetValue(RectangleStyleProperty); }
set { SetValue(RectangleStyleProperty, value); }
}
public static readonly DependencyProperty RectangleStyleProperty =
DependencyProperty.Register("RectangleStyle", typeof(Style), typeof(MyControl), new PropertyMetadata(null));
This would then be used like:
<MyControl>
<MyControl.RectangleStyle>
<Style TargetType="{x:Type Rectangle}">
<Setter Property="Fill" Value="Red"/>
</Style>
</MyControl.RectangleStyle>
</MyControl>
This is not working at all and I'm not sure if my approach is even correct.

Stupid syntax error. I needed to set the ItemsTemplate for the Inner ItemsControl. I was setting the content of the ItemsControl to be the Rectangle.
<ItemsControl ItemsSource="{Binding Rows}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding RowName}"/>
<ItemsControl ItemsSource="{Binding Statuses}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Rectangle Style="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Grid}, Path=RectangleStyle}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

Related

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.

Tabbing User Control In Listbox DataTemplate

I have a User Control in a ListBox and when I use the tab key to focus I'd like to get the focus on the TextBox in my Control instead of the Custom Control. How can I do it?
I simplified the code because In my control I have other UI Elements.
User Control Code:
<Grid>
<TextBox Name="txtFreeTextDescription" Style="{StaticResource TextBoxStyleLargeDynamic}" Text="{Binding Description, Mode=TwoWay, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" />
</Grid>
ListBox Code:
<ListBox Name="lsbItems" DataContext="{Binding}" KeyboardNavigation.TabNavigation="Local">
<ListBox.ItemTemplate>
<DataTemplate>
<local:SectionDynamicItem x:Name="ucSectionDynamicItem" Description="{Binding SectionItem.Description}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This works for me....
<ListBox ItemsSource="{Binding EmployeeList}"
KeyboardNavigation.TabNavigation="Continue">
<ItemsControl.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Focusable" Value="False"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="5" Focusable="False">
<TextBox Text="{Binding Name}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ListBox>
Check below link.
http://social.msdn.microsoft.com/forums/en-US/wpf/thread/98d8423c-9719-4291-94e2-c5bf3d80cd46/
Thanks
Rajnikant

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>

WPF Grid as ItemsPanel for a list dynamically bound to an ItemsControl

I am using a Grid as ItemsPanel for a list dynamically bound to an ItemsControl. The code below is working - with a remaining problem: I can’t find a way to dynamically initialize the ColumnDefinitions and RowDefinitions of the grid. As consequence all values are placed on top of each other.
<ItemsControl ItemsSource="{Binding Cells}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Grid.Row" Value="{Binding RowIndex}"/>
<Setter Property="Grid.Column" Value="{Binding ColumnIndex}"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Value}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Please be aware, that I am searching an answer according the MVVM pattern. Therefore sub classing and code behind are just workarounds, but no solutions.
You'll need some way to tell the Grid how many Rows/Columns it has. Perhaps as each Item loads you could check the value of RowIndex and ColumnIndex and add Rows/Columns to the Grid if needed.
As another alternative, perhaps you can expose RowCount and ColumnCount properties in your ViewModel that return the max RowIndex and ColumnIndex, and in the Grid's Loaded event add however many Columns/Rows you need.
I find it perfectly acceptable to use code-behind in MVVM IF the code is related to the UI only.
Another idea would be to arrange your items in your code-behind into a 2D grid before returning it to the View, and then bind that Grid to a DataGrid with AutoGenerateColumns=True and the headers removed
Update
My current solution to this problem is to use a set of AttachedProperties for a Grid that allow you to bind RowCount and ColumnCount properties to a property on the ViewModel
You can find the code for my version of the attached properties on my blog here, and they can be used like this:
<ItemsPanelTemplate>
<Grid local:GridHelpers.RowCount="{Binding RowCount}"
local:GridHelpers.ColumnCount="{Binding ColumnCount}" />
</ItemsPanelTemplate>
Your grid has zero rows and columns so everything will be displayed on top of each other. Do something like below and it will work.
<ItemsControl ItemsSource="{Binding Cells}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Grid.Row" Value="{Binding RowIndex}" />
<Setter Property="Grid.Column" Value="{Binding ColumnIndex}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Value}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
i found this from here The ItemsControl
<ItemsControl ItemsSource="{Binding VehicleMakes}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="3" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Width="300" Height="100" Click="ButtonOption_Click" Tag="{Binding Name}">
<StackPanel Orientation="Vertical">
<Image
Initialized="Image_Initialized"
Tag="{Binding ResourseImageName}"
Width="116"
Height="30"
Margin="0,0,0,10" >
</Image>
<TextBlock Text="{Binding Name}" VerticalAlignment="Bottom" HorizontalAlignment="Center"/>
</StackPanel>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

How to bind AccordionItem Visibility when using Accordion.ItemTemplate in Silverlight?

Accordion item Visibility property can be bound like this:
<layoutToolkit:Accordion x:Name="MyAccordion">
<layoutToolkit:AccordionItem Visibility="{Binding IsVisible, Converter={StaticResource VisibilityConverter}}">
...
</layoutToolkit:AccordionItem>
</layoutToolkit:Accordion>
But how to bind it when using Accordion.ItemTemplate?
<layoutToolkit:Accordion ItemsSource="{Binding AcordionItems}" x:Name="MyAccordion">
<layoutToolkit:Accordion.ItemTemplate>
<DataTemplate>
...
</DataTemplate>
</layoutToolkit:Accordion.ItemTemplate>
<layoutToolkit:Accordion.ContentTemplate>
<DataTemplate>
...
</DataTemplate>
</layoutToolkit:Accordion.ContentTemplate>
</layoutToolkit:Accordion>
I can bind IsVisible to elements inside DataTemplate, but then an empty accordion item is displayed. I need to be able to show/hide the whole accordion item.
I ended up using StackPanel with multiple Accordions:
<StackPanel Orientation="Vertical">
<ItemsControl ItemsSource="{Binding AcordionItems}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<layoutToolkit:Accordion Visibility="{Binding IsVisible, Converter=
{StaticResource VisibilityConverter}}">
<layoutToolkit:AccordionItem>
...
</layoutToolkit:AccordionItem>
</layoutToolkit:Accordion>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>

Resources