I have some ListBoxes in my application which will contain contacts of a phonebook. I want all these ListBoxes to have a DataTemplate as their ItemsTemplates with some functionality like, edit, remove and view as the code below shows:
<DataTemplate x:Key="ContactItemTemplate">
<TextBlock Foreground="Black" Background="{StaticResource DataTemplateBackgroundBrush}" Padding="5,10" Margin="4,3">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} {1}">
<Binding Path="FirstName"/>
<Binding Path="LastName"/>
</MultiBinding>
</TextBlock.Text>
<TextBlock.ContextMenu>
<ContextMenu FontFamily="B Yekan">
<MenuItem Header="Edit" Click="btn_EditContact_Click"/>
<MenuItem Header="Delete" Click="btn_DeleteContact_Click"/>
<MenuItem Header="View" Click="btn_EditContact_Click"/>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</DataTemplate>
It can't be written as a style in a ResourceDictionary and be added to the controls since eventhandlers can not be presented in ResourceDictionaries. So one way is to copy this template and it's handlers in every Window/Page that have a Contact ListBox like below:
<Page.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Styles/Controls_Style.xaml"/>
</ResourceDictionary.MergedDictionaries>
<DataTemplate x:Key="AttachmentsTemplate">
<Border Height="150" Width="120" BorderThickness="1" BorderBrush="{StaticResource DefaultBorderBrush}" Margin="2">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Text="{Binding Title}" HorizontalAlignment="Center" VerticalAlignment="Bottom" TextWrapping="WrapWithOverflow"/>
<Image Grid.Row="1" Source="{Binding Image}" Margin="5,0"/>
</Grid>
</Border>
</DataTemplate>
</ResourceDictionary>
</Page.Resources>
Is there any other way that I can write a DataTemplate with some functionality once and use it any where I want? Should I write a UserControl instead of a DataTemplate?
You can have event handlers in your dictionary, as long as they are part of the dictionary -- i.e, the dictionary has some code behind it. This is analogous to Page
s and UserControl
s; you need to add an x:Class
attribute. For example, Styles.xml might look like so:
<ResourceDictionary
x:Class="WPF.Styles"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1">
<DataTemplate x:Key="ContactItemTemplate" DataType="local:Person">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Column="0"
Foreground="Black"
Background="Yellow"
Padding="5,10"
Margin="4,3"
Text="{Binding Name}"
>
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Header="Edit!" Click="btn_EditContact_Click"/>
<MenuItem Header="Delete!" Click="btn_DeleteContact_Click"/>
<MenuItem Header="View!" Click="btn_EditContact_Click"/>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
<Button
Grid.Column="1"
Content="Edit"
Click="btn_EditContact_Click"/>
</Grid>
</DataTemplate>
</ResourceDictionary>
...with a corresponding Styles.cs (note the partial
):
public sealed partial class Styles
{
private void btn_EditContact_Click(object sender, EventArgs args)
{
Debug.WriteLine(args);
}
private void btn_DeleteContact_Click(object sender, EventArgs args)
{
Debug.WriteLine(args);
}
}
In my window, something like this:
<Grid.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Styles.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Grid.Resources>
...
<ItemsControl
ItemsSource="{Binding People}"
Grid.Row="3"
ItemTemplate="{StaticResource ContactItemTemplate}" />
For completeness, model object:
public class Person
{
public string Name { get; }
public Person(string name)
{
Name = name;
}
}
...and ItemsSource
:
public Person[] People { get; } =
{
new Person("Donald Duck"),
new Person("Mickey Mouse"),
new Person("Darth Vader"),
};
In general this works fine -- clicking the button, for example, invokes btn_EdxtContact_Click
. Sadly, there's a hitch with your scenario involving the context menu of a TextBox
;
details and possible workarounds in this post.
A small piece of additional info: The comments on this article has some discussion on ContextMenu
templating that might be illuminating.
...and one more TextBox
ContextMenu
link that might prove useful.