web-dev-qa-db-ja.com

ビューがロードされた後にコマンドを実行するWPFMVVM

プロジェクトベースのWPFとMVVMがあります。私のプロジェクトは、ビューを表示するコンテンツコントロール(ユーザーコントロール)を含むウィザードに基づいています。ビューが完全にロードされた後にコマンドを実行したいのですが、コマンドが実行された直後にユーザーにビューUIを表示してもらいたいです。

私は使ってみました:

<i:Interaction.Triggers>
    <i:EventTrigger EventName="Loaded">
        <i:InvokeCommandAction Command="{Binding StartProgressCommand}"/>
    </i:EventTrigger>
</i:Interaction.Triggers>

しかし、コマンドはビューUIが表示される前に実行され、私が探しているものではありません。

誰かがそれをどのように実装する必要があるのか​​考えていますか?

20
Ofir

これにディスパッチャーを使用し、優先度をApplicationIdleに設定して、すべてが終了したときに実行されるようにすることができます。

            Application.Current.Dispatcher.Invoke(
            DispatcherPriority.ApplicationIdle,
            new Action(() =>
            {
               StartProgressCommand.Invoke(args);

            }));

ディスパッチャの詳細 http://msdn.Microsoft.com/en-us/library/system.windows.threading.dispatcherpriority.aspx

乾杯。 ste。

13
Steoates

私たちはタイマーソリューションを使用しています-私もこれについて非常に疑っていましたが、それはうまく機能しているようです。

public static class DispatcherExtensions
{
    private static Dictionary<string, DispatcherTimer> timers =
        new Dictionary<string, DispatcherTimer>();
    private static readonly object syncRoot = new object();

    public static void DelayInvoke(this Dispatcher dispatcher, string namedInvocation,
        Action action, TimeSpan delay,
        DispatcherPriority priority = DispatcherPriority.Normal)
    {
        lock (syncRoot)
        {
            RemoveTimer(namedInvocation);
            var timer = new DispatcherTimer(delay, priority, (s, e) =>
                {
                    RemoveTimer(namedInvocation);
                    action();
                }, dispatcher);
            timer.Start();
            timers.Add(namedInvocation, timer);
        }
    }


    public static void CancelNamedInvocation(this Dispatcher dispatcher, string namedInvocation)
    {
        lock (syncRoot)
        {
            RemoveTimer(namedInvocation);
        }
    }

    private static void RemoveTimer(string namedInvocation)
    {
        if (!timers.ContainsKey(namedInvocation)) return;
        timers[namedInvocation].Stop();
        timers.Remove(namedInvocation);
    } 


} 

次に、を使用して呼び出します

Dispatcher.CurrentDispatcher.DelayInvoke("InitSomething",()=> {
    DoSomething();
},TimeSpan.FromSeconds(1));
1
Richard Friend

それを行う別の方法:

これを定義するxmlns:i="clr-namespace:System.Windows.Interactivity;Assembly=System.Windows.Interactivity"およびxmlns:mi="http://schemas.Microsoft.com/expression/2010/interactions"ユーザーコントロールXAMLで、プロジェクトにMicrosoft.Expression.Interactionsアセンブリを追加します。次のように、トリガーでCallMethodActionを使用します。

<i:Interaction.Triggers>
    <i:EventTrigger EventName="Loaded">
        <mi:CallMethodAction TargetObject="{Binding}" MethodName="StartProgressCommand"/>
    </i:EventTrigger>
</i:Interaction.Triggers>

トリガーをユーザーコントロールのルート要素内に配置します(例:grid)。そして、ViewModelクラスのStartProgressCommandを、コマンドから従来の通常のメソッドに変更します。例:

public void StartProgressCommand()
{
  /* put your program logic here*/
}

ユーザーコントロールがレンダリングされるたびに、メソッドが1回だけ実行されます。

1
randomguy

私はこれのためにこの解決策を持ってきました。作業の開始時にtrueに設定され、終了時にfalseに設定されたブールプロパティを使用して、ユーザーにバックグラウンド作業を通知できるようにしたかったのです。

基本的には

  • this に従ってUIレンダリング後にメソッドを起動するためのDispatcherTimer
  • パラメータとして渡されたActionを実行する非同期メソッド

電話:

this.LaunchThisWhenUiLoaded(() => { /*Stuff to do after Ui loaded here*/ });

メソッド:

private DispatcherTimer dispatchTimer;
private Action ActionToExecuteWhenUiLoaded;

/// <summary>
/// Handy method to launch an Action after full UI rendering
/// </summary>
/// <param name="toExec"></param>
protected void LaunchThisWhenUiLoaded(Action toExec)
{            
    ActionToExecuteWhenUiLoaded = toExec;
    // Call UiLoaded method when UI is loaded and rendered
    dispatchTimer = new DispatcherTimer(TimeSpan.Zero, DispatcherPriority.ContextIdle, UiLoaded, Application.Current.Dispatcher);
}

/// <summary>
/// Method called after UI rendered
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected async void UiLoaded(object sender, EventArgs e)
{
    this.IsBusy = true;    

    if (ActionToExecuteWhenUiLoaded != null)
        await Task.Run(ActionToExecuteWhenUiLoaded);
    dispatchTimer.Stop();

    this.IsBusy = false;
}

クリーンではないかもしれませんが、期待どおりに機能します。

0
eka808

ContentRendered イベントにバインドしようとしましたか?ロードされたイベントの後に発生しますが、これがUIスレッドがウィンドウのペイントを終了したことを保証するものかどうかはわかりません。

0
Dominik

「CommandExecute」メソッドの最初の行に「Thread.Sleep(10000)」を書き込むことができます。同じロードされたトリガーを使用します。

thread.Sleepを使用したくない場合は、「DispatcherTimer」に進むことができます。コマンドexecuteメソッドでタイマーを開始し、すべてのコードをタイマーティックイベントにシフトします。タイマー間隔を2秒に設定して、ユーザーがUIを感知できるようにします。

0
Bathineni

ビューのIsLoadedプロパティを確認できます。ビューがロードされた形式の場合はfalseを返し、ビューが完全にロードされるとこのプロパティはtrueになります。

ありがとう、ラジニカーント

0