WPF - ControlTemplate as Thumb control Template data binding - wpf

I have a custom MyThumb user control which implements Thumb class. This user control is templated using ControlTemplate which ItemsSource is bound to a ObservableCollection of my custom class with three properties. ObservableCollection is in public property in code behind named ImportFileColumns and it's populated before InitializeComponent(). Problem is, that ObservableCollection is populated with correct data, but ControlTemplate never gets data bound (TextBlock control should be populated using values from ColumnName property), so my assumption is that I'm doing something wrong in the XAML.
Here's XAML:
<ControlTemplate x:Key="columnsThumbTemplate">
<Grid Width="175" >
<ItemsControl Name="isTest" ItemsSource="{Binding ImportFileColumns}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" IsItemsHost="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Width="175">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" />
<ColumnDefinition Width="25" />
</Grid.ColumnDefinitions>
<Border BorderBrush="LightGray" BorderThickness="1,0,1,1" Background="White" Padding="10,5" Grid.Column="0">
<StackPanel Orientation="Horizontal" >
<TextBlock Text="{Binding Path=ColumnName}" Height="13" />
</StackPanel>
</Border>
<my:MyConnector x:Name="myRightConnector"
Template="{StaticResource tplRightConnector}"
Grid.Column="1"
IsConnectorClicked="False"
PreviewMouseLeftButtonDown="myConnector_PreviewMouseLeftButtonDown"
Canvas.Left="Auto"
Canvas.Right="Auto"
DragDelta="onDragDelta"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</ControlTemplate>
<my:MyThumb x:Name="myCustomColumnsThumb"
Canvas.Left="272"
Canvas.Top="400"
Template="{StaticResource columnsThumbTemplate}"
DragDelta="onDragDelta"
PreviewMouseLeftButtonDown="connector_PreviewMouseLeftButtonDown" />

Related

How can I set the binding for a control's property (which is inside DataTemplate and UserControl) to use the ItemSource's given property?

I would like to make a UserControl which have a DataTemplate, and inside that DataTemplate there are controls. I would like to bind to those nested (inside the DataTemplate) controls' properties so I can set them when I reuse this UserControl. The nested controls will use the ItemSource's properties but the property names of the ItemSource's properties could be different.
The UserControl:
<UserControl x:Class="ContextMenu.BaseFilterUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
x:Name="Self">
<Grid Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80" />
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="70" />
</Grid.RowDefinitions>
<TextBlock Grid.Column="0"
VerticalAlignment="Center"
HorizontalAlignment="Right"
Margin="10"
Text="Owners" />
<Button Grid.Column="1"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Margin="10"
Click="FilteButtonClicked"
Width="40"
Height="40"
x:Name="FilterButton">
<Popup x:Name="FilterBoxPopup"
PlacementTarget="{Binding ElementName=FilterButton}"
Placement="Bottom"
StaysOpen="False">
<Border BorderBrush="Black"
Background="White"
Margin="2">
<ListView ItemsSource="{Binding ElementName=Self, Path=FilterList}"
x:Name="FilterListView"
Height="300"
Width="150">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<!--<CheckBox IsChecked="{Binding IsChecked}" />-->
<!--<TextBlock Text="{Binding Name}" />-->
<!--This is where I don't know how to properly bind eg. the above control, things I tried:-->
<!--<TextBlock Text="{Binding ElementName=FilterListView, Path=FilterElementName}" />-->
<!--<TextBlock Text="{Binding ElementName=Self, Path=DataContext.FilterElementName}" />-->
<!--<TextBlock Text="{Binding ElementName=FilterListView, Path=DataContext.FilterElementName}" />-->
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Border>
</Popup>
</Button>
<TextBlock Grid.Column="3"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="10"
Text="{Binding ElementName=Self, Path=SelectedNames}" />
</Grid>
</UserControl>
This how the UserControl is used, the FilterElementName="Name" is what I would like to set, depending on the list binded with FilterList list:
<local:BaseFilterUserControl FilterList="{Binding Owners}"
FilterElementName="Name"
SelectedNames="{Binding SelectedNames}"/>
In this case the Owners is a simple IReadOnlyList of an Owner class. The Owner class has a string Name property. But I will use this UserControl again with different list eg. where I would like to use the Versions list's Release property (for the TextBlock inside the UserControl):
<local:BaseFilterUserControl FilterList="{Binding Versions}"
FilterElementName="Release"
SelectedNames="{Binding SelectedReleases}"/>
The ListView is properly populated with items, so the FilterList DependencyProperty is working. But the nested controls are only working when I hard code the bindings:
<TextBlock Text="{Binding Name}" />
For this to work you would need to bind the Path-property of your TextBlocks Text-Binding to the FilterElementName property of your UserControl. Unfortunately the Path property of the Binding class is not a DependencyProperty and therefore not bindable.
One way to to achieve your goal would be to use the DisplayMemberPath property of the ListView, which is bindable:
<ListView x:Name="FilterListView"
Width="150"
Height="300"
ItemsSource="{Binding ElementName=Self, Path=FilterList}"
DisplayMemberPath="{Binding ElementName=self, Path=FilterElementName}"/>
If this approach does not work because you need to specify a more complex ItemTemplate, another way would be create a property of type DataTemplate in your UserControl, use that as ItemTemplate in the ListView and specify it from outside like so:
<local:BaseFilterUserControl FilterList="{Binding Versions}"
SelectedNames="{Binding SelectedReleases}">
<local:BaseFilterUserControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Release}" />
</DataTemplate>
</local:BaseFilterUserControl.ItemTemplate>
</local:BaseFilterUserControl>

How To Organize ItemsPanel Layout?

I have the code below which I want to display a series of buttons horizontally with an image and text label, and below that row of buttons one label for the entire button group. What I currently have displays the buttons in a row, but the group label displays in the same row to the left of the buttons. I'm probably doing this wrong, but I'm impressed that I got this far. I've tried putting the Child ItemsPanel in a StackPanel, but then it shows no buttons - it seems to loose contact with the collection from the parent window. Is there an easy way to connect this collection and organize the label below the buttons? I'm thinking my next try is to collect the data from the parent in the child's code-behind and build an ItemsSource which I will then connect to the ItemTemplate, but I don't really know how to do that either.
Parent window snippet:
<User_Controls:ToolbarGroup GroupLabel="Group 1">
<User_Controls:ImageButton ButtonText="Test 1"/>
<User_Controls:ImageButton ButtonText="Test 2"/>
<User_Controls:ImageButton ButtonText="Test 3"/>
</User_Controls:ToolbarGroup>
Child window code:
<ItemsControl x:Class="Fiducia_WPF.User_Controls.ToolbarGroup"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:User_Controls="clr-namespace:Fiducia_WPF.User_Controls"
mc:Ignorable="d"
Loaded="ItemsControl_Loaded"
d:DesignHeight="170" d:DesignWidth="320">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Height="170" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<User_Controls:ImageButton ButtonText="Temp"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<Label Name="lblGroupTitle" Grid.Row="1" Grid.Column="0" Content="Group X" FontSize="16" FontWeight="Bold" Margin="0,0,0,0" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="Visible" />
</ItemsControl>
You should create a ControlTemplate for your ItemsControl:
<ItemsControl x:Class="Fiducia_WPF.User_Controls.ToolbarGroup" ...>
<ItemsControl.Template>
<ControlTemplate TargetType="ItemsControl">
<StackPanel>
<ItemsPresenter/>
<Label x:Name="lblGroupTitle" Content="Group X" ... />
</StackPanel>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Height="170" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<User_Controls:ImageButton ButtonText="Temp"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Setting the Content of the lblGroupTitle Label may probably better be done by binding it to a property of the ToolbarGroup control:
<Label Content="{Binding GroupTitle,
RelativeSource={RelativeSource AncestorType=User_Controls:ToolbarGroup}}" ... />
I'd just use a straight up Grid:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button Grid.Row="0" Grid.Column="0" ... />
<Button Grid.Row="0" Grid.Column="1" ... />
<Button Grid.Row="0" Grid.Column="2" ... />
<Label Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" />
</Grid>

Rendering templates preformances

I created a propertyGrid like.
I used an Item control which uses a viewmodel object.
The VM object has the property to be display and its relevant DataTemplates.
Everything wosrk fine. My Property grid actually act like the VS property grid
My problem is that every time the use changes the selected object (which lead to changes in the property grid) its took to much time (depends on the amount of the selected object properties). I figured out that the reason for the bad performances is the templates loading and rendering.
so I thought that I may solve this issues by creating real controls for each object, and not just uses templates. (Create buttons and text box for each property) this will solve the loading time, I hope..
1- is there any way to creates real controls (in code) using data templates?
2- is there any other way to improve the performances of my Property grid?
The main code for the propGrid is attached to this post
Thanks, Leon
CODE:
<DataTemplate x:Key="gridItemsControl" >
<Grid Visibility="{Binding Visibility}">
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="60" MaxWidth="350" Width="{Binding Source={StaticResource firstCulWidth},Path=Width,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged,Converter={StaticResource widthToGridLenConverter}}"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0" BorderBrush="#ff333333" BorderThickness="0.5" Grid.ColumnSpan="2"
Visibility ="{Binding IsCategoryItem,Converter={StaticResource isCategoryItemToVisibilityConverter},ConverterParameter=Category}">
<StackPanel Orientation="Horizontal" Background="#FFA9BFD4" Height="25">
<Expander Template="{StaticResource SimpleExpanderTemp}" ExpandDirection="Left" IsExpanded="{Binding IsExpanded, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Margin="3,0,5,0"/>
<Label Content="{Binding Path=CategoryName,Converter={StaticResource propertyNameToDiplayNameConverter}}" FontSize="12" Foreground="White" VerticalAlignment="Top" />
</StackPanel>
</Border>
<Border Grid.Column="0" BorderBrush="#ff333333" BorderThickness="0.5"
Visibility ="{Binding IsCategoryItem,Converter={StaticResource isCategoryItemToVisibilityConverter}}">
<Label Margin="5,0,0,0" Content="{Binding Path=DisplayName,Converter={StaticResource propertyNameToDiplayNameConverter}}"/>
</Border>
<GridSplitter Grid.Row="0" Grid.Column="0" Visibility ="{Binding IsCategoryItem,Converter={StaticResource isCategoryItemToVisibilityConverter}}" HorizontalAlignment="Right" Width="4" Background="Transparent"/>
<Border Grid.Column="1" BorderBrush="#ff333333" BorderThickness="0.5"
Visibility ="{Binding IsCategoryItem,Converter={StaticResource isCategoryItemToVisibilityConverter}}">
<ContentPresenter Grid.Column="1" Margin="10,1,10,1" HorizontalAlignment="Left"
ContentTemplate="{Binding Path=InlineTemplate}" />
</Border>
</Grid>
</DataTemplate>
Update: Here is the container
<ItemsControl Name="propsDataGrid" ItemsSource="{Binding Properties}" ItemTemplate ="{StaticResource gridItemsControl}">
<ItemsControl.ContextMenu>
<ContextMenu>
<MenuItem Header="Close all expanders" Click="MenuItem_Click" Visibility="{Binding IsAlphaBeticSort,Converter={StaticResource boolToVisConverter},ConverterParameter=VisForFalse}"></MenuItem>
<MenuItem Header="Expand all" Name="mnExpandeAll" Click="mnExpandeAll_Click" Visibility="{Binding IsAlphaBeticSort,Converter={StaticResource boolToVisConverter},ConverterParameter=VisForFalse}"></MenuItem>
</ContextMenu>
</ItemsControl.ContextMenu>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!--<ItemsControl.ItemTemplate>
<DataTemplate >
<Grid Visibility="{Binding Visibility}">
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="60" MaxWidth="350" Width="{Binding Source={StaticResource firstCulWidth},Path=Width,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged,Converter={StaticResource widthToGridLenConverter}}"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0" BorderBrush="#ff333333" BorderThickness="0.5" Grid.ColumnSpan="2"
Visibility ="{Binding IsCategoryItem,Converter={StaticResource isCategoryItemToVisibilityConverter},ConverterParameter=Category}">
<StackPanel Orientation="Horizontal" Background="#FFA9BFD4" Height="25">
<Expander Template="{StaticResource SimpleExpanderTemp}" ExpandDirection="Left" IsExpanded="{Binding IsExpanded, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Margin="3,0,5,0"/>
<Label Content="{Binding Path=CategoryName,Converter={StaticResource propertyNameToDiplayNameConverter}}" FontSize="12" Foreground="White" VerticalAlignment="Top" />
</StackPanel>
</Border>
<Border Grid.Column="0" BorderBrush="#ff333333" BorderThickness="0.5"
Visibility ="{Binding IsCategoryItem,Converter={StaticResource isCategoryItemToVisibilityConverter}}">
<Label Margin="5,0,0,0" Content="{Binding Path=DisplayName,Converter={StaticResource propertyNameToDiplayNameConverter}}"/>
</Border>
<GridSplitter Grid.Row="0" Grid.Column="0" Visibility ="{Binding IsCategoryItem,Converter={StaticResource isCategoryItemToVisibilityConverter}}" HorizontalAlignment="Right" Width="4" Background="Transparent"/>
<Border Grid.Column="1" BorderBrush="#ff333333" BorderThickness="0.5"
Visibility ="{Binding IsCategoryItem,Converter={StaticResource isCategoryItemToVisibilityConverter}}">
<ContentPresenter Grid.Column="1" Margin="10,1,10,1" HorizontalAlignment="Left"
ContentTemplate="{Binding Path=InlineTemplate}" />
</Border>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>-->
</ItemsControl>
DataTemplates create "real controls" there is no gain in doing something manually which is done automatically using templates, the only thing you should worry about is virtualization (so that the controls for properties not in view are not created) and how your templates are wired up so that their creation is not triggered needlessly.
(By the way, the way you create the grid, setting its width, looks like it might be redundant, you probably can just share the size.)
To make an ItemsControl virtualize the items you need to:
Set the ItemsPanel to a VirtualizingStackPanel
Have a control template which contains a ScrollViewer around the ItemsPresenter where the VirtualizingStackPanel will be.
Set ScrollViewer.CanContentScroll to true to enable scrolling by item which allows for virtualization.
e.g.
<ItemsControl ItemsSource="{Binding Items}"
ScrollViewer.CanContentScroll="True">
<ItemsControl.Template>
<ControlTemplate>
<Border Name="Bd" Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}" SnapsToDevicePixels="true"
Padding="1">
<ScrollViewer Padding="{TemplateBinding Padding}" Focusable="false">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</ScrollViewer>
</Border>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- ... -->
</ItemsControl>
If this is still too slow you could make the whole ItemsControl part of a template which applies per type as presumably the contents of this grid are dependent on the type of the item, thus a different grid should only be created if the item-type changes, also you might be able to cache the resulting templates as well (i never tried doing that though).

Silverlight - Applying Converters within Templates

I am currently updating the template of a ListPicker. In particular, I'm trying to style the contents of the full mode popup. This information appears to be defined in the following code:
<Popup x:Name="FullModePopup">
<Border Background="{StaticResource PhoneChromeBrush}">
<!-- Popup.Child should always be a Border -->
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<ContentControl Grid.Row="0" Content="{TemplateBinding FullModeHeader}"
Foreground="{StaticResource PhoneForegroundBrush}"
FontFamily="{StaticResource PhoneFontFamilySemiBold}"
FontSize="{StaticResource PhoneFontSizeNormal}"
HorizontalAlignment="Left" Margin="24 12 0 0"/>
<ListBox x:Name="FullModeSelector" Grid.Row="1"
FontSize="{TemplateBinding FontSize}"
Margin="{StaticResource PhoneMargin}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/>
<!-- Ensures all containers will be available during the Loaded event -->
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</Grid>
</Border>
</Popup>
My challenge is, I need to trim the text of each item bound in this popup list. More importantly, I need to do this with a converter. Is this even possible? How do I use a converter in this template? Traditionally, I've used something like:
<TextBlock Text="{Binding Path=Name, Converter={StaticResource myConverter}}" />
How do I apply a Converter to the items in the Popup of my ListPicker?
Thank you!
Is this what you are looking for?
Basically overriding the itemtemplate, putting in a textblock and apply your converter to the binding.
Like this
<ListBox ItemsSource="{StaticResource customers}" Width="350" Margin="0,5,0,10">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Padding="5,0,5,0"
Text="{Binding FirstName, Converter={StaticResource yourConverter}}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Here is the msdn documentation
HTH

How do I allow multiple items/content for a user control in WPF?

I've managed to get as far as redirecting content to my stackpanel as shown:
<UserControl
x:Name="taskItem">
<UserControl.ContentTemplate>
<DataTemplate>
<StackPanel>
<Label x:Name="labelHeader" Content="{Binding ElementName=taskItem,Path=Header}" FontFamily="Tahoma" FontSize="16" FontWeight="Bold" />
<Border BorderThickness="0,1,0,0" BorderBrush="#999999" Margin="5,0,5,0">
<StackPanel Margin="10,5,0,0">
<ContentPresenter Content="{TemplateBinding Content}" />
</StackPanel>
</Border>
</StackPanel>
</DataTemplate>
</UserControl.ContentTemplate>
I'm trying to create a control that has a header, a line under it, and then N number of child contents. However, in it's current implementation it won't allow more than one.
What am I doing wrong here?
A user control by definition has one child since it inherits from ContentControl. Make the user control have all the headers and then make ItemsControl the content of the UserControl. Apply your DataTemplate to the ItemTemplate property of the ItemsControl.
<UserControl x:Class="WindowsApplication1.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Name="MainHeadersGrid" Grid.Row="0">
<TextBlock Text="Put your headers here" />
</Grid>
<ItemsControl Name="childItemsWillGoInHere" ItemsSource="{Binding}" Grid.Row="1">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding PropertyOfItem}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</UserControl>
Now, assign the UserControl's template's DataContext to a collection of the objects you want to display.

Resources