databind the Source property of the WebBrowser in WPF

56,966

Solution 1

The problem is that WebBrowser.Source is not a DependencyProperty. One workaround would be to use some AttachedProperty magic to enable this ability.

public static class WebBrowserUtility
{
    public static readonly DependencyProperty BindableSourceProperty =
        DependencyProperty.RegisterAttached("BindableSource", typeof(string), typeof(WebBrowserUtility), new UIPropertyMetadata(null, BindableSourcePropertyChanged));

    public static string GetBindableSource(DependencyObject obj)
    {
        return (string) obj.GetValue(BindableSourceProperty);
    }

    public static void SetBindableSource(DependencyObject obj, string value)
    {
        obj.SetValue(BindableSourceProperty, value);
    }

    public static void BindableSourcePropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
    {
        WebBrowser browser = o as WebBrowser;
        if (browser != null)
        {
            string uri = e.NewValue as string;
            browser.Source = !String.IsNullOrEmpty(uri) ? new Uri(uri) : null;
        }
    }

}

Then in your xaml do:

<WebBrowser ns:WebBrowserUtility.BindableSource="{Binding WebAddress}"/>

Solution 2

I've amended Todd's excellent answer a little to produce a version that copes with either strings or Uris from the Binding source:

public static class WebBrowserBehaviors
{
    public static readonly DependencyProperty BindableSourceProperty =
        DependencyProperty.RegisterAttached("BindableSource", typeof(object), typeof(WebBrowserBehaviors), new UIPropertyMetadata(null, BindableSourcePropertyChanged));

    public static object GetBindableSource(DependencyObject obj)
    {
        return (string)obj.GetValue(BindableSourceProperty);
    }

    public static void SetBindableSource(DependencyObject obj, object value)
    {
        obj.SetValue(BindableSourceProperty, value);
    }

    public static void BindableSourcePropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
    {
        WebBrowser browser = o as WebBrowser;
        if (browser == null) return;

        Uri uri = null;

        if (e.NewValue is string )
        {
            var uriString = e.NewValue as string;
            uri = string.IsNullOrWhiteSpace(uriString) ? null : new Uri(uriString);
        }
        else if (e.NewValue is Uri)
        {
            uri = e.NewValue as Uri;
        }

        browser.Source = uri;
    }

Solution 3

I wrote a wrapper usercontrol, which makes use of the DependencyProperties:

XAML:

<UserControl x:Class="HtmlBox">
    <WebBrowser x:Name="browser" />
</UserControl>

C#:

public static readonly DependencyProperty HtmlTextProperty = DependencyProperty.Register("HtmlText", typeof(string), typeof(HtmlBox));

public string HtmlText {
    get { return (string)GetValue(HtmlTextProperty); }
    set { SetValue(HtmlTextProperty, value); }
}

protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) {
    base.OnPropertyChanged(e);
    if (e.Property == HtmlTextProperty) {
        DoBrowse();
    }
}
 private void DoBrowse() {
    if (!string.IsNullOrEmpty(HtmlText)) {
        browser.NavigateToString(HtmlText);
    }
}

and use it like so:

<Controls:HtmlBox HtmlText="{Binding MyHtml}"  />

The only trouble with this one is that the WebBrowser control is not "pure" wpf... it is actually just a wrapper for a win32 component. This means that the control won't respect the z-index, and will always overlay other element (eg: in a scrollviewer this might cause some trouble) more info about these win32-wpf issues on MSDN

Solution 4

Cool idea Todd.

I have done similar with the RichTextBox.Selection.Text in Silverlight 4 now. Thanks for your post. Works fine.

public class RichTextBoxHelper
{
    public static readonly DependencyProperty BindableSelectionTextProperty =
       DependencyProperty.RegisterAttached("BindableSelectionText", typeof(string), 
       typeof(RichTextBoxHelper), new PropertyMetadata(null, BindableSelectionTextPropertyChanged));

    public static string GetBindableSelectionText(DependencyObject obj)
    {
        return (string)obj.GetValue(BindableSelectionTextProperty);
    }

    public static void SetBindableSelectionText(DependencyObject obj, string value)
    {
        obj.SetValue(BindableSelectionTextProperty, value);
    }

    public static void BindableSelectionTextPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
    {
        RichTextBox rtb = o as RichTextBox;
        if (rtb != null)
        {
            string text = e.NewValue as string;
            if (text != null)
                rtb.Selection.Text = text;
        }
    }
}    

Here is the Xaml-Code.

<RichTextBox IsReadOnly='False' TextWrapping='Wrap' utilities:RichTextBoxHelper.BindableSelectionText="{Binding Content}"/>
Share:
56,966
Russ
Author by

Russ

As a key member of any technology architecture team I do what it takes to accomplish technical and business goals with quality, efficiency, and accountability. I specialize in providing technology leadership that inspires while bringing new ideas, and technologies that result in high performance, high availability enterprise products. My primary goal has always been to improve efficiency and productivity for the company through direct contributions as well as the technical development and mentorship of the team working with me. I bridge the Communication gap with leadership, team leads, project managers, and internal partners to guarantee reliable and regular software delivery. While simultaneously bringing a broad level of expertise in both agile software development and system integration, as well as a big picture approach to development with organizational goals and measurable business results.

Updated on July 08, 2022

Comments

  • Russ
    Russ almost 2 years

    Does anyone know how to databind the .Source property of the WebBrowser in WPF ( 3.5SP1 )? I have a listview that I want to have a small WebBrowser on the left, and content on the right, and to databind the source of each WebBrowser with the URI in each object bound to the list item.

    This is what I have as a proof of concept so far, but the "<WebBrowser Source="{Binding Path=WebAddress}"" does not compile.

    <DataTemplate x:Key="dealerLocatorLayout" DataType="DealerLocatorAddress">                
        <StackPanel Orientation="Horizontal">
             <!--Web Control Here-->
            <WebBrowser Source="{Binding Path=WebAddress}"
                ScrollViewer.HorizontalScrollBarVisibility="Disabled" 
                ScrollViewer.VerticalScrollBarVisibility="Disabled" 
                Width="300"
                Height="200"
                />
            <StackPanel Orientation="Vertical">
                <StackPanel Orientation="Horizontal">
                    <Label Content="{Binding Path=CompanyName}" FontWeight="Bold" Foreground="Blue" />
                    <TextBox Text="{Binding Path=DisplayName}" FontWeight="Bold" />
                </StackPanel>
                <TextBox Text="{Binding Path=Street[0]}" />
                <TextBox Text="{Binding Path=Street[1]}" />
                <TextBox Text="{Binding Path=PhoneNumber}"/>
                <TextBox Text="{Binding Path=FaxNumber}"/>
                <TextBox Text="{Binding Path=Email}"/>
                <TextBox Text="{Binding Path=WebAddress}"/>
            </StackPanel>
        </StackPanel>
    </DataTemplate>
    
  • Bijendra Singh
    Bijendra Singh almost 13 years
    This is exactly what I needed as I want to display my own html. Neat, simple, and I almost understand what its doing (-:
  • midspace
    midspace over 12 years
    Getting an Exception on that "new Uri(uri)" as the string is "". Perhaps it should be.... browser.Source = string.IsNullOrEmpty(uri) ? null : new Uri(uri);
  • Kurren
    Kurren about 9 years
    Note that this is only a one way binding and the BindableSource property will not change when the web browser page changes.
  • pgee70
    pgee70 over 8 years
    as a newbie this was a little hard for me to follow - writing something about having a private-public getter setter for "WebAddress" in the bound ViewModel with an update event for Property changed would have helped. this project has a similar example. github.com/thoemmi/WebBrowserHelper
  • Shivang Gangadia
    Shivang Gangadia over 5 years
    thanks. I took this and modified it to implement a "Html" property, which calls NavigateToString under the hood.
  • percentum
    percentum almost 4 years
    @pgee70 thanks, i followed your github example and works a treat