How to display controls above WebBrowserControl in Silverlight

Having problems when trying to render other controls over the WebBrowser control in Silverlight? You’re at the right place.

With the release of Silverlight 4.0, the WebBrowser control was introduced. But, it was designed only to be used in an ‘Out Of Browser’ application. However, with the release of Silverlight 5, the control can also be used in an ‘In-browser” trusted application.

While there are a lot of discussions around rendering controls over the “WebBrowser” control in Silverlight, successful solutions to this problem have been difficult to come by. I did my research while trying to resolve this problem for one of my customers and found a solution! But before I share the solution, let me give you a background to the problem.

Since the introduction of the WebBrowser control in Silverlight, many developers have been using it to display HTML content inside the application. But, the major limitation is the “Airspace” issue.

So, what is “Airspace”?

In an application window each pixel within the windows belongs to exactly only one HWND, which constitutes the airspace for that HWND. The HWND can render only on those pixels which belongs to it.

In a typical Silverlight OOB application, there will be only one Silverlight-HWND. So, the entire pixels in the Silverlight application belong to this HWND and that constitutes the airspace for that.

But, in our scenario when we introduce a WebBrowser control in our Silverlight OOB application, the Airspace is shared by the HWND of the WebBrowser control. And that is knows as Airspace issue.

The pixels on which the WebBrowser control resides will belong to the WebBrowser-HWND. So, Silverlight will not be able to render anything on the pixel which belongs to other HWND.

But, why does WebBrowser control create its own HWND unlike other Silverlight controls?

This is because; the WebBrowser control available in Silverlight is a wrapper around the “System.Windows.Controls.WebBrowser” control. The other Silverlight control will not create a new window for itself; rather it will be created under a single HWND. The WebBrowser control is not a real Silverlight control, but a wrapper around Windows HTML control.  These native controls will create their own HWND.

Because of this, the WebBrowser control always overlaps other controls within the application. For example, when the page with the fixed header and scroll functionality are being used or when the WebBrowser control is placed under a menu control. There could be other instances.

Solution to the issue:

By using the “WebBrowserBrush”, we can overcome this problem.

The WebBrowserBrush was introduced with the WebBrowser control, and they are designed to work together to display rich HTML content.

WebBrowserBrush is a type of Brush object which paints an area with HTML content. This HTML content is provided by a WebBrowser control. Just like the other brush types, you can use a WebBrowserBrush to fill a rectangle, geometry contents of path and much more.

So, how is this brush going to help us resolve this problem?  

To overcome this problem, you can hide the WebBrowser and use a WebBrowserBrush to paint the area with HTML content from the WebBrowser. The WebBrowserBrush will paint the HTML content in the same layer as other control, and will allow other controls to be displayed above it. We can show the WebBrowser control when it is needed. The only thing which we need to consider is to find an appropriate event to handle this logic.

Sample implementation:

In the sample application below, I want my combobox list to render above the WebBrowser when open. I have used the ComboBox_DropDownChanged event to handle the logic. When the list is open, I hide the WebBrowser control and paint the area with the WebBrowserBrush. I then bring the control back when the list is closed.

 - XAML code snippet -
  
 <UserControl x:Class="SilverlightApplication1_WebBrowser.MainPage"
     xmlns="schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:x="schemas.microsoft.com/winfx/2006/xaml"
     xmlns:d="schemas.microsoft.com/expression/blend/2008"
     xmlns:mc="schemas.openxmlformats.org/markup-compatibility/2006">
  
     <Grid x:Name="LayoutRoot" Background="White" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
         <Canvas HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
             <Grid HorizontalAlignment="Stretch"  VerticalAlignment="Stretch">
                 <Grid.RowDefinitions>
                     <RowDefinition Height="50" />
                     <RowDefinition Height="50" />
                     <RowDefinition Height="Auto" />
                 </Grid.RowDefinitions>
                 <Grid.ColumnDefinitions>
                     <ColumnDefinition Width="*"/>
                     <ColumnDefinition Width="50"/>
                 </Grid.ColumnDefinitions>
                 
                 <ComboBox Canvas.ZIndex="10" Grid.Row="0" x:Name="cbTestList"
                            DropDownOpened="cbTestList_DropDownOpened" DropDownClosed="cbTestList_DropDownClosed" >
                     <ComboBox.Items>
                         <ComboBoxItem Content="-- Select --" IsSelected="True"/>
                         <ComboBoxItem Content="Text1"/>
                         <ComboBoxItem Content="Text2"/>
                         <ComboBoxItem Content="Text3"/>
                         <ComboBoxItem Content="Text4"/>
                         <ComboBoxItem Content="Text5"/>
                         <ComboBoxItem Content="Text6"/>
                         <ComboBoxItem Content="Text7"/>
                         <ComboBoxItem Content="Text8"/>
                         <ComboBoxItem Content="Text9"/>
                         <ComboBoxItem Content="Text10"/>
                         <ComboBoxItem Content="Text11"/>
                         <ComboBoxItem Content="Text12"/>
                         <ComboBoxItem Content="Text13"/>
                         <ComboBoxItem Content="Text14"/>
                         <ComboBoxItem Content="Text15"/>
                     </ComboBox.Items>
                 </ComboBox>
                           
                 
                 <WebBrowser x:Name="wb" Height="500" Width="800" Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="2" 
                             Canvas.ZIndex="0" HorizontalAlignment="Stretch" 
                             VerticalAlignment="Stretch" />
                 
                 <TextBox x:Name="txtUrl" Margin="10" Grid.Row="1" Grid.Column="0"
                          Canvas.ZIndex="10"/>
  
                 <Button Grid.Column="1" Grid.Row="1" Margin="10" Canvas.ZIndex="10" Content="Go" 
                         Name="btnLoadContent" Click="btnLoadContent_Click" />
  
                 <Rectangle Grid.Column="0" Height="500" Width="800" Grid.ColumnSpan="2" Grid.Row="2" 
                             HorizontalAlignment="Stretch" 
                             VerticalAlignment="Stretch">
                     <Rectangle.Fill>
                         <WebBrowserBrush SourceName="wb" x:Name="WBB1"/>
                     </Rectangle.Fill>
                 </Rectangle>
             </Grid>
  
             
         </Canvas>
     </Grid>
 </UserControl>
  
 - end -

 

 - C# code snippet -
  
 public partial class MainPage : UserControl
     {
         bool _toggle;
  
         public MainPage()
         {
             InitializeComponent();
             _toggle = true;
  
             txtUrl.Text = "www.bing.com";
  
             wb.Navigate(new Uri(txtUrl.Text, UriKind.Absolute));            
         }
  
         private void btnLoadContent_Click(object sender, RoutedEventArgs e)
         {
             wb.Navigate(new Uri(txtUrl.Text, UriKind.Absolute));
         }
  
         private void cbTestList_DropDownOpened(object sender, EventArgs e)
         {
             ChangeWebBrowserVisibility();
         }
  
         private void ChangeWebBrowserVisibility()
         {
             if (_toggle)
             {
                 WBB1.Redraw();
                 wb.Visibility = System.Windows.Visibility.Collapsed;
             }
             else
             {
                 wb.Visibility = System.Windows.Visibility.Visible;
             }
             _toggle = !_toggle;
         }
  
         private void cbTestList_DropDownClosed(object sender, EventArgs e)
         {
             ChangeWebBrowserVisibility();
         }
     }
  
 - end -

 

Screen shot from Sample application.

 

Limitations:

Though we are able to overcome the problem using a WebBrowserBrush along with the WebBrowser control, we still have a couple of limitations which we need to consider.

  1. The user cannot interact with the WebBrowserBrush.
  2. The changes in the WebBrowser control will not reflect automatically unless redrawn.

 Conclusion:

This is a simple workaround to the problem and it will work if designed properly, considering the limitation we have.

Happy Coding!

Content developed by: Shiva N Shankar
Content reviewed by: Trevor Fellman