I have a requirement to focus on a specific textbox when a new view is loaded.
The solution was to add this line of code to the OnLoaded event for the view:
Dispatcher.BeginInvoke(() => { NameTextBox.Focus(); });
So this worked for one view, but not another. I spent some time debugging the problem and realized that the new view I was working on had a BusyIndicator that takes focus away from all controls since the BusyIndicator being set to true and false was occuring after the OnLoaded event.
So the solution is to call focus to the NameTextBox
after my BusyIndicator has been set to false. My idea was to create a reusable BusyIndicator control that handles this extra work. However, I am having trouble doing this in MVVM.
I started by making a simple extension of the toolkit:BusyIndicator:
public class EnhancedBusyIndicator : BusyIndicator
{
public UserControl ControlToFocusOn { get; set; }
private bool _remoteFocusIsEnabled = false;
public bool RemoteFocusIsEnabled
{
get
{
return _remoteFocusIsEnabled;
}
set
{
if (value == true)
EnableRemoteFocus();
}
}
private void EnableRemoteFocus()
{
if (ControlToFocusOn.IsNotNull())
Dispatcher.BeginInvoke(() => { ControlToFocusOn.Focus(); });
else
throw new InvalidOperationException("ControlToFocusOn has not been set.");
}
I added the control to my XAML file with no problem:
<my:EnhancedBusyIndicator
ControlToFocusOn="{Binding ElementName=NameTextBox}"
RemoteFocusIsEnabled="{Binding IsRemoteFocusEnabled}"
IsBusy="{Binding IsDetailsBusyIndicatorActive}"
...
>
...
<my:myTextBox (this extends TextBox)
x:Name="NameTextBox"
...
/>
...
</my:EnhancedBusyIndicator>
So the idea is when IsRemoteFocusEnabled
is set to true in my ViewModel (which I do after I've set IsBusy
to false in the ViewModel), focus will be set to NameTextBox
. And if it works, others could use the EnhancedBusyIndicator
and just bind to a different control and enable the focus appropriately in their own ViewModels, assuming their views have an intial BusyIndicator
active.
However, I get this exception when the view is loaded:
Set property 'foo.Controls.EnhancedBusyIndicator.ControlToFocusOn' threw an exception. [Line: 45 Position: 26]
Will this solution I am attempting work? If so, what is wrong with what I have thus far (cannot set the ControlToFocusOn
property)?
Update 1
I installed Visual Studio 10 Tools for Silverlight 5 and got a better error message when navigating to the new view. Now I gete this error message:
"System.ArgumentException: Object of type System.Windows.Data.Binding cannot be converted to type System.Windows.Controls.UserControl"
Also, I think I need to change the DataContext for this control. In the code-behind constructor, DataContext is set to my ViewModel. I tried adding a DataContext property to the EnhancedBusyIndicator
, but that did not work:
<my:EnhancedBusyIndicator
DataContext="{Binding RelativeSource={RelativeSource Self}}"
ControlToFocusOn="{Binding ElementName=NameTextBox}"
RemoteFocusIsEnabled="{Binding IsRemoteFocusEnabled}"
IsBusy="{Binding IsDetailsBusyIndicatorActive}"
...
>
Update 2
I need to change UserControl
to Control
since I will be wanting to set focus to TextBox
objects (which implement Control
). However, this does not solve the issue.
Without a
BusyIndicator
present in the view, the common solution to solve the focus problem is to add the codeto the
Loaded
event of the view. This actually works even with theBusyIndicator
present; however, theBusyIndicator
immediately takes focus away from the rest of the Silverlight controls. The solution is to invoke theFocus()
method of the control after theBusyIndicator
is not busy.I was able to solve it by making a control like this:
To use it, replace the
BusyIndicator
tags in your xaml with the newEnhancedBusyIndicator
and add the appropriate namespace.Add a new property,
ControlToFocusOn
inside the element, and bind it to an existing element in the view that you want focus to be on after theEnhancedBusyIndicator
disappears:In this case, I focused to a textbox called
NameTextBox
.That's it. This control will get focus every time we navigate to the page. While we are on the page, if the
EnhancedBusyIndicator
becomes busy and not busy agiain, focus will not go to the control; this only happens on initial load.If you want to allow the
EnhancedBusyIndicator
to focus to theControlToFocusOn
another time, add another property,AllowedToFocus
:When
AllowedToFocus
is set to true, the next timeEnhancedBusyIndicator
switches from busy to not busy, focus will go toControlToFocusOn
.AllowedToFocus can also be set to false when loading the view, to prevent focus from going to a control. If you bind
AllowedToFocus
to a ViewModel property, you may need to change theBindingMode
. By default, it isOneTime
.