Reputation: 4782
in my WPF project I create a custom ListView in Code Behind. In this ListView is a column that contains a button, defined by a datatemplate in my resource dictionary.
<DataTemplate x:Key="DataTemplate_EditButton">
<Button Style="{DynamicResource Button_Image}" Width="25" ... />
</DataTemplate>
When I initialize the ListView, I create the column with the following code:
GridViewColumn buttonColumn = new GridViewColumn();
DataTemplate dt = Application.Current.TryFindResource("DataTemplate_EditButton") as DataTemplate;
buttonColumn.CellTemplate = dt;
...
gridView.Columns.Add(buttonColumn);
Now I want to bind an event handler to the click event of the button. I cannot do it in the template, because I would need to create a code behind class for the Dictionary and I need the event handler in the ListView-UserControl anyway. When I create the column with the data template there is of course no way to access the button that is created for each row.
What would be the best way to deal with the click event of the buttons created in the described way?
Thanks in advance,
Frank
Upvotes: 2
Views: 3279
Reputation: 101613
Since your template is shared between many controls - good way might be to use routed commands. First declare a command (or use one of existing ones, for example from ApplicationCommands
class):
public static class Commands {
public static RoutedCommand EditRow = new RoutedCommand("Edit", typeof(Commands));
}
Use this command in your template:
<DataTemplate x:Key="DataTemplate_EditButton">
<Button x:Name="button" Command="{x:Static my:Commands.EditRow}" />
</DataTemplate>
Then bind to that command in your control (in constructor):
this.CommandBindings.Add(new CommandBinding(Commands.EditRow, EditButtonClicked));
private void EditButtonClicked(object sender, ExecutedRoutedEventArgs args)
{
var button = args.OriginalSource;
// do what you need here
}
Upvotes: 5
Reputation: 169390
What would be the best way to deal with the click event of the buttons created in the described way?
You need to wait until the Button
elements have actually been created before you can attach the event handler. You could do this by handling the StatusChanged
event for the ListView
's ItemContainerGenerator
.
Please refer to the following sample code. It should give you the idea.
private void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
{
ItemContainerGenerator icg = sender as ItemContainerGenerator;
if (icg.Status == System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated)
{
foreach (var item in icg.Items)
{
var container = icg.ContainerFromItem(item) as ListViewItem;
Button button = FindVisualChild<Button>(container);
if (button != null)
{
button.Click -= Button_Click;
button.Click += Button_Click;
}
}
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("clicked");
}
private static T FindVisualChild<T>(DependencyObject parent) where T : DependencyObject
{
for (int childCount = 0; childCount < VisualTreeHelper.GetChildrenCount(parent); childCount++)
{
DependencyObject child = VisualTreeHelper.GetChild(parent, childCount);
if (child != null && child is T)
return (T)child;
else
{
T childOfChild = FindVisualChild<T>(child);
if (childOfChild != null)
return childOfChild;
}
}
return null;
}
Upvotes: 2