在视图模型上显示多个视图的WPF-MVVM应用程序。用户可以在运行时从较不详细的视图转到更详细的视图。视图包含在边框内。在一组条件下,警报由视图模型触发。警报在边界背景上呈现为闪烁颜色的动画,以吸引用户注意。这个MultiDataTrigger为什么会在动画中引发异常?
问题是,当触发警报并且用户在运行时更改数据模板以获取更多详细信息时,WPF引擎在使用多数据触发器警报时会引发动画异常。引擎在使用Datatrigger时工作,在MultiDataTrigger上崩溃并且其他所有内容都相同。
例外情况是:不能动画不可变对象
的示例应用程序来演示问题上 '(0)(1)':
1主窗口用于托管和切换视图。
2意见,大和小。
1 viewmodel。
1个资源字典用于多和单datatriggers
App.xaml中:
<Application x:Class="AnimationSample.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:AnimationSample"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary Source="Dictionary1.xaml"/>
</Application.Resources>
</Application>
MainWindow.xaml:
<Window x:Class="AnimationSample.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:AnimationSample"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate DataType="{x:Type local:SampleViewModel}" x:Key="smallTemplate">
<local:SmallUserControl />
</DataTemplate>
<DataTemplate DataType="{x:Type local:SampleViewModel}" x:Key="largeTemplate">
<local:LargeUserControl />
</DataTemplate>
<DataTemplate DataType="{x:Type local:SampleViewModel}" x:Key="mainTemplate">
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding ElementName=ZoomSlider, Path=Value}" Value="1">
<Setter Property="ContentTemplate" Value="{StaticResource smallTemplate}"/>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=ZoomSlider, Path=Value}" Value="2">
<Setter Property="ContentTemplate" Value="{StaticResource largeTemplate}"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
<Style TargetType="{x:Type ContentControl}" x:Key="DisplayStyle">
<Setter Property="ContentTemplate" Value="{StaticResource smallTemplate}" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=ZoomSlider, Path=Value}" Value="1">
<Setter Property="ContentTemplate" Value="{StaticResource smallTemplate}"/>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=ZoomSlider, Path=Value}" Value="2">
<Setter Property="ContentTemplate" Value="{StaticResource largeTemplate}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<StackPanel Margin="8">
<Slider x:Name="ZoomSlider" Minimum="1" Maximum="2" IsSnapToTickEnabled="True" />
<ContentControl Content="{Binding}" Style="{StaticResource DisplayStyle}">
</ContentControl>
</StackPanel>
</Window>
MainWindow.cs:
using System.Windows;
namespace AnimationSample
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new SampleViewModel();
}
}
}
SampleViewMo del.cs:
using System;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Windows;
namespace AnimationSample
{
public class SampleViewModel : NotifyPropertyChangedBase<SampleViewModel>
{
private bool _alarm;
public bool Alarm
{
get { return _alarm; }
set
{
if (!_alarm.Equals(value))
{
_alarm = value;
OnPropertyChanged("Alarm");
}
}
}
private bool _flash;
public bool Flash
{
get { return _flash; }
set
{
if (!_flash.Equals(value))
{
_flash = value;
OnPropertyChanged("Flash");
}
}
}
private string _description = "I am an alarm!";
public string Description
{
get { return _description; }
set
{
if(!_description.Equals(value))
{
_description = value;
OnPropertyChanged("Description");
}
}
}
}
public abstract class NotifyPropertyChangedBase<T> : DependencyObject, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{ PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName)); }
}
protected virtual void OnPropertyChanged<T2>(Expression<Func<T, T2>> accessor)
{
OnPropertyChanged(PropertyName(accessor));
}
public static string PropertyName<T2>(Expression<Func<T, T2>> accessor)
{
return ((MemberExpression)accessor.Body).Member.Name;
}
}
}
Dictionary1.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:AnimationSample">
<Style x:Key="FlashyAlarmBorderStyle" TargetType="{x:Type Border}">
<Setter Property="BorderBrush" Value="Silver"/>
<Setter Property="Background" Value="Black" />
<Setter Property="BorderThickness" Value="2" />
<Setter Property="CornerRadius" Value="8" />
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Alarm}" Value="True" />
<Condition Binding="{Binding Flash}" Value="True" />
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="Orange"/>
<Setter Property="BorderBrush" Value="Orange"/>
<MultiDataTrigger.EnterActions>
<BeginStoryboard Name="faultBoard">
<Storyboard>
<ColorAnimation AutoReverse="True"
RepeatBehavior="Forever"
Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
Duration="00:00:01"
From="Silver"
To="Orange"/>
</Storyboard>
</BeginStoryboard>
</MultiDataTrigger.EnterActions>
<MultiDataTrigger.ExitActions>
<StopStoryboard BeginStoryboardName="faultBoard">
</StopStoryboard>
</MultiDataTrigger.ExitActions>
</MultiDataTrigger>
<!--<DataTrigger Binding="{Binding Alarm}" Value="True" >
<Setter Property="Background" Value="Orange"/>
<Setter Property="BorderBrush" Value="Orange"/>
<DataTrigger.EnterActions>
<BeginStoryboard Name="alarmBoard">
<Storyboard>
<ColorAnimation AutoReverse="True"
RepeatBehavior="Forever"
Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
Duration="00:00:01"
From="Silver"
To="Orange"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<StopStoryboard BeginStoryboardName="alarmBoard">
</StopStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>-->
</Style.Triggers>
</Style>
</ResourceDictionary>
LargeUserControl.xaml:
<UserControl x:Class="AnimationSample.LargeUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:AnimationSample"
mc:Ignorable="d" >
<Border Margin="8"
Style="{DynamicResource FlashyAlarmBorderStyle}">
<StackPanel>
<Label HorizontalAlignment="Center" Content="LARGE VIEW" Foreground="White"/>
<CheckBox Margin="8" IsChecked="{Binding Alarm}" Content="Turn on the alarm." Foreground="White" />
<CheckBox Margin="8" IsChecked="{Binding Flash}" Content="Turn on the flash!" Foreground="White" />
<Label Margin="8" Content="{Binding Description}" Foreground="White"/>
</StackPanel>
</Border>
</UserControl>
SmallUserControl.xaml:
<UserControl x:Class="AnimationSample.SmallUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:AnimationSample"
mc:Ignorable="d" >
<Border Margin="8"
Style="{DynamicResource FlashyAlarmBorderStyle}">
<StackPanel>
<Label HorizontalAlignment="Center" Content="SMALL VIEW" Foreground="White"/>
<CheckBox Margin="4" IsChecked="{Binding Alarm}" Content="Turn on the alarm." HorizontalAlignment="Center" Foreground="White" />
<CheckBox Margin="4" IsChecked="{Binding Flash}" Content="Turn on the flash!" HorizontalAlignment="Center" Foreground="White" />
</StackPanel>
</Border>
</UserControl>
如果您运行的代码,点击闹钟并闪烁eckboxes,那么控件将从黑色背景的银色边框变成闪烁的橙色。如果您将滑块从小到大的视图移动,如果使用多数据触发器,您将从wpf获得运行时间。如果您取消注释Dictionar1.xaml中的数据触发器并评论多数据触发器,然后重复上述操作,则应用程序运行正常。
为什么这个multidatatrigger在DataTemplate变化和datatrigger工作正常吗?唯一的区别是单个数据触发器是一个额外的布尔值。它如何被修复?
(是的,可以通过在viewmodel上创建一个属性来对这些布尔值进行聚合,但是这不应该被完成,并且不存在问题,这看起来像是一个wpf错误?)
什么是例外? – Mishka
不能在不可变对象上动画'(0)。(1)' –