2017-07-06 81 views
1

在视图模型上显示多个视图的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错误?)

+1

什么是例外? – Mishka

+0

不能在不可变对象上动画'(0)。(1)' –

回答

-1

删除,设置您尝试动画相同的属性(Background)二传手:

<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> 
+0

它对我有用。 – mm8

+0

你是对的!不知道我在吸烟...感谢评论! –

相关问题