WPF - Создание условного выражения выполнения анимации на свойстве связанного элемента данных

Можно также использовать HWNDSource и средства управления HWNDHost для встраивания средств управления WPF в WinForms (или любой Win32, действительно) приложение.

14
задан Drew Noakes 29 August 2009 в 18:12
поделиться

5 ответов

The final part of this puzzle is... DataTriggers. All you have to do is add one DataTrigger to your DataTemplate, bind it to IsCritical property, and whenever it's true, in it's EnterAction/ExitAction you start and stop highlighting storyboard. Here is completely working solution with some hard-coded shortcuts (you can definitely do better):

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>

Code behind:

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

Hope this helps :).

12
ответ дан 1 December 2019 в 13:47
поделиться

В этом случае используются триггеры стиля. (Я делаю это по памяти, поэтому могут быть некоторые ошибки)

  <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 December 2019 в 13:47
поделиться

Похоже, это странность с ColorAnimation, так как она отлично работает с DoubleAnimation. Вам необходимо явно указать свойство раскадровки "TargetName" для работы с 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>
2
ответ дан 1 December 2019 в 13:47
поделиться

Я бы создал два DataTemplate и использовал DataTemplateSelector. Ваш XAML будет выглядеть примерно так:

<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 автоматически установит все, что является критическим для шаблона с анимацией, а все остальное будет другим шаблоном. Это также универсально, поскольку позже вы можете использовать другое свойство для переключения шаблонов и / или добавления дополнительных шаблонов (схема низкой / нормальной / высокой важности).

2
ответ дан 1 December 2019 в 13:47
поделиться

Вот решение, которое начинает только анимацию, когда входящее обновление свойств является определенным значением. Полезно, если вы хотите привлечь внимание пользователя к чему-то с анимацией, но после этого пользователь должен вернуться к этому состоянию по умолчанию.

Предполагая, что ISCRICATION связан с контролем (или даже невидимым управлением), вы добавляете NotibleTargetupdupped на привязку и привязывают ElectTrigger к связыванию. Затем вы продлите элемент управления только для пожара на целевоевременное событие, когда входящее значение является тем, кого вы заинтересованы. Так ...

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>
1
ответ дан 1 December 2019 в 13:47
поделиться
Другие вопросы по тегам:

Похожие вопросы: