رافعی مهدی
یک شنبه 25 فروردین 1392, 21:49 عصر
سلام
اول اجازه بدید یه کم قصه تعریف کنم!
در برنامه ام یه UserControl دارم که نیاز به یک Storyboard با key-frame بایند شده به یک DP از همین UserControl داره. (پیچیده که نگفتم :چشمک: ) همه دوستان میدونند که بایند کردن یه Storyboard به یه DP یه نکته کوچیـــــک داره! storyboard ها قابل یخ زدن (Freezable) هستند (عجب ترجمه ای شد!) و به همین دلیل نمیشه باهاشون کارهایی از این دست رو به راحتی انجام داد. من قبلاً در اینجا (http://stackoverflow.com/q/15848381/668342) روش انجام این کار رو به صورت کامل توضیح دادم. (ببخشید که لینک انگلیسی میدم، ولی واقعاً دوباره به فارسی نوشتنش خیلی طول میکشه.)
خلاصه چیزهایی که اونجا نوشتم اینه که اگر مستقیماً در ControlTemplate.Resources یه Storyboard رو قرار بدیم، دیگه نمیشه (یا من بلد نیستم) key-frame ش رو به یه DP بایند کرد، Trick ای که در اینجا وجود داره اینه که storyboard رو ببریم در یکی از المانهای داخل Template. این کار یه مشکل رو (بایند نشدن key-frame) حل میکنه، یه مشکل جدید به وجود میاره! در این صورت دیگه به انواع Triggerها به جز EventTrigger دسترسی نخواهیم داشت. من در این UserControl به یه DataTrigger نیاز دارم تا بتونم زمانی که یکی از DPها تغییر مقدار میده و مساوی با یک مقدار مشخص میشه، یک Storyboard با keyframe بایند شده به یک DP (دیگه) رو شروع کنم.
خب آیا تمام انواع Trigger همین چهار تا هستند؟
1- Trigger
2- MultiTrigger
3- DataTrigger
4- MultiDataTrigger
البته که نه، تعداد بسیار زیادی Trigger دیگه هم وجود داره، که هر کدوم در جایگاه خودشان کلی کاربرد دارند! با اضافه کردن reference دو تا dll به برنامه، که خوشبختانه مربوط به خود Microsoft هم هستند و از بابت صحت و سقم کدهای داخلی اونها هیچ نگرانی وجود نداره، میتونید چند تا از این تعداد زیاد Trigger رو ببینید. بقیه این تعداد زیاد رو هم خودتان میتونید بسازید. :چشمک:
یه نمونه کد از یک Trigger جدید رو در همون تاپیکی که لینکش رو دادم، توضیح دادم.
خب حالا بریم سر اصل مطلب! میخواهم برای UserControl ای که توضیح دادم، با استفاده از DataTrigger موجود در یکی از اون دو تا dll که یادم رفت معرفیشون کنم، storyboard مورد نظر رو play کنم.
این دو تا dll:
System.Windows.Interactivity
Microsoft.Expression.Interactions
این هم کد:
<UserControl x:Class="WpfApplication1.UserControl2"
...
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
xmlns:local="clr-namespace:WpfApplication1">
<UserControl.Resources>
<Style x:Key="MyControlStyle" TargetType="UserControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Grid.Resources>
<Storyboard x:Key="MyStory">
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)" Storyboard.TargetName="brdBase">
<SplineColorKeyFrame KeyTime="0:0:1" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:UserControl2}}, Path=SpecialColor}"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</Grid.Resources>
<Border x:Name="brdBase" BorderThickness="1" BorderBrush="Gray">
<TextBox Text="{Binding SpecialText}"/>
</Border>
<i:Interaction.Triggers>
<ei:DataTrigger Binding="{Binding SpecialText}" Value="Fire!">
<ei:ControlStoryboardAction Storyboard="{StaticResource MyStory}" ControlStoryboardOption="Play"/>
</ei:DataTrigger>
</i:Interaction.Triggers>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Grid x:Name="grdRoot" DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:UserControl2}}}">
<UserControl Style="{DynamicResource MyControlStyle}"/>
</Grid>
</UserControl>
کد #C:
public partial class UserControl2 : UserControl
{
#region ________________________________________ SpecialColor
public Color SpecialColor
{
get { return (Color)GetValue(SpecialColorProperty); }
set { SetValue(SpecialColorProperty, value); }
}
public static readonly DependencyProperty SpecialColorProperty =
DependencyProperty.Register("SpecialColor",
typeof(Color),
typeof(UserControl2),
new FrameworkPropertyMetadata(Colors.Red));
#endregion
#region ________________________________________ SpecialText
public string SpecialText
{
get { return (string)GetValue(SpecialTextProperty); }
set { SetValue(SpecialTextProperty, value); }
}
public static readonly DependencyProperty SpecialTextProperty =
DependencyProperty.Register("SpecialText",
typeof(string),
typeof(UserControl2),
new FrameworkPropertyMetadata(string.Empty));
#endregion
public UserControl2()
{
InitializeComponent();
}
}
انتظار دارم در این کد، وقتی مقدار SpecialText رو برابر با !Fire ست میکنم، MyStory شروع بشه. این اتفاق با تایپ کردن عبارت !Fire در تکست باکس این UserControl در هنگام-اجرای برنامه میفته، ولی وقتی مقدار این DP رو در xaml تعیین میکنم، به صورت زیر، این اتفاق در زمان-طراحی نمیفته و فقط بعد از اجرای کد مثل روش قبل (تایپ کردن) اثرش رو نشون میده.
<Grid>
<local:UserControl2 SpecialText="Fire!"/>
</Grid>
کسی از دوستان عزیز میدونه چطور میشه این مشکل رو بر طرف کرد؟
_________________
پاورقی: من این سوال رو دارم به صورت موازی در StackOverflow (http://stackoverflow.com/q/15995870/668342) و msdn (http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/3d1b27bd-89c0-4a5f-a247-7e703f28e0ab) هم دنبال میکنم. اگر دوست داشتید خوشحال میشم در اون فرومها هم نظر بدید. خیلی ممنون!
اول اجازه بدید یه کم قصه تعریف کنم!
در برنامه ام یه UserControl دارم که نیاز به یک Storyboard با key-frame بایند شده به یک DP از همین UserControl داره. (پیچیده که نگفتم :چشمک: ) همه دوستان میدونند که بایند کردن یه Storyboard به یه DP یه نکته کوچیـــــک داره! storyboard ها قابل یخ زدن (Freezable) هستند (عجب ترجمه ای شد!) و به همین دلیل نمیشه باهاشون کارهایی از این دست رو به راحتی انجام داد. من قبلاً در اینجا (http://stackoverflow.com/q/15848381/668342) روش انجام این کار رو به صورت کامل توضیح دادم. (ببخشید که لینک انگلیسی میدم، ولی واقعاً دوباره به فارسی نوشتنش خیلی طول میکشه.)
خلاصه چیزهایی که اونجا نوشتم اینه که اگر مستقیماً در ControlTemplate.Resources یه Storyboard رو قرار بدیم، دیگه نمیشه (یا من بلد نیستم) key-frame ش رو به یه DP بایند کرد، Trick ای که در اینجا وجود داره اینه که storyboard رو ببریم در یکی از المانهای داخل Template. این کار یه مشکل رو (بایند نشدن key-frame) حل میکنه، یه مشکل جدید به وجود میاره! در این صورت دیگه به انواع Triggerها به جز EventTrigger دسترسی نخواهیم داشت. من در این UserControl به یه DataTrigger نیاز دارم تا بتونم زمانی که یکی از DPها تغییر مقدار میده و مساوی با یک مقدار مشخص میشه، یک Storyboard با keyframe بایند شده به یک DP (دیگه) رو شروع کنم.
خب آیا تمام انواع Trigger همین چهار تا هستند؟
1- Trigger
2- MultiTrigger
3- DataTrigger
4- MultiDataTrigger
البته که نه، تعداد بسیار زیادی Trigger دیگه هم وجود داره، که هر کدوم در جایگاه خودشان کلی کاربرد دارند! با اضافه کردن reference دو تا dll به برنامه، که خوشبختانه مربوط به خود Microsoft هم هستند و از بابت صحت و سقم کدهای داخلی اونها هیچ نگرانی وجود نداره، میتونید چند تا از این تعداد زیاد Trigger رو ببینید. بقیه این تعداد زیاد رو هم خودتان میتونید بسازید. :چشمک:
یه نمونه کد از یک Trigger جدید رو در همون تاپیکی که لینکش رو دادم، توضیح دادم.
خب حالا بریم سر اصل مطلب! میخواهم برای UserControl ای که توضیح دادم، با استفاده از DataTrigger موجود در یکی از اون دو تا dll که یادم رفت معرفیشون کنم، storyboard مورد نظر رو play کنم.
این دو تا dll:
System.Windows.Interactivity
Microsoft.Expression.Interactions
این هم کد:
<UserControl x:Class="WpfApplication1.UserControl2"
...
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
xmlns:local="clr-namespace:WpfApplication1">
<UserControl.Resources>
<Style x:Key="MyControlStyle" TargetType="UserControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Grid.Resources>
<Storyboard x:Key="MyStory">
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)" Storyboard.TargetName="brdBase">
<SplineColorKeyFrame KeyTime="0:0:1" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:UserControl2}}, Path=SpecialColor}"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</Grid.Resources>
<Border x:Name="brdBase" BorderThickness="1" BorderBrush="Gray">
<TextBox Text="{Binding SpecialText}"/>
</Border>
<i:Interaction.Triggers>
<ei:DataTrigger Binding="{Binding SpecialText}" Value="Fire!">
<ei:ControlStoryboardAction Storyboard="{StaticResource MyStory}" ControlStoryboardOption="Play"/>
</ei:DataTrigger>
</i:Interaction.Triggers>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Grid x:Name="grdRoot" DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:UserControl2}}}">
<UserControl Style="{DynamicResource MyControlStyle}"/>
</Grid>
</UserControl>
کد #C:
public partial class UserControl2 : UserControl
{
#region ________________________________________ SpecialColor
public Color SpecialColor
{
get { return (Color)GetValue(SpecialColorProperty); }
set { SetValue(SpecialColorProperty, value); }
}
public static readonly DependencyProperty SpecialColorProperty =
DependencyProperty.Register("SpecialColor",
typeof(Color),
typeof(UserControl2),
new FrameworkPropertyMetadata(Colors.Red));
#endregion
#region ________________________________________ SpecialText
public string SpecialText
{
get { return (string)GetValue(SpecialTextProperty); }
set { SetValue(SpecialTextProperty, value); }
}
public static readonly DependencyProperty SpecialTextProperty =
DependencyProperty.Register("SpecialText",
typeof(string),
typeof(UserControl2),
new FrameworkPropertyMetadata(string.Empty));
#endregion
public UserControl2()
{
InitializeComponent();
}
}
انتظار دارم در این کد، وقتی مقدار SpecialText رو برابر با !Fire ست میکنم، MyStory شروع بشه. این اتفاق با تایپ کردن عبارت !Fire در تکست باکس این UserControl در هنگام-اجرای برنامه میفته، ولی وقتی مقدار این DP رو در xaml تعیین میکنم، به صورت زیر، این اتفاق در زمان-طراحی نمیفته و فقط بعد از اجرای کد مثل روش قبل (تایپ کردن) اثرش رو نشون میده.
<Grid>
<local:UserControl2 SpecialText="Fire!"/>
</Grid>
کسی از دوستان عزیز میدونه چطور میشه این مشکل رو بر طرف کرد؟
_________________
پاورقی: من این سوال رو دارم به صورت موازی در StackOverflow (http://stackoverflow.com/q/15995870/668342) و msdn (http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/3d1b27bd-89c0-4a5f-a247-7e703f28e0ab) هم دنبال میکنم. اگر دوست داشتید خوشحال میشم در اون فرومها هم نظر بدید. خیلی ممنون!