databind la propriété Source du WebBrowser dans WPF

85

Quelqu'un sait-il comment relier les données à la propriété .Source du WebBrowser dans WPF (3.5SP1)? J'ai un listview que je veux avoir un petit WebBrowser sur la gauche et du contenu sur la droite, et pour relier la source de chaque WebBrowser avec l'URI dans chaque objet lié à l'élément de liste.

C'est ce que j'ai comme preuve de concept jusqu'à présent, mais le " <WebBrowser Source="{Binding Path=WebAddress}"" ne compile pas.

<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
la source

Réponses:

157

Le problème est que ce WebBrowser.Sourcen'est pas un fichier DependencyProperty. Une solution de contournement serait d'utiliser de la AttachedPropertymagie pour activer cette capacité.

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

}

Ensuite, dans votre xaml, faites:

<WebBrowser ns:WebBrowserUtility.BindableSource="{Binding WebAddress}"/>
Todd White
la source
9
Obtenir une exception sur ce "nouvel Uri (uri)" car la chaîne est "". Peut-être que cela devrait être .... browser.Source = string.IsNullOrEmpty (uri)? null: nouvel Uri (uri);
milieu du
5
Notez qu'il ne s'agit que d'une liaison à sens unique et que la propriété BindableSource ne change pas lorsque la page du navigateur Web change.
Kurren
1
en tant que débutant, c'était un peu difficile à suivre pour moi - écrire quelque chose sur le fait d'avoir un setter getter privé-public pour "WebAddress" dans le ViewModel lié avec un événement de mise à jour pour la propriété modifiée aurait aidé. ce projet a un exemple similaire. github.com/thoemmi/WebBrowserHelper
pgee70
Merci. Je l'ai pris et l'ai modifié pour implémenter une propriété "Html", qui appelle NavigateToString sous le capot.
Antony Scott
@ pgee70 merci, j'ai suivi votre exemple github et fonctionne un régal
percentum
33

J'ai légèrement modifié l'excellente réponse de Todd pour produire une version qui gère soit les chaînes soit les Uris de la source 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
la source
31

J'ai écrit un usercontrol wrapper, qui utilise 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);
    }
}

et utilisez-le comme ceci:

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

Le seul problème avec celui-ci est que le contrôle WebBrowser n'est pas "pur" wpf ... c'est en fait juste un wrapper pour un composant win32. Cela signifie que le contrôle ne respectera pas le z-index et superposera toujours un autre élément (par exemple: dans un scrollviewer, cela pourrait causer des problèmes) plus d'informations sur ces problèmes win32-wpf sur MSDN

RoelF
la source
C'est exactement ce dont j'avais besoin car je veux afficher mon propre html. Neat, simple, et je comprends presque ce que ça fait (-:
Murph
3

Cool idée Todd.

J'ai fait la même chose avec RichTextBox.Selection.Text dans Silverlight 4 maintenant. Merci pour votre message. Fonctionne très 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;
        }
    }
}    

Voici le Xaml-Code.

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

Vous pouvez également utiliser un contrôle proxy distinct . Cela s'applique non seulement au cas WebBrowser, mais à tout contrôle de ce type.

Max Galkin
la source
0

Il s'agit d'un raffinement de la réponse de Todd et Samuel pour tirer parti de certains principes de logique de base et utiliser l'opérateur de fusion nul

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 le navigateur est nul ou si l'emplacement est nul, nous ne pouvons pas utiliser ou naviguer vers une page nulle.
  2. Lorsque les éléments de # 1 ne sont pas nuls, lors de l'affectation, si la nouvelle valeur est un URI, utilisez-le. Si ce n'est pas le cas et que l'URI est nul, alors coalesce car il doit s'agir d'une chaîne qui peut être placée dans un URI; puisque # 1 impose que la chaîne ne puisse pas être nulle.
ΩmegaMan
la source
-3

Vous devez le déclarer dans les premières lignes du xamlfichier qui pointe vers le fichier de classe

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