在视图加载WPF MVVM后执行命令

时间:2012-10-23 08:58:45

标签: wpf mvvm triggers command

我有一个基于项目的WPF和MVVM。 我的项目基于一个包含内容控件的向导,该控件显示我的视图(用户控件) 我希望在完全加载视图后执行命令,我希望用户在执行命令后立即看到视图UI。

我尝试使用:

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

但是在我看到视图UI之前执行命令并且它不是我正在寻找的。

有没有人知道我应该如何实施它?

8 个答案:

答案 0 :(得分:16)

那是因为即使技术上加载了视图(即:所有组件都已在内存中准备好),您的应用程序还没有空闲,因此UI还没有刷新。

使用Loaded事件上的交互触发器设置命令已经很好了,因为没有更好的事件要附加到。
现在要等到显示用户界面,在StartProgress()中执行此操作(我在此假设这是StartProgressCommand指向的方法的名称):

public void StartProgress()
{
    new DispatcherTimer(//It will not wait after the application is idle.
                       TimeSpan.Zero,
                       //It will wait until the application is idle
                       DispatcherPriority.ApplicationIdle, 
                       //It will call this when the app is idle
                       dispatcherTimer_Tick, 
                       //On the UI thread
                       Application.Current.Dispatcher); 
}

private static void dispatcherTimer_Tick(object sender, EventArgs e)
{
    //Now the UI is really shown, do your computations
}

答案 1 :(得分:13)

您可以使用Dispatcher并将优先级设置为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。

答案 2 :(得分:1)

我们使用计时器解决方案 - 我对此非常怀疑,但它看起来确实很好。

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

答案 3 :(得分:1)

另一种方法:

在您的usercontrol XAML上定义此xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"xmlns:mi="http://schemas.microsoft.com/expression/2010/interactions",并在您的项目上添加Microsoft.Expression.Interactions程序集。在触发器上使用CallMethodAction,如下所示:

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

将triger放在usercontrol的根元素中,例如:grid。并在ViewModel类中将StartProgressCommand从命令更改为普通的旧常规方法,例如:

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

每次用户控件渲染时,它都会运行一次该方法。

答案 4 :(得分:0)

您可以检查IsLoaded属性的视图,当视图处于加载形式时它返回false,当视图完全加载时,此属性变为true。

谢谢, Rajnikant

答案 5 :(得分:0)

您是否尝试过绑定ContentRendered 事件?它将在加载的事件之后发生,但我不确定这是否是UI线程已完成绘制窗口的保证。

答案 6 :(得分:0)

您可以在“CommandExecute”方法的第一行中写入“Thread.Sleep(10000)”。使用相同的加载触发器。

如果您不想使用Thread.Sleep,那么您可以选择“DispatcherTimer”。在命令执行方法中启动一个计时器,并将所有代码转移到计时器刻度事件。 将您的计时器间隔设置为2秒,以便用户可以查看UI。

答案 7 :(得分:0)

我为这个解决方案提供了这个解决方案。 我想在工作开始时使用布尔属性设置为true,并在结尾处使用false以允许通知用户后台工作。

基本上,它使用

  • a DispatcherTimer根据this
  • 在UI渲染后启动方法
  • 一种异步方法,它将执行作为参数传递的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;
}

可能不是很干净但是按预期工作。