将请求从域传递到视图

时间:2012-07-13 07:39:22

标签: c# wpf multithreading

我正在开发一个WPF应用程序,用户可以通过按下UI上的按钮来启动进程。然后可以向用户提示他们必须执行的一系列动作以完成该过程。视图负责将初始请求传递给域以启动进程。该视图还负责显示用户完成该过程必须执行的步骤。 另一方面,域是对于确定必须由用户执行的步骤的正确响应。该域还能够检测用户何时完成所请求的步骤。

如果用户启动进程,并且该进程要求他们执行某些物理操作,那么我希望弹出一个框,其中会显示一条描述他们必须执行的操作的消息。操作完成后,域将检测到该操作,窗口应自动关闭。

将View中的请求传递给域很简单。我使用wpf ICommand模式执行此操作。它将信息传递回我发现具有挑战性的另一种方式。我知道绑定和INotifyProperyChanged接口,但我觉得这不适合我想要做的事情。

所以,这是我最初的尝试......

此接口由View实现并由Domain使用。它允许域与用户通信;

public interface IUserRequestMedium
{
    /// <summary>
    /// Ask the user to perform an action. User does
    /// not need to provide any feedback via the user
    /// interface, since it is possible for the 
    /// application to detect when the action has been
    /// carried out by the user. The dialog will be closed
    /// when either the requested action has been detected,
    /// or the user aborts.
    /// </summary>
    /// <param name="message">
    /// Request to be displayed to the user.
    /// </param>
    /// <param name="userAbortCallback">
    /// Callback invoked by the view when the user cancels
    /// the request.
    /// </param>
    /// <param name="actionDetectedCallback">
    /// Callback invoked by the domain to confirm the 
    /// that the requested action has been completed.
    /// </param>
    void AskUserToPerformDetectableAction(
        string message, Action userAbortCallback,
        out Action actionDetectedCallback);
}

这是View代码隐藏。其中一些代码来自网络上的教程(并随后被修改)。它不起作用,但我希望它能传达我的意图。

public partial class MainWindow : Window, IUserRequestMedium
{
    // Constructor and other stuff...

    public void AskUserToPerformDetectableAction(
        string message, Action userAbortCallback,
        out Action actionDetectedCallback)
    {
        Action closeWindow;
        NewWindowThread(
            () => new ActionRequestBox(message, userAbortCallback),
            out closeWindow);

        actionDetectedCallback = closeWindow;
}

    private Window newWindow;

    private void NewWindowThread(
        Func<Window> construction,
        out Action closeWindow)
    {
        var thread = new Thread(() =>
        {
            newWindow = construction();
            newWindow.Show();
            newWindow.Closed += (sender, e) => newWindow.Dispatcher.InvokeShutdown();
            System.Windows.Threading.Dispatcher.Run();
        });
        thread.SetApartmentState(ApartmentState.STA);
        thread.Start();

        Window rememberedWindow = newWindow;
        closeWindow = () =>
        {
            if (rememberedWindow != null)
                rememberedWindow.Dispatcher.Invoke(
                    System.Windows.Threading.DispatcherPriority.Normal,
                    new Action(Close));
        };
    }
}

这是域中的一个用法示例;

public class SomeDomainClass
{
    IUserRequestMedium userRequestMedium; // assume this has been assigned in constructor

    private Action notifyUserOfActionDetected;

    public void PerformSomeProcess()
    {
        bool processCannotBeCompletedWithoutPowerCycle = ...; // some logic
        if (processCannotBeCompletedWithoutPowerCycle)
        {
            userRequestMedium.AskUserToPerformDetectableAction(
                "Please cycle the power on the external device",
                CancelProcess,
                out notifyUserOfActionDetected);
        }
    }

    public void CancelProcess()
    {
        // User doesn't want to perform the required action
        // so process must be aborted...
    }

    private void OnPowerCycleDetected()
    {
        notifyUserOfActionDetected();
    }
}

我该如何使这项工作?这是我遇到的跨线程方面。当域检测到操作时,我没有成功地使窗口自动关闭。

或者,退一步,是否有更好的方法来解决这个问题?

1 个答案:

答案 0 :(得分:0)

在学习了一些关于Dispatcher.Invoke之后,这就是我最终的结果。到目前为止它似乎工作得很好。

    private Window activeRequestBox;

    // invoked on domain thread
    public void AskUserToPerformDetectableAction(
        string message, Action userAbortCallback,
        out Action actionDetectedCallback)
    {
        OpenDetectableActionRequestBox(message, userAbortCallback);
        actionDetectedCallback = CloseRequestBox;
    }

    private void OpenDetectableActionRequestBox(
        string message, Action userAbortCallback)
    {
        Action openWindow =
            () =>
            {
                activeRequestBox = new DetectableActionRequestBox(
                     message, userAbortCallback);
                activeRequestBox.Closed += RequestBoxClosedHandler;
                activeRequestBox.Show();
            };
        this.Dispatcher.Invoke(openWindow);            
    }


    // invoked on request box thread
    private void RequestBoxClosedHandler(object sender, EventArgs e)
    {
        activeRequestBox = null;
    }

    // invoked on domain thread
    private void CloseRequestBox()
    {
        if (activeRequestBox != null)
        {
            Action closeWindow =
                () => activeRequestBox.Close();
            this.Dispatcher.Invoke(closeWindow);
        }
    }