2017-07-06 15 views
1

ビューモデルに複数のビューを表示するWPF-MVVMアプリケーション。実行時に、より詳細なビューからより詳細なビューに移動することができます。ビューは境界内に含まれます。一連の条件の下で、アラームはビューモデルによってトリガされます。アラームは、境界線の背景に点滅する色のアニメーションとしてビューにレンダリングされ、ユーザーの注意を引き付けます。なぜこのMultiDataTriggerはアニメーションで例外をスローしますか?

問題は、アラームがトリガされ、ユーザーが詳細を取得するために実行時にデータ型を変更した場合、マルチデータトリガアラームを使用するとWPFエンジンがアニメーションの例外をスローします。エンジンはDatatriggerを使用しているときに動作し、MultiDataTriggerで他のすべてが同じであればクラッシュします。

例外がある: '(0)(1)' アニメーション化することはできません不変オブジェクトに

問題を実証するサンプルアプリケーション:ビューをホスティングして切り替えるための

1メインウィンドウ。

大小2表示。

1 viewmodel。マルチ単一datatriggersため

1つのリソース辞書

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> 

あなたがコードを実行した場合と、アラームとフラッシュをクリックしてくださいコントロールが黒い背景の銀色のボーダーから点滅するオレンジ色に変わります。小さなビューから大きなビューにスライダを移動すると、マルチデータトリガを使用するとwpfから実行時間が得られます。 Dictionar1.xamlのdatatriggerのコメントを外してmultidatatriggerにコメントする場合は、上記を繰り返して、アプリケーションは正常に実行されます。

DataTemplateの変更とデータトリガーでこのマルチデータトリガーギャグが正常に機能するのはなぜですか?単一のデータトリガーとの唯一の違いは、余分なブール値の1つです。どのように修正することができますか?

(はい、これらのブール値を集計するために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

あなたは正しいです!私が何を喫煙しているかわからない...レビューをありがとう! –

関連する問題