enlazar la propiedad Source del WebBrowser en WPF

85

¿Alguien sabe cómo enlazar datos de la propiedad .Source del WebBrowser en WPF (3.5SP1)? Tengo una vista de lista que quiero tener un pequeño WebBrowser a la izquierda y contenido a la derecha, y vincular el origen de cada WebBrowser con el URI en cada objeto vinculado al elemento de la lista.

Esto es lo que tengo como prueba de concepto hasta ahora, pero el " <WebBrowser Source="{Binding Path=WebAddress}"" no se compila.

<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>
Russ
fuente

Respuestas:

157

El problema es que WebBrowser.Sourceno es un DependencyProperty. Una solución alternativa sería usar algo de AttachedPropertymagia para habilitar esta habilidad.

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;
        }
    }

}

Luego en tu xaml haz:

<WebBrowser ns:WebBrowserUtility.BindableSource="{Binding WebAddress}"/>
Todd White
fuente
9
Obteniendo una excepción en ese "nuevo Uri (uri)" ya que la cadena es "". Quizás debería ser .... browser.Source = string.IsNullOrEmpty (uri)? nulo: nuevo Uri (uri);
espacio intermedio
5
Tenga en cuenta que esto es solo un enlace unidireccional y la propiedad BindableSource no cambiará cuando cambie la página del navegador web.
Kurren
1
como novato, esto fue un poco difícil de seguir para mí: escribir algo sobre tener un setter getter privado-público para "WebAddress" en el ViewModel enlazado con un evento de actualización para la propiedad cambiada habría ayudado. este proyecto tiene un ejemplo similar. github.com/thoemmi/WebBrowserHelper
pgee70
Gracias. Tomé esto y lo modifiqué para implementar una propiedad "Html", que llama a NavigateToString bajo el capó.
Antony Scott
@ pgee70 gracias, seguí tu ejemplo de github y funciona de maravilla
percentum
33

He modificado un poco la excelente respuesta de Todd para producir una versión que se adapte a las cadenas o Uris de la fuente de enlace:

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;
    }
Samuel Jack
fuente
31

Escribí un control de usuario de envoltura, que hace uso de 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);
    }
}

y úsalo así:

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

El único problema con este es que el control WebBrowser no es wpf "puro" ... en realidad es solo un contenedor para un componente win32. Esto significa que el control no respetará el índice z y siempre se superpondrá a otro elemento (por ejemplo: en un visor de desplazamiento esto podría causar algunos problemas) más información sobre estos problemas de win32-wpf en MSDN

RoelF
fuente
Esto es exactamente lo que necesitaba, ya que quiero mostrar mi propio html. Limpio, simple y casi entiendo lo que está haciendo (-:
Murph
3

Genial idea, Todd.

He hecho algo similar con RichTextBox.Selection.Text en Silverlight 4 ahora. Gracias por tu publicación. Funciona bien.

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;
        }
    }
}    

Aquí está el código Xaml.

<RichTextBox IsReadOnly='False' TextWrapping='Wrap' utilities:RichTextBoxHelper.BindableSelectionText="{Binding Content}"/>
Olaf Japp
fuente
1

También puede utilizar un control de proxy especial por separado . Es aplicable no solo al caso de WebBrowser, sino a cualquier control de este tipo.

Max Galkin
fuente
0

Este es un refinamiento de la respuesta de Todd y Samuel para aprovechar algunas premisas lógicas básicas, así como para usar el operador de fusión nulo.

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

    if ((browser != null) && (e.NewValue != null))
        browser.Source = e.NewValue as Uri ?? new Uri((string)e.NewValue);

}
  1. Si el navegador es nulo o la ubicación es nula, no podemos usar o navegar a una página nula.
  2. Cuando los elementos en el n. ° 1 no son nulos, al asignar, si el nuevo valor es un URI, utilícelo. Si no es así y el URI es nulo, entonces se fusiona porque tiene que ser una cadena que se pueda poner en un URI; ya que # 1 impone que la cadena no puede ser nula.
ΩmegaMan
fuente
-3

Debe declararlo en las primeras líneas del xamlarchivo que apunta al archivo de clase

xmlns:reportViewer="clr-namespace:CoMS.Modules.Report" 
Guillermo
fuente