Wpf 在自定义控件代码中处理事件

Wpf 在自定义控件代码中处理事件,wpf,events,automation,custom-controls,code-behind,Wpf,Events,Automation,Custom Controls,Code Behind,好的,这可能是一个相当愚蠢的问题,但我已经搜索了相当长的时间,但找不到一个有效的解决方案 我有一个从控件继承的自定义控件,其中应包括代码隐藏自动化 例如,选中时选择控件TextBox的所有文本,或在更改该TextBox的内容时生成一个密切匹配的列表 public class vokDataGridEdit : Control { static vokDataGridEdit() { DefaultStyleKeyProperty.OverrideMetadata(

好的,这可能是一个相当愚蠢的问题,但我已经搜索了相当长的时间,但找不到一个有效的解决方案

我有一个从
控件
继承的自定义控件,其中应包括代码隐藏自动化

例如,选中时选择控件
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:我如何将事件
选择内容
(要在获得焦点时选择所有
文本框
内容,请注意:它是
CellEditingTemplate
数据网格
的一部分)绑定到
GotKeyboardFocus
?事件在应用程序代码中通常很好,但对于自定义控件,它们不起作用,因为样式没有真正的“代码隐藏”


问题2:假设我有一个包含单词数组的依赖属性。根据
文本框
的内容,我想从Dependency属性的数组中选择一些单词,并将它们传递给自定义控件中的
列表框
列表框的内容只能由自定义控件管理,不能由使用该控件的任何人管理。是否有首选/规范的MVVM模式来实现此功能?

通常您应该只发布一个问题,而不是多个问题。对于第一个问题,您可以在中使用
事件设置器,例如隐式
样式
ode>UserControl
的资源:

<Style TargetType="TextBox">
    <EventSetter Event="GotKeyboardFocus" Handler="SelectContent"/>
</Style>
在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>
xmlns:i=“clr命名空间:System.Windows.Interactivity;assembly=System.Windows.Interactivity”

部分解决方案:

最后,我让direct控件上的事件正常工作(
上下文菜单中的控件仍然没有获得EventHandler…)

显然,关键是使用
GetTemplateChild()
按名称获取文本框,然后关联事件处理程序:

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

和代码(未显示依赖项属性):

使用系统;
使用System.Windows;
使用System.Windows.Controls;
使用System.Windows.Input;
使用System.Windows.Media;
命名空间应用程序控件
{
/// 
///DataGrid编辑控件(请参阅:https://www.c-sharpcorner.com/article/wpf-routed-events/ 对于路由事件)
/// 
公共类vokDataGridEdit:控件
{
静态vokDataGridEdit()
{
OverrideMetadata(typeof(vokDataGridEdit)、new FrameworkPropertyMetadata(typeof(vokDataGridEdit));
}
应用程序模板()上的公共重写无效
{
base.OnApplyTemplate();
//仅用于演示目的,请检查以前的实例并首先删除处理程序
如果(这个.GetTemplateChild(“TextBox”)是TextBox按钮)
{
button.PreviewMouseLeftButtonDown+=此。选择内容准备;
button.GotKeyboardFocus+=this.SelectContent;
button.MouseDoubleClick+=此.SelectContent;
//button.GotFocus+=this.SelectContent;
}
}
/// 
///准备控件,以确保其在后续事件触发前具有焦点
/// 
private void SelectContentPreparation(对象发送者,鼠标按钮ventargs e)
{
如果(发送方为文本框tb)
{
如果(!tb.IsKeyboardFocusWithin)
{
e、 已处理=正确;
tb.Focus();
}
}
}
私有void SelectContent(对象发送方、路由目标方)
{
如果(发送方为文本框tb)
{
e、 已处理=正确;
tb.SelectAll();
}
}
}
}

Hi Rekshino,我将样式添加到文本框中,但构建失败,声称它需要
x:Class
指令,而且无论如何,处理程序将被标记为在设计器中找不到…代码请参见问题。查看我的答案,将其放到
UserControl
的资源中。处理程序也必须位于
UserControl
中。如果我没有找到,请原谅我很理解。但是这个控件是一个自定义控件,样式在
Generic.xaml
中,而不是
UserControl
中,带有代码隐藏。因此我没有
UserCon
<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();
            }
        }
    }
}