Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/338.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在多窗口UWP应用程序中引发和处理事件_C#_Windows_Multithreading_Uwp_Multiple Views - Fatal编程技术网

C# 在多窗口UWP应用程序中引发和处理事件

C# 在多窗口UWP应用程序中引发和处理事件,c#,windows,multithreading,uwp,multiple-views,C#,Windows,Multithreading,Uwp,Multiple Views,我创建了一些测试代码,这样我就可以试着找出如何在UWP中正确使用多个窗口。我想看看是否可以触发一个事件,并让多个窗口在事件处理程序中更新其UI。我终于找到了一个可行的方法,但我不完全确定它为什么有效 这是在我的页面中创建的类 public class NumberCruncher { private static Dictionary<int, Tuple<CoreDispatcher, NumberCruncher>> StaticDispatchers { g

我创建了一些测试代码,这样我就可以试着找出如何在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));
            });
        }
    }
}
我有一个按钮,可以创建主页的新视图。然后我有另一个按钮调用
SendInNumber()
方法

当我导航到主页时,我为窗口和NumberRuncher实例注册了Dispatcher。然后,在触发事件时,我使用该特定调度器的NumberRuncher EventHandler

这可以在不引发封送异常的情况下工作。如果我尝试使用当前类的EventHandler

await dispatcher.Value.Item1.RunAsync(CoreDispatcherPriority.Normal, () =>
        {
            Debug.WriteLine($"invoking {dispatcher.Key}");
            NumberEvent?.Invoke(null, new NumberEventArgs(id, value));
        });
我在尝试将项添加到listView时遇到封送处理异常。但是,如果我在主页中维护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
方法相同的线程上调用

如果尝试使用当前类的
EventHandler
,则在尝试将该项添加到
列表视图时会出现封送异常

发生第一个错误是因为您试图在另一个窗口的dispatcher(
dispatcher.Value.Item1
)上触发当前类的事件。假设事件在窗口1上,并且
dispatcher.Value.Item1
属于窗口2。进入
Dispatcher
块后,您将启动窗口2的UI线程,并启动窗口1的
NumberEvent
NumberRuncher
将在窗口2的UI线程上运行窗口1的处理程序,从而导致异常

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


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

GetCurrentView返回指向活动窗口的指针,而不一定是包含试图更新的listView实例的窗口。因此,我认为您最终使用了错误的UI线程(因为每个窗口都有自己的UI线程)。您尝试过使用“listView.Dispatcher”吗?这应该将调用封送到此listView实例的右UI线程。文档说GetCurrentView返回活动视图,但我设置了一个线程,将ApplicationView.GetForCurrentView().Id和CoreApplication.GetCurrentView()的状态打印到调试控制台。当我有两个窗口时,每个窗口的输出是不同的。这让我相信GetCurrentView()返回的是该UI线程的视图,而不是活动窗口。是的,但是您的事件是否会在UI线程上触发?如果是这样,那么您首先就不需要调度器。您似乎需要它这一事实表明事件不会在与您尝试更新的ListView相对应的UI线程上触发。如果只执行“listView.Dispatcher.RunAsync”,会发生什么?(而不是CoreApp.GetCurrentView().CoreWin.Dispatcher.RunAsync)如果我使用listView调度器,我会在listView中为我打开的每个窗口收到一条消息,这样我会看到
-23473在窗口-23473上发送123
两次,打开两个窗口。这就是我所认为的情况。我还猜测,存储SynchronizationContext然后使用它来更新UI是有效的,因为我使用UI线程来更新UI。
SynchronizationContext
有效的原因是您将其存储在页面构造函数中,该构造函数必须在UI线程上运行,因此上下文与正确的UI线程关联:-)
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}");
        });
    }