如何通过编程方式触发文本框TextChanged事件

3

我正在使用 .net 4.5,在一个 WPF 项目中。我想要触发一个文本框的TextChangedEvent事件。以下是我的做法:

tb.RaiseEvent(new RoutedEventArgs(TextBox.TextChangedEvent));

我之前在Button.ClickEvent上做过相同类型的RaiseEvent操作,如何做这个?
这会产生以下错误:
异常:抛出:“类型为'System.Windows.RoutedEventArgs'的对象无法转换为类型'System.Windows.Controls.TextChangedEventArgs'。”(System.ArgumentException)抛出了一个System.ArgumentException:“类型为'System.Windows.RoutedEventArgs'的对象无法转换为类型'System.Windows.Controls.TextChangedEventArgs'。”
[编辑]
实际文本框文本更改是通过附加行为处理的,如下所示。我想在另一个控件的附加行为中以编程方式引发事件的地方。在后面的行为中,我确实有文本框对象。
public class textboxTextChangedBehavior : Behavior<TextBox>
{
    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.TextChanged += OnTextChanged;
    }

    protected override void OnDetaching()
    {
        AssociatedObject.TextChanged -= OnTextChanged;
        base.OnDetaching();
    }

    private void OnTextChanged(object sender, TextChangedEventArgs args)
    {
        var textBox = (sender as TextBox);
        if (textBox != null)
        {
            //Populate ObservableCollection
        }
    }
}

我尝试发起事件的地方:

public class FindPopupBehavior : Behavior<Popup>
{
    protected override void OnAttached()
    {
        AssociatedObject.Opened += _OpenFindPopup;
    }

    protected override void OnDetaching()
    {
        AssociatedObject.Opened -= _OpenFindPopup;
    }

    void _OpenFindPopup(object sender, EventArgs e)
    {
        //Fake a TextBox text changed event
        if (TextBoxObject == null)
            return;

        TextBox tb = TextBoxObject as TextBox;

        if (tb.Text == "")
            return;

        tb.RaiseEvent(new RoutedEventArgs(TextBox.TextChangedEvent));
    }

    public static readonly DependencyProperty TextBoxProperty =
        DependencyProperty.Register("TextBoxObject", typeof(TextBox), typeof(FindPopupBehavior), new UIPropertyMetadata(null));

    public object TextBoxObject
    {
        get { return (object)GetValue(TextBoxProperty); }
        set { SetValue(TextBoxProperty, value); }
    }
}

[编辑2]

该文本框位于弹出窗口中。当此弹出窗口关闭时,ObservableCollection会被清除,但文本框的文本仍然存在,只是被隐藏了。如果重新打开弹出窗口并且文本框中有文本,它需要重新填充ObservableCollection。它在textchanged行为中执行此操作。这就是为什么我考虑模拟事件的原因。


专业的开发者不会触发框架事件...总有更好的方法。 - Sheridan
你的帮助一直非常出色,我对你所说的话很感兴趣。 - Hank
5个回答

2
手动引发.NET事件不可取,即使没有其他原因,也是不必要的。想一想...你并不真正想引发一个事件。你不需要事件的功能,那么为什么要引发它呢?
如果我正确理解了你的情况,那么你实际上想做的是执行代码的特定部分。因此,你的问题是由于该代码部分目前被封装在事件处理程序中造成的。然而,这并不意味着你必须引发该事件才能执行该代码。
相反,只需将相关代码简单地移动到一个方法中,你可以从事件处理程序和任何其他地方调用它,而无需不必要地引发任何事件:
private void OnTextChanged(object sender, TextChangedEventArgs args)
{
    var textBox = (sender as TextBox);
    if (textBox != null)
    {
        PopulateObservableCollection();
    }
}

private void PopulateObservableCollection()
{
    // Populate ObservableCollection
}

如果您特别需要访问文本框,则请进一步描述您的情况...总有方法。
更新 >>>
逻辑思考一下... 您想调用此方法并需要访问 Popup 中的 TextBox,并且每次打开 Popup 都需要这样做。那么为什么不处理 Popup.Opened事件 呢?
private void PopupOpened(object sender, EventArgs e)
{
    // Check TextBox and Populate ObservableCollection
}

1
第一段绝对是关键。还在努力想方设法如何从弹出行为中调用新方法? - Hank
我已经考虑过这个问题,我试图避免代码重复。这意味着一开始分离代码是没有意义的。嗯,我想我有了解决方案,我创建了一个单独的静态类,实际上我已经有一个持有我的ObservableCollection的单例类,所以我把方法转移到了这个类中,现在可以从两个行为中访问它了。 - Hank

1
你可以手动触发事件,例如:

如下:

    MouseButtonEventArgs args = new MouseButtonEventArgs(Mouse.PrimaryDevice,100, MouseButton.Left);
    args.RoutedEvent = UIElement.PreviewMouseLeftButtonUpEvent;
    tb.RaiseEvent(args);

0
为什么不直接调用事件的方法? 我为您创建了一个小示例应用程序:
它执行以下操作:
  • 我有一个按钮和一个文本框。文本框上有一个OnTextChanged事件,我想在单击按钮时执行该文本框事件。

首先,您的WPF应用程序,在我的情况下,只包含一个具有文本框和按钮的网格。

<Grid>
    <TextBox Name="txtTextBox"></TextBox>
    <Button Name="btnDemo" Content="Click me"></Button>
</Grid>

我知道,布局方面这并不好看,但只是为了演示。

在您的后台代码中,您需要执行以下几个步骤:

为按钮和文本框添加一个事件处理程序:

public MainWindow()
{
    InitializeComponent();

    btnDemo.Click += BtnDemoOnClick;
    txtTextBox.TextChanged += TxtTextBoxOnTextChanged;
}

这是您的TxtTextBoxOnTextChanged事件的代码:
private void TxtTextBoxOnTextChanged(object sender, TextChangedEventArgs textChangedEventArgs)
{
    MessageBox.Show("The textbox event has been fired.");
}

正如您所看到的,这只是显示一个消息框。

然后是您按钮的事件:

private void BtnDemoOnClick(object sender, RoutedEventArgs routedEventArgs)
{
    TxtTextBoxOnTextChanged(this, new TextChangedEventArgs(routedEventArgs.RoutedEvent, UndoAction.None));
}

希望它有所帮助。


不确定这是否有效,我的实际文本更改事件是由一个附加行为处理的,而这个RaiseEvent发生在另一个控件的附加行为中。我将在问题中提供额外的信息。 - Hank

0

这可能会导致堆栈溢出,因为如果回想起对象的事件,它将再次被textboxTextChangedBehavior捕获

所以,不要创建循环,你可以简单地改变你的

private void OnTextChanged(object sender, TextChangedEventArgs args)
    {
        var textBox = (sender as TextBox);
        if (textBox != null)
        {}
    }

private void OnTextChanged(object sender, TextChangedEventArgs args)
    {
        var textBox = (sender as TextBox);
        if (textBox != null)
        {}

        // now your textboxTextChangedBehavior does't block your event 
        args.Handled = false;
    }

[编辑]

嗯,也许问题更多地在于您如何实现您的目标。

但如果我理解正确,您的弹出窗口已经打开,并且您正在尝试提高TextChanged,对吗?

因此,您可以在_OpenFindPopup中设置文本(添加#或其他内容),这将引发TextChanged,现在您可以删除#并填充ObservableCollection


实际文本不会更改,文本框位于弹出窗口中,当弹出窗口关闭时,ObservableCollection将被清除,当弹出窗口打开并且文本框中有文本时,它需要再次填充ObservableCollection。它被填充的地方是当文本框文本更改时,所以我想我只需模拟一个事件。 - Hank

0

一种方法是你可以这样做...

   EventName.Raise(this, new EventArgs());

我正在使用附加行为进行操作:TextChangedEvent.Raise(tb, new EventArgs()); 似乎没有起作用,或者我走错了路。 - Hank
在问题中添加了一些更多的信息。 - Hank

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接