MVVM Light Messenger Events Firing Multiple Times

If you register for a message in the ctor of your views code-behind, eg:

Messenger.Default.Register<DialogMessage>(this, DialogMessageHandler);

Every time you reload the view, i.e by navigating to it, the callback method (in this example DialogMessageHandler) can get called multiple times from the previous times ctor ran.

To fix this all you need to do create an event handler for the PhoneApplicationPage Unloaded and unregister the message (you can unregister more specifically using one of the other overloads):

Messenger.Default.Unregister(this);

Improved GridView & ListView Row Selection in ASP.NET 4.0

Previous versions of GridView and ListView selection behaviour operating in a potentially confusing way to the user. For example, if the third row was selected and then page 2 was selected, the third row on page 2 would also be selected. ASP.NET 4 fixes this by adding the EnablePersistedSelection and DataKeyNames properties to provide what MSDN calls "Persisted Selection".

To make selection work more as expected simple set EnablePersistedSelection="True" on the GridView or ListView and set the DataKeyNames property to the unique\primary key\property of the underlying databound object\row\etc.

Controlling HTML IDs in ASP.NET 4.0 Webforms

ASP.NET 4 introduces more control over how client-side IDs are rendered by ASP.NET controls. By default ASP.NET 4 will render client IDs in the same way as ASP.NET 3.5 and earlier.

To change the way IDs are generated, the ClientIDMode property can be set - this can either be done on individual controls or at the page level in the @Page directive.

The default ClientIDMode value for controls is Inherit and the default for the Page is AutoID - if we change ClientIDMode at the Page level, the controls on that page will inherit the setting.

The options for ClientIDMode are:

  • AutoID: (Page default) generates IDs the same way as prior versions. Use for backward compatibility or when you don't care about what IDs are generated.
  • Inherit: (Control default): uses the value from the parent container.
  • Static: always sets the client-side ID to the same as the server-side ID property. Use to guarantee the client-side ID of a control - may result in duplicate IDs on the client which may cause problems if you have JavaScript looking for the control by ID.
  • Predictable: Can be used for content inside a databound GridView or ListView to append the unique id/key of the bound object (e.g. the primary key of a databound database record).

The following snippets of HTML output shows how the different settings change client-side IDs when a Label control called "NumberInStockLabel" is used inside a databound ListView.

AutoID, No ClientIDRowSuffix Specified

<td><span id="AutoIDListView_ctrl0_NumberInStockLabel">22</span></td>

Predictable, ClientIDRowSuffix=ProductID

<td><span id="PredictableListView_NumberInStockLabel_44">22</span></td>

The 44 at the end of the ID is the ProductID property from the underlying databound item.

Predictable, No ClientIDRowSuffix Specified

<td><span id="PredictableListViewNoClientIDRowSuffix_NumberInStockLabel_0">22</span></td>

The 0 at the end of the ID is a simple sequential number generated by ASP.NET, this is because we have not specified a value for ClientIDRowSuffix; subsequent rows would end _1 _2 _3 etc.

Static, No ClientIDRowSuffixSpecified

<td><span id="NumberInStockLabel">22</span></td>

The id is exactly the same as the one we set in markup; subsequent rows would all have exactly the same id.

Complete Source Markup

<%@ Page Language="C#" AutoEventWireup="true"
CodeBehind="Products.aspx.cs"
Inherits="clientIDs.Products"
ViewStateMode="Disabled" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>


        <asp:ObjectDataSource ID="odsProducts" runat="server"
            SelectMethod="GetProducts"
            TypeName="clientIDs.MockProductDB" />


        <h3>ListView, AutoID, No ClientIDRowSuffix</h3>
        <table>
            <tr>
                <th>Product ID</th>
                <th>Description</th>
                <th>Stock</th>
            </tr>
            <asp:ListView ID="AutoIDListView" runat="server"
                DataSourceID="odsProducts"
                ClientIDMode="AutoID"
                ClientIDRowSuffix="ProductID">
                <ItemTemplate>
                    <tr>
                        <td><%# Eval("ProductID") %></td>
                        <td><%# Eval("Description")%></td>
                        <td><asp:Label ID="NumberInStockLabel" runat="server" Text='<%# Eval("NumberInStock") %>' /></td>
                    </tr>
                </ItemTemplate>
            </asp:ListView>
        </table>



        <h3>ListView, Predictable, ClientIDRowSuffix=ProductID</h3>
        <table>
            <tr>
                <th>Product ID</th>
                <th>Description</th>
                <th>Stock</th>
            </tr>
            <asp:ListView ID="PredictableListView" runat="server"
                DataSourceID="odsProducts"
                ClientIDMode="Predictable"
                ClientIDRowSuffix="ProductID">
                <ItemTemplate>
                    <tr>
                        <td><%# Eval("ProductID") %></td>
                        <td><%# Eval("Description")%></td>
                        <td><asp:Label ID="NumberInStockLabel" runat="server" Text='<%# Eval("NumberInStock") %>' /></td>
                    </tr>
                </ItemTemplate>
            </asp:ListView>
        </table>



         <h3>ListView, Predictable, No ClientIDRowSuffix set</h3>
        <table>
            <tr>
                <th>Product ID</th>
                <th>Description</th>
                <th>Stock</th>
            </tr>
            <asp:ListView ID="PredictableListViewNoClientIDRowSuffix" runat="server"
                DataSourceID="odsProducts"
                ClientIDMode="Predictable">
                <ItemTemplate>
                    <tr>
                        <td><%# Eval("ProductID") %></td>
                        <td><%# Eval("Description")%></td>
                        <td><asp:Label ID="NumberInStockLabel" runat="server" Text='<%# Eval("NumberInStock") %>' /></td>
                    </tr>
                </ItemTemplate>
            </asp:ListView>
        </table>





        <h3>ListView, Static, No ClientIDRowSuffix set</h3>
        <table>
            <tr>
                <th>Product ID</th>
                <th>Description</th>
                <th>Stock</th>
            </tr>
            <asp:ListView ID="StaticListView" runat="server"
                DataSourceID="odsProducts"
                ClientIDMode="Static">
                <ItemTemplate>
                    <tr>
                        <td><%# Eval("ProductID") %></td>
                        <td><%# Eval("Description")%></td>
                        <td><asp:Label ID="NumberInStockLabel" runat="server" Text='<%# Eval("NumberInStock") %>' /></td>
                    </tr>
                </ItemTemplate>
            </asp:ListView>
        </table>



    </div>
    </form>
</body>
</html>

 

Complete HTML Output

 
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><title>
 
</title></head>
<body>
    <form method="post" action="Products.aspx" id="form1">
<div class="aspNetHidden">
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKLTkwMjEwMjAxN2QYBAUOU3RhdGljTGlzdFZpZXcPFCsADmRkZGRkZGQ8KwAEAAIEZGRkZgL/////D2QFJlByZWRpY3RhYmxlTGlzdFZpZXdOb0NsaWVudElEUm93U3VmZml4DxQrAA5kZGRkZGRkPCsABAACBGRkZGYC/////w9kBRNQcmVkaWN0YWJsZUxpc3RWaWV3DxQrAA5kZGRkZGRkPCsABAACBGQVAQlQcm9kdWN0SUQUKwAEFCsAAQIsFCsAAQIDFCsAAQLIIhQrAAECYGYC/////w9kBQ5BdXRvSURMaXN0Vmlldw8UKwAOZGRkZGRkZDwrAAQAAgRkFQEJUHJvZHVjdElEFCsABBQrAAECLBQrAAECAxQrAAECyCIUKwABAmBmAv////8PZBoTurOO0ZN1r5PaB5cscPKdDFEGlp47JvHGSM55AYQ9" />
</div>
 
    <div>
       
 
 
 
 
        <h3>ListView, AutoID, No ClientIDRowSuffix</h3>
        <table>
            <tr>
                <th>Product ID</th>
                <th>Description</th>
                <th>Stock</th>
            </tr>
           
                    <tr>
                        <td>44</td>
                        <td>product 1</td>
                        <td><span id="AutoIDListView_ctrl0_NumberInStockLabel">22</span></td>
                    </tr>
               
                    <tr>
                        <td>3</td>
                        <td>product 2</td>
                        <td><span id="AutoIDListView_ctrl1_NumberInStockLabel">11</span></td>
                    </tr>
               
                    <tr>
                        <td>4424</td>
                        <td>product 3</td>
                        <td><span id="AutoIDListView_ctrl2_NumberInStockLabel">1</span></td>
                    </tr>
               
                    <tr>
                        <td>96</td>
                        <td>product 4</td>
                        <td><span id="AutoIDListView_ctrl3_NumberInStockLabel">0</span></td>
                    </tr>
               
        </table>
 
 
 
        <h3>ListView, Predictable, ClientIDRowSuffix=ProductID</h3>
        <table>
            <tr>
                <th>Product ID</th>
                <th>Description</th>
                <th>Stock</th>
            </tr>
           
                    <tr>
                        <td>44</td>
                        <td>product 1</td>
                        <td><span id="PredictableListView_NumberInStockLabel_44">22</span></td>
                    </tr>
               
                    <tr>
                        <td>3</td>
                        <td>product 2</td>
                        <td><span id="PredictableListView_NumberInStockLabel_3">11</span></td>
                    </tr>
               
                    <tr>
                        <td>4424</td>
                        <td>product 3</td>
                        <td><span id="PredictableListView_NumberInStockLabel_4424">1</span></td>
                    </tr>
               
                    <tr>
                        <td>96</td>
                        <td>product 4</td>
                        <td><span id="PredictableListView_NumberInStockLabel_96">0</span></td>
                    </tr>
               
        </table>
 
 
 
         <h3>ListView, Predictable, No ClientIDRowSuffix set</h3>
        <table>
            <tr>
                <th>Product ID</th>
                <th>Description</th>
                <th>Stock</th>
            </tr>
           
                    <tr>
                        <td>44</td>
                        <td>product 1</td>
                        <td><span id="PredictableListViewNoClientIDRowSuffix_NumberInStockLabel_0">22</span></td>
                    </tr>
               
                    <tr>
                        <td>3</td>
                        <td>product 2</td>
                        <td><span id="PredictableListViewNoClientIDRowSuffix_NumberInStockLabel_1">11</span></td>
                    </tr>
               
                    <tr>
                        <td>4424</td>
                        <td>product 3</td>
                        <td><span id="PredictableListViewNoClientIDRowSuffix_NumberInStockLabel_2">1</span></td>
                    </tr>
               
                    <tr>
                        <td>96</td>
                        <td>product 4</td>
                        <td><span id="PredictableListViewNoClientIDRowSuffix_NumberInStockLabel_3">0</span></td>
                    </tr>
               
        </table>
 
 
 
 
 
        <h3>ListView, Static, No ClientIDRowSuffix set</h3>
        <table>
            <tr>
                <th>Product ID</th>
                <th>Description</th>
                <th>Stock</th>
            </tr>
           
                    <tr>
                        <td>44</td>
                        <td>product 1</td>
                        <td><span id="NumberInStockLabel">22</span></td>
                    </tr>
               
                    <tr>
                        <td>3</td>
                        <td>product 2</td>
                        <td><span id="NumberInStockLabel">11</span></td>
                    </tr>
               
                    <tr>
                        <td>4424</td>
                        <td>product 3</td>
                        <td><span id="NumberInStockLabel">1</span></td>
                    </tr>
               
                    <tr>
                        <td>96</td>
                        <td>product 4</td>
                        <td><span id="NumberInStockLabel">0</span></td>
                    </tr>
               
        </table>
 
 
 
    </div>
    </form>
</body>
</html>

URL Routing in ASP.Net 4 Web Forms

ASP.Net 4 simplifies the routing experience introduced in ASP.NET 3.5 SP1. Routing allows the use of more meaningful or 'friendly' URLs (which may also benefit search engine ranking).

For example, rather than .../ShowCountryDetails.aspx?country=australia routing could be enabled so the url looked like .../countries/australia

Defining Routes

You can define routes using the MapPageRoute method of the RouteCollection class. The code below is an extract from Global.asax.cs, the method RegisterRoutes is called from application start, and it's here that a route is registered:

protected void Application_Start(object sender, EventArgs e)
{
    RegisterRoutes(RouteTable.Routes);
}



void RegisterRoutes(RouteCollection routes)
{
    /*  Create a routing which maps SayHello to ShowGreeting.aspx
        and defines 2 parameters, greeting and name. */
    routes.MapPageRoute("ShowGreetingRoute",
        "SayHello/{greeting}/{name}",
        "~/ShowGreeting.aspx");
}
 

Accessing URL Param Values In Markup

When routing is in use, RouteValue expressions can be used in markup:

<asp:Literal Text="<%$RouteValue:greeting%>" runat="server" />

Accessing URL Param Values In Code

In code we can use Page.RouteData to get the values of any routed URL parameter values; we can either check for a null before calling ToString() or as in the following example use ContainsKey to check first (in practice you wouldn't use 'magic' string literals for "greeting", rather you'd define these as constants, etc.):

string greetingText = "no greeting text specified";
       
if (Page.RouteData.Values.ContainsKey("greeting"))
        greetingText = Page.RouteData.Values["greeting"].ToString();

Creating Routed URLs in Markup

We can create routed URLs in markup by using RouteUrl expressions:

<asp:HyperLink ID="declarativeLinkToRoutedPage"
    NavigateUrl="<%$RouteUrl:greeting=bonjour, name=Bob, routename=ShowGreetingRoute%>"
    Text="Say hello to Bob in French" runat="server" />

Creating Routed URLs in Code

Instead of setting NavigateUrl using RouteUrl in markup, we could set it in code RouteValueDictionary & VirtualPathData:

/// <summary>
/// Example of setting a Hyperlink's NavigateUrl in code
/// </summary>
private void SetHyperlinkNavigateUrlProperty()
{
    RouteValueDictionary routeParamValues = new RouteValueDictionary
    {
        {"greeting", "goodnight"},
        {"name", "Fred"}
    };

    VirtualPathData vpd = RouteTable.Routes.GetVirtualPath(null, routeParamValues);

    linkSetInCodeToRoutedPage.NavigateUrl = vpd.VirtualPath;
}

Using Routed Values as Parameters in Bound Data Sources

We can define RouteParameter in a data source that is being bound to.

The following markup shows a DetailsView being bound to an ObjectDataSource. The SelectParameters collection defines a RouteParameter which will pass the value of the 'name' URL routed parameter to the GetPersonsAge method of the MockDataBase class.

<asp:ObjectDataSource ID="odsPersonsAge" runat="server"
    SelectMethod="GetPersonsAge" TypeName="ASPNET4Routing.MockDataBase">
    <SelectParameters>
        <asp:RouteParameter Name="name" RouteKey="name" Type="String" />
    </SelectParameters>
</asp:ObjectDataSource>
      
<asp:DetailsView ID="DetailsView1" runat="server" DataSourceID="odsPersonsAge"
                AutoGenerateRows="true" /> 

<asp:ObjectDataSource ID="odsPersonsAge" runat="server"
    SelectMethod="GetPersonsAge" TypeName="ASPNET4Routing.MockDataBase">
    <SelectParameters>
        <asp:RouteParameter Name="name" RouteKey="name" Type="String" />
    </SelectParameters>
</asp:ObjectDataSource>

Managing ViewState in ASP.Net 4

ASP.Net 4 allows us to use an opt-in viewstate approach rather than an opt-out approach in previous ASP.Net versions (EnableViewState was available in prior versions, but if you disable viewstate, all children of the page/container also inherited this with no one to override and opt-in).

To use the opt-in approach in ASP.Net 4, the new ViewStateMode property can be used. The following example disables viewstate for the whole page (all children will inherit this disabled value) but Label2 opts in to viewstate. The codebehind sets the values of the labels only on the first page load - not any subsequent postbacks.

<%@ Page Language="C#" AutoEventWireup="true"
 CodeFile="Default.aspx.cs" Inherits="_Default"
 ViewStateMode="Disabled"
 %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <p><asp:Label ID="Label1" Text="not set" runat="server" /></p>
        <p><asp:Label ID="Label2" Text="not set" ViewStateMode="Enabled" runat="server" /></p>
        <p><asp:Button Text="do a postback" runat="server" /></p>
    </div>
    </form>
</body>
</html>

Code behind:

protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
        Label1.Text = "this text has been set from code behind only on the first load i.e. not set in any following postbacks.";
        Label2.Text = Label1.Text;
    }
}

The first time the page loads, the 2 text boxes show the text as set in the code behind:

 

 



Once a postback happens (in this case by clicking the button), no code is executed in the Page_Load event due to the if (!IsPostBack) check. This means that the Text property of the labels will be set to what's initially declared in the aspx mark-up (i.e. "not set"); however because we have set ViewStateMode="Enabled" on Label2, it's Text will be retained in viewstate between callbacks:

 

Setting Meta Tags in ASP.Net 4

ASP.Net 4 adds 2 new Meta tag related properties to the Page. They can be used to set meta tags for keywords and description.

You can set them in the code behind:

Page.MetaKeywords = "keyword1, keyword2, keyword3";
Page.MetaDescription = "Example of new meta tag support in ASP.Net 4";

You can also set in the @Page directive:

<%@ Page Language="C#" AutoEventWireup="true"
MetaKeywords="keyword1, keyword2, keyword3"
MetaDescription="Example of new meta tag support in ASP.Net 4"
CodeFile="Default.aspx.cs" Inherits="_Default" %>

The ouput of either of these methods renders html similar to the following:


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head><title>
    ASP.NET 4 Meta Tag Support
</title><meta name="description" content="Example of new meta tag support in ASP.Net 4" /><meta name="keywords" content="keyword1, keyword2, keyword3" /></head>
<body>
 </body>
</html>
 

Session State Compression in ASP.NET 4

ASP.NET 4 introduces the ability to turn on compression of session data.

Compression is disabled by default, to enable it modify the web.config file and set the new compressionEnabled attribute to true:

<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0"/>
<sessionState
mode="InProc"
stateConnectionString="tcpip=127.0.0.1:42424"
stateNetworkTimeout="10"
sqlConnectionString="data source=127.0.0.1;Integrated Security=SSPI"
sqlCommandTimeout="30"
customProvider=""
compressionEnabled="true" cookieless="UseCookies" cookieName="ASP.NET_SessionId" timeout="20" allowCustomSqlDatabase="false" regenerateExpiredSessionId="true" partitionResolverType="" useHostingIdentity="true"> <providers> <clear /> </providers> </sessionState> </system.web> </configuration>

The <sessionState> element is not defined in either the root machine or web config, and according to msdn the above (minus the compressionEnabled attribute) is the default.

The choice to enable compression should only be taken if the server is not already CPU bound as compression adds an additional compression/decompression overhead.

Permanently Redirecting a Page in ASP.NET 4

Rather that using Response.Redirect("newpage.aspx"), ASP.NET 4 introduces the RedirectPermanent method.

To signal that the redirect is of a permanent nature (such as a url to a page changing permanently) you can now call:

Response.RedirectPermanent("newpage.aspx");

This will issue a http response HTTP 301 (Moved Permanently) rather than the normal HTTP 302 (Found). This can potentially remove a round trip to the server.

From msdn.com: "Search engines and other user agents that recognize permanent redirects will store the new URL that is associated with the content, which eliminates the unnecessary round trip made by the browser for temporary redirects."

Notification Window "toast" in Silverlight 4

To create "toast" notifications (small popup messages that appear at the bottom right on Windows) the NotificationWindow class can be used.

NotificationWindow will only work in out-of-browser mode, if you try to show a notification when running in-browser a System.InvalidOperationException is thrown.

The default size of the toast is 400 x 100, the size can be altered by setting NotificationWindow's Width & Height, but the max size is 400 x 100;

Example 1 - Creating Some Simple Content

The code below shows a notification that contains a  TextBlock with the text "hello" and show it for 2 seconds:

var toast = new NotificationWindow();

// Create some content to show
var tb = new TextBlock();
tb.Text = "hello";

// Set content of the toast before we show it
toast.Content = tb;

// Show notification for 2 seconds
toast.Show(2000);

Example 2 - Using a UserControl as Content

This example shows how a UserControl defined elsewhere in XAML can be used as the content of the notification:

var toast = new NotificationWindow();

// Create an instance of a UserControl called SampleToast
// that is defined in XAML
var toastContent = new SampleToast();

// Set the content of the notification window to that of the UserControl
toast.Content = toastContent;

// Optionally set size to be different from the
// default of 400 x 100
toast.Width = 200;
toast.Height = 50;

// Show notification for 2 seconds
toast.Show(5000);

MSDN Docs

 

Hosting HTML Content in Silverlight 4

In Silverlight 4 we can present HTML content to the user.

The WebBrowser Control

For example to display the content of DontCodeTired.com we can set the Source property in XAML:

<WebBrowser Name="webBrowser" Source="http://www.dontcodetired.com" />

Or we can use the Navigate method of WebBrowser:

webBrowser.Navigate(new Uri(@"http://www.dontcodetired.com"));

The user can navigate to links, right-click and open in new tab (which opens up tab in a full web browser), view source, etc.

To use the WebBrowser the Silverlight app must be running out of browser (OOB), if you view when embedded in a web page (i.e. not OOB) the control displays a message saying that it can only be used in Out-of-Browser mode.

WebBrowser control running in-browser

WebBrowserBrush

Using the new WebBrowserBrush (previously HtmlBrush in beta 4) we can use the contents of a WebBrowser to fill another element.

For example, to fill an Ellipse with the content of a WebBrowser named "webBrowser" we define a WebBrowserBrush and sets its SourceName:

<Ellipse>
    <Ellipse.Fill>
        <WebBrowserBrush SourceName="webBrowser"  />
    </Ellipse.Fill>
</Ellipse>

The following example creates an ellipse which is filled with a blurred version of the content of a WebBrowser. One thing to note is that we hook into the WebBrowser's LoadCompleted event to ensure the ellipse is updated whenever a page has been loaded into the WebBrowser. When run it looks like the image below: 



XAML:

<UserControl x:Class="SL4HTMLHosting.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.RowDefinitions>
            <RowDefinition Height="1*" />
            <RowDefinition Height="1*" />
        </Grid.RowDefinitions>
       
        <WebBrowser Name="webBrowser"
                    Source="http://www.dontcodetired.com"
                    Grid.Row="0"
                    LoadCompleted="webBrowser_LoadCompleted">
        </WebBrowser>
       
        <Ellipse HorizontalAlignment="Stretch"
                    VerticalAlignment="Stretch"
                    Grid.Row="1">
            <Ellipse.Effect>
                <BlurEffect Radius="5" />
            </Ellipse.Effect>
            <Ellipse.Fill>
                <WebBrowserBrush x:Name="browserBrush"
                                    SourceName="webBrowser"  />
            </Ellipse.Fill>
        </Ellipse>
          
    </Grid>
</UserControl>

Code Behind:

using System.Windows.Controls;

namespace SL4HTMLHosting
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
        }

        private void webBrowser_LoadCompleted(object sender, System.Windows.Navigation.NavigationEventArgs e)
        {
            browserBrush.Redraw();
        }
    }
}

Interacting With WebBrowser Content

The following example uses WebBrowser.NavigateToString which allows a string of HTML to be rendered rather than a page at some Uri. The example creates a simple html button which uses window.external.notify to pass a message to the WebBroswer control via it's ScriptNotify event.

XAML:

<Grid x:Name="LayoutRoot" Background="White">            
    <WebBrowser Name="webBrowser"
                ScriptNotify="webBrowser_ScriptNotify">                  
    </WebBrowser>          
</Grid>

Code Behind:

using System.Windows.Controls;
using System.Text;
using System.Windows;

namespace SL4HTMLHosting
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
           
            // Create some basic HTML on the fly containing a simple button,
            // that when clicked passes a message to the containing WebBrowser
            var html = new StringBuilder();
            html.Append("<html>");
            html.Append("   <body>");
            html.Append(@"       <input type=""button"" value=""Show Message Box"" onclick=""window.external.notify('button click');"" /> ");
            html.Append("   </body>");
            html.Append("</html>");

            webBrowser.NavigateToString(html.ToString());
        }



        private void webBrowser_ScriptNotify(object sender, NotifyEventArgs e)
        {
            MessageBox.Show(string.Format("Message from web page: '{0}'.",e.Value));
        }
    }
}