WPF 應用 - 走馬燈動畫

1. 功能

  • 作一個圖片集合控件,在指定大小的區域內,以走馬燈的動畫效果呈現圖片。
  • 能根據圖片的數量決定動畫的方向。當圖片集合的大小大於指定的大小,圖片往反方向走,以便於能看到底部的圖片;當圖片集合的大小小於指定的大小時,圖片往正方向走,以便於保證全部圖片都一直在可視區域內。
  • 根據圖片的數量,決定動畫的時間,保持速度不變。

2. 效果

3. 代碼

3.1 內部動畫流動控件 FlowImagesUserControl
<UserControl x:Class="WpfAppTemplate.FlowImagesUserControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:WpfAppTemplate"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800" x:Name="selfUserControl">
    <StackPanel Orientation="Horizontal">
        <ItemsControl x:Name="itemsControl" 
                      ItemsSource="{Binding}"                   
                      Loaded="itemsControl_Loaded"
                      MouseEnter="itemsControl_MouseEnter"
                      MouseLeave="itemsControl_MouseLeave">
            <ItemsControl.RenderTransform>
                <TransformGroup>
                    <TranslateTransform X="0" Y="0"></TranslateTransform>
                </TransformGroup>
            </ItemsControl.RenderTransform>

            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <StackPanel Orientation="Horizontal"/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>

            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Image Source="{Binding PicPath}" ToolTip="{Binding Name}" x:Name="img" 
                           Width="{Binding ImageWidth, ElementName=selfUserControl}"/>
                </DataTemplate>
            </ItemsControl.ItemTemplate>

            <ItemsControl.Resources>
                <Storyboard x:Key="storyBoard">
                    <DoubleAnimation Storyboard.TargetName="itemsControl"  
                                     Storyboard.TargetProperty="(RenderTransform).(TransformGroup.Children)[0].(TranslateTransform.X)" 
                                     To="{Binding ToX, ElementName=selfUserControl}" 
                                     Duration="{Binding Duration, ElementName=selfUserControl}"                                     
                                     AutoReverse="True"
                                     RepeatBehavior="Forever" />
                </Storyboard>
            </ItemsControl.Resources>
        </ItemsControl>
    </StackPanel>
</UserControl>
public partial class FlowImagesUserControl : UserControl
{
    /// <summary>
    /// 圖片大小
    /// </summary>
    public double ImageWidth
    {
        get { return (double)GetValue(ImageWidthProperty); }
        set { SetValue(ImageWidthProperty, value); }
    }

    public static readonly DependencyProperty ImageWidthProperty =
        DependencyProperty.Register("ImageWidth", typeof(double), typeof(FlowImagesUserControl), new PropertyMetadata(60.0));

    /// <summary>
    /// 動畫結束值
    /// </summary>
    public double ToX
    {
        get { return (double)GetValue(ToXProperty); }
        set { SetValue(ToXProperty, value); }
    }

    public static readonly DependencyProperty ToXProperty =
        DependencyProperty.Register("ToX", typeof(double), typeof(FlowImagesUserControl), new PropertyMetadata(500.0));
        
    /// <summary>
    /// 動畫時間
    /// </summary>
    public Duration Duration
    {
        get { return (Duration)GetValue(DurationProperty); }
        set { SetValue(DurationProperty, value); }
    }

    public static readonly DependencyProperty DurationProperty =
        DependencyProperty.Register("Duration", typeof(Duration), typeof(FlowImagesUserControl), new PropertyMetadata(new Duration(new System.TimeSpan(0,0,2))));

    private Storyboard _storyboard;
    
    public FlowItemsUserControl()
    {
        InitializeComponent();

        _storyboard = itemsControl.FindResource("storyBoard") as Storyboard;

        itemsControl.SizeChanged += (s, e) =>
        {
            if (this.IsLoaded)
            {
                InitParamsOfStoryboard();
            }
        };
    }

    private void itemsControl_Loaded(object sender, RoutedEventArgs e)
    {
        InitParamsOfStoryboard(); 
    }

    private void itemsControl_MouseEnter(object sender, MouseEventArgs e)
    {             
        _storyboard.Pause();
    }

    private void itemsControl_MouseLeave(object sender, MouseEventArgs e)
    {
        _storyboard.Resume();
    }

    private void InitParamsOfStoryboard()
    {
        _storyboard.Stop();

        // 當集合的大小發生改變時,根據集合的大小決定動畫的位移
        ToX = this.ActualWidth - itemsControl.ActualWidth;

        // 根據位移來決定動畫的總時間,以每秒走 25px 的速度,避免不一樣位移相同時間致使速度不可控
        Duration = new Duration(new System.TimeSpan(0, 0, System.Math.Abs((int)ToX/25)));

        _storyboard.Begin();
    }
}
3.2 在窗口中添加 FlowImagesUserControl 控件
<local:FlowItemsUserControl DataContext="{Binding Cars}" ImageWidth="100"/>
<Button Grid.Row="1" Content="刪除一輛車" Click="Button_Click" 
        Width="100" Background="DarkSalmon"/>
public partial class ContentWindow : Window
{        
    public ContentWindow()
    {
        InitializeComponent();
        this.DataContext = new CarsViewModel();
    }
    
    private void Button_Click_DeleteACar(object sender, RoutedEventArgs e)
    {            
        var carList = (this.DataContext as CarsViewModel).Cars;
    
        if (carList != null && carList.Count > 0)
        {
            carList.RemoveAt(carList.Count - 1);
        }
    }
}
public class Car
{        
    public string Name { get; set; }
    public string PicPath { get; set; }        
}

public class NotifyPropertyChanged : System.ComponentModel.INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public void OnProperty(string propertyName)
    {
        PropertyChangedEventHandler propertyChanged = PropertyChanged;
        if (propertyChanged != null)
        {
            propertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

public class CarsViewModel : NotifyPropertyChanged
{
    private ObservableCollection<Car> _Cars;
    public ObservableCollection<Car> Cars
    {
        get { return _Cars; }
        set 
        {
            _Cars = value;
            OnProperty("Cars");
        }
    }        

    public CarsViewModel()
    {
        InitCars();
    }

    private void InitCars()
    {
        string path = AppDomain.CurrentDomain.BaseDirectory + "/Resources/Images/Cars";
        if (!Directory.Exists(path))
        {
            return;
        }

        Cars = new ObservableCollection<Car>();

        // 獲取文件夾下的全部車輛圖片
        FileInfo[] fileArr = new DirectoryInfo(path).GetFiles();

        for (int i = 0, l = fileArr.Count(); i < l; i++)
        {
            Cars.Add(new Car()
            {
                PicPath = fileArr[i].FullName,
                Name = fileArr[i].Name.Split('.')[0] //fileArr[i].Name = 寶馬.jpg
            });
        }
    }
}
3.3 鼠標移入圖片效果

鼠標移入圖片時,動畫會中止。這裏再給圖片添加一個鼠標移入效果,當鼠標移入時,圖片變大,鼠標移出後,圖片恢復大小。express

<Style TargetType="Image" x:Key="MoveInBiggerImageStyle">
    <Setter Property="RenderTransform">
        <Setter.Value>
            <TransformGroup>
                <ScaleTransform/>
            </TransformGroup>
        </Setter.Value>
    </Setter>
    <Style.Triggers>
        <Trigger Property="IsMouseOver" Value="True">
            <Setter Property="Cursor" Value="Hand"/>
            <Trigger.EnterActions>
                <BeginStoryboard>
                    <Storyboard>
                        <DoubleAnimation Storyboard.TargetProperty="(RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)"
                                         To="1.1" Duration="0:0:0.2"/>
                        <DoubleAnimation Storyboard.TargetProperty="(RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)"
                                         To="1.1" Duration="0:0:0.2"/>
                    </Storyboard>
                </BeginStoryboard>
            </Trigger.EnterActions>
            <Trigger.ExitActions>
                <BeginStoryboard>
                    <Storyboard>
                        <DoubleAnimation Storyboard.TargetProperty="(RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)"
                                         To="1" Duration="0:0:0.5"/>
                        <DoubleAnimation Storyboard.TargetProperty="(RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)"
                                         To="1" Duration="0:0:0.5"/>
                    </Storyboard>
                </BeginStoryboard>
            </Trigger.ExitActions>
        </Trigger>
    </Style.Triggers>
</Style>

...

<Image x:Name="img" 
       Source="{Binding PicPath}"
       ToolTip="{Binding Name}"                           
       Width="{Binding ImageWidth, ElementName=selfUserControl}" 
       Style="{StaticResource MoveInBiggerImageStyle}">
</Image>

相關文章
相關標籤/搜索