Обрабатывать события в пользовательском коде управления - PullRequest
0 голосов
/ 25 марта 2020

Хорошо, это, вероятно, довольно тупой вопрос, но я долго искал, но не смог найти решение для этого, которое работает ...

У меня есть пользовательский элемент управления, унаследованный от Control, который должен включать код, стоящий за автоматизацией.

Для выбора выберите весь текст элементов управления TextBox, если он выбран, или создайте список близких совпадений при изменении содержимого этого TextBox.

public class vokDataGridEdit : Control
{
    static vokDataGridEdit()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(vokDataGridEdit), new FrameworkPropertyMetadata(typeof(vokDataGridEdit)));

        // Events internal to control (??? found on some how-to's)
        EventManager.RegisterClassHandler(typeof(vokDataGridEdit), UIElement.GotKeyboardFocusEvent, new RoutedEventHandler(OnSelectContent), true);
    }

    // Dependecy Properties ...

    // The Event that shall Fire when the TextBox gets Focus / Editing Mode
    public static void SelectContent(object sender, RoutedEventArgs e)
    {
        if (sender is TextBox tb)
        {
            tb.SelectAll();
        }
    }
}

И элементы управления Стиль шаблона:

<ResourceDictionary xmlns       = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x     = "http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:ccont = "clr-namespace:App.Controls">

    <!-- Default style for the Validation Buttons -->
    <Style TargetType="{x:Type ccont:vokDataGridEdit}">

        <Setter Property="SnapsToDevicePixels"  Value="true" />

        <Setter Property="Template">
            <Setter.Value>

                <ControlTemplate TargetType="{x:Type ccont:vokDataGridEdit}">

                    <TextBox Text                               = "{Binding Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource AncestorType=ccont:vokDataGridEdit}}"
                             BorderThickness                    = "0"
                             ContextMenuService.Placement       = "Right"
                             ContextMenuService.PlacementTarget = "{Binding Path=., RelativeSource={RelativeSource Self}}"
                             GotKeyboardFocus                   = "SelectContent">

                        <TextBox.ContextMenu>
                            <ContextMenu>
                                <ContextMenu.Template>
                                    <ControlTemplate>

                                        <Border CornerRadius    = "5"
                                                Background      = "LightGray"
                                                BorderThickness = "1" 
                                                BorderBrush     = "Gray"
                                                Padding         = "2">

                                            <StackPanel Orientation="Vertical">

                                                <!-- Title -->
                                                <TextBlock Text="Test" />

                                                <!-- TODO: List of matches -->
                                                <TextBox Text               = "{Binding Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource AncestorType=ccont:vokDataGridEdit}}" 
                                                         BorderThickness    = "0" />

                                            </StackPanel>

                                        </Border>

                                    </ControlTemplate>
                                </ContextMenu.Template>
                            </ContextMenu>
                        </TextBox.ContextMenu>

                    </TextBox>

                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</ResourceDictionary>

Вопрос 1: Как связать событие SelectContent (чтобы выбрать все содержимое TextBox, когда оно получит фокус, нб : это часть DataGrid для CellEditingTemplate) до GotKeyboardFocus? События обычно нормальны в коде приложений, но для пользовательского элемента управления они не работают, так как на самом деле не существует «кода позади» для стиля ...

Вопрос 2: Предполагая, что я Свойство зависимости, содержащее массив слов. Основываясь на содержимом TextBox, я хотел бы выбрать несколько слов из массива в свойстве зависимости и передать их в ListBox в пользовательском элементе управления (Контент ListBox должен управляться только Пользовательский элемент управления, никем не использующий этот элемент управления. Есть ли предпочтительная / каноническая схема MVVM для реализации этого?

Ответы [ 2 ]

1 голос
/ 25 марта 2020

Обычно вы должны оставлять только один вопрос, а не несколько. Что касается первого, вы можете использовать EventSetter, например, в неявных Style в ресурсах UserControl:

<Style TargetType="TextBox">
    <EventSetter Event="GotKeyboardFocus" Handler="SelectContent"/>
</Style>

По второму вопросу - реализовать свойство, которое является подмножеством вашего списка, и обновить его. соответственно, например, если свойство зависимости было изменено (см. свойство измененный обратный вызов) или были изменены другие значения, от которых зависит подмножество.

В качестве альтернативы вы можете использовать поведение для TextBox и обрабатывать там необходимые вам события. См., Например, выбрать все поведение:

public class SelectAllBehavior : Behavior<TextBox>
{
    private bool _doSelectAll = false;
    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.GotFocus += AssociatedObject_GotFocus;
        AssociatedObject.PreviewMouseUp += AssociatedObject_MouseUp;
        AssociatedObject.PreviewMouseDown += AssociatedObject_MouseDown;
    }

    private void AssociatedObject_MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
    {
        if (_doSelectAll)
        {
            AssociatedObject.Dispatcher.BeginInvoke((Action) (()=>{ AssociatedObject.SelectAll(); }));
        }
        _doSelectAll = false;
    }

    private void AssociatedObject_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
    {
        _doSelectAll = !AssociatedObject.IsFocused;
    }

    private void AssociatedObject_GotFocus(object sender, System.Windows.RoutedEventArgs e)
    {
        AssociatedObject.SelectAll();
    }

    protected override void OnDetaching()
    {
        AssociatedObject.GotFocus -= AssociatedObject_GotFocus;
        AssociatedObject.PreviewMouseUp -= AssociatedObject_MouseUp;
        AssociatedObject.PreviewMouseDown -= AssociatedObject_MouseDown;
        base.OnDetaching();
    }
}

Использование этого поведения в XAML:

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
<TextBox Text="Some text">
    <i:Interaction.Behaviors>
        <local:SelectAllBehavior/>
    </i:Interaction.Behaviors>
</TextBox>
0 голосов
/ 26 марта 2020

Частичное решение:

Наконец, я получил событие на прямом управлении для работы (элементы управления в ContextMenu все еще не получают EventHandlers ...).

Очевидно, что точка использовала GetTemplateChild(), чтобы получить TextBox по имени, а затем связать обработчики событий:

<ResourceDictionary xmlns       = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x     = "http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:ccont = "clr-namespace:App.Controls">

    <!-- Default style for the Validation Buttons -->
    <Style TargetType="{x:Type ccont:vokDataGridEdit}">

        <Setter Property="SnapsToDevicePixels"  Value="true" />

        <Setter Property="Template">
            <Setter.Value>

                <ControlTemplate TargetType="{x:Type ccont:vokDataGridEdit}">

                    <TextBox Text                               = "{Binding Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource AncestorType=ccont:vokDataGridEdit}}"
                             BorderThickness                    = "0"
                             ContextMenuService.Placement       = "Right"
                             ContextMenuService.PlacementTarget = "{Binding Path=., RelativeSource={RelativeSource Self}}"
                             x:Name                             = "TextBox">

                        <TextBox.ContextMenu>
                            <ContextMenu x:Name="Menu">
                                <ContextMenu.Template>
                                    <ControlTemplate>

                                        <Border CornerRadius    = "5"
                                                Background      = "LightGray"
                                                BorderThickness = "1" 
                                                BorderBrush     = "Gray"
                                                Padding         = "2">

                                            <StackPanel Orientation="Vertical">

                                                <!-- Title -->
                                                <TextBlock Text="Test" x:Name = "Test" />

                                                <!-- TODO: List of matches -->
                                                <TextBox Text               = "{Binding Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource AncestorType=ccont:vokDataGridEdit}}" 
                                                         BorderThickness    = "0" />

                                            </StackPanel>

                                        </Border>

                                    </ControlTemplate>
                                </ContextMenu.Template>
                            </ContextMenu>
                        </TextBox.ContextMenu>

                    </TextBox>

                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</ResourceDictionary>

и код (свойства зависимости не показаны):

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace App.Controls
{
    /// <summary>
    /// DataGrid Edit control (see: https://www.c-sharpcorner.com/article/wpf-routed-events/ for RoutedEvents)
    /// </summary>
    public class vokDataGridEdit : Control
    {
        static vokDataGridEdit()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(vokDataGridEdit), new FrameworkPropertyMetadata(typeof(vokDataGridEdit)));
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            // Demo purpose only, check for previous instances and remove the handler first  
            if (this.GetTemplateChild("TextBox") is TextBox button)
            {
                button.PreviewMouseLeftButtonDown   += this.SelectContentPreparation;
                button.GotKeyboardFocus             += this.SelectContent;
                button.MouseDoubleClick             += this.SelectContent;
                //button.GotFocus                     += this.SelectContent;
            }
        }

        /// <summary>
        /// Prepare the Control to ensure it has focus before subsequent event fire
        /// </summary>
        private void SelectContentPreparation(object sender, MouseButtonEventArgs e)
        {
            if (sender is TextBox tb)
            {
                if (!tb.IsKeyboardFocusWithin)
                {
                    e.Handled = true;
                    tb.Focus();
                }
            }
        }

        private void SelectContent(object sender, RoutedEventArgs e)
        {
            if (sender is TextBox tb)
            {
                e.Handled = true;
                tb.SelectAll();
            }
        }
    }
}
...