当ItemsSource在WPF中的大小更改时,如何使应用程序背景闪烁?

时间:2012-08-15 13:37:40

标签: c# wpf xaml triggers

我的顶级菜单中显示了错误通知列表。

<MenuItem Header="{Binding NotificationList.UnreadCount}"
    HeaderStringFormat="Notifications ({0})"
    ItemsSource="{Binding NotificationList.Notifications}">
 </MenuItem>

我想做的是&#34;眨眼&#34;应用程序通过将背景颜色更改为红色然后每次NotificationList的大小更改(然后大于零)返回(ColorAnimation,1s,AutoRevert)。 NotificationList在更改时已经通知视图。

任何人都可以帮我编写正确的触发器来更改ItemsSource的大小,在此触发器中,更改应用程序窗口的背景颜色(不是元素本身)

2 个答案:

答案 0 :(得分:1)

我不知道如何使用触发器执行此操作,但您可以创建一个attached behaviour,为ItemsSource的CollectionChanged事件添加处理程序。

前提是主窗口的背景设置为SolidColorBrush,如下所示

<Window ...>
    <Window.Background>
        <SolidColorBrush Color="{x:Static SystemColors.ControlColor}"/>
    </Window.Background>
    ...
</Window>

这样的附加行为可能如下所示:

public class ItemsControlBehaviours
{
    public static readonly DependencyProperty BlinkMainWindowOnItemsSourceChangeProperty =
        DependencyProperty.RegisterAttached(
            "BlinkMainWindowOnItemsSourceChange", typeof(bool), typeof(ItemsControlBehaviours),
            new PropertyMetadata(BlinkMainWindowOnItemsSourceChangePropertyChanged));

    public static bool GetBlinkMainWindowOnItemsSourceChange(ItemsControl itemsControl)
    {
        return (bool)itemsControl.GetValue(BlinkMainWindowOnItemsSourceChangeProperty);
    }

    public static void SetBlinkMainWindowOnItemsSourceChange(ItemsControl itemsControl, bool value)
    {
        itemsControl.SetValue(BlinkMainWindowOnItemsSourceChangeProperty, value);
    }

    private static void BlinkMainWindowOnItemsSourceChangePropertyChanged(
        DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        ItemsControl itemsControl = obj as ItemsControl;
        INotifyCollectionChanged collection;

        if (itemsControl != null &&
            (collection = itemsControl.ItemsSource as INotifyCollectionChanged) != null)
        {
            if ((bool)e.NewValue)
            {
                collection.CollectionChanged += ItemsSourceCollectionChanged;
            }
            else
            {
                collection.CollectionChanged -= ItemsSourceCollectionChanged;
            }
        }
    }

    private static void ItemsSourceCollectionChanged(
        object sender, NotifyCollectionChangedEventArgs e)
    {
        SolidColorBrush background =
            Application.Current.MainWindow.Background as SolidColorBrush;

        if (background != null)
        {
            ColorAnimation backgroundAnimation = new ColorAnimation
            {
                To = Colors.Red,
                Duration = TimeSpan.FromSeconds(1),
                AutoReverse = true
            };

            background.BeginAnimation(
                SolidColorBrush.ColorProperty, backgroundAnimation);
        }
    }
}

请注意,闪烁颜色和持续时间在这里是硬编码的,您可能需要找到一种参数化方法。

现在,您可以在任何设置了ItemsSource属性的ItemsControl上使用此行为:

<ListBox ItemsSource="..." Background="Transparent"
    local:ItemsControlBehaviours.BlinkMainWindowOnItemsSourceChange="True" />

答案 1 :(得分:0)

这可能并不像你想的那么简单,但它可以满足你的需要。所有这些代码都在我的测试工具的主窗口中,所以有些东西可能需要根据您的具体情况进行更改。但这个想法是一样的。

我们需要做的第一件事就是让自己在集合发生变化时获得一个事件。我使用了一个可观察的集合,因为如果你有约束力,我会认为你正在使用它:

  ObservableCollection<String> m_NotificationList;
  // Propertized for binding purposes
  public ObservableCollection<String> NotificationList
  {
     get
     {
        return m_NotificationList;
     }
  }

  public MainWindow()
  {
     InitializeComponent();

     this.DataContext = this;

     m_NotificationList = new ObservableCollection<string>() { "hey", "ho", "lets", "go" };
     m_NotificationList.CollectionChanged += CollectionChangeCallback;
  }

下一步是定义一些路由事件,我们可以在闪存时发送到窗口。我们在mainwindow的类定义中执行此操作,如上所述:

     public static readonly RoutedEvent FlashEvent =
     EventManager.RegisterRoutedEvent("Flash",
     RoutingStrategy.Tunnel, typeof(RoutedEventHandler), typeof(MainWindow));

  public event RoutedEventHandler Flash
  {
     add { AddHandler(FlashEvent, value); }
     remove { RemoveHandler(FlashEvent, value); }
  }

现在我们需要在集合更新时定义我们的回调,再次在mainwindow的类定义中:

void CollectionChangeCallback(object sender, EventArgs e)
{
   // Don't fire if we don't want to flash
   if (m_NotificationList.Count > 0)
      window.RaiseEvent(new RoutedEventArgs(FlashEvent));
}

现在我们转到XAML并为我们的MainWindow添加一个触发器,它处理我们刚创建的路由事件并执行我们想要做的动画:

<Window.Triggers>
   <EventTrigger RoutedEvent="local:MainWindow.Flash" >
      <BeginStoryboard>
         <Storyboard>
            <ColorAnimation AutoReverse="True"
                                  Duration="0:0:1"
                                  FillBehavior="Stop"
                                  From="White"
                                  Storyboard.TargetName="window"
                                  Storyboard.TargetProperty="Background.Color"
                                  To="Red" />
         </Storyboard>
      </BeginStoryboard>
   </EventTrigger>
</Window.Triggers>

这可以按照我在测试工具中的要求进行操作。这有点尴尬,但我找不到更好的方法来做到这一点。为清楚起见,我还将在此处包含我的XAML代码隐藏。它是一个带有MenuItem的空窗口和一个向集合添加字符串的按钮。你可以看到闪光灯以及事件聚集在一起的方式来实现它。

XAML:

<Window x:Class="WPFTestbed.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WPFTestbed"
        x:Name="window"
        Title="MainWindow"
        Width="525"
        Height="350">

   <Window.Triggers>
      <EventTrigger RoutedEvent="local:MainWindow.Flash" >
         <BeginStoryboard>
            <Storyboard>
               <ColorAnimation AutoReverse="True"
                                     Duration="0:0:1"
                                     FillBehavior="Stop"
                                     From="White"
                                     Storyboard.TargetName="window"
                                     Storyboard.TargetProperty="Background.Color"
                                     To="Red" />
            </Storyboard>
         </BeginStoryboard>
      </EventTrigger>
   </Window.Triggers>

   <StackPanel>
      <MenuItem x:Name="menu"
                Header="{Binding NotificationList.Count}"
                HeaderStringFormat="Notifications ({0})"
                ItemsSource="{Binding NotificationList}">
      </MenuItem>
      <Button Click="Button_Click"
              Content="Hi" />
   </StackPanel>
</Window>

背后的代码( - 包括空格):

namespace WPFTestbed
{
   /// <summary>
   /// Interaction logic for MainWindow.xaml
   /// </summary>
   public partial class MainWindow : Window
   {
      public static readonly RoutedEvent FlashEvent =
         EventManager.RegisterRoutedEvent("Flash",
         RoutingStrategy.Tunnel, typeof(RoutedEventHandler), typeof(MainWindow));

      public event RoutedEventHandler Flash
      {
         add { AddHandler(FlashEvent, value); }
         remove { RemoveHandler(FlashEvent, value); }
      }

      ObservableCollection<String> m_NotificationList;

      public ObservableCollection<String> NotificationList
      {
         get
         {
            return m_NotificationList;
         }
      }

      public MainWindow()
      {
         InitializeComponent();

         this.DataContext = this;

         m_NotificationList = new ObservableCollection<string>() { "hey", "ho", "lets", "go" };
         m_NotificationList.CollectionChanged += CollectionChangeCallback;
      }

      void CollectionChangeCallback(object sender, EventArgs e)
      {
         if (m_NotificationList.Count > 0)
            window.RaiseEvent(new RoutedEventArgs(FlashEvent));
      }
      private void Button_Click(object sender, RoutedEventArgs e)
      {
         m_NotificationList.Add("Another");
      }
   }
}