0

ユーザーがUIのボタンを押すことでプロセスを開始できるWPFアプリケーションに取り組んでいます。その後、ユーザーは、プロセスを完了するために実行する必要がある一連のアクションを求められる場合があります。ビューは、プロセスを開始するための最初のリクエストをドメインに渡す役割を果たします。ビューは、プロセスを完了するためにユーザーが実行する必要がある手順を表示する役割も果たします。一方、ドメインは、ユーザーが実行する必要がある手順を解決するための正に応答です。ドメインは、要求されたステップをユーザーがいつ完了したかを検出することもできます。

ユーザーがプロセスを開始し、そのプロセスで何らかの物理的なアクションを実行する必要がある場合、何をしなければならないかを説明するメッセージがポップアップ表示されるようにしたいと思います。アクションが完了すると、ドメインによって検出され、ウィンドウが自動的に閉じます。

ビューからドメインにリクエストを渡すのは簡単です。これは、wpf ICommand パターンを使用して行います。私がやりがいを感じているのは、逆に情報を渡すことです。私はバインディングと INotifyProperyChanged インターフェイスを認識していますが、これが私がやろうとしていることに適しているとは思いません。

だから、ここに私の最初の試みがあります...

このインターフェイスはビューによって実装され、ドメインによって消費されます。ドメインがユーザーと通信できるようにします。

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);
}

ビューのコード ビハインドを次に示します。このコードの一部は、Web 上のチュートリアルから取得された (その後、マングルされた) ものです。機能していませんが、私の意図が伝わることを願っています。

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();
    }
}

どうすればこれを機能させることができますか?私が引っかかっているのはクロススレッドの側面です。ドメインによってアクションが検出されたときにウィンドウを自動的に閉じることができませんでした。

または、一歩後退して、この問題を解決するためのより良いアプローチはありますか?

4

1 に答える 1

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);
        }
    }
于 2012-07-13T10:06:42.860 に答える