WPF 数据触发器和 UI 调度程序
WPF Datatrigger and UI dispatcher
我在方法调用中多次更新 属性 值。
我的 UI 元素绑定到数据属性,我的 viewModel 实现了 INotifyPropertychnaged。
我将数据触发器设置为更新 UI 值。它适用于方法调用结束时的最终 属性 值。但是,我想根据方法调用中的 属性 值更改来刷新 UI。最好的方法是什么?
隐藏代码:
private void Button_Click(object sender, RoutedEventArgs e)
{
OrderViewModel model = this.DataContext as OrderViewModel;
model.OrderStatus = OrderViewModel.OrderStatuses.Updating;
// Processing order logic
// takes about few seconds, Here I want to update UI with Refreshing Icon inprogress.gif
model.OrderStatus = OrderViewModel.OrderStatuses.Updated;
}
XAML代码:
<Window.Resources>
<BitmapImage x:Key="NotCreatedSource" UriSource="/Images/notcreated_20x20.png" />
<Style TargetType="Image" x:Key="OrderLineItem">
<Setter Property="Source" Value="{StaticResource NotCreatedSource}" />
<Setter Property="Height" Value="32" />
<Setter Property="Width" Value="24" />
<Style.Triggers>
<DataTrigger Binding="{Binding OrderStatus, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Value="Updated">
<Setter Property="Source" Value="/Images/checkmark_icon.png" ></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding OrderStatus, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Value="Updating">
<Setter Property="Source" Value="/Images/inprogress.gif" ></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<StackPanel>
<StackPanel Margin="12,100,30,0" Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Top">
<Image Style="{StaticResource OrderLineItem}" Name="ConnectionImage" />
</StackPanel>
<Button Width="250" Height="25" Margin="20" Click="Button_Click">Change status</Button>
</StackPanel>
型号Class:
public class OrderViewModel : INotifyPropertyChanged
{
public OrderStatuses OrderStatus
{
get
{
return _orderstatus;
}
set
{
_orderstatus = value;
RaisePropertyChanged("OrderStatus");
}
}
private OrderStatuses _orderstatus;
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
public enum OrderStatuses
{
Updated = 1,
Updating,
NotCreated,
Failed,
NotAvailable
}
}
并且在 UI 中,基于这 2 个状态,我在命令控制旁边有图像图标。它从不显示更新图标,但在调用结束时显示已更新。
如何在 UI 上显示两种状态更新?
问题是您正在阻止 UI 线程进行处理,WPF 不会呈现。最骇人听闻的解决方法就像 VB6 中的旧 Application.DoEvents
:在处理期间使用空操作委托定期调用 this.Dispatcher.Invoke
。
稍微好一点——但仍然回避了核心问题——将处理和最终更新包装在传递给 this.Dispatcher.BeginInvoke
的委托中。这是一个有点hacky的延续。
逻辑上干净的方法是通过 Task(或线程或 BackgroundWorker 或任何其他异步 API)将处理移动到后台,并在任务延续中完成状态更新。
OrderViewModel model = this.DataContext as OrderViewModel;
model.OrderStatus = OrderViewModel.OrderStatuses.Updating;
Task.Factory.StartNew(() => /* processing order logic */)
.ContinueWith(t => model.OrderStatus = OrderViewModel.OrderStatuses.Updated);
根据您的 .NET Framework 版本,您可以使用 await
而不是 ContinueWith
。
我在方法调用中多次更新 属性 值。 我的 UI 元素绑定到数据属性,我的 viewModel 实现了 INotifyPropertychnaged。 我将数据触发器设置为更新 UI 值。它适用于方法调用结束时的最终 属性 值。但是,我想根据方法调用中的 属性 值更改来刷新 UI。最好的方法是什么?
隐藏代码:
private void Button_Click(object sender, RoutedEventArgs e)
{
OrderViewModel model = this.DataContext as OrderViewModel;
model.OrderStatus = OrderViewModel.OrderStatuses.Updating;
// Processing order logic
// takes about few seconds, Here I want to update UI with Refreshing Icon inprogress.gif
model.OrderStatus = OrderViewModel.OrderStatuses.Updated;
}
XAML代码:
<Window.Resources>
<BitmapImage x:Key="NotCreatedSource" UriSource="/Images/notcreated_20x20.png" />
<Style TargetType="Image" x:Key="OrderLineItem">
<Setter Property="Source" Value="{StaticResource NotCreatedSource}" />
<Setter Property="Height" Value="32" />
<Setter Property="Width" Value="24" />
<Style.Triggers>
<DataTrigger Binding="{Binding OrderStatus, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Value="Updated">
<Setter Property="Source" Value="/Images/checkmark_icon.png" ></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding OrderStatus, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Value="Updating">
<Setter Property="Source" Value="/Images/inprogress.gif" ></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<StackPanel>
<StackPanel Margin="12,100,30,0" Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Top">
<Image Style="{StaticResource OrderLineItem}" Name="ConnectionImage" />
</StackPanel>
<Button Width="250" Height="25" Margin="20" Click="Button_Click">Change status</Button>
</StackPanel>
型号Class:
public class OrderViewModel : INotifyPropertyChanged
{
public OrderStatuses OrderStatus
{
get
{
return _orderstatus;
}
set
{
_orderstatus = value;
RaisePropertyChanged("OrderStatus");
}
}
private OrderStatuses _orderstatus;
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
public enum OrderStatuses
{
Updated = 1,
Updating,
NotCreated,
Failed,
NotAvailable
}
}
并且在 UI 中,基于这 2 个状态,我在命令控制旁边有图像图标。它从不显示更新图标,但在调用结束时显示已更新。
如何在 UI 上显示两种状态更新?
问题是您正在阻止 UI 线程进行处理,WPF 不会呈现。最骇人听闻的解决方法就像 VB6 中的旧 Application.DoEvents
:在处理期间使用空操作委托定期调用 this.Dispatcher.Invoke
。
稍微好一点——但仍然回避了核心问题——将处理和最终更新包装在传递给 this.Dispatcher.BeginInvoke
的委托中。这是一个有点hacky的延续。
逻辑上干净的方法是通过 Task(或线程或 BackgroundWorker 或任何其他异步 API)将处理移动到后台,并在任务延续中完成状态更新。
OrderViewModel model = this.DataContext as OrderViewModel;
model.OrderStatus = OrderViewModel.OrderStatuses.Updating;
Task.Factory.StartNew(() => /* processing order logic */)
.ContinueWith(t => model.OrderStatus = OrderViewModel.OrderStatuses.Updated);
根据您的 .NET Framework 版本,您可以使用 await
而不是 ContinueWith
。