2009-08-18 11 views
14

Bir veri nesnesine sahibim - IsCritical özelliğinin açığa çıktığı Notification adlı özel bir sınıf var. Bir bildirim süresinin dolması halinde, geçerlilik süresi vardır ve kullanıcının dikkati çekilmelidir. WPF - Bir animasyonun yürütme koşulunu bağlı veri öğesinin bir özelliği için koşullu hale getirme

bu test verileri ile bir senaryo düşünün:

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

ikinci öğe nabız gibi atan bir geçmişi olan ItemsControl görünmelidir. İşte, gri ve sarı arasındaki arka planı canlandırmayı düşündüğüm araçları gösteren basit bir veri şablonu alıntısı. Ben emin değilim ne

<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 değeri üzerinde bu animasyon koşullu yapmak nasıl. Bağımlı değer false ise, #DDD varsayılan arka plan rengi korunmalıdır.

cevap

11

bu bulmacanın son parçası olan ... DataTriggers çalışmak explicity storyboard'lar "HedefAdı" özelliğini belirtmek gerekir. Tek yapmanız gereken, DataTemplate'ınıza bir DataTrigger eklemek, bunu IsCritical özelliğine bağlamaktır ve her ne zaman doğruysa, EnterAction/ExitAction'da, storyboard'u vurgulayıp başlatırsınız. Burada tamamen (kesinlikle daha iyi yapabilir) bazı kodlanmış kısayollar çözüm çalışıyor:

Xaml: arkasında

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

Kodu:

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)); 
     } 
    } 
    } 
} 

Umut bu yardımcı olur:).

+1

@Anvanka - bunun için teşekkürler. Daha önce DataTrigger EnterActions veya ExitActions kullanmamıştım. Ayrıca detaylı örnek için teşekkürler - büyük bir cevap ve ödülün layık. –

+0

Rica ederim :). Yardım edebileceğime sevindim. – Anvaka

0

Bu durumda stil tetikleyicileri kullanıyorsunuz.

<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

umut verici görünüyor, Teşekkürler. Denemeye ve sana geri dönmeme izin ver. –

+1

Hayır, çalışmıyor. Hatayı alın: 'Özellik Belirleyici' Tetikleyicileri 'erişilebilir bir erişimci erişimi olmadığı için ayarlanamaz.' –

+0

Peki, bu şu an üzerinde çalışabileceğimden biraz daha karmaşık olacak. Eminim bunu yapmanın bir yolu var, ama muhtemelen tamamen farklı bir yoldan gitmelisin. Tetikleyicileri okumak için iyi bir fırsat ... – Will

2

Ne yapacağını iki datatemplates oluşturmak ve bir DataTemplateSelector kullanmaktır (bazı hatalar olabilir bu yüzden bellekten yapıyorum).

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

Ve DataTemplateSelector benzer bir şey olurdu: ile şablona kritiktir

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; 
     } 
    } 
} 

Bu arada, WPF otomatik ayarlayacaktır şey senin XAML gibi bir şey olurdu animasyon ve diğer her şey diğer şablon olacak. Bu aynı zamanda, daha sonra, şablonları değiştirmek ve/veya daha fazla şablon eklemek için farklı bir özellik kullanabilirsiniz (A Düşük/Normal/Yüksek önem şeması). DoubleAnimation ile iyi çalıştığı için ColorAnimation ile bir sıkıntı gibi görünüyor.

+0

Bu ilginç bir cevap, ama istediğim kadar esnek değil. Örneğin, veri şablonunda farklı özelliklerin durumuna bağlı olarak canlandırılması gereken birden çok öğe varsa ne olur? Benim durumumda da, gerçek veri şablonu sadece 'den çok daha karmaşıktır. Bu yüzden XAML'ım ile bu şekilde çok fazla çoğaltma yapacağım. Gerçi bazı insanlarla tanışabilir. Detaylı açıklama için +1! –

2

Sen 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 - cevabınız için teşekkürler. Düzenlemeyle, ihtiyaçlarımı karşılar, ama @Anvanka size doğru bir cevap verdi (temelde aynı), bu yüzden ona verdim. Hepsi aynı +1. –

1

Gelen özellik güncelleştirmesi belirli bir değer olduğunda, yalnızca animasyonu başlatan bir çözüm. Kullanıcının dikkatini animasyonla bir şeye çekmek istiyorsanız, ancak daha sonra kullanıcı arayüzü varsayılan durumuna dönmelidir.

IsCritical'in bir denetime (veya görünmez bir denetime) bağlı olduğunu varsayarsak, NotifyOnTargetUpdated öğesini bağlamaya eklersiniz ve bir EventTrigger'ı Binding.TargetUpdated olayına bağlarsınız. Gelen değer ilgilendiğiniz biridir Sonra sadece yangın TargetUpdated olay kontrolünü uzatın. Yani ...

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; 
     } 
    } 

ve XAML dosyasında ..

<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> 
İlgili konular