Introduction
I had a requirement to set the initial focus on a TextBox
but there was a catch. When the UserControl
that contained the TextBox
appeared, there was always a UserControl
in the foreground that was providing busy information, or an error condition. Therefore the normal setting of focus on initialization or loading did not work. To make things even worse, I designed the main Window to use ContentPresenters
whose Content
was used to determine the DataTemplate
to use for the ViewModel
associated with the Content
.
The Control
The code used to catch the change in the Content
of a ContentPresenter
is done by creating a new Control
derived from the ContentPresenter
:
public class ActionableContentPresenter : ContentPresenter
{
static ActionableContentPresenter()
{
ContentProperty.OverrideMetadata(typeof(ActionableContentPresenter),
new FrameworkPropertyMetadata(new PropertyChangedCallback(OnContentChanged)));
}
private static void OnContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var mcc = d as ActionableContentPresenter;
mcc?.ContentChanged?.Invoke(mcc,
new DependencyPropertyChangedEventArgs(ContentProperty, e.OldValue, e.NewValue));
}
public event DependencyPropertyChangedEventHandler ContentChanged;
}
Then all that is needed in the main Window (or UserControl
) is to associate an event
handler to the ContentChanged
event
of this Control
:
public MainWindow()
{
InitializeComponent();
ActionableContentPresenter.ContentChanged += ActionableContentPresenter_ContentChanged;
}
In the case of the sample with this article, I simply check if the Content
for the ActionableContentPresenter
is null
, and if it is, the focus can be set:
private void ActionableContentPresenter_ContentChanged(object sender,
DependencyPropertyChangedEventArgs e)
{
var mcc = sender as ActionableContentPresenter;
if (mcc.Content == null )
{
if (DisableSetFocus.IsChecked == false)
{
if (SetFocusTextBox2.IsChecked == false) TextBox1.Focus();
else TextBox2.Focus();
}
}
}
The Sample
The sample has a the main content which contains a number of controls, including two CheckBox
controls. One of the CheckBox
controls will enable the set focus and the other will changes the focus from the first TextBox
to the second TextBox
. When the "Show Overlay" Button
is clicked, and control with a Panel
with a single "Close" Button
will appear. This Panel
will completely overlay the other controls in the Window
so only the "Close" Button
can be clicked. When this Button
is clicked, the overlay Control
will disappear. If the "Disable Focus
" CheckBox
is selected, there will be no focus set to any Control
on the form. However, if it is not IsChecked
, then the focus will appear in either the first or the second TextBox
, depending on if the "Focus on TextBox 2 (otherwise TextBox 1)" CheckBox
is IsChecked
.
Initial Screen
Screen after "Show Overlay" button clicked
If the "Show Alternate Content" Button
is Clicked, then a Panel
with a TextBox
and Button
will overlay all the Controls except the Button
controls. If the "Show Overlay" Button
is clicked, the same overlay Panel
with a single Button
will appear, and, if the "Disable Focus" CheckBox
is not selected, the focus will be on the TextBox
. Otherwise there will be no focus.
Screen after "Show Alternate Content" button is clicked
Screen after "Show Alternate Content" and then "Show Overlay" buttons are clicked
Points of Interest
This ability to connect to handlers to existing DependencyProperties
should be remembered because it opens up a lot of options on doing unconventional tasks.
I have also thought about how to make this a behavior, and plan to investigate the option.
History
- 05/05/2016: Initial version
- 05/16/2016: Added
PriorityBinding
to article and code for comparison
- 05/21/2016: Updated code to include a
ContentPresentor
set focus example