Как я могу поймать событие, сгенерированное дочерним элементом в родительском элементе управления? - PullRequest
0 голосов
/ 03 мая 2019

Я создал элемент управления listbox и переопределил стиль ListBoxItem следующим образом:

    <Style TargetType="{x:Type customControls:CheckedListBox}"
           BasedOn="{StaticResource {x:Type ListBox}}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate>
                    <Expander x:Name="PART_Expander" IsExpanded="True">
                        <ItemsPresenter/>
                    </Expander>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        <Setter Property="SelectionMode" Value="Multiple"/>
        <Style.Resources>
            <Style TargetType="ListBoxItem" x:Name="PART_ListBoxItem">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate>
                            <CheckBox Selector.IsSelected="True" IsChecked="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}, Path=IsSelected}" Content="{Binding Path=.}">
                            </CheckBox>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
                <Setter Property="IsSelected" Value="True"/>
            </Style>
        </Style.Resources>
    </Style>

В коде позади CheckedListBox я вызываю событие, когда нажимаются какие-либо флажки:

       protected override void OnSelectionChanged(SelectionChangedEventArgs e)
        {
            base.OnSelectionChanged(e);
            RaiseRefreshDataEvent(e.RemovedItems);
        }
        protected virtual void RaiseRefreshDataEvent(IList changedItems)
        {
            RefreshDataSetEventArgs args = new RefreshDataSetEventArgs(CheckedListBox.RefreshDataSetEvent, changedItems);
            RaiseEvent(args);
        }
        public static readonly RoutedEvent RefreshDataSetEvent =
    EventManager.RegisterRoutedEvent("RefreshDataSet", RoutingStrategy.Bubble,
    typeof(RefreshDataSetEventHandler), typeof(CheckedListBox));

        public event RefreshDataSetEventHandler RefreshDataSet
        {
            add { AddHandler(RefreshDataSetEvent, value); }
            remove { RemoveHandler(RefreshDataSetEvent, value); }
        }


        public delegate void RefreshDataSetEventHandler(Object sender, RefreshDataSetEventArgs e);
 public class RefreshDataSetEventArgs : RoutedEventArgs
        {
            public IList ValueChanged { get; set; }
            public RefreshDataSetEventArgs(RoutedEvent routedEvent, IList valuesChanged) : base(routedEvent)
            {
                ValueChanged = valuesChanged;
            }
        }

Теперь я хочу перехватить RefreshDataSetEvent в одном из родительских элементов CheckedListBox.Я смог сделать это, просматривая объекты визуального дерева, находя CheckedListBox и добавляя метод из родительского класса к объекту, используя RefreshDataSet.Это кажется как хак для меня.Я ожидал бы, что если событие действительно пузырится в визуальном дереве, я должен быть в состоянии перехватить его без необходимости явного добавления обработчика к исходному объекту.Любая идея, как я могу поймать это событие без явной установки обработчика объекта?

EDIT

CheckBoxList используется в другом настраиваемом элементе управления с именем FilterDataGrid (это класс, который я хотел быобрабатывать событие).Вот стиль для FilterDataGrid:

<Style TargetType="{x:Type customControls:FilterDataGrid}"
            BasedOn="{StaticResource {x:Type DataGrid}}">
        <Style.Resources>
            <Style TargetType="{x:Type DataGridColumnHeader}" x:Name="PART_DataGridColumnHeader">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate>
                            <StackPanel>
                                <customControls:CheckedListBox ItemsSource="{Binding Path=.}"/>
                            </StackPanel>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
                <Setter Property="Height" Value="Auto"/>
                <Setter Property="Background" Value="Black"/>
            </Style>
        </Style.Resources>
    </Style>

Итак, когда я пытаюсь добавить customControls: CheckedListBox.RefreshDataSet = "TestThis" к стилю, я получаю сообщение об ошибке, указывающее на использование EventSetter.Когда я добавляю следующее к стилю для установщика событий, я получаю «Неизвестная ошибка сборки, ссылка на объект не установлена ​​на экземпляр объекта»: Согласно документам, вы не можете использовать набор событий в файле Generic.xaml,Или, по крайней мере, это то, что я извлек из:

Установщики событий не могут использоваться в стиле, который содержится в словаре ресурсов темы.Это связано с тем, что словарь ресурсов темы во время выполнения часто представляет собой потерянные двоичные файлы XAML (BAML) и не имеет какой-либо определенной области, где может существовать сопровождающий код, определяющий обработчики.

Так что ямне интересно, как мне настроить Eventhandler, если я не могу указать его в файле Themes / Generic.xaml.

1 Ответ

0 голосов
/ 06 мая 2019

Мне удалось заставить это работать, зарегистрировав обработчик класса в EventManager.В открытый конструктор для класса FilterDataGrid поместите следующий код:

EventManager.RegisterClassHandler(typeof(FilterDataGrid), CheckedListBox.RefreshDataSetEvent, new CheckedListBox.RefreshDataSetEventHandler(FilterDataGridRefreshEventHandler));

Учитывая, что оба FilterDataGrid и CheckedListBox являются пользовательскими элементами управления, я решил, что жесткая связь между ними была бы приемлемой (не стесняйтесь, дайте мне знатьиз потенциальных ловушек, я все еще довольно новичок в этой вещи управления разработки).

...