Skip to content

Commit 0a28ddc

Browse files
authored
Handle XamlWriter.Save failing on non-public internal types (#780)
When WPF's XamlWriter.Save encounters non-public types like System.Windows.Baml2006.KeyRecord (common in .NET 11 with merged ResourceDictionaries and DataTemplates), it throws InvalidOperationException. Catch that in ToXamlString and fall back to producing only the PNG snapshot instead of crashing the test.
1 parent db3b399 commit 0a28ddc

9 files changed

Lines changed: 99 additions & 31 deletions

src/Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<PropertyGroup>
44
<LangVersion>preview</LangVersion>
55
<NoWarn>CS1591;CS0649;CS8632;CA1416;NU1608;NU1109</NoWarn>
6-
<Version>4.2.0</Version>
6+
<Version>4.2.1</Version>
77
<AssemblyVersion>1.0.0</AssemblyVersion>
88
<LangVersion>preview</LangVersion>
99
<PackageTags>Xaml, Wpf, Verify</PackageTags>

src/Directory.Packages.props

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,21 @@
44
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
55
</PropertyGroup>
66
<ItemGroup>
7-
<PackageVersion Include="MarkdownSnippets.MsBuild" Version="28.1.0" />
7+
<PackageVersion Include="MarkdownSnippets.MsBuild" Version="28.2.0" />
88
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.4.0" />
99
<PackageVersion Include="NUnit" Version="4.5.1" />
1010
<PackageVersion Include="NUnit3TestAdapter" Version="6.2.0" />
1111
<PackageVersion Include="ProjectDefaults" Version="1.0.172" />
1212
<PackageVersion Include="Shipwreck.Phash.Bitmaps" Version="0.5.0" />
1313
<PackageVersion Include="System.Drawing.Common" Version="10.0.6" />
14-
<PackageVersion Include="Verify" Version="31.15.0" />
14+
<PackageVersion Include="Verify" Version="31.16.1" />
1515
<PackageVersion Include="Verify.DiffPlex" Version="3.1.2" />
16-
<PackageVersion Include="Verify.NUnit" Version="31.15.0" />
16+
<PackageVersion Include="Verify.NUnit" Version="31.16.1" />
1717
<PackageVersion Include="Verify.Phash" Version="3.1.0" />
1818
<PackageVersion Include="Microsoft.Sbom.Targets" Version="4.1.5" />
1919
<PackageVersion Include="Argon" Version="0.33.5" />
20-
<PackageVersion Include="DiffEngine" Version="19.1.2" />
21-
<PackageVersion Include="EmptyFiles" Version="8.17.2" />
20+
<PackageVersion Include="DiffEngine" Version="19.1.3" />
21+
<PackageVersion Include="EmptyFiles" Version="8.18.1" />
2222
<PackageVersion Include="SimpleInfoName" Version="3.2.0" />
2323
</ItemGroup>
2424
</Project>

src/Tests/DataTemplateWindow.xaml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<Window x:Class="Tests.DataTemplateWindow"
2+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4+
Height="200"
5+
Width="300"
6+
Title="DataTemplateWindow">
7+
<Window.Resources>
8+
<ResourceDictionary>
9+
<ResourceDictionary.MergedDictionaries>
10+
<ResourceDictionary>
11+
<DataTemplate DataType="{x:Type TabItem}">
12+
<TextBlock Text="{Binding Header}" />
13+
</DataTemplate>
14+
</ResourceDictionary>
15+
</ResourceDictionary.MergedDictionaries>
16+
</ResourceDictionary>
17+
</Window.Resources>
18+
<TabControl>
19+
<TabItem Header="Tab1">
20+
<TextBlock Text="Content" />
21+
</TabItem>
22+
</TabControl>
23+
</Window>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using System.Windows;
2+
3+
namespace Tests;
4+
5+
public partial class DataTemplateWindow : Window
6+
{
7+
public DataTemplateWindow() =>
8+
InitializeComponent();
9+
}
1.45 KB
Loading
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<DataTemplateWindow
2+
Title="DataTemplateWindow"
3+
Width="300"
4+
Height="200"
5+
xmlns="clr-namespace:Tests;assembly=Tests"
6+
xmlns:av="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
7+
<av:TabControl>
8+
<av:TabItem
9+
IsSelected="True"
10+
Header="Tab1">
11+
<av:TextBlock
12+
Text="Content" />
13+
</av:TabItem>
14+
</av:TabControl>
15+
</DataTemplateWindow>

src/Tests/TheTests.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,10 @@ public async Task UserControl()
3737

3838
#endregion
3939

40+
[Test]
41+
public async Task DataTemplateWindow()
42+
{
43+
var window = new DataTemplateWindow();
44+
await Verify(window);
45+
}
4046
}

src/Verify.Xaml/VerifyXaml.cs

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System.Windows;
1+
using System.Windows;
22

33
using VerifyTests.Xaml;
44

@@ -25,21 +25,26 @@ public static void Initialize()
2525
static ConversionResult ElementToImage(FrameworkElement element, IReadOnlyDictionary<string, object> context)
2626
{
2727
var pngStream = WpfUtils.ScreenCapture(element);
28-
return new(
29-
null,
30-
[
31-
new("xml", element.ToXamlString()),
32-
new("png", pngStream)
33-
]);
28+
var xaml = element.ToXamlString();
29+
var targets = new List<Target> { new("png", pngStream) };
30+
if (xaml != null)
31+
{
32+
targets.Insert(0, new("xml", xaml));
33+
}
34+
35+
return new(null, targets);
3436
}
3537

3638
static ConversionResult WindowToImage(Window window, IReadOnlyDictionary<string, object> context)
3739
{
3840
var pngStream = WpfUtils.ScreenCapture(window);
39-
return new(null,
40-
[
41-
new("xml", window.ToXamlString()),
42-
new("png", pngStream)
43-
]);
41+
var xaml = window.ToXamlString();
42+
var targets = new List<Target> { new("png", pngStream) };
43+
if (xaml != null)
44+
{
45+
targets.Insert(0, new("xml", xaml));
46+
}
47+
48+
return new(null, targets);
4449
}
45-
}
50+
}

src/Verify.Xaml/WpfUtils.cs

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,23 +19,33 @@ static class WpfUtils
1919
NewLineOnAttributes = true,
2020
};
2121

22-
public static string ToXamlString(this FrameworkElement element)
22+
public static string? ToXamlString(this FrameworkElement element)
2323
{
24-
var document = new XDocument();
25-
using (var writerToDoc = document.CreateWriter())
24+
try
2625
{
27-
XamlWriter.Save(element, writerToDoc);
28-
}
26+
var document = new XDocument();
27+
using (var writerToDoc = document.CreateWriter())
28+
{
29+
XamlWriter.Save(element, writerToDoc);
30+
}
2931

30-
Purge(document);
32+
Purge(document);
3133

32-
var builder = new StringBuilder();
33-
using (var writerToString = XmlWriter.Create(builder, writerSettings))
34+
var builder = new StringBuilder();
35+
using (var writerToString = XmlWriter.Create(builder, writerSettings))
36+
{
37+
document.WriteTo(writerToString);
38+
}
39+
40+
return builder.ToString();
41+
}
42+
catch (InvalidOperationException)
3443
{
35-
document.WriteTo(writerToString);
44+
// XamlWriter.Save cannot serialize non-public types that may appear
45+
// in the visual tree (e.g. System.Windows.Baml2006.KeyRecord).
46+
// Return null so callers can still produce the PNG snapshot.
47+
return null;
3648
}
37-
38-
return builder.ToString();
3949
}
4050

4151
public static Stream ScreenCapture(FrameworkElement element)
@@ -99,4 +109,4 @@ static void Purge(XDocument document)
99109
}
100110
}
101111
}
102-
}
112+
}

0 commit comments

Comments
 (0)