Shai
Shai

Reputation: 569

Displaying HTML in WPF using MVVM

I have HTML-Source string saved in SQL Server table called "Report" in HTMLReport field (field type is NTEXT). Now I need to display that stored HTML into WPF Window. HTML tags and inline-CSS need to be interpreted on this WPF Window.

Can someone help me to complete this code?

HTMLView.xaml

  <Window x:Class="MyProject.HTMLView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
    Title="HTML View" Height="454" Width="787"
    >
  <Grid Name="grid1">
    <WindowsFormsHost>

    <wf:RichTextBox x:Name="reportHTML" Text="{Binding DisplayHTML, Mode=OneWay}"/>

       <!-- How do i bind dispaly HTML page here-->

    </WindowsFormsHost>
  </Grid>
</Window>

HTMLViewModel.cs

  namespace MyProject
  {
    public class HTMLViewModel: ViewModelBase
    {
      public HTMLViewModel()
      {
            //Reading from SQL Server table 
            //SELECT HTMLReport FROM Report WHERE ID=123
            //OK, I have HTMLString from database over here
            //Now I am assigning that to DisplayHTML Property

           DisplayHTML ="<table><tr><td><b>This text should be in table with bold fond </b></td></tr></table>";
      }

      private string _displayHTML;
      public string DisplayHTML
      {
        get
        {
            return _displayHTML;
        }

        set
        {
            if (_displayHTML!= value)
            {
                _displayHTML= value;
                OnPropertyChanged("DisplayHTML");
            }
        }
    }
  }

Upvotes: 11

Views: 32593

Answers (3)

SerjG
SerjG

Reputation: 3570

I would recommend to use Exceed WPF ToolKit RichTextBox which can bind to string representation of Xaml markup using Text property. For converting Html to Xaml I used HtmlToXamlConverter nuget package. HtmlToXamlConverter.ConvertHtmlToXaml(html, false)

If you want to display it as TextBlock as me here are some styles:

Style TargetType="RichTextBox" x:Key="RichTextBoxStyle">
        <Setter Property="FontFamily" Value="{StaticResource OverpassRegular}" />
        <Setter Property="Foreground" Value="White" />
        <Setter Property="FontWeight" Value="DemiBold" />
        <Setter Property="Opacity" Value="0.95" />
        <Setter Property="Background" Value="Transparent" />
        <Setter Property="CaretBrush" Value="White" />
    </Style>

    <Style x:Key="RichTextBoxAsTextBlock" TargetType="xctk:RichTextBox" BasedOn="{StaticResource RichTextBoxStyle}">
        <Setter Property="Cursor" Value="Arrow" />
        <Setter Property="SelectionBrush" Value="Transparent" />
        <Setter Property="IsReadOnly" Value="True" />
        <Setter Property="BorderThickness" Value="0" />
        <Setter Property="BorderBrush" Value="Transparent" />
        <Setter Property="FontWeight" Value="Normal" />
        <Setter Property="Foreground">
            <Setter.Value>
                <SolidColorBrush Color="{DynamicResource Primary.ForegroundColor}" />
            </Setter.Value>
        </Setter>
        <Setter Property="TextFormatter">
            <Setter.Value>
                <xctk:XamlFormatter />
            </Setter.Value>
        </Setter>
    </Style>

Upvotes: 0

Jose
Jose

Reputation: 344

I created a control based on this article:

http://blogs.msdn.com/b/ryanvog/archive/2009/01/20/clipping-legacy-content-hosted-inside-a-wpf-scrolling-region.aspx

...

public class ScrollableWebBrowser : WindowsFormsHost
{
    [DllImport("GDI32.DLL", EntryPoint = "CreateRectRgn")]
    private static extern IntPtr CreateRectRgn(Int32 x1, Int32 y1, Int32 x2, Int32 y2);

    [DllImport("User32.dll", SetLastError = true)]
    private static extern Int32 SetWindowRgn(IntPtr hWnd, IntPtr hRgn, Boolean bRedraw);

    private Int32 _topLeftX = -1;
    private Int32 _topLeftY = -1;
    private Int32 _bottomRightX = -1;
    private Int32 _bottomRightY = -1;
    bool _disposed = false;

    public static readonly DependencyProperty HtmlProperty = DependencyProperty.Register("Html", typeof(string), typeof(ScrollableWebBrowser), new PropertyMetadata(OnHtmlChanged));
    public string Html
    {
        get { return (string)GetValue(HtmlProperty); }
        set { SetValue(HtmlProperty, value); }
    }

    public ScrollableWebBrowser()
    {
        EventManager.RegisterClassHandler(typeof(ScrollViewer), ScrollViewer.ScrollChangedEvent, new ScrollChangedEventHandler(ScrollHandler));
    }

    static void OnHtmlChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ((ScrollableWebBrowser)d).UpdateWebBrowser();
    }

    void UpdateWebBrowser()
    {
        if (this.Child == null)
            this.Child = new System.Windows.Forms.WebBrowser();

        System.Windows.Forms.WebBrowser browser = this.Child as System.Windows.Forms.WebBrowser;
        if (browser != null)
            browser.DocumentText = System.Net.WebUtility.HtmlDecode(Html);
    }

    private void ScrollHandler(Object sender, ScrollChangedEventArgs ea)
    {
        PresentationSource presentationSource = HwndSource.FromVisual(this);
        if (presentationSource == null)
            return;

        Visual rootVisual = presentationSource.RootVisual;
        if (rootVisual == null)
            return;

        ScrollViewer scrollViewer = (ScrollViewer)sender;
        if (!scrollViewer.IsDescendantOf(rootVisual))
            return;

        Rect hostRect = this.TransformToAncestor(rootVisual).TransformBounds(new Rect(this.Padding.Left, this.Padding.Right, this.RenderSize.Width, this.RenderSize.Height));
        Rect intersectRect = Rect.Intersect(scrollViewer.TransformToAncestor(rootVisual).TransformBounds(new Rect(0, 0, scrollViewer.ViewportWidth, scrollViewer.ViewportHeight)), hostRect);
        Int32 topLeftX = 0;
        Int32 topLeftY = 0;
        Int32 bottomRightX = 0;
        Int32 bottomRightY = 0;

        if (intersectRect != Rect.Empty)
        {
            topLeftX = (Int32)(intersectRect.TopLeft.X - hostRect.TopLeft.X);
            topLeftY = (Int32)(intersectRect.TopLeft.Y - hostRect.TopLeft.Y);
            bottomRightX = (Int32)(intersectRect.BottomRight.X - hostRect.TopLeft.X);
            bottomRightY = (Int32)(intersectRect.BottomRight.Y - hostRect.TopLeft.Y);
        }

        if (_topLeftX != topLeftX || _topLeftY != topLeftY || _bottomRightX != bottomRightX || _bottomRightY != bottomRightY)
        {
            _topLeftX = topLeftX;
            _topLeftY = topLeftY;
            _bottomRightX = bottomRightX;
            _bottomRightY = bottomRightY;

            SetWindowRgn(this.Handle, CreateRectRgn(_topLeftX, _topLeftY, _bottomRightX, _bottomRightY), true);
        }
    }

    protected override void Dispose(Boolean disposing)
    {
        if (disposing)
        {
            try
            {
                if (!_disposed && this.Child != null)
                    this.Child.Dispose();

                _disposed = true;
            }
            finally
            {
                base.Dispose(disposing);
            }
        }
    }
}

Upvotes: 1

default.kramer
default.kramer

Reputation: 6103

You'll probably want to use a WPF RichTextBox instead of the Winforms one. Note that its Document property is of type FlowDocument. Since you have HTML, you will need a way to convert HTML to a FlowDocument. This question and answer describe a way to do the conversion.

Upvotes: 5

Related Questions