-
Notifications
You must be signed in to change notification settings - Fork 79
More markdowntextblock improvements #771
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 14 commits
Commits
Show all changes
28 commits
Select commit
Hold shift + click to select a range
d70af24
Adding cornerradius to image
niels9001 86ce5b6
Making images scale accordingly
niels9001 0de08e8
Merge branch 'main' into niels9001/mdtb-improvements
niels9001 0811436
Apply suggestion from @Copilot
Arlodotexe 4cc9798
Add ImageProviderConstraintTest to verify theme constraint behavior
Arlodotexe db7cf00
Remove references about image corner radius
niels9001 acb4755
Update MyImage.cs
niels9001 bcfe6c6
XAML styling
niels9001 c7fd09a
Fix image scaling
niels9001 ffcd8ff
Add visual sample for image rescaling
niels9001 563f4b4
Update components/MarkdownTextBlock/tests/ImageProviderConstraintTest.cs
niels9001 9e35244
Update MarkdownTextBlockImageProviderSample.xaml
niels9001 ce2ce57
Merge branch 'niels9001/mdtb-improvements' of https://github.com/Comm…
niels9001 a2bfa7e
Update MarkdownTextBlockImageProviderSample.xaml.cs
niels9001 4501564
Update MarkdownTextBlockImageProviderSample.xaml.cs
niels9001 ad46d46
Change image tag in tests
niels9001 f93803e
Update MarkdownTextBlock.md
niels9001 b4fab55
Update MarkdownTextBlockImageProviderSample.xaml.cs
niels9001 4492759
Update MarkdownTextBlockImageProviderSample.xaml.cs
niels9001 079b375
Update MarkdownTextBlockImageProviderSample.xaml.cs
niels9001 cabc532
Revert "Change image tag in tests"
Arlodotexe 44e39a4
Use html syntax for specifying custom image size inline where appropr…
Arlodotexe 5a1d4f7
Fix async void issues
Arlodotexe b5c2514
Removing sample
niels9001 585d3d0
Updating more default values
niels9001 ca31fca
Update MarkdownThemes.cs
niels9001 277fd43
Update MarkdownThemes.cs
niels9001 cd6f78c
Merge branch 'main' into niels9001/mdtb-improvements
niels9001 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
138 changes: 138 additions & 0 deletions
138
components/MarkdownTextBlock/samples/MarkdownTextBlockImageProviderSample.xaml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,138 @@ | ||
| <?xml version="1.0" encoding="utf-8" ?> | ||
| <!-- Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license. See the LICENSE file in the project root for more information. --> | ||
| <Page x:Class="MarkdownTextBlockExperiment.Samples.MarkdownTextBlockImageProviderSample" | ||
| xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | ||
| xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | ||
| xmlns:controls="using:CommunityToolkit.WinUI.Controls" | ||
| xmlns:d="http://schemas.microsoft.com/expression/blend/2008" | ||
| xmlns:local="using:MarkdownTextBlockExperiment.Samples" | ||
| xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" | ||
| xmlns:muxc="using:Microsoft.UI.Xaml.Controls" | ||
| mc:Ignorable="d"> | ||
|
|
||
| <ScrollViewer> | ||
| <StackPanel Padding="16" | ||
| Spacing="16"> | ||
| <TextBlock Style="{StaticResource TitleTextBlockStyle}" | ||
| Text="IImageProvider Constraint Testing" /> | ||
|
|
||
| <TextBlock Foreground="{ThemeResource SystemFillColorCautionBrush}" | ||
| TextWrapping="Wrap"> | ||
| This sample demonstrates image constraint behavior when using a custom IImageProvider. | ||
| The test provider returns images with specific dimensions to verify constraint logic. | ||
| </TextBlock> | ||
|
|
||
| <Border Padding="16" | ||
| Background="{ThemeResource CardBackgroundFillColorDefaultBrush}" | ||
| CornerRadius="8"> | ||
| <StackPanel Spacing="12"> | ||
| <TextBlock FontWeight="SemiBold" | ||
| Text="Test Configuration" /> | ||
|
|
||
| <StackPanel Orientation="Horizontal" | ||
| Spacing="16"> | ||
| <StackPanel Spacing="4"> | ||
| <TextBlock FontSize="12" | ||
| Text="Provider Image Width:" /> | ||
| <muxc:NumberBox x:Name="ProviderWidthBox" | ||
| Maximum="2000" | ||
| Minimum="10" | ||
| SpinButtonPlacementMode="Compact" | ||
| Value="100" /> | ||
| </StackPanel> | ||
| <StackPanel Spacing="4"> | ||
| <TextBlock FontSize="12" | ||
| Text="Provider Image Height:" /> | ||
| <muxc:NumberBox x:Name="ProviderHeightBox" | ||
| Maximum="2000" | ||
| Minimum="10" | ||
| SpinButtonPlacementMode="Compact" | ||
| Value="100" /> | ||
| </StackPanel> | ||
| </StackPanel> | ||
|
|
||
| <StackPanel Orientation="Horizontal" | ||
| Spacing="16"> | ||
| <StackPanel Spacing="4"> | ||
| <TextBlock FontSize="12" | ||
| Text="Theme MaxWidth:" /> | ||
| <muxc:NumberBox x:Name="ThemeMaxWidthBox" | ||
| Maximum="2000" | ||
| Minimum="0" | ||
| SpinButtonPlacementMode="Compact" | ||
| Value="500" /> | ||
| </StackPanel> | ||
| <StackPanel Spacing="4"> | ||
| <TextBlock FontSize="12" | ||
| Text="Theme MaxHeight:" /> | ||
| <muxc:NumberBox x:Name="ThemeMaxHeightBox" | ||
| Maximum="2000" | ||
| Minimum="0" | ||
| SpinButtonPlacementMode="Compact" | ||
| Value="500" /> | ||
| </StackPanel> | ||
| </StackPanel> | ||
|
|
||
| <Button x:Name="ApplyButton" | ||
| Click="ApplyButton_Click" | ||
| Content="Apply and Test" /> | ||
| </StackPanel> | ||
| </Border> | ||
|
|
||
| <!-- Test Cases --> | ||
| <Border Padding="16" | ||
| Background="{ThemeResource CardBackgroundFillColorDefaultBrush}" | ||
| CornerRadius="8"> | ||
| <StackPanel Spacing="12"> | ||
| <TextBlock FontWeight="SemiBold" | ||
| Text="Test Case 1: Small image, larger theme constraint" /> | ||
| <TextBlock FontSize="12" | ||
| Foreground="{ThemeResource SystemFillColorNeutralBrush}" | ||
| TextWrapping="Wrap"> | ||
| Expected: Image should maintain natural size (100x100), NOT be enlarged to theme constraint (500x500). | ||
| </TextBlock> | ||
| <controls:MarkdownTextBlock x:Name="TestCase1" | ||
| Text="" /> | ||
| <TextBlock x:Name="TestCase1Result" | ||
| FontSize="12" /> | ||
| </StackPanel> | ||
| </Border> | ||
|
|
||
| <Border Padding="16" | ||
| Background="{ThemeResource CardBackgroundFillColorDefaultBrush}" | ||
| CornerRadius="8"> | ||
| <StackPanel Spacing="12"> | ||
| <TextBlock FontWeight="SemiBold" | ||
| Text="Test Case 2: Large image (800x600), smaller theme constraint (400)" /> | ||
| <TextBlock FontSize="12" | ||
| Foreground="{ThemeResource SystemFillColorNeutralBrush}" | ||
| TextWrapping="Wrap"> | ||
| Expected: Image should be constrained to theme MaxWidth (400), not remain at 800. | ||
| </TextBlock> | ||
| <controls:MarkdownTextBlock x:Name="TestCase2" | ||
| Text="" /> | ||
| <TextBlock x:Name="TestCase2Result" | ||
| FontSize="12" /> | ||
| </StackPanel> | ||
| </Border> | ||
|
|
||
| <Border Padding="16" | ||
| Background="{ThemeResource CardBackgroundFillColorDefaultBrush}" | ||
| CornerRadius="8"> | ||
| <StackPanel Spacing="12"> | ||
| <TextBlock FontWeight="SemiBold" | ||
| Text="Test Case 3: Image with no provider dimensions (unknown natural size)" /> | ||
| <TextBlock FontSize="12" | ||
| Foreground="{ThemeResource SystemFillColorNeutralBrush}" | ||
| TextWrapping="Wrap"> | ||
| Expected: Image should NOT have theme constraints applied (MaxWidth should be Infinity), since we don't know if constraints would enlarge it. | ||
| </TextBlock> | ||
| <controls:MarkdownTextBlock x:Name="TestCase3" | ||
| Text="" /> | ||
| <TextBlock x:Name="TestCase3Result" | ||
| FontSize="12" /> | ||
| </StackPanel> | ||
| </Border> | ||
| </StackPanel> | ||
| </ScrollViewer> | ||
| </Page> |
236 changes: 236 additions & 0 deletions
236
components/MarkdownTextBlock/samples/MarkdownTextBlockImageProviderSample.xaml.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,236 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
| // See the LICENSE file in the project root for more information. | ||
|
|
||
| using CommunityToolkit.WinUI.Controls; | ||
|
|
||
| #if WINAPPSDK | ||
| using Microsoft.UI.Xaml.Media.Imaging; | ||
| #elif WINDOWS_UWP | ||
| using Windows.UI.Xaml.Media.Imaging; | ||
| #endif | ||
| using Windows.Storage.Streams; | ||
|
|
||
| namespace MarkdownTextBlockExperiment.Samples; | ||
|
|
||
| /// <summary> | ||
| /// Sample demonstrating IImageProvider constraint behavior for manual testing. | ||
| /// This helps verify the fix for: https://github.com/CommunityToolkit/Labs-Windows/pull/771 | ||
| /// | ||
| /// Bug description: | ||
| /// When using IImageProvider, if natural dimensions weren't set on the image, | ||
| /// theme constraints would incorrectly be applied, potentially enlarging small images. | ||
| /// </summary> | ||
| [ToolkitSample(id: nameof(MarkdownTextBlockImageProviderSample), "Image Provider Constraints", | ||
| description: "Manual test for IImageProvider constraint behavior - verifies small images aren't enlarged")] | ||
| public sealed partial class MarkdownTextBlockImageProviderSample : Page | ||
| { | ||
| public MarkdownTextBlockImageProviderSample() | ||
| { | ||
| this.InitializeComponent(); | ||
| this.Loaded += OnLoaded; | ||
| } | ||
|
|
||
| private void OnLoaded(object sender, RoutedEventArgs e) | ||
| { | ||
| ApplyConfiguration(); | ||
| } | ||
|
|
||
| private void ApplyButton_Click(object sender, RoutedEventArgs e) | ||
| { | ||
| ApplyConfiguration(); | ||
| } | ||
|
|
||
| private void ApplyConfiguration() | ||
| { | ||
| var providerWidth = ProviderWidthBox.Value; | ||
| var providerHeight = ProviderHeightBox.Value; | ||
| var themeMaxWidth = ThemeMaxWidthBox.Value; | ||
| var themeMaxHeight = ThemeMaxHeightBox.Value; | ||
|
|
||
| // Test Case 1: Small image with larger theme constraint | ||
| // The image should NOT be enlarged | ||
| var provider1 = new TestImageProvider(providerWidth, providerHeight); | ||
| var config1 = new MarkdownConfig | ||
| { | ||
| ImageProvider = provider1, | ||
| Themes = new MarkdownThemes | ||
| { | ||
| ImageMaxWidth = themeMaxWidth, | ||
| ImageMaxHeight = themeMaxHeight | ||
| } | ||
| }; | ||
| TestCase1.Config = config1; | ||
| TestCase1.Text = ""; | ||
| TestCase1.Text = ""; | ||
|
|
||
| // Test Case 2: Large image with smaller theme constraint | ||
| // The image SHOULD be constrained | ||
| var provider2 = new TestImageProvider(800, 600); | ||
| var config2 = new MarkdownConfig | ||
| { | ||
| ImageProvider = provider2, | ||
| Themes = new MarkdownThemes | ||
| { | ||
| ImageMaxWidth = 400, | ||
| ImageMaxHeight = 400 | ||
| } | ||
| }; | ||
| TestCase2.Config = config2; | ||
| TestCase2.Text = ""; | ||
| TestCase2.Text = ""; | ||
|
|
||
| // Test Case 3: Image with no dimensions from provider | ||
| // Theme constraints should NOT be applied | ||
| var provider3 = new TestImageProvider(0, 0); | ||
| var config3 = new MarkdownConfig | ||
| { | ||
| ImageProvider = provider3, | ||
| Themes = new MarkdownThemes | ||
| { | ||
| ImageMaxWidth = themeMaxWidth, | ||
| ImageMaxHeight = themeMaxHeight | ||
| } | ||
| }; | ||
| TestCase3.Config = config3; | ||
| TestCase3.Text = ""; | ||
| TestCase3.Text = ""; | ||
|
|
||
| // Give time for images to load, then check results | ||
| DispatcherQueue.TryEnqueue(async () => | ||
| { | ||
| await Task.Delay(1000); | ||
| UpdateResults(); | ||
| }); | ||
| } | ||
|
|
||
| private void UpdateResults() | ||
| { | ||
| var image1 = FindImage(TestCase1); | ||
| var image2 = FindImage(TestCase2); | ||
| var image3 = FindImage(TestCase3); | ||
|
|
||
| var providerWidth = ProviderWidthBox.Value; | ||
| var themeMaxWidth = ThemeMaxWidthBox.Value; | ||
|
|
||
| // Test Case 1: Small image should stay small | ||
| if (image1 != null) | ||
| { | ||
| var pass = Math.Abs(image1.MaxWidth - providerWidth) < 1; | ||
| TestCase1Result.Text = $"MaxWidth: {image1.MaxWidth:F0} (Expected: {providerWidth}) - {(pass ? "✅ PASS" : "❌ FAIL - Image incorrectly enlarged!")}"; | ||
| TestCase1Result.Foreground = new SolidColorBrush(pass ? Microsoft.UI.Colors.Green : Microsoft.UI.Colors.Red); | ||
| } | ||
| else | ||
| { | ||
| TestCase1Result.Text = "❌ Image not found"; | ||
| TestCase1Result.Foreground = new SolidColorBrush(Microsoft.UI.Colors.Red); | ||
| } | ||
|
|
||
| // Test Case 2: Large image should be constrained to 400 | ||
| if (image2 != null) | ||
| { | ||
| var pass = Math.Abs(image2.MaxWidth - 400) < 1; | ||
| TestCase2Result.Text = $"MaxWidth: {image2.MaxWidth:F0} (Expected: 400) - {(pass ? "✅ PASS" : "❌ FAIL")}"; | ||
| TestCase2Result.Foreground = new SolidColorBrush(pass ? Microsoft.UI.Colors.Green : Microsoft.UI.Colors.Red); | ||
| } | ||
| else | ||
| { | ||
| TestCase2Result.Text = "❌ Image not found"; | ||
| TestCase2Result.Foreground = new SolidColorBrush(Microsoft.UI.Colors.Red); | ||
| } | ||
|
|
||
| // Test Case 3: No dimensions - theme should NOT be applied | ||
| if (image3 != null) | ||
| { | ||
| var pass = double.IsInfinity(image3.MaxWidth); | ||
| TestCase3Result.Text = $"MaxWidth: {image3.MaxWidth} (Expected: Infinity) - {(pass ? "✅ PASS" : "❌ FAIL - Theme incorrectly applied!")}"; | ||
| TestCase3Result.Foreground = new SolidColorBrush(pass ? Microsoft.UI.Colors.Green : Microsoft.UI.Colors.Red); | ||
| } | ||
| else | ||
| { | ||
| TestCase3Result.Text = "❌ Image not found"; | ||
| TestCase3Result.Foreground = new SolidColorBrush(Microsoft.UI.Colors.Red); | ||
| } | ||
| } | ||
|
|
||
| private Image? FindImage(MarkdownTextBlock markdown) | ||
| { | ||
| return FindDescendant<Image>(markdown); | ||
| } | ||
|
|
||
| private T? FindDescendant<T>(DependencyObject parent) where T : DependencyObject | ||
| { | ||
| var count = VisualTreeHelper.GetChildrenCount(parent); | ||
| for (int i = 0; i < count; i++) | ||
| { | ||
| var child = VisualTreeHelper.GetChild(parent, i); | ||
| if (child is T result) | ||
| return result; | ||
| var descendant = FindDescendant<T>(child); | ||
| if (descendant != null) | ||
| return descendant; | ||
| } | ||
| return null; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Test image provider that downloads a real image but reports configurable dimensions. | ||
| /// This simulates providers that know the image size vs those that don't. | ||
| /// </summary> | ||
| private class TestImageProvider : IImageProvider | ||
| { | ||
| private readonly double _width; | ||
| private readonly double _height; | ||
| private readonly string _realImageUrl; | ||
|
|
||
| // Use a small, fast-loading image | ||
| private const string DefaultImageUrl = "https://devblogs.microsoft.com/commandline/wp-content/uploads/sites/33/2025/09/ShortcutConflict.png"; | ||
|
|
||
| public TestImageProvider(double width, double height, string? realImageUrl = null) | ||
| { | ||
| _width = width; | ||
| _height = height; | ||
| _realImageUrl = realImageUrl ?? DefaultImageUrl; | ||
| } | ||
|
|
||
| public bool ShouldUseThisProvider(string url) => url.Contains("provider://"); | ||
|
|
||
| public async Task<Image> GetImage(string url) | ||
| { | ||
| var image = new Image(); | ||
|
|
||
| // Load a real image so it's visible | ||
| try | ||
| { | ||
| var httpClient = new System.Net.Http.HttpClient(); | ||
niels9001 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| var data = await httpClient.GetByteArrayAsync(_realImageUrl); | ||
| var bitmap = new BitmapImage(); | ||
Arlodotexe marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| using (var stream = new InMemoryRandomAccessStream()) | ||
| { | ||
| await stream.WriteAsync(data.AsBuffer()); | ||
| stream.Seek(0); | ||
| await bitmap.SetSourceAsync(stream); | ||
| } | ||
| image.Source = bitmap; | ||
| } | ||
| catch | ||
| { | ||
| // Fallback to empty bitmap if download fails | ||
| image.Source = new BitmapImage(); | ||
Arlodotexe marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| // Only set dimensions if they're valid | ||
| // This simulates providers that may or may not know the image size | ||
| if (_width > 0) | ||
| { | ||
| image.Width = _width; | ||
| } | ||
| if (_height > 0) | ||
| { | ||
| image.Height = _height; | ||
| } | ||
|
|
||
| return image; | ||
| } | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.