gpt4 book ai didi

c# - 进行隧道事件的正确方法

转载 作者:太空狗 更新时间:2023-10-30 00:51:49 31 4
gpt4 key购买 nike

编辑:我想我问了一些 XY 问题。我真的不关心让隧道事件正常工作,我关心的是让一个事件从父窗口的代码背后引发,然后被拾取并使用react通过作为该窗口的子控件的控件,无需明确告诉子控件其父控件是谁并手动订阅该事件。


我试图在父控件中引发一个事件,并让子控件监听该事件并对其使用react。根据我的研究,我认为我只需要执行一个 RoutedEvent 但我做错了。

这是一个 MCVE,展示了我的尝试,它是一个简单的程序,里面有一个窗口和一个 UserControl。

<Window x:Class="RoutedEventsTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:RoutedEventsTest"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Button Name="button" Click="ButtonBase_OnClick" HorizontalAlignment="Left"
VerticalAlignment="Top">Unhandled in parent</Button>
<local:ChildControl Grid.Row="1"/>
</Grid>
</Window>
using System.Windows;

namespace RoutedEventsTest
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
TestEventHandler += MainWindow_TestEventHandler;
}

void MainWindow_TestEventHandler(object sender, RoutedEventArgs e)
{
button.Content = "Handeled in parent";
e.Handled = false;
}

private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
RaiseEvent(new RoutedEventArgs(TestEvent));
}

public static readonly RoutedEvent TestEvent = EventManager.RegisterRoutedEvent("TestEvent", RoutingStrategy.Tunnel, typeof(RoutedEventHandler), typeof(MainWindow));

public event RoutedEventHandler TestEventHandler
{
add { AddHandler(TestEvent, value); }
remove { RemoveHandler(TestEvent, value); }
}
}
}
<UserControl x:Class="RoutedEventsTest.ChildControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<TextBlock Name="textBlock">Unhandeled in child</TextBlock>
</Grid>
</UserControl>
using System.Windows;
using System.Windows.Controls;

namespace RoutedEventsTest
{
public partial class ChildControl : UserControl
{
public ChildControl()
{
InitializeComponent();
AddHandler(MainWindow.TestEvent, new RoutedEventHandler(TestEventHandler));
}

private void TestEventHandler(object sender, RoutedEventArgs routedEventArgs)
{
textBlock.Text = "Handled in child";
routedEventArgs.Handled = false;
}
}
}

当我运行该程序时,父窗口的 react 与我预期的一样,但子 UserControl 从未运行我传递给 AddHandler 的委托(delegate)。

将子控件更改为

public partial class ChildControl : UserControl
{
public ChildControl()
{
InitializeComponent();
AddHandler(TestEvent, new RoutedEventHandler(TestEventHandler));
}

public static readonly RoutedEvent TestEvent = EventManager.RegisterRoutedEvent("TestEvent", RoutingStrategy.Tunnel, typeof(RoutedEventHandler), typeof(ChildControl));

private void TestEventHandler(object sender, RoutedEventArgs routedEventArgs)
{
textBlock.Text = "Handled in child";
routedEventArgs.Handled = false;
}
}

也没有解决问题。我进行了大量搜索,发现了很多关于如何执行从子级到父级的冒泡事件的示例,但我找不到一个完整的示例来说明如何执行从父级到子级的隧道事件。

最佳答案

如果您查看 MSDN article on routed events in WPF ( archived ) 更仔细一点,您会看到上面写着:

Bubble is the most common and means that an event will bubble (propagate) up the visual tree from the source element until either it has been handled or it reaches the root element. This allows you to handle an event on an object further up the element hierarchy from the source element.

Tunnel events go in the other direction, starting at the root element and traversing down the element tree until they are handled or reach the source element for the event. This allows upstream elements to intercept the event and handle it before the event reaches the source element. Tunnel events have their names prefixed with Preview by convention (such as PreviewMouseDown).

这确实违反直觉,但隧道事件会向源元素传播。在您的例子中,root 元素是 MainWindow,但 source 元素实际上是 ChildControl。当您在 MainWindow 中引发事件时,它恰好是 sourceroot

Source 元素是调用 RaiseEvent 方法的元素,即使 RoutedEvent 不是该元素的成员也是如此。 ,因为 RaiseEvent 是一个公共(public)方法,其他元素可以使另一个元素成为隧道事件的源元素。

换句话说,您需要类似的东西(添加了 Preview 前缀,因为这是隧道事件的约定):

// ChildControl is the event source
public partial class ChildControl : UserControl
{
public readonly static RoutedEvent PreviewEvent =
EventManager.RegisterRoutedEvent(
"PreviewEvent",
RoutingStrategy.Tunnel,
typeof(RoutedEventHandler),
typeof(ChildControl));

public ChildControl()
{
InitializeComponent();
AddHandler(PreviewEvent,
new RoutedEventHandler((s, e) => Console.WriteLine("Child handler")));
}

private void Button_Click(object sender, RoutedEventArgs e)
{
// make this control the source element for tunneling
this.RaiseEvent(new RoutedEventArgs(PreviewEvent));
}
}

MainWindow 中:

public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
AddHandler(ChildControl.PreviewEvent,
new RoutedEventHandler((s, e) => Console.WriteLine("Parent handler")));
}
}

如果您使用现有的隧道事件,事情会更简单,但请注意,它们仍然在 Button 上定义为源,而不是根元素:

// this uses the existing Button.PreviewMouseUpEvent tunneled event
public partial class ChildControl : UserControl
{
public ChildControl()
{
InitializeComponent();
AddHandler(Button.PreviewMouseUpEvent,
new RoutedEventHandler((s, e) => Console.WriteLine("Child handler")));
}
}

public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
AddHandler(Button.PreviewMouseUpEvent,
new RoutedEventHandler((s, e) => Console.WriteLine("Parent handler")));
}
}

这还会将以下内容输出到控制台(鼠标松开时):

Parent handler
Child handler

当然,如果您在父处理程序中将 Handled 属性设置为 true,则不会调用子处理程序。

[更新]

如果你想从父控件引发事件,但让子控件成为事件源,你可以简单地从外部调用子控件的公共(public) RaiseEvent 方法:

public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
AddHandler(ChildControl.PreviewEvent,
new RoutedEventHandler((s, e) => Console.WriteLine("Parent handler")));
}

private void Button_Click(object sender, RoutedEventArgs e)
{
// raise the child event from the main window
childCtrl.RaiseEvent(new RoutedEventArgs(ChildControl.PreviewEvent));
}
}

// child control handles its routed event, but doesn't know who triggered it
public partial class ChildControl : UserControl
{
public readonly static RoutedEvent PreviewEvent =
EventManager.RegisterRoutedEvent(
"PreviewEvent",
RoutingStrategy.Tunnel,
typeof(RoutedEventHandler),
typeof(ChildControl));

public ChildControl()
{
InitializeComponent();
AddHandler(PreviewEvent,
new RoutedEventHandler((s, e) => Console.WriteLine("Child handler")));
}
}

根据您的实际用例,您似乎希望父窗口在没有实际隧道的情况下通知子控件。在那种情况下,我不确定您是否需要事件? IE。这有什么问题:

public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}

private void Button_Click(object sender, RoutedEventArgs e)
{
childCtrl.DoSomething(this, "MainWindow just sent you an event");
}
}

public partial class ChildControl : UserControl
{
public ChildControl()
{
InitializeComponent();
}

public void DoSomething(UIElement sender, string message)
{
Console.WriteLine(sender.ToString() + ": " + message);
}
}

关于c# - 进行隧道事件的正确方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24876053/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com