- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
编辑:我想我问了一些 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
中引发事件时,它恰好是 source 和 root。
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/
我想了解 Ruby 方法 methods() 是如何工作的。 我尝试使用“ruby 方法”在 Google 上搜索,但这不是我需要的。 我也看过 ruby-doc.org,但我没有找到这种方法。
Test 方法 对指定的字符串执行一个正则表达式搜索,并返回一个 Boolean 值指示是否找到匹配的模式。 object.Test(string) 参数 object 必选项。总是一个
Replace 方法 替换在正则表达式查找中找到的文本。 object.Replace(string1, string2) 参数 object 必选项。总是一个 RegExp 对象的名称。
Raise 方法 生成运行时错误 object.Raise(number, source, description, helpfile, helpcontext) 参数 object 应为
Execute 方法 对指定的字符串执行正则表达式搜索。 object.Execute(string) 参数 object 必选项。总是一个 RegExp 对象的名称。 string
Clear 方法 清除 Err 对象的所有属性设置。 object.Clear object 应为 Err 对象的名称。 说明 在错误处理后,使用 Clear 显式地清除 Err 对象。此
CopyFile 方法 将一个或多个文件从某位置复制到另一位置。 object.CopyFile source, destination[, overwrite] 参数 object 必选
Copy 方法 将指定的文件或文件夹从某位置复制到另一位置。 object.Copy destination[, overwrite] 参数 object 必选项。应为 File 或 F
Close 方法 关闭打开的 TextStream 文件。 object.Close object 应为 TextStream 对象的名称。 说明 下面例子举例说明如何使用 Close 方
BuildPath 方法 向现有路径后添加名称。 object.BuildPath(path, name) 参数 object 必选项。应为 FileSystemObject 对象的名称
GetFolder 方法 返回与指定的路径中某文件夹相应的 Folder 对象。 object.GetFolder(folderspec) 参数 object 必选项。应为 FileSy
GetFileName 方法 返回指定路径(不是指定驱动器路径部分)的最后一个文件或文件夹。 object.GetFileName(pathspec) 参数 object 必选项。应为
GetFile 方法 返回与指定路径中某文件相应的 File 对象。 object.GetFile(filespec) 参数 object 必选项。应为 FileSystemObject
GetExtensionName 方法 返回字符串,该字符串包含路径最后一个组成部分的扩展名。 object.GetExtensionName(path) 参数 object 必选项。应
GetDriveName 方法 返回包含指定路径中驱动器名的字符串。 object.GetDriveName(path) 参数 object 必选项。应为 FileSystemObjec
GetDrive 方法 返回与指定的路径中驱动器相对应的 Drive 对象。 object.GetDrive drivespec 参数 object 必选项。应为 FileSystemO
GetBaseName 方法 返回字符串,其中包含文件的基本名 (不带扩展名), 或者提供的路径说明中的文件夹。 object.GetBaseName(path) 参数 object 必
GetAbsolutePathName 方法 从提供的指定路径中返回完整且含义明确的路径。 object.GetAbsolutePathName(pathspec) 参数 object
FolderExists 方法 如果指定的文件夹存在,则返回 True;否则返回 False。 object.FolderExists(folderspec) 参数 object 必选项
FileExists 方法 如果指定的文件存在返回 True;否则返回 False。 object.FileExists(filespec) 参数 object 必选项。应为 FileS
我是一名优秀的程序员,十分优秀!