2

我创建了一些测试代码,以便尝试弄清楚如何在 UWP 中正确使用多个窗口。我想看看我是否可以触发一个事件并让多个窗口在事件处理程序中更新它们的 UI。我终于得到了一些工作,但我不完全确定它为什么工作。

这是在我的页面中创建的课程

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 时,我为窗口和 NumberCruncher 实例注册了 Dispatcher。然后在触发事件时,我使用 NumberCruncher EventHandler 来处理特定的 Dispatcher。

这可以在不引发封送处理异常的情况下工作。如果我尝试使用当前类的 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}");
        });
    }

这里发生了什么?

4

1 回答 1

1

要记住的重要一点是,当一个事件被触发时,订阅的方法会在与该方法相同的线程上被调用Invoke

如果我尝试使用当前类的EventHandler,则在尝试将项目添加到listView.

发生第一个错误是因为您试图在另一个 Window 的调度程序( dispatcher.Value.Item1) 上触发当前类的事件。假设事件在Window 1上并且dispatcher.Value.Item1属于Window 2。一旦进入Dispatcher块内,您就是Window 2NumberEvent的UI 线程并触发Window 1 的将在 Window 2 的NumberCruncherUI 线程上运行Window 1 的处理程序,这会导致异常。

但是,这不起作用,并在尝试更新时引发封送处理异常listView

GetCurrentView()方法返回当前活动的视图。因此,当时处于活动状态的任何应用程序视图都将被返回。在您的情况下,它将是您单击按钮的那个。如果您Invoke在目标 Window 的 UI 线程上调用该方法,则不需要NumberEvent处理程序中的任何其他代码。

于 2018-01-07T10:36:08.700 回答