用相同的方法与多个UI调度程序一起发布的可能解决方案? [英] Possible solution to issue with multiple UI dispatchers in the same method?

查看:85
本文介绍了用相同的方法与多个UI调度程序一起发布的可能解决方案?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用多个UI调度程序修改绑定到UI的列表时遇到问题.遇到第一个调度程序时刚刚退出的方法.如果我将整个方法都包装在调度程序中,则可以使用,但是我还有另一种解决方案,但不确定是否合适:

I'm having a problem with using multiple UI dispatchers to modify an list that is bound to the UI. The method with just exit when it hits the first dispatcher. If I wrap the entire method in the dispatcher it works, but I have another solution but I'm not sure it's appropriate:

基本上,我有一个套接字在无休止的循环中侦听来自媒体设备的网络命令.找到一个后,将调用ProcessCommand.

Basically, I have a socket listening in a never-ending loop for network commands from a media device. When it finds one, it calls ProcessCommand.

该函数调用50多种方法之一来处理特定命令.这些函数存储内部状态,但是主要引发我的主应用程序可以订阅的事件,以便它知道何时更改了诸如卷之类的内容,并且可以更新UI.

That function calls one of 50+ methods to process the specific commands. Those functions store the internal state, but mainly raise events my main application can subscribe to so it knows when something like the volume has changed and I can update the UI.

这些工作得很好,除了开头提到的一种情况,我需要修改在同一方法中使用多个调度程序绑定的状态对象,但该方法无法正常工作,并将该方法包装在一个大型调度程序中似乎可以正常工作

These work pretty well, except the one case noted at the start where I need to modify a state object that is bound using several dispatchers in the same method and it not working, and wrapping the method in one big dispatcher seems to work.

我发现的另一种解决方案是在UI调度程序的套接字后台侦听器中运行ProcessCommand,即:

Another solution I found is to run the ProcessCommand running in the socket background listener on the UI dispatcher, ie:

                    CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
                    { 
                       ProcessCommand(command);
                    });

然后这将滴入所有50多个ProcessCommandXYZ方法中,也进入我的主应用程序中,在该应用程序中我在多个页面上订阅这些事件,我目前必须在每个页面上使用UI调度程序,所以类似:

This will then trickle down into all of the individual 50+ ProcessCommandXYZ methods, and also in my main app where I subscribe to these events on multiple pages I currently have to use a UI dispatcher on every one, so something like:

套接字侦听器后台任务> {DispatcherUI ProcessCommand}> ProcessVolumeCommand>引发OnVolumeChangedEvent>订户更新UI

Socket listener background task > {DispatcherUI ProcessCommand} > ProcessVolumeCommand > Raise OnVolumeChangedEvent > Subscriber updates UI

会做到这一点,因此我不需要将每个事件订阅者都放入一个调度程序中,但最重要的是,可以用一种方法解决多个调度程序所遇到的问题:

Would make it so I don't need to put each event subscriber into a dispatcher, but most importantly fixes problem I have with multiple dispatchers in one method:

在事件中正确使用Dispatcher来更新UI

这似乎是一个很好的解决方案吗?

Does this seem like a good solution?

推荐答案

当您订阅在Non-ui线程上引发的事件时,这是我针对此类情况的解决方案.

Here is my solution for such situations when you subscribed to event that raises on Non-ui thread.

//subscribe to event
//handler (OnPlayerVolumeChanged) will be invoked on subscriber (UI) thread
var subscriptionToken = EventSubscription.FromEvent<VolumeChangedEventArgs>(
                            handler => _player.VolumeChanged += handler,
                            handler => _player.VolumeChanged -= handler,
                            OnPlayerVolumeChanged,
                            HandlerThreadOption.SubscriberThread);

//unsubscribe
subscriptionToken.Unsubscribe();

这是实现:

public enum HandlerThreadOption
{
    PublisherThread,
    SubscriberThread,
    BackgroundThread
}

public class EventSubscriptionToken : IDisposable
{
    private readonly Action _unsubscribe;


    public EventSubscriptionToken(Action unsubscribe)
    {
        _unsubscribe = unsubscribe;
    }


    public void Unsubscribe()
    {
        _unsubscribe();
    }


    void IDisposable.Dispose()
    {
        Unsubscribe();
    }
}


public static class EventSubscription
{
    public static EventSubscriptionToken FromEvent<TEventArgs>(
        Action<EventHandler<TEventArgs>> addHandler,
        Action<EventHandler<TEventArgs>> removeHandler,
        EventHandler<TEventArgs> handler,
        HandlerThreadOption threadOption)
    {
        var threadSpecificHandler = GetHandler(handler, threadOption);
        addHandler(threadSpecificHandler);

        return new EventSubscriptionToken(() => removeHandler(threadSpecificHandler));
    }

    public static EventSubscriptionToken FromEvent(
        Action<EventHandler> addHandler,
        Action<EventHandler> removeHandler,
        EventHandler handler,
        HandlerThreadOption threadOption)
    {
        var threadSpecificHandler = GetHandler(handler, threadOption);
        addHandler(threadSpecificHandler);

        return new EventSubscriptionToken(() => removeHandler(threadSpecificHandler));
    }


    private static EventHandler<T> GetHandler<T>(EventHandler<T> handler, HandlerThreadOption threadOption)
    {
        switch (threadOption)
        {
            case HandlerThreadOption.PublisherThread:
                return handler;
            case HandlerThreadOption.SubscriberThread:
                return GetCurrentThreadExecutionStrategy(handler);
            case HandlerThreadOption.BackgroundThread:
                return GetBackgroundThreadExecutionStrategy(handler);
            default:
                throw new ArgumentOutOfRangeException("threadOption");
        }
    }

    private static EventHandler GetHandler(EventHandler handler, HandlerThreadOption threadOption)
    {
        switch (threadOption)
        {
            case HandlerThreadOption.PublisherThread:
                return handler;
            case HandlerThreadOption.SubscriberThread:
                return GetCurrentThreadExecutionStrategy(handler);
            case HandlerThreadOption.BackgroundThread:
                return GetBackgroundThreadExecutionStrategy(handler);
            default:
                throw new ArgumentOutOfRangeException("threadOption");
        }
    }

    private static EventHandler<T> GetBackgroundThreadExecutionStrategy<T>(EventHandler<T> action)
    {
        return (sender, e) => Task.Factory.StartNew(() => action(sender, e));
    }

    private static EventHandler GetBackgroundThreadExecutionStrategy(EventHandler handler)
    {
        return (sender, e) => Task.Factory.StartNew(() => handler(sender, e));
    }

    private static EventHandler<T> GetCurrentThreadExecutionStrategy<T>(EventHandler<T> action)
    {
        var currentSynchronizationContext = SynchronizationContext.Current;

        return (sender, e) => PostToSynchronizationContext(currentSynchronizationContext, () => action(sender, e));
    }

    private static EventHandler GetCurrentThreadExecutionStrategy(EventHandler handler)
    {
        var currentSynchronizationContext = SynchronizationContext.Current;

        return (sender, e) => PostToSynchronizationContext(currentSynchronizationContext, () => handler(sender, e));
    }

    private static void PostToSynchronizationContext(SynchronizationContext synchronizationContext, Action action)
    {
        try
        {
            synchronizationContext.Post(state => action(), null);
        }
        catch (Exception ex)
        {
            if (!ex.Message.StartsWith("The operation cannot be completed because the window is being closed", StringComparison.Ordinal))
            {
                throw;
            }
        }
    }
}

这篇关于用相同的方法与多个UI调度程序一起发布的可能解决方案?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆