在多 window UWP 应用程序中引发和处理事件
Raising and handling events in a multi window UWP app
我创建了一些测试代码,这样我就可以尝试弄清楚如何在 UWP 中正确使用多个 windows。我想看看我是否可以触发一个事件并让多个 windows 在事件处理程序中更新它们的 UI。我终于得到了一些有用的东西,但我不完全确定它为什么有用。
这是在我的页面中创建的 class
public class NumberCruncher
{
private static Dictionary<int, Tuple<CoreDispatcher, NumberCruncher>> StaticDispatchers { get; set; }
static NumberCruncher()
{
StaticDispatchers = new Dictionary<int, Tuple<CoreDispatcher, NumberCruncher>>();
}
public NumberCruncher()
{
}
public event EventHandler<NumberEventArgs> NumberEvent;
public static void Register(int id, CoreDispatcher dispatcher, NumberCruncher numberCruncher)
{
StaticDispatchers.Add(id, new Tuple<CoreDispatcher, NumberCruncher>(dispatcher, numberCruncher));
}
public async Task SendInNumber(int id, int value)
{
foreach (var dispatcher in StaticDispatchers)
{
await dispatcher.Value.Item1.RunAsync(CoreDispatcherPriority.Normal, () =>
{
Debug.WriteLine($"invoking {dispatcher.Key}");
dispatcher.Value.Item2.NumberEvent?.Invoke(null, new NumberEventArgs(id, value));
});
}
}
}
这是我的 MainPage 代码的相关部分
NumberCruncher numberCruncher;
public MainPage()
{
this.InitializeComponent();
numberCruncher = new NumberCruncher();
numberCruncher.NumberEvent += NumberCruncher_NumberEvent;
}
private async void NumberCruncher_NumberEvent(object sender, NumberEventArgs e)
{
listView.Items.Add($"{e.Id} sent {e.Number}");
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
NumberCruncher.Register(ApplicationView.GetForCurrentView().Id, Window.Current.Dispatcher, numberCruncher);
}
我有一个按钮可以创建 MainPage 的新视图。然后我有另一个调用 SendInNumber()
方法的按钮。
当我导航到 MainPage 时,我为 window 注册了 Dispatcher 和 NumberCruncher 的实例。然后在触发事件时,我为特定的 Dispatcher 使用 NumberCruncher EventHandler。
这不会抛出封送处理异常。如果我尝试使用当前 class 的 EventHandler
await dispatcher.Value.Item1.RunAsync(CoreDispatcherPriority.Normal, () =>
{
Debug.WriteLine($"invoking {dispatcher.Key}");
NumberEvent?.Invoke(null, new NumberEventArgs(id, value));
});
我在尝试将项目添加到 listView 时收到封送处理异常。但是,如果我在我的 MainPage 中维护 SynchronizationContext,然后使用 SynchronizationContext.Post 更新 listView。效果很好
SynchronizationContext synchronizationContext;
public MainPage()
{
this.InitializeComponent();
numberCruncher = new NumberCruncher();
numberCruncher.NumberEvent += NumberCruncher_NumberEvent;
synchronizationContext = SynchronizationContext.Current;
}
private async void NumberCruncher_NumberEvent(object sender, NumberEventArgs e)
{
synchronizationContext.Post(_ =>
{
listView.Items.Add($"{e.Id} sent {e.Number}");
}, null);
}
然而,这不起作用并在尝试更新 listView 时抛出封送处理异常。
private async void NumberCruncher_NumberEvent(object sender, NumberEventArgs e)
{
await CoreApplication.GetCurrentView().CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
listView.Items.Add($"{e.Id} sent {e.Number}");
});
}
这是怎么回事?
要记住的重要一点是,当一个事件被触发时,订阅的方法在与 Invoke
方法相同的线程上被调用。
If I try to use the current class's EventHandler
, I get a marshaling exception when trying to add the item to the listView
.
发生第一个错误是因为您试图在 另一个 Window 的调度程序 上触发当前 class 的事件(dispatcher.Value.Item1
).假设事件发生在 Window 1 并且 dispatcher.Value.Item1
属于 Window 2。一旦进入 Dispatcher
块,您就是 Window 2 的 UI 线程并触发 的 NumberEvent
]Window 1's NumberCruncher
将 运行 Window 1's 处理程序 Window 2's UI 线程并导致异常。
However this does not work and throws a marshaling exception when trying to update listView
.
GetCurrentView()
方法 returns 当前活动视图。因此,无论哪个应用程序视图在那一刻处于活动状态,都将被返回。在您的情况下,它将是您单击按钮的那个。如果您在目标 Window 的 UI 线程上调用 Invoke
方法,则不需要在 NumberEvent
处理程序中添加任何其他代码。
我创建了一些测试代码,这样我就可以尝试弄清楚如何在 UWP 中正确使用多个 windows。我想看看我是否可以触发一个事件并让多个 windows 在事件处理程序中更新它们的 UI。我终于得到了一些有用的东西,但我不完全确定它为什么有用。
这是在我的页面中创建的 class
public class NumberCruncher
{
private static Dictionary<int, Tuple<CoreDispatcher, NumberCruncher>> StaticDispatchers { get; set; }
static NumberCruncher()
{
StaticDispatchers = new Dictionary<int, Tuple<CoreDispatcher, NumberCruncher>>();
}
public NumberCruncher()
{
}
public event EventHandler<NumberEventArgs> NumberEvent;
public static void Register(int id, CoreDispatcher dispatcher, NumberCruncher numberCruncher)
{
StaticDispatchers.Add(id, new Tuple<CoreDispatcher, NumberCruncher>(dispatcher, numberCruncher));
}
public async Task SendInNumber(int id, int value)
{
foreach (var dispatcher in StaticDispatchers)
{
await dispatcher.Value.Item1.RunAsync(CoreDispatcherPriority.Normal, () =>
{
Debug.WriteLine($"invoking {dispatcher.Key}");
dispatcher.Value.Item2.NumberEvent?.Invoke(null, new NumberEventArgs(id, value));
});
}
}
}
这是我的 MainPage 代码的相关部分
NumberCruncher numberCruncher;
public MainPage()
{
this.InitializeComponent();
numberCruncher = new NumberCruncher();
numberCruncher.NumberEvent += NumberCruncher_NumberEvent;
}
private async void NumberCruncher_NumberEvent(object sender, NumberEventArgs e)
{
listView.Items.Add($"{e.Id} sent {e.Number}");
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
NumberCruncher.Register(ApplicationView.GetForCurrentView().Id, Window.Current.Dispatcher, numberCruncher);
}
我有一个按钮可以创建 MainPage 的新视图。然后我有另一个调用 SendInNumber()
方法的按钮。
当我导航到 MainPage 时,我为 window 注册了 Dispatcher 和 NumberCruncher 的实例。然后在触发事件时,我为特定的 Dispatcher 使用 NumberCruncher EventHandler。
这不会抛出封送处理异常。如果我尝试使用当前 class 的 EventHandler
await dispatcher.Value.Item1.RunAsync(CoreDispatcherPriority.Normal, () =>
{
Debug.WriteLine($"invoking {dispatcher.Key}");
NumberEvent?.Invoke(null, new NumberEventArgs(id, value));
});
我在尝试将项目添加到 listView 时收到封送处理异常。但是,如果我在我的 MainPage 中维护 SynchronizationContext,然后使用 SynchronizationContext.Post 更新 listView。效果很好
SynchronizationContext synchronizationContext;
public MainPage()
{
this.InitializeComponent();
numberCruncher = new NumberCruncher();
numberCruncher.NumberEvent += NumberCruncher_NumberEvent;
synchronizationContext = SynchronizationContext.Current;
}
private async void NumberCruncher_NumberEvent(object sender, NumberEventArgs e)
{
synchronizationContext.Post(_ =>
{
listView.Items.Add($"{e.Id} sent {e.Number}");
}, null);
}
然而,这不起作用并在尝试更新 listView 时抛出封送处理异常。
private async void NumberCruncher_NumberEvent(object sender, NumberEventArgs e)
{
await CoreApplication.GetCurrentView().CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
listView.Items.Add($"{e.Id} sent {e.Number}");
});
}
这是怎么回事?
要记住的重要一点是,当一个事件被触发时,订阅的方法在与 Invoke
方法相同的线程上被调用。
If I try to use the current class's
EventHandler
, I get a marshaling exception when trying to add the item to thelistView
.
发生第一个错误是因为您试图在 另一个 Window 的调度程序 上触发当前 class 的事件(dispatcher.Value.Item1
).假设事件发生在 Window 1 并且 dispatcher.Value.Item1
属于 Window 2。一旦进入 Dispatcher
块,您就是 Window 2 的 UI 线程并触发 的 NumberEvent
]Window 1's NumberCruncher
将 运行 Window 1's 处理程序 Window 2's UI 线程并导致异常。
However this does not work and throws a marshaling exception when trying to update
listView
.
GetCurrentView()
方法 returns 当前活动视图。因此,无论哪个应用程序视图在那一刻处于活动状态,都将被返回。在您的情况下,它将是您单击按钮的那个。如果您在目标 Window 的 UI 线程上调用 Invoke
方法,则不需要在 NumberEvent
处理程序中添加任何其他代码。