c# - 将传统事件添加到自定义控件

标签 c# .net wpf xaml

我一直在尝试通过大量谷歌搜索和 SO 来解决这个问题,但不幸的是我无法解决这个问题。我读得越多,就越困惑。

我想构建一个自动完成文本框作为自定义控件。

我的自定义控件:

<UserControl x:Class="ApplicationStyling.Controls.AutoCompleteTextBox"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:local="clr-namespace:ApplicationStyling.Controls"
             mc:Ignorable="d"
             d:DesignHeight="300"
             d:DesignWidth="300"
             Name="AutoCompleteBox">
    <Grid>
        <TextBox Grid.Row="3"
                 Style="{DynamicResource InputBox}"
                 x:Name="SearchBox"
                 Text="{Binding Text}"
                 TextChanged="{Binding ElementName=AutoCompleteBox, Path=TextChanged}"/>

        <ListBox x:Name="SuggestionList"
                 Visibility="Collapsed"
                 ItemsSource="{Binding ElementName=AutoCompleteTextBox, Path=SuggestionsSource}"
                 SelectionChanged="{Binding ElementName=AutoCompleteBox, Path=SelectionChanged}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Label Content="{Binding Label}" />
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
</UserControl>

我的代码背后:

using System.Collections;
using System.Windows;
using System.Windows.Controls;

namespace ApplicationStyling.Controls
{
    /// <summary>
    /// Interaction logic for AutoCompleteTextBox.xaml
    /// </summary>
    public partial class AutoCompleteTextBox : UserControl
    {
           
        public static readonly DependencyProperty SuggestionsSourceProperty;
        public static readonly DependencyProperty TextProperty;

        // Events
        public static readonly RoutedEvent TextChangedProperty;
        public static readonly RoutedEvent SelectionChangedProperty;


        static AutoCompleteTextBox()
        {
            // Attributes
            AutoCompleteTextBox.SuggestionsSourceProperty = DependencyProperty.Register("SuggestionsSource", typeof(IEnumerable), typeof(AutoCompleteTextBox));
            AutoCompleteTextBox.TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(AutoCompleteTextBox));

            // Events
            AutoCompleteTextBox.TextChangedProperty = EventManager.RegisterRoutedEvent("TextChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AutoCompleteTextBox));
            AutoCompleteTextBox.SelectionChangedProperty = EventManager.RegisterRoutedEvent("SelectionChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AutoCompleteTextBox));

        }

        #region Events
        public event RoutedEventHandler TextChanged
        {
            add { AddHandler(TextChangedProperty, value); }
            remove { RemoveHandler(TextChangedProperty, value); }
        }

        // This method raises the Tap event
        void RaiseTextChangedEvent()
        {
            RoutedEventArgs newEventArgs = new RoutedEventArgs(AutoCompleteTextBox.TextChangedProperty);
            RaiseEvent(newEventArgs);
        }



        public event RoutedEventHandler SelectionChanged
        {
            add { AddHandler(SelectionChangedProperty, value); }
            remove { RemoveHandler(SelectionChangedProperty, value); }
        }

        // This method raises the Tap event
        void RaiseSelectionChangedEvent()
        {
            RoutedEventArgs newEventArgs = new RoutedEventArgs(AutoCompleteTextBox.SelectionChangedProperty);
            RaiseEvent(newEventArgs);
        }

        #endregion

        #region DProperties
        /// <summary>
        /// IEnumerable ItemsSource Property for the Suggenstion Box
        /// </summary>
        public IEnumerable SuggestionsSource
        {
            get
            {
                return (IEnumerable)GetValue(AutoCompleteTextBox.SuggestionsSourceProperty);
            }
            set
            {
                SetValue(AutoCompleteTextBox.SuggestionsSourceProperty, value);
            }
        }

        /// <summary>
        /// This is the Text attribute which routes to the Textbox
        /// </summary>
        public string Text
        {
            get
            {
                return (string)GetValue(AutoCompleteTextBox.TextProperty);
            }
            set
            {
                SetValue(AutoCompleteTextBox.TextProperty, value);
            }
        }
        #endregion

        public AutoCompleteTextBox()
        {
            InitializeComponent();
            SearchBox.TextChanged += (sender, args) => RaiseTextChangedEvent();
            SuggestionList.SelectionChanged += (sender, args) => RaiseSelectionChangedEvent();
        }


    }
}

最后,我使用它的方式:

<asc:AutoCompleteTextBox x:Name="ShareAutoCompleteBox"
                                 Grid.Row="3"
                                 SelectionChanged="ShareAutoCompleteBox_SelectionChanged"
                                 TextChanged="ShareAutoCompleteBox_TextChanged"/>

其中 asc 是通过 app.xaml 加载的外包类库的命名空间。

无论如何,我在 XAML 中的 TextBox.TextChanged 属性以及运行代码时遇到的问题:

System.InvalidCastException: Unable to cast object of type 'System.Reflection.RuntimeEventInfo' to type 'System.Reflection.MethodInfo'.

那么这里到底发生了什么?我想将 AutoCompleteTextBox TextChanged 转发到自定义控件模板中的 TextBox。与 SelectionChangedListbox 相同。

我从 https://msdn.microsoft.com/en-us/library/ms752288(v=vs.100).aspx 中获取了大部分代码(对于事件)和其他一些 SO 质疑自定义属性的代码。

不确定,问题是什么,我期待您的帮助。

最佳答案

发生异常是因为您试图将 TextChanged 字段的值绑定(bind)到需要方法引用的属性。它真的让 WPF 感到困惑。 :)

只需从 XAML 中的 TextBox 元素中删除 TextChanged 属性:

TextChanged="{Binding ElementName=AutoCompleteBox, Path=TextChanged}"

您已经在构造函数中订阅了该事件,这就足够了。如果您确实想在构造函数中使用 TextChanged 属性而不是 订阅,那么您可以这样做,但您需要提供一个实际的事件处理程序,例如代码隐藏中的一个方法。该方法将只调用 RaiseTextChangedEvent() 方法,就像您当前的事件处理程序所做的那样。只是它将是类中的命名方法,而不是构造函数中声明的匿名方法。

同样的事情适用于其他事件。


也就是说,您可能会重新考虑实现转发的事件。通常,您的控件的 Text 属性将绑定(bind)到某个模型对象的属性,当该绑定(bind)属性更改时,该对象本身可以做出适当的 react 。它不需要 UserControl 对象上的单独事件来告诉它它的值已更改。

关于c# - 将传统事件添加到自定义控件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36254145/

相关文章:

c# - 如何在保持持久性无知的同时创建丰富的领域对象?

c# - 多线程.NET队列问题

c# - 我如何创建一个项目集合

c# - 惊人的错误 "The given key was not present in the dictionary."

c# - 当 IIS 应用程序加载时,应用程序配置设置是否保存在内存中?

c# - ToUniversalTime() 和 ToLocalTime() 之间的区别

c# - 如何补救 "The breakpoint will not currently be hit. No symbols have been loaded for this document."警告?

wpf - 何时在 WPF 绑定(bind)中使用路径?

wpf - 如何将样式触发器应用于 WPF 中的数据模板

c# - 全局样式不起作用