[英]Control Template Storyboard, set value in other control within same template
我被要求在現有的DateTimePicker控件周圍創建一個hack。 通常,日期/時間選擇器具有日歷的精彩圖像,然后是旁邊的文本框,用於顯示實際日期。 用戶可以單擊圖像並向其顯示彈出日歷,並且在選擇時,日期將刷新到文本框區域中。
我遇到的問題是其他設計師不喜歡日歷圖形,只想要一個純文本框控件,但如果用戶雙擊,打開一個彈出日歷,獲取日期並刷新它。 感謝我在S / O上發現的其他幫助,我非常接近。
所以,要描述我的控件模板,並保持簡單(非自定義控件,除非我需要根據建議)。 控件模板基於文本框控件。 我們為文本框設置了PART_ContentHost的邊框,然后是標准日歷控件的彈出框。
對於控件模板觸發器,我有一個鏈接到ScrollViewer(文本框輸入區域)到它的MouseDoubleClick事件。 如果已觸發,則將彈出窗口的IsOpen設置為true並公開日歷。 這很好用。
現在,完成它。 如果用戶從日歷中選擇日期,則下一個觸發器將關閉彈出窗口(IsOpen設置為false)。 這也有效。
我的問題。 我還希望在選擇時選擇日期並將其ToString()日期表示放入ScrollViewer.Content(x:Name =“PART_ContentHost”)。
<ControlTemplate TargetType="{x:Type TextBox}" x:Key="CTTextBox" >
<StackPanel>
<Border x:Name="targetBorder"
BorderBrush="{TemplateBinding BorderBrush}"
SnapsToDevicePixels="true">
<ScrollViewer x:Name="PART_ContentHost"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
Foreground="{TemplateBinding Foreground}" />
</Border>
<Popup PlacementTarget="{Binding ElementName=PART_ContentHost}" x:Name="PopCal">
<Calendar x:Name="ActualCalendar"/>
</Popup>
</StackPanel>
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="ScrollViewer.MouseDoubleClick" SourceName="PART_ContentHost">
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames
Storyboard.TargetName="PopCal"
Storyboard.TargetProperty="(Popup.IsOpen)">
<DiscreteBooleanKeyFrame KeyTime="00:00:00" Value="True"/>
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="Calendar.SelectedDatesChanged" SourceName="ActualCalendar">
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames
Storyboard.TargetName="PopCal"
Storyboard.TargetProperty="(Popup.IsOpen)">
<DiscreteBooleanKeyFrame KeyTime="00:00:00" Value="False"/>
</BooleanAnimationUsingKeyFrames>
</Storyboard>
WHAT WOULD I PUT HERE to have the selected date of the popup calendar
inserted into the content of the PART_ContentHost...
<Storyboard>
<BooleanAnimationUsingKeyFrames
Storyboard.TargetName="PART_ContentHost"
Storyboard.TargetProperty="(Content)">
<DiscreteBooleanKeyFrame KeyTime="00:00:00" Value=" ????? "/>
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<Style TargetType="{x:Type TextBox}" x:Key="STextBox" >
<!--<Setter Property="OverridesDefaultStyle" Value="True"/>-->
<Setter Property="FontFamily" Value="Arial" />
<Setter Property="FontSize" Value="12" />
<Setter Property="Height" Value="20" />
<Setter Property="Width" Value="100" />
<Setter Property="VerticalContentAlignment" Value="Bottom" />
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="HorizontalAlignment" Value="Left" />
<!-- Padding is Left, Top, Right, Bottom -->
<Setter Property="Padding" Value="2,0,0,2" />
<Setter Property="Margin" Value="0,0,0,0" />
<Setter Property="Visibility" Value="Visible" />
<Setter Property="IsEnabled" Value="True" />
<Setter Property="CharacterCasing" Value="Upper" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="BorderBrush" Value="Black" />
<Setter Property="Background" Value="White" />
<Setter Property="Foreground" Value="Black" />
<Setter Property="Template" Value="{StaticResource CTTextBox}" />
</Style>
我確信問題可以通過多種方式解決,但在這些情況下,我通常會做出附加行為。 但是情況不太正常,因為實現了控制的模板。 在任何情況下,我認為附加行為適合這種情況,在你的位置我會這樣做。
附加行為是非常強大且方便的解決方案,完全滿足MVVM模式,也可以在Blend中使用(具有預定義的接口)。 附加行為 - 是附加屬性,它具有一個事件處理程序來更改此屬性,並且所有邏輯都在此處理程序中實現。
在開始意識到這種行為之前,我會考慮對模板進行的一些更改。
我有點不明白你為什么使用PART_ContentHost
ScrollViewer控件,也許會有幾個日期,需要用滾動顯示它們。 在WPF中,顯示內容需要兩個控件:
這是他們的主要目標。 第一個最輕的通常總是在模板中使用,但它不支持我們需要協調工作的事件,因此我選擇了ContentControl
。 在小東西上為模板添加了綁定屬性,為Popup
設置:
AllowsTransparency="True"
VerticalOffset="4"
HorizontalOffset="-5"
為了更好的可視化 現在轉到行為的例子。
XAML
<Window.Resources>
<ControlTemplate x:Key="CTTextBox" TargetType="{x:Type TextBox}">
<StackPanel AttachedBehaviors:SelectDateBehavior.IsEnabled="True"> <!-- Here is determined behaviour -->
<Border x:Name="targetBorder"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
TextBlock.Foreground="{TemplateBinding Foreground}"
SnapsToDevicePixels="True">
<ContentControl x:Name="ContentHost"
Content="{TemplateBinding Text}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Margin="4,0,0,0" />
</Border>
<Popup x:Name="PopCal"
AllowsTransparency="True"
VerticalOffset="4"
HorizontalOffset="-5"
PlacementTarget="{Binding ElementName=ContentHost}">
<Calendar x:Name="ActualCalendar" />
</Popup>
</StackPanel>
</ControlTemplate>
<Style TargetType="{x:Type TextBox}" x:Key="STextBox">
<Setter Property="FontFamily" Value="Arial" />
<Setter Property="FontSize" Value="12" />
<Setter Property="Height" Value="25" />
<Setter Property="Width" Value="100" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="CharacterCasing" Value="Upper" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="BorderBrush" Value="Gray" />
<Setter Property="Background" Value="AliceBlue" />
<Setter Property="Foreground" Value="Black" />
<Setter Property="Template" Value="{StaticResource CTTextBox}" />
</Style>
</Window.Resources>
<Grid>
<TextBox Style="{StaticResource STextBox}"
Text="Select date" />
</Grid>
Atatched Behavior
public class SelectDateBehavior
{
#region IsEnabled Dependency Property
public static readonly DependencyProperty IsEnabledProperty;
public static void SetIsEnabled(DependencyObject DepObject, bool value)
{
DepObject.SetValue(IsEnabledProperty, value);
}
public static bool GetIsEnabled(DependencyObject DepObject)
{
return (bool)DepObject.GetValue(IsEnabledProperty);
}
static SelectDateBehavior()
{
IsEnabledProperty = DependencyProperty.RegisterAttached("IsEnabled",
typeof(bool),
typeof(SelectDateBehavior),
new UIPropertyMetadata(false, IsEnabledChanged));
}
#endregion
#region IsEnabledChanged Handler
private static void IsEnabledChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
Panel panel = sender as Panel;
if (panel != null)
{
if (e.NewValue is bool && ((bool)e.NewValue) == true)
{
panel.Loaded += new RoutedEventHandler(panelLoaded);
}
else
{
panel.Loaded -= new RoutedEventHandler(panelLoaded);
}
}
}
#endregion
#region Panel Loaded Handler
private static void panelLoaded(object sender, RoutedEventArgs e)
{
Panel panel = sender as Panel;
Border border = panel.FindName("targetBorder") as Border;
ContentControl contentHost = border.FindName("ContentHost") as ContentControl;
Popup popup = panel.FindName("PopCal") as Popup;
if (popup != null)
{
Calendar calendar = popup.FindName("ActualCalendar") as Calendar;
calendar.SelectedDatesChanged += new EventHandler<SelectionChangedEventArgs>(calendarSelectedDatesChanged);
}
if (contentHost != null)
{
contentHost.MouseDoubleClick += new MouseButtonEventHandler(contentHostMouseDoubleClick);
}
}
#endregion
#region ContentHost MouseDoubleClick Handler
private static void contentHostMouseDoubleClick(object sender, MouseButtonEventArgs e)
{
ContentControl contentHost = sender as ContentControl;
Border border = contentHost.Parent as Border;
Panel panel = border.Parent as Panel;
Popup popup = panel.FindName("PopCal") as Popup;
if (popup != null)
{
popup.IsOpen = true;
}
}
#endregion
#region Calendar SelectedDatesChanged Handler
private static void calendarSelectedDatesChanged(object sender, SelectionChangedEventArgs e)
{
Calendar calendar = sender as Calendar;
Popup popup = calendar.Parent as Popup;
Panel panel = popup.Parent as Panel;
Border border = panel.FindName("targetBorder") as Border;
ContentControl contentHost = border.FindName("ContentHost") as ContentControl;
if (popup != null)
{
contentHost.Content = calendar.SelectedDate;
popup.IsOpen = false;
}
}
#endregion
}
Output
在此處設置當前日期:
private static void calendarSelectedDatesChanged(object sender, SelectionChangedEventArgs e)
{
// Skipped a few lines of code
if (popup != null)
{
contentHost.Content = calendar.SelectedDate;
popup.IsOpen = false;
}
}
Some notes
讓我提請注意一些功能。 首先,我們必須擺脫EvenTrigger Storyboard,因為在優先級最高的設置值的WPF動畫中,這意味着如果我們在動畫中設置值IsOpen
,則無法從其他來源(代碼等)進行訪問。 所以我把所有的觸發器/事件留在了側面行為上。
其次,解決方案與模板和控件的結構緊密相關。 這意味着如果你必須改變模板的結構,你必須改變和行為(可能不多)。
此示例可在此處獲得 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.