我有一個程序,其中有一個加載圖像,當發生長時間的行爲時。我想使用一個ContentControl
,該行爲包含Button
,或者顯示加載Image
。我已將Trigger
設置爲IsLoading
屬性,因此它可以交換Content
。如何等待觸發器在WPF中採取行動
查看:
<Window x:Class="UIHangsTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:UIHangsTest"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:VM />
</Window.DataContext>
<Grid>
<ContentControl>
<ContentControl.Style>
<Style TargetType="ContentControl" >
<Setter Property="Content">
<Setter.Value>
<Button Content="shiny disappearing button" Command="{Binding DoCommand}" IsDefault="True" />
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding IsLoading}" Value="True" >
<Setter Property="Content">
<Setter.Value>
<Image />
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</Grid>
</Window>
使用此,如果您需要運行在UI(調度)線程的長時間運行的任務。
視圖模型:
namespace UIHangsTest
{
using System;
using DevExpress.Mvvm;
using System.Threading;
using System.Windows;
using System.Windows.Threading;
public class VM: ViewModelBase
{
public VM()
{
DoCommand = new DelegateCommand(Do,() => true);
IsLoading = false;
}
private void Do()
{
// I have set some content control's trigger to show a waiting symbol if IsLoading is true.
IsLoading = true;
var handle = new ManualResetEventSlim(false);
// shows 1.
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
// I'd like to wait for the UI to complete the swapping of ContentControl's Content
Application.Current.Dispatcher.Invoke(new Action(() => {
handle.Set();
// shows 1
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
}), DispatcherPriority.Background);
handle.Wait();
// for 1 second, the empty image should be visible, but it's the half clicked Button.
Thread.Sleep(1000);
IsLoading = false;
}
public DelegateCommand DoCommand { get; private set; }
// not working:
// public bool IsLoading { get; set; }
// working:
public bool IsLoading
{
get
{
return _isLoading;
}
set
{
_isLoading = value;
RaisePropertyChanged("IsLoading");
}
}
}
}
我不知道,那DO命令將在UI線程上運行。而且,如果我更改了代碼,那麼在後臺線程上運行長效操作,如果您沒有更改屬性(_'!l),它仍然不會將Content
更改爲空Image
。
用這個,如果你能在一些後臺線程中運行長時間運行的任務:
private void Do()
{
IsLoading = true;
// shows 1
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
Task.Factory.StartNew(() =>
{
// shows 5
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
//this operation is performed on a background thread...
Thread.Sleep(1000);
}).ContinueWith(task =>
{
IsLoading = false;
}, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
}
編輯:
第一種方法的工作原理。因此,如果您只更改IsLoading
設置爲RaisePropertyChanged("IsLoading")
,那麼它會正常工作。無需將長時間運行的任務放在後臺線程(我首先想避免的)中。在我的具體情況下,Thread.Sleep(1000)
是一個長時間運行的進程,它還需要在UI(調度程序)線程上運行,因爲可能會通過創建Dialog窗口來向用戶通知異常情況。
感謝您的回答@ MM8,但'Image'沒有顯示。我更接近解決方案。請檢查更新的問題 – ntohl
您還需要在IsLoading屬性的setter中引發PropertyChanged事件。看到我編輯的答案。 – mm8
哦。我真笨。謝謝。我從最小的例子中跳過了一些簡單的代碼 – ntohl