Skip to content

Commit

Permalink
Merge pull request #1976 from Vijay-Nirmal/MarkdownTextBlock-ResizeImage
Browse files Browse the repository at this point in the history
MarkdownTextBlock - Resize image when overflow and Added ImageMaxWidth and ImageMaxHeight property
  • Loading branch information
michael-hawker authored May 6, 2018
2 parents 4f141fd + 0a8d730 commit c0f0aef
Show file tree
Hide file tree
Showing 6 changed files with 172 additions and 61 deletions.
4 changes: 3 additions & 1 deletion Microsoft.Toolkit.Uwp.SampleApp/Shell.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,9 @@
Foreground="{StaticResource Brush-Grey-01}"
ImageResolving="DocumentationTextblock_ImageResolving"
LinkClicked="DocumentationTextblock_OnLinkClicked"
IsFocusEngagementEnabled="False" />
IsFocusEngagementEnabled="False"
ImageMaxWidth="500"
ImageMaxHeight="500"/>
</ScrollViewer>
</PivotItem>
</Pivot>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,42 @@ public partial class MarkdownTextBlock
typeof(MarkdownTextBlock),
new PropertyMetadata(null, OnPropertyChangedStatic));

/// <summary>
/// Gets the dependency property for <see cref="ImageMaxHeight"/>
/// </summary>
public static readonly DependencyProperty ImageMaxHeightProperty = DependencyProperty.Register(
nameof(ImageMaxHeight),
typeof(double),
typeof(MarkdownTextBlock),
new PropertyMetadata(0.0, OnPropertyChangedStatic));

/// <summary>
/// Gets the dependency property for <see cref="ImageMaxWidth"/>
/// </summary>
public static readonly DependencyProperty ImageMaxWidthProperty = DependencyProperty.Register(
nameof(ImageMaxWidth),
typeof(double),
typeof(MarkdownTextBlock),
new PropertyMetadata(0.0, OnPropertyChangedStatic));

/// <summary>
/// Gets or sets the MaxWidth for images.
/// </summary>
public double ImageMaxWidth
{
get { return (double)GetValue(ImageMaxWidthProperty); }
set { SetValue(ImageMaxWidthProperty, value); }
}

/// <summary>
/// Gets or sets the MaxHeight for images.
/// </summary>
public double ImageMaxHeight
{
get { return (double)GetValue(ImageMaxHeightProperty); }
set { SetValue(ImageMaxHeightProperty, value); }
}

/// <summary>
/// Gets or sets the stretch used for images.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ private void RenderMarkdown()
renderer.TextWrapping = TextWrapping;
renderer.LinkForeground = LinkForeground;
renderer.ImageStretch = ImageStretch;
renderer.ImageMaxHeight = ImageMaxHeight;
renderer.ImageMaxWidth = ImageMaxWidth;
renderer.WrapCodeBlock = WrapCodeBlock;

_rootElement.Child = renderer.Render();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,16 @@ public partial class MarkdownRenderer
/// </summary>
public Stretch ImageStretch { get; set; }

/// <summary>
/// Gets or sets the MaxHeight for images.
/// </summary>
public double ImageMaxHeight { get; set; }

/// <summary>
/// Gets or sets the MaxWidth for images.
/// </summary>
public double ImageMaxWidth { get; set; }

/// <summary>
/// Gets or sets a value indicating whether to wrap text in the Code Block, or use Horizontal Scroll.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Documents;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;

namespace Microsoft.Toolkit.Uwp.UI.Controls.Markdown.Render
{
Expand Down Expand Up @@ -250,14 +251,31 @@ protected override async void RenderImage(ImageInline element, IRenderContext co
}

var image = new Image();
var imageContainer = new InlineUIContainer() { Child = image };
var scrollViewer = new ScrollViewer();
var viewbox = new Viewbox();
scrollViewer.Content = viewbox;
viewbox.Child = image;
var imageContainer = new InlineUIContainer() { Child = scrollViewer };

LinkRegister.RegisterNewHyperLink(image, element.Url);

image.Source = resolvedImage;
image.HorizontalAlignment = HorizontalAlignment.Left;
image.VerticalAlignment = VerticalAlignment.Top;
image.Stretch = ImageStretch;
scrollViewer.VerticalScrollMode = ScrollMode.Disabled;
scrollViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Disabled;
viewbox.StretchDirection = StretchDirection.DownOnly;

if (ImageMaxHeight > 0)
{
viewbox.MaxHeight = ImageMaxHeight;
}

if (ImageMaxWidth > 0)
{
viewbox.MaxWidth = ImageMaxWidth;
}

if (element.ImageWidth > 0)
{
Expand All @@ -281,6 +299,20 @@ protected override async void RenderImage(ImageInline element, IRenderContext co
image.Stretch = Stretch.Fill;
}

// If image size is given then scroll to view overflown part
if (element.ImageHeight > 0 || element.ImageWidth > 0)
{
scrollViewer.HorizontalScrollMode = ScrollMode.Auto;
scrollViewer.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;
}

// Else resize the image
else
{
scrollViewer.HorizontalScrollMode = ScrollMode.Disabled;
scrollViewer.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;
}

ToolTipService.SetToolTip(image, element.Tooltip);

// Try to add it to the current inlines
Expand Down
147 changes: 88 additions & 59 deletions docs/controls/MarkdownTextBlock.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ keywords: windows 10, uwp, windows community toolkit, uwp community toolkit, uwp

# MarkdownTextBlock XAML Control

The *MarkdownTextBlock control* provides full markdown parsing and rendering for Universal Windows Apps. Originally created for the open source reddit app Baconit, the control was engineered to be simple to use and very efficient. One of the main design considerations for the control was it needed to be performant enough to provide a great user experience in virtualized lists. With the custom markdown parser and efficient XAML rendering, we were able to achieve excellent performance; providing a smooth UI experience even with complex Markdown on low end hardware.
The [MarkdownTextBlock control](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.uwp.ui.controls.markdowntextblock) provides full markdown parsing and rendering for Universal Windows Apps. Originally created for the open source reddit app Baconit, the control was engineered to be simple to use and very efficient. One of the main design considerations for the control was it needed to be performant enough to provide a great user experience in virtualized lists. With the custom markdown parser and efficient XAML rendering, we were able to achieve excellent performance; providing a smooth UI experience even with complex Markdown on low end hardware.

Under the hood, the control uses XAML sub elements to build the visual rendering tree for the Markdown input. We chose to use full XAML elements over using the RichEditTextBlock control because the RichEditTextBlock isn’t flexible enough to correctly render all of the standard Markdown styles.

Expand All @@ -30,7 +30,8 @@ Here are some limitations you may encounter:
- All images are stretched with the same stretch value (defined by ImageStretch property)
- Relative Links & Relative Images needs to be handled manually using `LinkClicked` event.

## Example Image
## Sample Output

Note: scrolling is smooth, the gif below is not.

![MarkdownTextBlock animation](../resources/images/Controls-MarkdownTextBlock.gif "MarkdownTextBlock")
Expand All @@ -39,61 +40,85 @@ Note: scrolling is smooth, the gif below is not.

The MarkdownTextBlock control is highly customizable to blend with any theme. Customizable properties include:

* IsTextSelectionEnabled
* UseSyntaxHighlighting
* CodeStyling
* CodeBackground
* CodeBorderBrush
* CodeBorderThickness
* CodeForeground
* CodeFontFamily
* CodeMargin
* CodePadding
* InlineCodeBorderThickness
* InlineCodeBackground
* InlineCodeBorderBrush
* InlineCodePadding
* InlineCodeFontFamily
* Header1FontWeight
* Header1FontSize
* Header1Margin
* Header2FontWeight
* Header2FontSize
* Header2Margin
* Header3FontWeight
* Header3FontSize
* Header3Margin
* Header4FontWeight
* Header4FontSize
* Header4Margin
* Header5FontWeight
* Header5FontSize
* Header5Margin
* Header6FontWeight
* Header6FontSize
* Header6Margin
* HorizontalRuleBrush
* HorizontalRuleMargin
* HorizontalRuleThickness
* ListMargin
* ListGutterWidth
* ListBulletSpacing
* ParagraphMargin
* QuoteBackground
* QuoteBorderBrush
* QuoteBorderThickness
* QuoteForeground
* QuoteMargin
* QuotePadding
* TableBorderBrush
* TableBorderThickness
* TableCellPadding
* TableMargin
* TextWrapping
* WrapCodeBlock
| Property | Type | Description |
| -- | -- | -- |
| CodeBackground | Brush | Gets or sets the brush used to fill the background of a code block |
| CodeBorderBrush | Brush | Gets or sets the brush used to render the border fill of a code block |
| CodeBorderThickness | Thickness | Gets or sets the thickness of the border around code blocks |
| CodeFontFamily | FontFamily | Gets or sets the font used to display code. If this is `null`, then Windows.UI.Xaml.Media.FontFamily is used |
| CodeForeground | Brush | Gets or sets the brush used to render the text inside a code block. If this is `null`, then Foreground is used |
| CodeMargin | Thickness | Gets or sets the space between the code border and the text |
| CodePadding | Thickness | Gets or sets space between the code border and the text |
| CodeStyling | StyleDictionary | Gets or sets the Default Code Styling for Code Blocks |
| EmojiFontFamily | FontFamily | Gets or sets the font used to display emojis. If this is `null`, then Segoe UI Emoji font is used |
| Header1FontSize | double | Gets or sets the font size for level 1 headers |
| Header1FontWeight | FontWeight | Gets or sets the font weight to use for level 1 headers |
| Header1Foreground | Brush | Gets or sets the foreground brush for level 1 headers |
| Header1Margin | Thickness | Gets or sets the margin for level 1 headers |
| Header2FontSize | double | Gets or sets the font size for level 2 headers |
| Header2FontWeight | FontWeight | Gets or sets the font weight to use for level 2 headers |
| Header2Foreground | Brush | Gets or sets the foreground brush for level 2 headers |
| Header2Margin | Thickness | Gets or sets the margin for level 2 headers |
| Header3FontSize | double | Gets or sets the font size for level 3 headers |
| Header3FontWeight | FontWeight | Gets or sets the font weight to use for level 3 headers |
| Header3Foreground | Brush | Gets or sets the foreground brush for level 3 headers |
| Header3Margin | Thickness | Gets or sets the margin for level 3 headers |
| Header4FontSize | double | Gets or sets the font size for level 4 headers |
| Header4FontWeight | FontWeight | Gets or sets the font weight to use for level 4 headers |
| Header4Foreground | Brush | Gets or sets the foreground brush for level 4 headers |
| Header4Margin | Thickness | Gets or sets the margin for level 4 headers |
| Header5FontSize | double | Gets or sets the font size for level 5 headers |
| Header5FontWeight | FontWeight | Gets or sets the font weight to use for level 5 headers |
| Header5Foreground | Brush | Gets or sets the foreground brush for level 5 headers |
| Header5Margin | Thickness | Gets or sets the margin for level 5 headers |
| Header6FontSize | double | Gets or sets the font size for level 6 headers |
| Header6FontWeight | FontWeight | Gets or sets the font weight to use for level 6 headers |
| Header6Foreground | Brush | Gets or sets the foreground brush for level 6 headers |
| Header6Margin | Thickness | Gets or sets the margin for level 6 headers |
| HorizontalRuleBrush | Brush | Gets or sets the brush used to render a horizontal rule. If this is `null`, then HorizontalRuleBrush is used |
| HorizontalRuleMargin | Thickness | Gets or sets the margin used for horizontal rules |
| HorizontalRuleThickness | double | Gets or sets the vertical thickness of the horizontal rule |
| ImageMaxHeight | double | Gets or sets the MaxHeight for images |
| ImageMaxWidth | double | Gets or sets the MaxWidth for images |
| ImageStretch | Stretch | Gets or sets the stretch used for images |
| InlineCodeBackground | Brush | Gets or sets the foreground brush for inline code. |
| InlineCodeBorderBrush | Brush | Gets or sets the border brush for inline code |
| InlineCodeBorderThickness | Thickness | Gets or sets the thickness of the border for inline code |
| InlineCodeFontFamily | FontFamily | Gets or sets the font used to display code. If this is `null`, then `Windows.UI.Xaml.Media.FontFamily` is used |
| InlineCodePadding | Thickness | Gets or sets the foreground brush for inline code |
| IsTextSelectionEnabled | bool | Gets or sets a value indicating whether text selection is enabled |
| LinkForeground | Brush | Gets or sets the brush used to render links. If this is `null`, then Foreground is used |
| ListBulletSpacing | double | Gets or sets the space between the list item bullets/numbers and the list item content |
| ListGutterWidth | double | Gets or sets the width of the space used by list item bullets/numbers |
| ListMargin | Thickness | Gets or sets the margin used by lists |
| ParagraphMargin | Thickness | Gets or sets the margin used for paragraphs |
| QuoteBackground | Brush | Gets or sets the brush used to fill the background of a quote block |
| QuoteBorderBrush | Brush | Gets or sets the brush used to render a quote border. If this is null, then [QuoteBorderBrush](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.uwp.ui.controls.markdowntextblock.quoteborderbrush#Microsoft_Toolkit_Uwp_UI_Controls_MarkdownTextBlock_QuoteBorderBrush) is used |
| QuoteBorderThickness | Thickness | Gets or sets the thickness of quote borders. |
| QuoteForeground | Brush | Gets or sets the brush used to render the text inside a quote block. If this is `null`, then Foreground is used |
| QuoteMargin | Thickness | Gets or sets the space outside of quote borders |
| QuotePadding | Thickness | Gets or sets the space between the quote border and the text |
| TableBorderBrush | boBrushol | Gets or sets the brush used to render table borders. If this is null, then [TableBorderBrush](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.uwp.ui.controls.markdowntextblock.tableborderbrush#Microsoft_Toolkit_Uwp_UI_Controls_MarkdownTextBlock_TableBorderBrush) is used |
| TableBorderThickness | double | Gets or sets the thickness of any table borders |
| TableCellPadding | Thickness | Gets or sets the padding inside each cell |
| TableMargin | Thickness | Gets or sets the margin used by tables |
| Text | string | Gets or sets the markdown text to display |
| TextWrapping | TextWrapping | Gets or sets the word wrapping behavior |
| UriPrefix | string | Gets or sets the Prefix of Uri |
| UseSyntaxHighlighting | bool | Gets or sets a value indicating whether to use Syntax Highlighting on Code |
| WrapCodeBlock | bool | Gets or sets a value indicating whether to Wrap the Code Block or use a Horizontal Scroll |

## Events

| Events | Description |
| -- | -- |
| CodeBlockResolving | Fired when a Code Block is being Rendered. The default implementation is to output the CodeBlock as Plain Text. You must set `Handled` to `true` in order to process your changes |
| ImageClicked | Fired when an image element in the markdown was tapped |
| ImageResolving |
Fired when an image from the markdown document needs to be resolved. The default implementation is basically. You must set `Handled` to `true` in order to process your changes |
| LinkClicked | Fired when a link element in the markdown was tapped |
| MarkdownRendered | Fired when the text is done parsing and formatting. Fires each time the markdown is rendered |

### LinkClicked

Use this event to handle clicking on links for Markdown, by default the MarkdownTextBlock does not handle Clicking on Links.
Expand Down Expand Up @@ -184,21 +209,25 @@ This will likely require intimate knowledge of the implementation of the `Markdo
* [MarkdownRenderer and Helpers](https://github.com/Microsoft/UWPCommunityToolkit/blob/master/Microsoft.Toolkit.Uwp.UI.Controls/MarkdownTextBlock/Render)
* [Sample App custom markdown renderer](https://github.com/Microsoft/UWPCommunityToolkit/blob/master/Microsoft.Toolkit.Uwp.SampleApp/Controls/SampleAppMarkdownRenderer)

## Example Code
## Sample Code

[MarkdownTextBlock Sample Page](https://github.com/Microsoft/UWPCommunityToolkit/tree/master/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/MarkdownTextBlock)
[MarkdownTextBlock Sample Page Source](https://github.com/Microsoft/UWPCommunityToolkit/tree/master/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/MarkdownTextBlock). You can see this in action in [UWP Community Toolkit Sample App](https://www.microsoft.com/store/apps/9NBLGGH4TLCQ).

## Default Template

[MarkdownTextBlock XAML File](https://github.com/Microsoft/UWPCommunityToolkit/blob/master/Microsoft.Toolkit.Uwp.UI.Controls/MarkdownTextBlock/MarkdownTextBlock.xaml) is the XAML template used in the toolkit for the default styling.

## Requirements (Windows 10 Device Family)
## Requirements

| [Device family](http://go.microsoft.com/fwlink/p/?LinkID=526370) | Universal, 10.0.14393.0 or higher |
| Device family | Universal, 10.0.14393.0 or higher |
| --- | --- |
| Namespace | Microsoft.Toolkit.Uwp.UI.Controls |
| NuGet package | [Microsoft.Toolkit.Uwp.UI.Controls](https://www.nuget.org/packages/Microsoft.Toolkit.Uwp.UI.Controls/) |

## API

* [MarkdownTextBlock source code](https://github.com/Microsoft/UWPCommunityToolkit/tree/master/Microsoft.Toolkit.Uwp.UI.Controls/MarkdownTextBlock)
* [Markdown Parser source code](https://github.com/Microsoft/UWPCommunityToolkit/tree/master/Microsoft.Toolkit.Parsers/Markdown)

## Related Topics

* [Markdown Parser](https://docs.microsoft.com/en-us/windows/uwpcommunitytoolkit/parsers/markdownparser)

0 comments on commit c0f0aef

Please sign in to comment.