22

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

我尝试使用:

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

但是该命令是在我看到视图 UI 之前执行的,这不是我想要的。

有谁知道我应该如何实现它?

4

8 回答 8

17

您可以为此使用 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

干杯。圣。

于 2012-10-23T12:21:33.720 回答
17

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

在事件上使用交互触发器设置命令Loaded已经很好了,因为没有更好的事件可以附加到。
现在要真正等到 UI 显示出来,在你的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
}
于 2012-10-23T12:35:17.250 回答
2

另一种方法:

在您的用户控件 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>

将触发器放在用户控件的根元素内,例如:grid。并将您的 ViewModel 类中的 StartProgressCommand 从命令更改为普通的旧常规方法,例如:

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

每次您的用户控件呈现时,它都会准确地运行一次该方法。

于 2014-08-25T04:34:54.227 回答
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);
    } 


} 

然后我们调用 using

Dispatcher.CurrentDispatcher.DelayInvoke("InitSomething",()=> {
    DoSomething();
},TimeSpan.FromSeconds(1));
于 2012-10-23T11:46:29.807 回答
0

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

谢谢, 拉吉尼坎特

于 2012-10-23T09:27:03.317 回答
0

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

于 2012-10-23T09:33:24.813 回答
0

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

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

于 2012-10-23T11:05:29.637 回答
0

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

基本上,它使用

称呼 :

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

也许不是干净的,但它按预期工作。

于 2014-06-25T21:09:47.617 回答