如何使背景颜色动画为新颜色和背面而不会失去原始颜色的轨迹?

时间:2014-01-30 20:57:31

标签: c# wpf xaml animation

因此,我正在尝试创建一个简单的动画,将背景从初始颜色转换为新颜色并返回。

我遇到的最初问题是我在MouseDownEvent上创建了一个触发动画的触发器,但是用户可以在第一个动画完成之前触发另一个动画。这个新动画将从当前的阴影到新颜色和背面进行动画制作。通过在动画进行过程中逐步重新启动动画,原始颜色将丢失。

解决这个问题的最简单方法可能是我将完成的事件用于动画。但是,我不喜欢这个解决方案,因为我希望我的动画在资源字典中是自定义样式而不是控件本身的一部分。如果动画在资源字典中是自定义样式,则它将无法访问控件本身的代码。在保持样式和控件之间的分离的同时,是否有一种很好的方法可以使动画工作?

然后我有了不同的想法。导致错误的原因是我将border.background.color设置为新颜色并返回到新颜色,因此如果我在旧动画启动时启动新动画,则新动画将从之前动画所处的任何颜色值开始。但是,如果我将动画设置为返回原始背景颜色的某个已保存属性值,那么即使用户重新启动动画,我也不会遇到问题。

 <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:Components="clr-namespace:DaedalusGraphViewer.Components"
                xmlns:Converters="clr-namespace:DaedalusGraphViewer.Components.Converters"
                >
     <Converters:ThicknessToLeftThicknessConverter x:Key="ThicknessToLeftThicknessConverter" />

  <Style x:Key="SearchBoxListViewItemStyle" TargetType="ListViewItem">
    <Setter Property="HorizontalContentAlignment" Value="Left"/>
    <Style.Triggers>
      <Trigger Property="IsMouseOver" Value="True">
        <Setter Property="Background" Value="LightBlue" />
      </Trigger>
    </Style.Triggers>
  </Style>


  <Style x:Key="{x:Type Components:SearchBox}" TargetType="{x:Type Components:SearchBox}">    
    <Style.Resources>

    </Style.Resources>
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="{x:Type Components:SearchBox}">
            <Border x:Name="Border"
                  Background="{TemplateBinding Background}"
                  BorderBrush="{TemplateBinding BorderBrush}"
                  BorderThickness="{TemplateBinding BorderThickness}">
            <Grid x:Name="LayoutGrid">
              <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="Auto" />

              </Grid.ColumnDefinitions>
              <ScrollViewer 
                x:Name="PART_ContentHost"
                Grid.Column="0" 
                VerticalAlignment="Center"
                />
              <Label 
                x:Name="DefaultTextLabel"
                Grid.Column="0"
                Foreground="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TextColor}"
                Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=LabelText}"
                VerticalAlignment="Center"
                FontStyle="Italic"
                />




              <Popup x:Name="RecentSearchesPopup"
                     IsOpen="False"
                     >
                <ListView 
                  x:Name="PreviousSearchesListView"
                  ListView.ItemContainerStyle="{StaticResource SearchBoxListViewItemStyle}" 
                  >
                </ListView>
              </Popup>

              <Border 
                x:Name="PreviousSearchesBorder"
                Grid.Column="2"
                HorizontalAlignment="Stretch"
                VerticalAlignment="Stretch"
                Background="LightGray"
                BorderBrush="{TemplateBinding BorderBrush}"
                BorderThickness="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness, 
                Converter={StaticResource ThicknessToLeftThicknessConverter}}"
                >
                <Image 
                  x:Name="PreviousSearchesIcon"
                  ToolTip="Previous Searches"
                  Width="15"
                  Height="15" 
                  Stretch="Uniform"
                  HorizontalAlignment="Center"
                  VerticalAlignment="Center"
                  Source="pack://application:,,,/DaedalusGraphViewer;component/Images/Previous.png" 
                  />
              </Border>
            </Grid>
          </Border>
          <ControlTemplate.Triggers>
            <Trigger Property="HasText" Value="True">
              <Setter Property="Visibility" TargetName="DefaultTextLabel" Value="Hidden" />
            </Trigger>
            <Trigger 
              SourceName="DefaultTextLabel"
              Property="IsMouseOver" 
              Value="True" 
              >
              <Setter Property="Cursor" Value="IBeam" />
            </Trigger>

            <!--<EventTrigger RoutedEvent="Mouse.MouseDown" SourceName="PreviousSearchesBorder">
                <BeginStoryboard>
                  <Storyboard>
                    <ColorAnimation 
                      AutoReverse="True"
                      Duration="0:0:0.2"
                      Storyboard.TargetName="PreviousSearchesBorder"
                      Storyboard.TargetProperty="(Border.Background).Color"
                      To="Black"
                      />
                  </Storyboard>
                </BeginStoryboard>
            </EventTrigger>-->


            <Trigger Property="IsPopupOpening" Value="True">
              <Trigger.EnterActions>
                <BeginStoryboard>
                  <Storyboard>
                    <ColorAnimationUsingKeyFrames
                      Storyboard.TargetName="PreviousSearchesBorder"
                      Storyboard.TargetProperty="(Border.Background).Color"
                      >
                      <LinearColorKeyFrame KeyTime="0:0:0.0" Value="{x:Static Components:SearchBox.DefaultRecentSearchesButtonColor}" />
                      <LinearColorKeyFrame KeyTime="0:0:0.2" Value="Black" />
                      <LinearColorKeyFrame KeyTime="0:0:0.4" Value="{x:Static Components:SearchBox.DefaultRecentSearchesButtonColor}" />
                    </ColorAnimationUsingKeyFrames>
                    <!--<ColorAnimation 
                      AutoReverse="True"
                      Duration="0:0:0.2"
                      Storyboard.TargetName="PreviousSearchesBorder"
                      Storyboard.TargetProperty="(Border.Background).Color"
                      To="Black"
                      />-->
                  </Storyboard>
                </BeginStoryboard>
              </Trigger.EnterActions>
            </Trigger>

然而,为了做到这一点,我需要存储原始的背景属性,我还没有得到它的工作。我无法使用绑定,因为动画中的属性必须是可冻结的,所以我尝试在控件上创建一个静态属性,该属性设置为控件加载事件的原始值。

我将颜色设置为后面代码中的背景颜色,但样式不反映该属性。

我在xaml中的静态引用是否正确?如果是这样,那么当样式应该从静态引用中加载颜色时,是不是onapplytemplate?

3 个答案:

答案 0 :(得分:2)

好吧,如果没有冻结错误,就无法使用DynamicResource。

如果在运行时加载Xaml文件并将StaticResource添加到Xaml文件中会怎样?

这是一个例子。 (我使用了一些快速和脏的Xaml文件解析,但它适用于概念验证。)

将StoryBoardResourceDictionary.xaml的属性更改为Content并复制(如果更新)并删除MSBuild:Compile设置。

<强> StoryBoardResourceDictionary.xaml

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Style x:Key="WindowWithTrigger" TargetType="Window">
        <Style.Triggers>
            <EventTrigger RoutedEvent="Mouse.MouseDown">
                <EventTrigger.Actions>
                    <BeginStoryboard>
                        <Storyboard>
                            <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)" >
                                <EasingColorKeyFrame KeyTime="0:0:2" Value="White"/>
                                <!-- OriginalBackground is added at runtime -->
                                <EasingColorKeyFrame KeyTime="0:0:4" Value="{StaticResource OriginalBackground}"/> <!-- Load at runtime -->
                            </ColorAnimationUsingKeyFrames>
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger.Actions>
            </EventTrigger>
        </Style.Triggers>
    </Style>
</ResourceDictionary>

<强> MainWindow.xaml

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="BackgroundAnimationBlend.MainWindow"
    x:Name="Window"
    Title="MainWindow"
    Width="640" Height="480" 
    Style="{DynamicResource WindowWithTrigger}"
    Background="DarkBlue"> <!--Change background to whatever color you want -->
</Window>

<强> MainWindow.xaml.cs

using System.IO;
using System.Text;
using System.Windows;
using System.Windows.Markup;

namespace BackgroundAnimationBlend
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            var rd2 = LoadFromFile();
            this.Resources.MergedDictionaries.Add(rd2);
        }

        public ResourceDictionary LoadFromFile()
        {
            const string file = "Styles/StoryBoardResourceDictionary.xaml";

            if (!File.Exists(file))
                return null;

            using (var fs = new StreamReader(file))
            {
                string xaml = string.Empty;
                string line;
                bool replaced = false;
                while ((line = fs.ReadLine()) != null)
                {
                    if (!replaced)
                    {
                        if (line.Contains("OriginalBackground"))
                        {
                            xaml += string.Format("<Color x:Key=\"OriginalBackground\">{0}</Color>", Background);
                            replaced = true;
                            continue;
                        }
                    }
                    xaml += line;
                }
                // Read in an EnhancedResourceDictionary File or preferably an GlobalizationResourceDictionary file
                return XamlReader.Load(new MemoryStream(Encoding.UTF8.GetBytes(xaml))) as ResourceDictionary;
            }
        }
    }
}

我现在不知道如何扩展它。所以也许这是一个疯狂的想法。但是在运行时加载样式并在加载样式之前将当前背景作为xaml字符串注入样式是我唯一有用的想法。

答案 1 :(得分:1)

我举了一个小例子来说明你如何解决问题(通过我所理解的)。

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
<Window.Resources>
    <Color x:Key="background">yellow</Color>
    <SolidColorBrush x:Key="backgroundBrush" Color="{StaticResource background}"/>
    <Storyboard x:Key="Storyboard1">
        <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)" Storyboard.TargetName="grid">
            <SplineColorKeyFrame KeyTime="0:0:0.3" Value="Black"/>
            <SplineColorKeyFrame KeyTime="0:0:0.6" Value="{StaticResource background}"/>
        </ColorAnimationUsingKeyFrames>
    </Storyboard>
</Window.Resources>
<Grid x:Name="grid" Background="{StaticResource backgroundBrush}">
    <Button x:Name="button" Content="Button" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" RenderTransformOrigin="0.5,0.5">
        <Button.Triggers>
            <EventTrigger RoutedEvent="Button.Click">
                <BeginStoryboard Storyboard="{StaticResource Storyboard1}" Name="Storyboard1"/>
            </EventTrigger>
        </Button.Triggers>
    </Button>

</Grid>

我已将Color background 定义为StaticResource,以将其用作网格的背景颜色和动画的值。

在上面的示例中,Grid有一个黄色背景,当点击按钮时,它会变为黑色,然后变回黄色。

答案 2 :(得分:1)

所以我最后做的就是使用FillBehavior = Stop。我是动画片的新手,所以我不知道如何在不访问已完成的事件的情况下轻松地阻止动画的重新激活(必须将其附加到控件中,从而在单独的情况下击败具有动画的样式的目的资源文件)。无论如何,我将赏金给予韵律,因为他的答案确实解决了我的问题,尽管以更困难的方式。我知道我想要做的事情必须简单,因为没有人想要为一种颜色设置动画并从一种风格中恢复,但我找不到任何有关该问题的Stack Overflow问题