Sunday, June 12, 2011

Set focus to Silverlight application and to a scrollviewer content

First, when the webbrowser is displaying the Silverlight app, the HTML element of the Silverlight plugin(object tag) might not have the focus.

This is independent of the Silverlight focus.

I've written a simple test Silverlight application which starts a DispatcherTimer and outputs in the debug output the currently focused element:

The MainPage xaml:
Background="White">

The code behind:
public partial class MainPage : UserControl
{
DispatcherTimer t = new DispatcherTimer()
{
Interval = new TimeSpan(0, 0, 1)
};
public MainPage()
{
InitializeComponent();
t.Tick += new EventHandler(t_Tick);
t.Start();
}

void t_Tick(object sender, EventArgs e)
{
object elem = FocusManager.GetFocusedElement();
if (elem == null)
{
Debug.WriteLine("Reported focused element is null");
}
else
{
Debug.WriteLine(elem);
}
}
}

The "Reported focused element is null" is written to debug output.

Using this method we can see which element has focus in the application and try to fix it.

What I did next is to set the focus in the Loaded event:

public MainPage()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(MainPage_Loaded);
t.Tick += new EventHandler(t_Tick);
t.Start();
}
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
this.Focus();
}

This makes it to report that MyControl has the focus.

Note that if we would remove MyControl it will says that no control has focus, even if we focus to the main page control.
That's because there is no control in the MainPage which can have the focus. All focusable elements are the ones deriving from Control class.

Also, if we keep MyControl but remove the this.Focus() call from Loaded, it will still report that no control has focus.

Now, having MyControl and this.Focus() call set, we still cant process any key press on the MyControl.
That's because the focus is not set to the HTML Element of the Silverlight plugin (the object tag).
This also shows that focus in Silverlight is different then the real focus in HTML page, between the HTML elements of the page.
What's interesting is that we can process the key press in the main page control.
So even if the FocusManager reports null, the key press events can be process on the main page control.

In order to set focus on the Silverlight app, we have two options.
#1 We can make it from the HTML:


Make sure the object tag has id set:


#2. Make it from app code:

void MainPage_Loaded(object sender, RoutedEventArgs e)
{
HtmlPage.Plugin.Focus();
}

Now we can process the keys in the MyControl control.
We don't need to call this.Focus() anymore.

What I wanted next is to put the MyControl in a ScrollViewer

Background="White">

Without IsTabStop the focus goes to the ScrollViewer.
But with it, the reported focused element is null.
I changed the way focus is set by posting the call in the thread's message stack:

void MainPage_Loaded(object sender, RoutedEventArgs e)
{
Dispatcher.BeginInvoke(() =>
{
HtmlPage.Plugin.Focus();
});
}

This made it to work.
Without using BeingInvoke, it didn't work.