加载视图后执行命令 WPF MVVM

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13026826/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 23:33:35  来源:igfitidea点击:

Execute command after view is loaded WPF MVVM

wpfmvvmtriggerscommand

提问by Ofir

I have a project based WPF and MVVM. My project is based on a wizard containing a content control which shows my views (User Controls) I want to execute a command after the view is loaded completely, I would like the user to see the view UI immediately after the command will be executed.

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

I tried using :

我尝试使用:

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

But the command is executed before I see the view UI and it's not what I'm looking for.

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

Does anyone have an idea how should I need to implement it?

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

采纳答案by Louis Kottmann

That's because even though technically the view is loaded (i.e: all the components are ready in memory), your app is not idle yet, and thus the UI isn't refreshed yet.

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

Setting a command using interaction triggers on the Loadedevent is already good, as there is no better event to attach to.
Now to really wait until the UI is shown, do this in your StartProgress()(I'm assuming here that this is the name of the method that StartProgressCommandpoint to):

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
}

回答by Steoates

You could use the Dispatcher for this and set the priority to ApplicationIdle so that it will on execute when everything has finished

您可以为此使用 Dispatcher 并将优先级设置为 ApplicationIdle,以便在一切完成后执行

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

            }));

more information on the dispatcher http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcherpriority.aspx

有关调度程序的更多信息http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcherpriority.aspx

cheers. ste.

干杯。圣。

回答by Richard Friend

We use a the timer solution - i too was very dubious about this but it does seem to work fine.

我们使用计时器解决方案 - 我也对此非常怀疑,但它似乎工作正常。

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


} 

Then we invoke using

然后我们调用使用

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

回答by randomguy

another way to do it:

另一种方法:

define this xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"and xmlns:mi="http://schemas.microsoft.com/expression/2010/interactions"on your usercontrol XAML and add Microsoft.Expression.Interactions assembly on your project. use CallMethodAction on your trigger, just as bellow:

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

Put the triger inside the root element of your usercontrol, e.g: grid. And change your StartProgressCommand, in your ViewModel class, from command to plain old regular Method, e.g:

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

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

It'll run the method exactly one time every time your user control rendered.

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

回答by eka808

I came with this solution for this one. I wanted to use a boolean property set as true at start of work and to false at the end to allow to notify user of background work.

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

Basically, it uses

基本上,它使用

  • a DispatcherTimerto launch a method after UI render according to this
  • An async method wich will execute the Actionpassed as a parameter
  • aDispatcherTimer根据this在 UI 渲染后启动一个方法
  • 将执行Action作为参数传递的异步方法

Call :

称呼 :

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

Method :

方法 :

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

Maybe not the clean but it works as expected.

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

回答by rajnikant rajwadi

You can check IsLoaded property for view, when view is in loaded form it returns false, when view is fully loaded, this property become true.

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

Thanks, Rajnikant

谢谢,拉吉尼坎特

回答by Dominik

Have you tried binding to the ContentRendered event? It will occur after the loaded event, yet I′m not sure whether or not this is a gurantee that the UI thread has finished painting the window then.

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

回答by Bathineni

You can Write a "Thread.Sleep(10000)" in the first line of "CommandExecute" method. Use the same loaded trigger.

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

if you don't want to use Thread.Sleep then you can go for "DispatcherTimer". Start a timer in your command execute method and shift all your code to timer tick event. set your timer interval to 2 seconds, so that user will sen the UI.

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