c# WPF中自定義加載時實現(xiàn)帶動畫效果的Form和FormItem
背景
今天我們來談一下我們自定義的一組WPF控件Form和FormItem,然后看一下如何自定義一組完整地組合WPF控件,在我們很多界面顯示的時候我們需要同時顯示文本、圖片并且我們需要將這些按照特定的順序整齊的排列在一起,這樣的操作當(dāng)然通過定義Grid和StackPanel然后組合在一起當(dāng)然也是可以的,我們的這一組控件就是將這個過程組合到一個Form和FormItem中間去,從而達(dá)到這樣的效果,我們首先來看看這組控件實現(xiàn)的效果。
一 動畫效果
看了這個效果之后我們來看看怎么來使用Form和FormItem控件,后面再進(jìn)一步分析這兩個控件的一些細(xì)節(jié)信息。
<xui:TabControl Canvas.Left="356" Canvas.Top="220"> <xui:TabItem Header="Overview"> <xui:Form Margin="2" > <xui:FormItem Content="Test1" Height="30"></xui:FormItem> <xui:FormItem Content="Test2" Height="30"></xui:FormItem> <xui:FormItem Content="Test3" Height="30"></xui:FormItem> </xui:Form> </xui:TabItem> <xui:TabItem Header="Plumbing"> <xui:Form Margin="2" Columns="2" Rows="2"> <xui:FormItem Content="Demo1" Height="30" Margin="5 2"></xui:FormItem> <xui:FormItem Content="Demo2" Height="30" Margin="5 2"></xui:FormItem> <xui:FormItem Content="Demo3" Height="30" Margin="5 2"></xui:FormItem> <xui:FormItem Content="Demo4" Height="30" Margin="5 2"></xui:FormItem> </xui:Form> </xui:TabItem> <xui:TabItem Header="Clean"> <xui:TextBox Text="Test2" Width="220" Height=" 30" Margin="5"></xui:TextBox> </xui:TabItem> </xui:TabControl>
這個里面xui命名控件是我們的自定義控件庫的命名空間,這個里面的TableControl也是一種特殊的自定義的TableControl,關(guān)于這個TableControl我們后面也會進(jìn)一步分析。
二 自定義控件實現(xiàn)
按照上面的順序我們先來分析Form控件,然后再分析FormItem控件的實現(xiàn)細(xì)節(jié)
2.1 Form
通過上面的代碼我們發(fā)現(xiàn)Form是可以承載FormItem的,所以它是一個可以承載子控件的容器控件,這里Form是集成ItemsControl的,我們來看看具體的代碼
public class Form : ItemsControl { static Form() { DefaultStyleKeyProperty.OverrideMetadata(typeof(Form), new FrameworkPropertyMetadata(typeof(Form))); } public double HeaderWidth { get { return (double)GetValue(HeaderWidthProperty); } set { SetValue(HeaderWidthProperty, value); } } // Using a DependencyProperty as the backing store for HeaderWidth. This enables animation, styling, binding, etc... public static readonly DependencyProperty HeaderWidthProperty = DependencyProperty.Register("HeaderWidth", typeof(double), typeof(Form), new PropertyMetadata(70D)); public int Rows { get { return (int)GetValue(RowsProperty); } set { SetValue(RowsProperty, value); } } // Using a DependencyProperty as the backing store for Rows. This enables animation, styling, binding, etc... public static readonly DependencyProperty RowsProperty = DependencyProperty.Register("Rows", typeof(int), typeof(Form), new PropertyMetadata(0)); public int Columns { get { return (int)GetValue(ColumnsProperty); } set { SetValue(ColumnsProperty, value); } } // Using a DependencyProperty as the backing store for Columns. This enables animation, styling, binding, etc... public static readonly DependencyProperty ColumnsProperty = DependencyProperty.Register("Columns", typeof(int), typeof(Form), new PropertyMetadata(1)); }
然后我們再來看看Form的樣式文件
<Style TargetType="local:Form"> <Setter Property="Padding" Value="10"></Setter> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="local:Form"> <ScrollViewer Background="#eee" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto"> <UniformGrid Columns="{TemplateBinding Columns}" Rows="{TemplateBinding Rows}" IsItemsHost="True" Background="Transparent" VerticalAlignment="Top" Margin="{TemplateBinding Padding}"></UniformGrid> </ScrollViewer> </ControlTemplate> </Setter.Value> </Setter> <Setter Property="ItemTemplate"> <Setter.Value> <DataTemplate> <ContentPresenter Content="{Binding}" Focusable="False"></ContentPresenter> </DataTemplate> </Setter.Value> </Setter> </Style>
這里我們使用UniformGrid作為內(nèi)容承載容器,所以我們現(xiàn)在清楚了為什么需要定義Columns和Rows這兩個依賴項屬性了,這個UniformGrid是嵌套在ScrollerViewer中的,所以如果其子控件超出了一定范圍,其子控件外面是會顯示滾動條的。
2.2 FormItem
FormItem是從ListBoxItem繼承而來,而ListBoxItem又是從ContentControl繼承而來的,所以可以添加到任何具有Content屬性的控件中去,常見的ListBoxItem可以放到ListBox中,也可以放到ItemsControl中去,ListBoxItem可以橫向和TreeViewItem進(jìn)行比較,只不過TreeViewItem是直接從HeaderedItemsControl繼承過來的,然后再繼承自ItemsControl。兩者有很多的共同之處,可以做更多的橫向比較,我們今天只是來講ListBoxItem,首先看看我們使用的樣式,這里貼出前端代碼:
<Style TargetType="local:FormItem"> <Setter Property="Margin" Value="0 0 0 15"></Setter> <Setter Property="Background" Value="#fff"></Setter> <Setter Property="Height" Value="50"></Setter> <Setter Property="HorizontalAlignment" Value="Stretch"></Setter> <Setter Property="VerticalAlignment" Value="Stretch"></Setter> <Setter Property="Padding" Value="6"></Setter> <Setter Property="Foreground" Value="{StaticResource DarkColor}"></Setter> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="local:FormItem"> <Grid Background="{TemplateBinding Background}" Height="{TemplateBinding Height}"> <Grid.ColumnDefinitions> <ColumnDefinition Width="{Binding HeaderWidth,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=local:Form}}"></ColumnDefinition> <ColumnDefinition Width="*"></ColumnDefinition> </Grid.ColumnDefinitions> <Rectangle Width="3" HorizontalAlignment="Left" Fill="{StaticResource Highlight}"></Rectangle> <StackPanel VerticalAlignment="Center" HorizontalAlignment="Left" Margin="13 0 0 0" Orientation="Horizontal"> <Image x:Name="Icon" Source="{TemplateBinding Icon}" Width="24" Height="24" Margin="0 0 10 0" VerticalAlignment="Center" HorizontalAlignment="Left"></Image> <TextBlock Text="{TemplateBinding Title}" Foreground="{TemplateBinding Foreground}" VerticalAlignment="Center" HorizontalAlignment="Left"></TextBlock> </StackPanel> <ContentPresenter Margin="{TemplateBinding Padding}" Grid.Column="1" HorizontalAlignment="{TemplateBinding HorizontalAlignment}" VerticalAlignment="{TemplateBinding VerticalAlignment}"></ContentPresenter> </Grid> <ControlTemplate.Triggers> <Trigger Property="Icon" Value="{x:Null}"> <Setter Property="Visibility" Value="Collapsed" TargetName="Icon"></Setter> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style>
這里我們重寫了ListBoxItem 的ControlTemplate,我們需要注意的一個地方就是我們使用了
<ContentPresenter Margin="{TemplateBinding Padding}" Grid.Column="1" HorizontalAlignment="{TemplateBinding HorizontalAlignment}" VerticalAlignment="{TemplateBinding VerticalAlignment}"></ContentPresenter>
來替代ListBoxItem的Content,我們需要始終記住,只有控件擁有Content屬性才能使用ContentPresenter ,這個屬性是用來呈現(xiàn)控件的Content。
另外一個需要重點介紹的就是FormItem這個類中的代碼,這個控件在加載的時候所有的效果都是在后臺中進(jìn)行加載的,首先貼出相關(guān)的類的實現(xiàn),然后再做進(jìn)一步的分析。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Media.Animation; namespace X.UI { public class FormItem : ListBoxItem { static FormItem() { DefaultStyleKeyProperty.OverrideMetadata(typeof(FormItem), new FrameworkPropertyMetadata(typeof(FormItem))); } public FormItem() { System.Windows.Media.TranslateTransform transform = EnsureRenderTransform<System.Windows.Media.TranslateTransform>(this); transform.X = transform.Y = 100; Opacity = 0; IsVisibleChanged += FormItem_IsVisibleChanged; } void FormItem_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) { if (this.Parent is Form) { if (!IsVisible) { int index = (this.Parent as Form).Items.IndexOf(this); System.Windows.Media.TranslateTransform transform = EnsureRenderTransform<System.Windows.Media.TranslateTransform>(this); DoubleAnimation da = new DoubleAnimation() { From = 0, To = 100, EasingFunction = new CircleEase { EasingMode = EasingMode.EaseOut } }; transform.BeginAnimation(System.Windows.Media.TranslateTransform.XProperty, da); transform.BeginAnimation(System.Windows.Media.TranslateTransform.YProperty, da); DoubleAnimation daopacity = new DoubleAnimation { From = 1, To = 0, }; this.BeginAnimation(UIElement.OpacityProperty, daopacity); } else { int index = (this.Parent as Form).Items.IndexOf(this); System.Windows.Media.TranslateTransform transform = EnsureRenderTransform<System.Windows.Media.TranslateTransform>(this); DoubleAnimation da = new DoubleAnimation() { From = 100, To = 0, BeginTime = TimeSpan.FromMilliseconds(100 * (index + 1)), Duration = TimeSpan.FromMilliseconds(666), EasingFunction = new CircleEase { EasingMode = EasingMode.EaseOut } }; transform.BeginAnimation(System.Windows.Media.TranslateTransform.XProperty, da); transform.BeginAnimation(System.Windows.Media.TranslateTransform.YProperty, da); DoubleAnimation daopacity = new DoubleAnimation { From = 0, To = 1, BeginTime = TimeSpan.FromMilliseconds(100 * (index + 1)), Duration = TimeSpan.FromMilliseconds(666), EasingFunction = new CircleEase { EasingMode = EasingMode.EaseOut } }; this.BeginAnimation(UIElement.OpacityProperty, daopacity); } } } private T EnsureRenderTransform<T>(UIElement uiTarget) where T : Transform { if (uiTarget.RenderTransform is T) return uiTarget.RenderTransform as T; else { T instance = typeof(T).Assembly.CreateInstance(typeof(T).FullName) as T; uiTarget.RenderTransform = instance; return instance; } } public string Title { get { return (string)GetValue(TitleProperty); } set { SetValue(TitleProperty, value); } } // Using a DependencyProperty as the backing store for Title. This enables animation, styling, binding, etc... public static readonly DependencyProperty TitleProperty = DependencyProperty.Register("Title", typeof(string), typeof(FormItem), new PropertyMetadata("")); public ImageSource Icon { get { return (ImageSource)GetValue(IconProperty); } set { SetValue(IconProperty, value); } } // Using a DependencyProperty as the backing store for Icon. This enables animation, styling, binding, etc... public static readonly DependencyProperty IconProperty = DependencyProperty.Register("Icon", typeof(ImageSource), typeof(FormItem), new PropertyMetadata(null)); } }
這里在FormItem的構(gòu)造函數(shù)中,添加了一個IsVisibleChanged事件,這個事件會在加載當(dāng)前控件的時候發(fā)生,另外當(dāng)當(dāng)前控件的屬性值發(fā)生變化的時候會觸發(fā)該效果。其實效果就是同時在X和Y方向做一個平移的效果,這個也是一個常用的效果。
我們重點討論的是下面的這段代碼:
private T EnsureRenderTransform<T>(UIElement uiTarget) where T : Transform { if (uiTarget.RenderTransform is T) return uiTarget.RenderTransform as T; else { T instance = typeof(T).Assembly.CreateInstance(typeof(T).FullName) as T; uiTarget.RenderTransform = instance; return instance; } }
這里我們創(chuàng)建TranslateTransform的時候是使用的System.Windows.Media.TranslateTransform transform = EnsureRenderTransform<System.Windows.Media.TranslateTransform>(this);這個方法,而不是每次都new一個對象,每次new一個對象的效率是很低的,而且會占據(jù)內(nèi)存,我們?nèi)绻呀?jīng)創(chuàng)建過當(dāng)前對象完全可以重復(fù)利用,這里我們使用了帶泛型參數(shù)的函數(shù)來實現(xiàn)當(dāng)前效果,typeof(T).Assembly.CreateInstance(typeof(T).FullName) as T,核心是通過程序集來創(chuàng)建對象,這種方式我們也是經(jīng)常會使用的,比如我們可以通過獲取應(yīng)用程序級別的程序集來通過Activator.CreateInstance來創(chuàng)建窗體等一系列的對象,這種通過反射的機制來擴(kuò)展的方法是我們需要特別留意的,另外寫代碼的時候必須注重代碼的質(zhì)量和效率,而不僅僅是實現(xiàn)了某一個功能,這個在以后的開發(fā)過程中再一點點去積累去吸收。
以上就是c# WPF中自定義加載時實現(xiàn)帶動畫效果的Form和FormItem的詳細(xì)內(nèi)容,更多關(guān)于c# wpf實現(xiàn)帶動畫效果的Form和FormItem的資料請關(guān)注腳本之家其它相關(guān)文章!
- C# WPF如何反射加載Geometry幾何圖形數(shù)據(jù)圖標(biāo)
- c# 實現(xiàn)網(wǎng)頁加載后將頁面截取為長圖片
- C# 根據(jù)表格偶數(shù)、奇數(shù)加載不同顏色
- C# 動態(tài)加載程序集信息
- C#中調(diào)用DLL時未能加載文件或程序集錯誤的處理方法(詳解)
- C#中加載dll并調(diào)用其函數(shù)的實現(xiàn)方法
- c# 動態(tài)加載dll文件,并實現(xiàn)調(diào)用其中的簡單方法
- C#使用Jquery zTree實現(xiàn)樹狀結(jié)構(gòu)顯示 異步數(shù)據(jù)加載
- C#使用反射加載多個程序集的實現(xiàn)方法
- C#實現(xiàn)動態(tài)加載dll的方法
- c#動態(tài)加載卸載DLL的方法
- 3種C# 加載Word的方法