我有一个基于项目的WPF和MVVM。 我的项目基于一个包含内容控件的向导,该控件显示我的视图(用户控件) 我希望在完全加载视图后执行命令,我希望用户在执行命令后立即看到视图UI。
我尝试使用:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding StartProgressCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
但是在我看到视图UI之前执行命令并且它不是我正在寻找的。 p>
有没有人知道我应该如何实施它?
答案 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以允许通知用户后台工作。
基本上,它使用
DispatcherTimer
根据this 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;
}
可能不是很干净但是按预期工作。