我有一个程序,当有一个加载图像时,会发生持久的动作。我想使用ContentControl
,其中包含Button
操作,或显示加载Image
。我已将Trigger
设置为IsLoading
属性,因此可以交换Content
。
查看:
<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线程上运行。但是,如果我更改代码,那么长时间操作在后台线程上运行,它仍然不会将Content
更改为空Image
,如果你没有提高属性已经改变(_'!l )。
如果您可以在某个后台线程上运行长时间运行的任务,请使用此选项:
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
setter更改为RaisePropertyChanged("IsLoading")
,那么它将正常工作。无需将长时间运行的任务放在后台线程中(我想首先避免)。在我的特定情况下,Thread.Sleep(1000)
是一个长时间运行的进程,它还需要在UI(调度程序)线程上运行,因为可能创建Dialog窗口以通知用户异常。
答案 0 :(得分:3)
您应该在后台线程上执行长时间运行的操作,即本例中的Thread.Sleep
:
private void Do()
{
IsLoading = true;
Task.Factory.StartNew(() =>
{
//this operation is performed on a background thread...
Thread.Sleep(1000);
}).ContinueWith(task =>
{
IsLoading = false;
}, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
}
UI线程不能同时睡眠和更新您的视图。
您还需要在PropertyChanged
属性的setter中引发IsLoading
事件:
private bool _isLoading;
public bool IsLoading
{
get { return _isLoading; }
set { _isLoading = value; RaisePropertyChanged(); }
}