2009-08-18 5 views
14

IsCritical 속성을 노출하는 Notification이라는 사용자 지정 클래스가있는 데이터 개체가 있습니다. 알림은 알림이 만료되는 경우 유효 기간을 가지며 사용자의 관심을 유도해야합니다. WPF - 바인딩 된 데이터 항목의 속성에 따라 조건부로 애니메이션 실행

이 테스트 데이터와 시나리오를 상상해 :

_source = new[] { 
    new Notification { Text = "Just thought you should know" }, 
    new Notification { Text = "Quick, run!", IsCritical = true }, 
    }; 

두 번째 항목은 펄스 배경으로 ItemsControl에 나타납니다. 다음은 회색과 노랑 사이에 배경을 애니메이트하려는 생각을 보여주는 간단한 데이터 템플릿 발췌 부분입니다. 내가 대해 확실 해요 무엇

<DataTemplate DataType="Notification"> 
    <Border CornerRadius="5" Background="#DDD"> 
    <Border.Triggers> 
     <EventTrigger RoutedEvent="Border.Loaded"> 
     <BeginStoryboard> 
      <Storyboard> 
      <ColorAnimation 
       Storyboard.TargetProperty="Background.Color" 
       From="#DDD" To="#FF0" Duration="0:0:0.7" 
       AutoReverse="True" RepeatBehavior="Forever" /> 
      </Storyboard> 
     </BeginStoryboard> 
     </EventTrigger> 
    </Border.Triggers> 
    <ContentPresenter Content="{TemplateBinding Content}" /> 
    </Border> 
</DataTemplate> 

IsCritical의 값에 따라이 애니메이션 조건부 만드는 방법입니다. 바운드 값이 false이면 #DDD의 기본 배경색을 유지해야합니다.

답변

11

이 퍼즐의 마지막 부분은 ... DataTriggers와 함께 작동하도록 명시 적으로 스토리 보드 "에서 targetName"속성을 지정해야합니다. 하나의 DataTrigger를 DataTemplate에 추가하고 IsCritical 속성에 바인딩하면 True이면 EnterAction/ExitAction에서 스토리 보드 강조 표시를 시작하고 중지 할 수 있습니다. 여기에 완전히 (당신은 확실히 더 나은 작업을 수행 할 수 있습니다) 일부 하드 코딩 된 바로 가기와 솔루션을하고있다 :

XAML : 뒤에

<Window x:Class="WpfTest.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="Notification Sample" Height="300" Width="300"> 
    <Window.Resources> 
    <DataTemplate x:Key="NotificationTemplate"> 
     <Border Name="brd" Background="Transparent"> 
     <TextBlock Text="{Binding Text}"/> 
     </Border> 
     <DataTemplate.Triggers> 
     <DataTrigger Binding="{Binding IsCritical}" Value="True"> 
      <DataTrigger.EnterActions> 
      <BeginStoryboard Name="highlight"> 
       <Storyboard> 
       <ColorAnimation 
        Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)" 
        Storyboard.TargetName="brd" 
        From="#DDD" To="#FF0" Duration="0:0:0.5" 
        AutoReverse="True" RepeatBehavior="Forever" /> 
       </Storyboard> 
      </BeginStoryboard> 
      </DataTrigger.EnterActions> 
      <DataTrigger.ExitActions> 
      <StopStoryboard BeginStoryboardName="highlight"/> 
      </DataTrigger.ExitActions> 
     </DataTrigger> 
     </DataTemplate.Triggers> 
    </DataTemplate> 
    </Window.Resources> 
    <Grid> 
    <Grid.RowDefinitions> 
     <RowDefinition Height="*"/> 
     <RowDefinition Height="Auto"/> 
    </Grid.RowDefinitions> 
    <ItemsControl ItemsSource="{Binding Notifications}" 
        ItemTemplate="{StaticResource NotificationTemplate}"/> 
    <Button Grid.Row="1" 
      Click="ToggleImportance_Click" 
      Content="Toggle importance"/> 
    </Grid> 
</Window> 

코드 :

using System.Collections.Generic; 
using System.ComponentModel; 
using System.Windows; 

namespace WpfTest 
{ 
    public partial class Window1 : Window 
    { 
    public Window1() 
    { 
     InitializeComponent(); 
     DataContext = new NotificationViewModel(); 
    } 

    private void ToggleImportance_Click(object sender, RoutedEventArgs e) 
    { 
     ((NotificationViewModel)DataContext).ToggleImportance(); 
    } 
    } 

    public class NotificationViewModel 
    { 
    public IList<Notification> Notifications 
    { 
     get; 
     private set; 
    } 

    public NotificationViewModel() 
    { 
     Notifications = new List<Notification> 
         { 
          new Notification 
          { 
           Text = "Just thought you should know" 
          }, 
          new Notification 
          { 
           Text = "Quick, run!", 
           IsCritical = true 
          }, 
         }; 
    } 

    public void ToggleImportance() 
    { 
     if (Notifications[0].IsCritical) 
     { 
     Notifications[0].IsCritical = false; 
     Notifications[1].IsCritical = true; 
     } 
     else 
     { 
     Notifications[0].IsCritical = true; 
     Notifications[1].IsCritical = false; 
     } 
    } 
    } 

    public class Notification : INotifyPropertyChanged 
    { 
    private bool _isCritical; 

    public string Text { get; set; } 

    public bool IsCritical 
    { 
     get { return _isCritical; } 
     set 
     { 
     _isCritical = value; 
     InvokePropertyChanged("IsCritical"); 
     } 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 

    private void InvokePropertyChanged(string name) 
    { 
     var handler = PropertyChanged; 
     if (handler != null) 
     { 
     handler(this, new PropertyChangedEventArgs(name)); 
     } 
    } 
    } 
} 

희망이 도움이 :).

+1

@Anvanka - 감사합니다. 이전에는 DataTrigger EnterActions 또는 ExitActions를 사용하지 않았습니다. 구체적인 예를 들어 주셔서 감사합니다. 좋은 답변과 현상금에 합당한 가치가 있습니다. –

+0

당신은 환영합니다 :). 내가 도울 수있어서 기쁩니다. – Anvaka

0

이 경우 스타일 트리거를 사용합니다.

<Style TargetType="Border"> 
    <Style.Triggers> 
     <DataTrigger Binding="{Binding IsCritical}" Value="true"> 
     <Setter Property="Triggers"> 
     <Setter.Value> 
      <EventTrigger RoutedEvent="Border.Loaded"> 
       <BeginStoryboard> 
       <Storyboard> 
        <ColorAnimation 
        Storyboard.TargetProperty="Background.Color" 
        From="#DDD" To="#FF0" Duration="0:0:0.7" 
        AutoReverse="True" RepeatBehavior="Forever" /> 
       </Storyboard> 
       </BeginStoryboard> 
      </EventTrigger> 
     </Setter.Value> 
     </Setter> 
     </DataTrigger> 
    </Style.Triggers> 
    </Style> 
+0

약속 같은데, 감사. 그것을 밖으로 시도하고 당신에게 돌아 가자. –

+1

아니, 작동하지 않습니다. 오류 : 'Property Setter'접근 가능 세트 접근자가 없기 때문에 트리거를 설정할 수 없습니다. ' –

+0

글쎄, 지금은 해결할 수있는 것보다 조금 더 복잡해 질 것입니다. 이 작업을 수행 할 수있는 방법이있을 것이라고 확신하지만 아마도 완전히 다른 방식으로 진행해야 할 것입니다. 트리거를 읽는 좋은 기회 ... – Will

2

은 내가 할 것이 두 가지 DataTemplates을 만들고 DataTemplateSelector를 사용하는 것입니다 (몇 가지 버그가있을 수 있습니다 그래서 메모리에서이 일을하고 있습니다).

<ItemsControl 
ItemsSource="{Binding ElementName=Window, Path=Messages}"> 
<ItemsControl.Resources> 
    <DataTemplate 
     x:Key="CriticalTemplate"> 
     <Border 
      CornerRadius="5" 
      Background="#DDD"> 
      <Border.Triggers> 
       <EventTrigger 
        RoutedEvent="Border.Loaded"> 
        <BeginStoryboard> 
         <Storyboard> 
          <ColorAnimation 
           Storyboard.TargetProperty="Background.Color" 
           From="#DDD" 
           To="#FF0" 
           Duration="0:0:0.7" 
           AutoReverse="True" 
           RepeatBehavior="Forever" /> 
         </Storyboard> 
        </BeginStoryboard> 
       </EventTrigger> 
      </Border.Triggers> 
      <TextBlock 
       Text="{Binding Path=Text}" /> 
     </Border> 
    </DataTemplate> 
    <DataTemplate 
     x:Key="NonCriticalTemplate"> 
     <Border 
      CornerRadius="5" 
      Background="#DDD"> 
      <TextBlock 
       Text="{Binding Path=Text}" /> 
     </Border> 
    </DataTemplate> 
</ItemsControl.Resources> 
<ItemsControl.ItemsPanel> 
    <ItemsPanelTemplate> 
     <StackPanel /> 
    </ItemsPanelTemplate> 
</ItemsControl.ItemsPanel> 
<ItemsControl.ItemTemplateSelector> 
    <this:CriticalItemSelector 
     Critical="{StaticResource CriticalTemplate}" 
     NonCritical="{StaticResource NonCriticalTemplate}" /> 
</ItemsControl.ItemTemplateSelector> 

그리고 DataTemplateSelector가 비슷한 것 : 다음과 템플릿에 중요한

class CriticalItemSelector : DataTemplateSelector 
{ 
    public DataTemplate Critical 
    { 
     get; 
     set; 
    } 

    public DataTemplate NonCritical 
    { 
     get; 
     set; 
    } 

    public override DataTemplate SelectTemplate(object item, 
      DependencyObject container) 
    { 
     Message message = item as Message; 
     if(item != null) 
     { 
      if(message.IsCritical) 
      { 
       return Critical; 
      } 
      else 
      { 
       return NonCritical; 
      } 
     } 
     else 
     { 
      return null; 
     } 
    } 
} 

이 방법, WPF가 자동으로 설정됩니다 아무것도 귀하의 XAML은 무언가 같이 일 것입니다 애니메이션 등 모든 것이 다른 템플릿이 될 것입니다. 이것은 나중에 다른 속성을 사용하여 템플릿을 전환하거나 더 많은 템플릿 (낮음/보통/높음 중요도 구성표)을 추가 할 수 있다는 점에서 일반적입니다.

+0

이것은 흥미로운 대답이지만, 내가 원하는만큼 융통성이 없다. 예를 들어, 데이터 템플릿 내에 여러 속성의 상태에 따라 애니메이션을 적용해야하는 요소가 여러 개있는 경우 어떻게해야할까요? 제 경우에도 실제 데이터 템플릿은''보다 훨씬 복잡하므로이 방법을 통해 제 XAML에 많은 복제를 도입 할 것입니다. 그래도 어떤 사람들에게는 적합 할 수도 있습니다. 자세한 설명은 +1! –

2

DoubleAnimation에서 잘 작동하기 때문에 ColorAnimation과의 유사성이있는 것으로 보입니다. 당신은 ColorAnimation

<Window.Resources> 

    <DataTemplate x:Key="NotificationTemplate"> 

     <DataTemplate.Triggers> 
      <DataTrigger Binding="{Binding Path=IsCritical}" Value="true"> 
       <DataTrigger.EnterActions> 
        <BeginStoryboard> 
         <Storyboard> 
          <ColorAnimation 
           Storyboard.TargetProperty="Background.Color" 
           Storyboard.TargetName="border" 
           From="#DDD" To="#FF0" Duration="0:0:0.7" 
           AutoReverse="True" RepeatBehavior="Forever" /> 
         </Storyboard> 
        </BeginStoryboard> 
       </DataTrigger.EnterActions> 
      </DataTrigger> 
     </DataTemplate.Triggers> 

     <Border x:Name="border" CornerRadius="5" Background="#DDD" > 
      <TextBlock Text="{Binding Text}" /> 
     </Border> 

    </DataTemplate> 

</Window.Resources> 

<Grid> 
    <ItemsControl x:Name="NotificationItems" ItemsSource="{Binding}" ItemTemplate="{StaticResource NotificationTemplate}" /> 
</Grid> 
+0

@TFD - 답변 해 주셔서 감사합니다. 귀하의 편집과 함께, 그것은 내 필요에 어울리지 만 @Anvanka는 정답 (기본적으로 동일)을 당신에게 줬다. 그래서 나는 그/그녀에게 그것을 주었다. +1 똑같이. –

1

들어오는 속성 업데이트가 특정 값일 때만 애니메이션을 시작하는 솔루션입니다. 애니메이션으로 무언가에 사용자의주의를 끌기를 원하지만 나중에 UI가 기본 상태로 돌아 가야하는 경우에 유용합니다.

IsCritical이 컨트롤 (또는 보이지 않는 컨트롤)에 바인딩되었다고 가정하면 NotifyOnTargetUpdated를 바인딩에 추가하고 EventTrigger를 Binding.TargetUpdated 이벤트에 연결합니다. 입력 값이 관심있는 일 때 그럼 당신은 해고에 TargetUpdated 이벤트를 컨트롤을 확장 할 수 있습니다. 그래서 ...

public class CustomTextBlock : TextBlock 
    { 
     public CustomTextBlock() 
     { 
      base.TargetUpdated += new EventHandler<DataTransferEventArgs>(CustomTextBlock_TargetUpdated); 
     } 

     private void CustomTextBlock_TargetUpdated(object sender, DataTransferEventArgs e) 
     { 
      // don't fire the TargetUpdated event if the incoming value is false 
      if (this.Text == "False") e.Handled = true; 
     } 
    } 

과 XAML 파일에 ..

<DataTemplate> 
.. 
<Controls:CustomTextBlock x:Name="txtCustom" Text="{Binding Path=IsCritical, NotifyOnTargetUpdated=True}"/> 
.. 
<DataTemplate.Triggers> 
<EventTrigger SourceName="txtCustom" RoutedEvent="Binding.TargetUpdated"> 
    <BeginStoryboard> 
    <Storyboard>..</Storyboard> 
    </BeginStoryboard> 
</EventTrigger> 
</DataTemplate.Triggers> 
</DataTemplate> 
관련 문제