简体   繁体   中英

Binding an event to a control created with a data template in WPF

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

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
}

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;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM