ligação de dados à propriedade Source do WebBrowser em WPF

86

Alguém sabe como vincular a propriedade .Source do WebBrowser no WPF (3.5SP1)? Eu tenho um listview que quero ter um pequeno WebBrowser à esquerda e conteúdo à direita, e para databind a fonte de cada WebBrowser com o URI em cada objeto vinculado ao item da lista.

Isso é o que tenho como prova de conceito até agora, mas o " <WebBrowser Source="{Binding Path=WebAddress}"" não 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
fonte

Respostas:

158

O problema é que WebBrowser.Sourcenão é um DependencyProperty. Uma solução alternativa seria usar um pouco de AttachedPropertymagia para habilitar essa habilidade.

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

}

Então, em seu xaml, faça:

<WebBrowser ns:WebBrowserUtility.BindableSource="{Binding WebAddress}"/>
Todd White
fonte
9
Obtendo uma exceção nesse "novo Uri (uri)", pois a string é "". Talvez devesse ser .... browser.Source = string.IsNullOrEmpty (uri)? null: novo Uri (uri);
midspace,
5
Observe que esta é apenas uma ligação unilateral e a propriedade BindableSource não será alterada quando a página do navegador da web mudar.
Kurren
1
como um novato, isso foi um pouco difícil para mim seguir - escrever algo sobre ter um setter getter privado-público para "WebAddress" no ViewModel vinculado com um evento de atualização para Propriedade alterada teria ajudado. este projeto tem um exemplo semelhante. github.com/thoemmi/WebBrowserHelper
pgee70
obrigado. Peguei isso e modifiquei para implementar uma propriedade "Html", que chama NavigateToString sob o capô.
Antony Scott
@ pgee70 obrigado, segui seu exemplo do github e funcionou muito bem
percentum
33

Eu alterei um pouco a excelente resposta de Todd para produzir uma versão que lida com strings ou Uris da fonte Binding:

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
fonte
31

Escrevi um controle de usuário de wrapper, que usa 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);
    }
}

e usá-lo assim:

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

O único problema com este é que o controle WebBrowser não é wpf "puro" ... na verdade, é apenas um invólucro para um componente win32. Isso significa que o controle não respeitará o Z-index e sempre sobreporá outro elemento (por exemplo: em um scrollviewer, isso pode causar alguns problemas) mais informações sobre esses problemas win32-wpf no MSDN

RoelF
fonte
Isso é exatamente o que eu precisava, pois desejo exibir meu próprio html. Legal, simples e quase entendo o que está fazendo (-:
Murph
3

Idéia legal, Todd.

Eu fiz semelhante com o RichTextBox.Selection.Text no Silverlight 4 agora. Obrigado pela sua postagem. Funciona bem.

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

Aqui está o código Xaml.

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

Você também pode usar um controle proxy separado especial . É aplicável não apenas ao caso WebBrowser, mas a qualquer controle desse tipo.

Max Galkin
fonte
0

Este é um refinamento da resposta de Todd e Samuel para tirar vantagem de algumas premissas lógicas básicas, bem como usar o operador de coalescência 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. Se o navegador for nulo ou o local for nulo, não podemos usar ou navegar para uma página nula.
  2. Quando os itens em # 1 não são nulos, ao atribuir, se o novo valor for um URI, use-o. Caso contrário, e o URI for nulo, então, coalescer para que seja uma string que possa ser colocada em um URI; já que # 1 impõe que a string não pode ser nula.
ΩmegaMan
fonte
-3

Você precisa declará-lo nas primeiras linhas do xamlarquivo que está apontando para o arquivo de classe

xmlns:reportViewer="clr-namespace:CoMS.Modules.Report" 
William
fonte