将传统事件添加到自定义控件。

时间:2020-11-27 00:07:21

I have been trying to figure this out with lots of googling and SO, but unfortunately I cannot solve this issue. The more I read, the more confused I get.

我一直试图用大量的谷歌搜索来解决这个问题,但不幸的是我无法解决这个问题。我读得越多,就越糊涂。

I would like to build an autocomplete textbox as a custom control.

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

My CustomControl:

我的CustomControl:

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

My Code Behind:

我的代码:

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


    }
}

And lastly, the way I use it:

最后,我使用它的方式:

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

where asc is the namespace for the outsourced class library which is loaded via app.xaml.

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

Anyways, the issues I am getting in the XAML at the TextBox.TextChanged attribute, and when running the code:

无论如何,我在文本框中获取的XAML中的问题。TextChanged属性,运行代码时:

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

系统。InvalidCastException:无法对类型的System.Reflection对象进行强制转换。RuntimeEventInfo System.Reflection.MethodInfo”“输入”。

So what exactly is going on here? I would like to forward the AutoCompleteTextBox TextChanged to the TextBox within the Custom Control Template. Same with the SelectionChanged to the Listbox.

这里到底发生了什么?我希望将AutoCompleteTextBox TextBox文本更改转发到自定义控件模板中的TextBox。选择更改为列表框也是一样。

I took most of the code from either https://msdn.microsoft.com/en-us/library/ms752288(v=vs.100).aspx (for the events) and from some other SO questions the code for the custom properties.

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

Not sure, what the problem is and I am looking forward to your help.

不知道是什么问题,我期待您的帮助。

1 个解决方案

#1


0  

The exception is happening because you are trying to bind the value of the TextChanged field to an attribute that expects a method reference. It's really confusing to WPF. :)

发生异常是因为您试图将TextChanged字段的值绑定到期望方法引用的属性。这让WPF很困惑。:)

Just remove the TextChanged attribute from the TextBox element in your XAML:

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

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

You already subscribe to the event in your constructor, which is enough. If you do want to use the TextChanged attribute instead of subscribing in the constructor, then you can do that, but you need to provide an actual event handler, e.g. a method in the code-behind. That method would just call the RaiseTextChangedEvent() method, just as your current event handler does. It's just that it would be a named method in the class instead of an anonymous method declared in the constructor.

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

Same thing applies to the other event.

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


That said, you might reconsider implementing the forwarded events at all. Typically, your control's Text property would be bound to the property of some model object, which can itself react appropriately when that bound property changes. It shouldn't need a separate event on the UserControl object to tell it that its value has changed.

也就是说,您可能会重新考虑实现转发事件。通常,控件的文本属性会绑定到某个模型对象的属性,当绑定属性发生变化时,模型对象本身可以做出适当的响应。它不需要在UserControl对象上单独的事件来告诉它它的值已经改变了。

#1


0  

The exception is happening because you are trying to bind the value of the TextChanged field to an attribute that expects a method reference. It's really confusing to WPF. :)

发生异常是因为您试图将TextChanged字段的值绑定到期望方法引用的属性。这让WPF很困惑。:)

Just remove the TextChanged attribute from the TextBox element in your XAML:

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

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

You already subscribe to the event in your constructor, which is enough. If you do want to use the TextChanged attribute instead of subscribing in the constructor, then you can do that, but you need to provide an actual event handler, e.g. a method in the code-behind. That method would just call the RaiseTextChangedEvent() method, just as your current event handler does. It's just that it would be a named method in the class instead of an anonymous method declared in the constructor.

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

Same thing applies to the other event.

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


That said, you might reconsider implementing the forwarded events at all. Typically, your control's Text property would be bound to the property of some model object, which can itself react appropriately when that bound property changes. It shouldn't need a separate event on the UserControl object to tell it that its value has changed.

也就是说,您可能会重新考虑实现转发事件。通常,控件的文本属性会绑定到某个模型对象的属性,当绑定属性发生变化时,模型对象本身可以做出适当的响应。它不需要在UserControl对象上单独的事件来告诉它它的值已经改变了。