Example #1
0
        private static void onHtmlChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var owner = (RichTextBlock)d;
            var status = owner.GetStatus();

            if (status == null)
            {
                status = new RichTextBlockStatus();
                owner.SetStatus(status);

                owner.Loaded += Owner_Loaded;
                owner.Unloaded += Owner_Unloaded;

                status.RequestedThemePropertyToken = owner.RegisterPropertyChangedCallback(FrameworkElement.RequestedThemeProperty, onRequestedThemeChanged);
                status.ForegroundToken = owner.RegisterPropertyChangedCallback(RichTextBlock.ForegroundProperty, onForegroundChanged);
                owner.SizeChanged += onSizeChanged;
            }

            var newValue = e.NewValue as string;
            if (string.IsNullOrWhiteSpace(newValue))
                return;

            var html = (HtmlDocument)HtmlParser.Parse(newValue, true);

            owner.Blocks.Clear();

            status.BaseUri = owner.GetBaseUri();
            status.Foreground = owner.Foreground;
            status.ActualWidth = owner.ActualWidth;
            status.RequestedTheme = owner.RequestedTheme;

            status.Images.Clear();
            status.Hyperlinks.Clear();
            status.Lines.Clear();
            status.HyperlinkButtons.Clear();

            var paragraph = new Paragraph();
            CreateChildren(paragraph.Inlines, html.Body, status);
            owner.Blocks.Add(paragraph);
        }
Example #2
0
 private static void SetStatus(this FrameworkElement @this, RichTextBlockStatus value) => @this.SetValue(StatusPorperty, value);
Example #3
0
        private static void CreateElement(InlineCollection parent, Node node, RichTextBlockStatus status)
        {
            switch (node)
            {
                case Element element:
                    switch (element.TagName.ToLower())
                    {
                        case "a":
                            {
                                // We want to change BaseUri easily, so we use `GetAttribute("href")` instead of `HtmlAnchorElement.Href`
                                var href = element.GetAttribute("href");

                                if (!status.TryCreateUri(href, out var uri))
                                    break;

                                parent.Add(new Run() { Text = " " });

                                var hyperlink = new Hyperlink()
                                {
                                    NavigateUri = uri,
                                    Foreground = status.Foreground
                                };

                                foreach (var child in element.ChildNodes)
                                {
                                    switch (child)
                                    {
                                        case HtmlImageElement childElement:
                                            if (CreateImage(element, status) is Image image)
                                            {
                                                if (hyperlink.Inlines.Count != 0)
                                                {
                                                    hyperlink.SetUri(href);
                                                    status.Hyperlinks.Add(hyperlink);
                                                    parent.Add(hyperlink);
                                                }

                                                var button = new HyperlinkButton()
                                                {
                                                    NavigateUri = uri,
                                                    Content = image,
                                                    RequestedTheme = status.RequestedTheme
                                                };
                                                button.SetUri(href);
                                                status.HyperlinkButtons.Add(button);
                                                parent.Add(new InlineUIContainer() { Child = button });

                                                hyperlink = new Hyperlink()
                                                {
                                                    NavigateUri = uri,
                                                    Foreground = status.Foreground
                                                };
                                            }
                                            break;
                                        default:
                                            CreateElement(hyperlink.Inlines, child, status);
                                            break;
                                    }
                                    break;
                                }

                                if (hyperlink.Inlines.Count != 0)
                                {
                                    hyperlink.SetUri(href);
                                    status.Hyperlinks.Add(hyperlink);
                                    parent.Add(hyperlink);
                                }

                                parent.Add(new Run() { Text = " " });
                            }
                            break;
                        case "img":
                            {
                                if (CreateImage(element, status) is Image image)
                                    parent.Add(new InlineUIContainer() { Child = image });
                            }
                            break;
                        case "strong":
                        case "b":
                            {
                                var span = new Span() { FontWeight = FontWeights.Bold };
                                CreateChildren(span.Inlines, element, status);
                                parent.Add(span);
                            }
                            break;
                        case "div":
                        case "font":
                        case "p":
                        case "span":
                            {
                                var span = new Span();
                                foreach (var s in ParseStyle(element.GetAttribute("style")))
                                    switch (s.Key)
                                    {
                                        case "font-size":
                                            {
                                                var value = s.Value;

                                                double fontSize;
                                                if (value.EndsWith("px"))
                                                    fontSize = double.Parse(value.Remove(value.Length - 2));
                                                else if (value.EndsWith("%"))
                                                    fontSize = 14 * double.Parse(value.Remove(value.Length - 1)) / 100;
                                                else
                                                    fontSize = 14 * double.Parse(value);

                                                span.FontSize = fontSize;
                                            }
                                            break;
                                        case "font-weight":
                                            switch (s.Value)
                                            {
                                                case "bold":
                                                    span.FontWeight = FontWeights.Bold;
                                                    break;
                                            }
                                            break;
                                    }

                                CreateChildren(span.Inlines, element, status);
                                parent.Add(span);
                            }
                            break;
                        case "br":
                            if (element.NextSibling is Text nextText &&
                                nextText.Data.StartsWith("\n"))
                                break;

                            parent.Add(new LineBreak());
                            break;
                        case "hr":
                            parent.Add(new LineBreak());

                            var line = new Border()
                            {
                                BorderThickness = new Thickness(0, 1, 0, 0),
                                BorderBrush = status.Foreground,
                                Margin = new Thickness(8, 0, 8, 0),
                                Height = 1,
                            };

                            if (status.ActualWidth > 16)
                                line.Width = status.ActualWidth - 16;

                            status.Lines.Add(line);
                            parent.Add(new InlineUIContainer() { Child = line });

                            parent.Add(new LineBreak());
                            break;
                        case "iframe": // Ignore
                        case "script":
                        case "noscript":
                            break;
#if DEBUG
                        default:
                            Debug.WriteLine($"Ignore unknown tag {element.TagName}");
                            break;
#endif

                    }
                    break;
                case Text text:
                    parent.Add(new Run() { Text = text.Data });
                    break;
            }
        }
Example #4
0
 private static void CreateChildren(InlineCollection parent, Element node, RichTextBlockStatus status)
 {
     foreach (var child in node.ChildNodes)
         CreateElement(parent, child, status);
 }
Example #5
0
        private static Image CreateImage(Element image, RichTextBlockStatus status)
        {
            var src = image.GetAttribute("src");
            if (!status.TryCreateUri(src, out var uri))
                return null;

            if (!uri.Scheme.StartsWith("http"))
                return null;

            var result = new Image()
            {
                Source = UriToBitmapImageConverter.Instance.Convert(uri),
                MaxWidth = status.ActualWidth
            };

            result.SetUri(src);
            result.ImageOpened += Result_ImageOpened;

            if (image.HasAttribute("width"))
                result.Width = double.Parse(image.GetAttribute("width"));
            else
                result.Width = 0;

            if (image.HasAttribute("height"))
                result.Height = double.Parse(image.GetAttribute("height"));
            else
                result.Height = 0;

            status.Images.Add(result);
            return result;
        }