Reputation: 6124
I have a textBox that needs to be "transformed" into a DatePicker under certain circumstances.
easily done with template:
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="local:MyTextBox.IsDate" Value="True">
<Setter Property="Template" Value="{StaticResource DateTextBoxTemplate}" />
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
and then:
<ControlTemplate x:Key="DateTextBoxTemplate" TargetType="TextBox">
<DatePicker x:Name="DateContent"
Text="{Binding RelativeSource={RelativeSource AncestorType=TextBox}, Path=Text, Mode=TwoWay}" />
</ControlTemplate>
the trouble is: the focus is not "transferred" to the datepicker when I click on it.
i.e: if I click on the control, the datePicker does not get the focus. I have to click on it once again for this to happen.
I know I can do in my code behind:
protected override void OnGotFocus(RoutedEventArgs e)
{
base.OnGotFocus(e);
if (IsDate)
{
DatePicker dateContent = Template.FindName("DateContent", this) as DatePicker;
if (dateContent != null) dateContent.Focus();
}
}
but this does not really satisfy me as I'm pretty sure there is a way to do it all in xaml and I just don't know about it.
I found an other question mentioning the FocusManager.FocusedElement="{Binding ElementName=DateContent}"
option, but I have no clue as to where I could put this piece of code: it cannot be added to the controlTemplate (as I suspected), and if I put it on a grid encapsulating the datePicker in the template, it is basically useless.
so can I do this in xaml only? and if yes, how?
Upvotes: 1
Views: 2162
Reputation: 84656
I've used this in the past and it has worked
<ControlTemplate x:Key="DateTextBoxTemplate" TargetType="TextBox">
<DatePicker x:Name="DateContent" Text="{Binding RelativeSource={RelativeSource AncestorType=TextBox}, Path=Text, Mode=TwoWay}"/>
<ControlTemplate.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter TargetName="DateContent"
Property="FocusManager.FocusedElement"
Value="{Binding ElementName=DateContent}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Upvotes: 6
Reputation: 20746
You can achieve the desired behavior by telling the DatePicker
that it should be focused by default when loaded. To do this you can create an Attached Behavior like this:
public static class FocusExtensions
{
public static bool GetIsDefaultFocusElement(DependencyObject obj)
{
return (bool)obj.GetValue(IsDefaultFocusElementProperty);
}
public static void SetIsDefaultFocusElement(DependencyObject obj, bool value)
{
obj.SetValue(IsDefaultFocusElementProperty, value);
}
public static readonly DependencyProperty IsDefaultFocusElementProperty =
DependencyProperty.RegisterAttached("IsDefaultFocusElement", typeof(bool), typeof(FocusExtensions), new UIPropertyMetadata(false, OnIsDefaultFocusElementChanged));
private static void OnIsDefaultFocusElementChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var fe = (FrameworkElement)d;
if (!(bool)e.NewValue)
{
return;
}
if (fe.IsLoaded)
{
SetFocus(fe);
}
else
{
fe.Loaded += OnDefaultFocusElementLoaded;
}
}
private static void OnDefaultFocusElementLoaded(object sender, RoutedEventArgs e)
{
var fe = (FrameworkElement) sender;
fe.Loaded -= OnDefaultFocusElementLoaded;
SetFocus(fe);
}
private static void SetFocus(FrameworkElement element)
{
element.Focus();
}
}
Then you can set the IsDefaultFocusElement
attached property on the DatePicker
and every time the DatePicker
is loaded it will receive focus:
<ControlTemplate x:Key="DateTextBoxTemplate" TargetType="TextBox">
<DatePicker x:Name="DateContent"
Text="{Binding RelativeSource={RelativeSource AncestorType=TextBox}, Path=Text, Mode=TwoWay}"
my:FocusExtensions.IsDefaultFocusElement="True" />
</ControlTemplate>
This behavior can be used in any other places in your application where you need to specify an element that has to be focused by default (e.g. when you open a dialog and you want the first field to be focused right away).
Upvotes: 2