Browse Source

Merge pull request #9577 from AvaloniaUI/merge-resources

Implement compile time MergedDictionaties
pull/9682/head
Max Katz 4 years ago
committed by GitHub
parent
commit
1c796873be
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorPicker.xaml
  2. 10
      src/Avalonia.Controls.ColorPicker/Themes/Fluent/Fluent.xaml
  3. 4
      src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorPicker.xaml
  4. 10
      src/Avalonia.Controls.ColorPicker/Themes/Simple/Simple.xaml
  5. 1
      src/Avalonia.Themes.Fluent/Accents/Base.xaml
  6. 2
      src/Avalonia.Themes.Fluent/Controls/Button.xaml
  7. 131
      src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml
  8. 2
      src/Avalonia.Themes.Fluent/Controls/RepeatButton.xaml
  9. 2
      src/Avalonia.Themes.Fluent/Controls/ToggleButton.xaml
  10. 4
      src/Avalonia.Themes.Fluent/FluentTheme.xaml
  11. 8
      src/Avalonia.Themes.Fluent/FluentTheme.xaml.cs
  12. 128
      src/Avalonia.Themes.Simple/Controls/SimpleControls.xaml
  13. 2
      src/Avalonia.Themes.Simple/SimpleTheme.xaml
  14. 4
      src/Avalonia.Themes.Simple/SimpleTheme.xaml.cs
  15. 4
      src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaRuntimeXamlLoader.cs
  16. 1
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs
  17. 2
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/IXamlAstGroupTransformer.cs
  18. 75
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlIncludeGroupTransformer.cs
  19. 113
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlMergeResourceGroupTransformer.cs
  20. 2
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
  21. 5
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlDocumentParseException.cs
  22. 1
      src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
  23. 26
      src/Markup/Avalonia.Markup.Xaml/Styling/MergeResourceInclude.cs
  24. 4
      src/Markup/Avalonia.Markup.Xaml/Styling/ResourceInclude.cs
  25. 4
      src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs
  26. 22
      tests/Avalonia.Benchmarks/Themes/ThemeBenchmark.cs
  27. 124
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/MergeResourceIncludeTests.cs

4
src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorPicker.xaml

@ -3,10 +3,6 @@
xmlns:controls="using:Avalonia.Controls" xmlns:controls="using:Avalonia.Controls"
xmlns:primitives="using:Avalonia.Controls.Primitives"> xmlns:primitives="using:Avalonia.Controls.Primitives">
<ResourceDictionary.MergedDictionaries>
<ResourceInclude Source="avares://Avalonia.Controls.ColorPicker/Themes/Fluent/ColorView.xaml" />
</ResourceDictionary.MergedDictionaries>
<ControlTheme x:Key="{x:Type ColorPicker}" <ControlTheme x:Key="{x:Type ColorPicker}"
TargetType="ColorPicker"> TargetType="ColorPicker">
<Setter Property="CornerRadius" Value="{DynamicResource ControlCornerRadius}" /> <Setter Property="CornerRadius" Value="{DynamicResource ControlCornerRadius}" />

10
src/Avalonia.Controls.ColorPicker/Themes/Fluent/Fluent.xaml

@ -50,13 +50,13 @@
<ResourceDictionary.MergedDictionaries> <ResourceDictionary.MergedDictionaries>
<!-- Primitives --> <!-- Primitives -->
<ResourceInclude Source="avares://Avalonia.Controls.ColorPicker/Themes/Fluent/ColorPreviewer.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Controls.ColorPicker/Themes/Fluent/ColorPreviewer.xaml" />
<ResourceInclude Source="avares://Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSlider.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSlider.xaml" />
<ResourceInclude Source="avares://Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSpectrum.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSpectrum.xaml" />
<!-- Controls --> <!-- Controls -->
<ResourceInclude Source="avares://Avalonia.Controls.ColorPicker/Themes/Fluent/ColorPicker.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Controls.ColorPicker/Themes/Fluent/ColorPicker.xaml" />
<ResourceInclude Source="avares://Avalonia.Controls.ColorPicker/Themes/Fluent/ColorView.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Controls.ColorPicker/Themes/Fluent/ColorView.xaml" />
</ResourceDictionary.MergedDictionaries> </ResourceDictionary.MergedDictionaries>
</ResourceDictionary> </ResourceDictionary>

4
src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorPicker.xaml

@ -3,10 +3,6 @@
xmlns:controls="using:Avalonia.Controls" xmlns:controls="using:Avalonia.Controls"
xmlns:primitives="using:Avalonia.Controls.Primitives"> xmlns:primitives="using:Avalonia.Controls.Primitives">
<ResourceDictionary.MergedDictionaries>
<ResourceInclude Source="avares://Avalonia.Controls.ColorPicker/Themes/Simple/ColorView.xaml" />
</ResourceDictionary.MergedDictionaries>
<ControlTheme x:Key="{x:Type ColorPicker}" <ControlTheme x:Key="{x:Type ColorPicker}"
TargetType="ColorPicker"> TargetType="ColorPicker">
<Setter Property="CornerRadius" Value="0" /> <Setter Property="CornerRadius" Value="0" />

10
src/Avalonia.Controls.ColorPicker/Themes/Simple/Simple.xaml

@ -50,13 +50,13 @@
<ResourceDictionary.MergedDictionaries> <ResourceDictionary.MergedDictionaries>
<!-- Primitives --> <!-- Primitives -->
<ResourceInclude Source="avares://Avalonia.Controls.ColorPicker/Themes/Simple/ColorPreviewer.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Controls.ColorPicker/Themes/Simple/ColorPreviewer.xaml" />
<ResourceInclude Source="avares://Avalonia.Controls.ColorPicker/Themes/Simple/ColorSlider.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Controls.ColorPicker/Themes/Simple/ColorSlider.xaml" />
<ResourceInclude Source="avares://Avalonia.Controls.ColorPicker/Themes/Simple/ColorSpectrum.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Controls.ColorPicker/Themes/Simple/ColorSpectrum.xaml" />
<!-- Controls --> <!-- Controls -->
<ResourceInclude Source="avares://Avalonia.Controls.ColorPicker/Themes/Simple/ColorPicker.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Controls.ColorPicker/Themes/Simple/ColorPicker.xaml" />
<ResourceInclude Source="avares://Avalonia.Controls.ColorPicker/Themes/Simple/ColorView.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Controls.ColorPicker/Themes/Simple/ColorView.xaml" />
</ResourceDictionary.MergedDictionaries> </ResourceDictionary.MergedDictionaries>
</ResourceDictionary> </ResourceDictionary>

1
src/Avalonia.Themes.Fluent/Accents/Base.xaml

@ -22,6 +22,7 @@
<Thickness x:Key="TextControlThemePadding">10,6,6,5</Thickness> <Thickness x:Key="TextControlThemePadding">10,6,6,5</Thickness>
<sys:Double x:Key="IconElementThemeHeight">20</sys:Double> <sys:Double x:Key="IconElementThemeHeight">20</sys:Double>
<sys:Double x:Key="IconElementThemeWidth">20</sys:Double> <sys:Double x:Key="IconElementThemeWidth">20</sys:Double>
<Thickness x:Key="ButtonPadding">8,5,8,6</Thickness>
<!-- Override system shape defaults --> <!-- Override system shape defaults -->
<CornerRadius x:Key="ControlCornerRadius">3</CornerRadius> <CornerRadius x:Key="ControlCornerRadius">3</CornerRadius>

2
src/Avalonia.Themes.Fluent/Controls/Button.xaml

@ -8,8 +8,6 @@
</StackPanel> </StackPanel>
</Border> </Border>
</Design.PreviewWith> </Design.PreviewWith>
<Thickness x:Key="ButtonPadding">8,5,8,6</Thickness>
<ControlTheme x:Key="{x:Type Button}" TargetType="Button"> <ControlTheme x:Key="{x:Type Button}" TargetType="Button">
<Setter Property="Background" Value="{DynamicResource ButtonBackground}" /> <Setter Property="Background" Value="{DynamicResource ButtonBackground}" />

131
src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml

@ -4,72 +4,71 @@
<Styles.Resources> <Styles.Resources>
<ResourceDictionary> <ResourceDictionary>
<ResourceDictionary.MergedDictionaries> <ResourceDictionary.MergedDictionaries>
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/Button.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/Button.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/RadioButton.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/RadioButton.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/Expander.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/Expander.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/ProgressBar.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/ProgressBar.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/AutoCompleteBox.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/AutoCompleteBox.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/Calendar.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/Calendar.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/CalendarButton.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/CalendarButton.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/CalendarDayButton.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/CalendarDayButton.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/CalendarItem.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/CalendarItem.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/CaptionButtons.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/CaptionButtons.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/Carousel.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/Carousel.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/CheckBox.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/CheckBox.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/FlyoutPresenter.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/FlyoutPresenter.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/GridSplitter.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/GridSplitter.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/ItemsControl.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/ItemsControl.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/Label.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/Label.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/ListBox.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/ListBox.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/ListBoxItem.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/ListBoxItem.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/MenuScrollViewer.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/MenuScrollViewer.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/Menu.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/Menu.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/MenuItem.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/MenuItem.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/MenuFlyoutPresenter.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/MenuFlyoutPresenter.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/NotificationCard.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/NotificationCard.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/NumericUpDown.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/NumericUpDown.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/OverlayPopupHost.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/OverlayPopupHost.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/PopupRoot.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/PopupRoot.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/PathIcon.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/PathIcon.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/RepeatButton.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/RepeatButton.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/ScrollBar.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/ScrollBar.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/ScrollViewer.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/ScrollViewer.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/Separator.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/Separator.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/SplitButton.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/SplitButton.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/SplitView.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/SplitView.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/TabControl.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/TabControl.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/TabItem.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/TabItem.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/TabStrip.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/TabStrip.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/TabStripItem.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/TabStripItem.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/TextBox.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/TextBox.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/ToggleButton.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/ToggleButton.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/ToggleSwitch.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/ToggleSwitch.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/ToolTip.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/ToolTip.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/TitleBar.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/TitleBar.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/TransitioningContentControl.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/TransitioningContentControl.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/TreeView.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/TreeView.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/TreeViewItem.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/TreeViewItem.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/WindowNotificationManager.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/WindowNotificationManager.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/Window.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/Window.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/ComboBox.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/ComboBox.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/ComboBoxItem.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/ComboBoxItem.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/ContentControl.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/ContentControl.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/ContextMenu.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/ContextMenu.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/DataValidationErrors.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/DataValidationErrors.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/DateTimePickerShared.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/DateTimePickerShared.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/DatePicker.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/DatePicker.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/TimePicker.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/TimePicker.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/DropDownButton.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/DropDownButton.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/EmbeddableControlRoot.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/EmbeddableControlRoot.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/Slider.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/Slider.xaml" />
<!-- ManagedFileChooser comes last because it uses (and overrides) styles for a multitude of other controls...the dialogs were originally UserControls, after all --> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/ManagedFileChooser.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/ManagedFileChooser.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/SelectableTextBlock.xaml"/>
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/SelectableTextBlock.xaml"/> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/RefreshContainer.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/RefreshContainer.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/RefreshVisualizer.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Fluent/Controls/RefreshVisualizer.xaml" />
</ResourceDictionary.MergedDictionaries> </ResourceDictionary.MergedDictionaries>
</ResourceDictionary> </ResourceDictionary>
</Styles.Resources> </Styles.Resources>

2
src/Avalonia.Themes.Fluent/Controls/RepeatButton.xaml

@ -10,8 +10,6 @@
</Border> </Border>
</Design.PreviewWith> </Design.PreviewWith>
<Thickness x:Key="ButtonPadding">8,5,8,6</Thickness>
<ControlTheme x:Key="{x:Type RepeatButton}" TargetType="RepeatButton"> <ControlTheme x:Key="{x:Type RepeatButton}" TargetType="RepeatButton">
<Setter Property="Background" Value="{DynamicResource RepeatButtonBackground}" /> <Setter Property="Background" Value="{DynamicResource RepeatButtonBackground}" />
<Setter Property="Foreground" Value="{DynamicResource RepeatButtonForeground}" /> <Setter Property="Foreground" Value="{DynamicResource RepeatButtonForeground}" />

2
src/Avalonia.Themes.Fluent/Controls/ToggleButton.xaml

@ -10,8 +10,6 @@
</Border> </Border>
</Design.PreviewWith> </Design.PreviewWith>
<Thickness x:Key="ButtonPadding">8,5,8,6</Thickness>
<ControlTheme x:Key="{x:Type ToggleButton}" TargetType="ToggleButton"> <ControlTheme x:Key="{x:Type ToggleButton}" TargetType="ToggleButton">
<Setter Property="Background" Value="{DynamicResource ToggleButtonBackground}" /> <Setter Property="Background" Value="{DynamicResource ToggleButtonBackground}" />
<Setter Property="Foreground" Value="{DynamicResource ToggleButtonForeground}" /> <Setter Property="Foreground" Value="{DynamicResource ToggleButtonForeground}" />

4
src/Avalonia.Themes.Fluent/FluentTheme.xaml

@ -4,8 +4,8 @@
<Styles.Resources> <Styles.Resources>
<ResourceDictionary> <ResourceDictionary>
<ResourceDictionary.MergedDictionaries> <ResourceDictionary.MergedDictionaries>
<ResourceInclude Source="/Accents/AccentColors.xaml" /> <MergeResourceInclude Source="/Accents/AccentColors.xaml" />
<ResourceInclude Source="/Accents/Base.xaml" /> <MergeResourceInclude Source="/Accents/Base.xaml" />
</ResourceDictionary.MergedDictionaries> </ResourceDictionary.MergedDictionaries>
<!-- These are not part of MergedDictionaries so we can add or remove them easier --> <!-- These are not part of MergedDictionaries so we can add or remove them easier -->

8
src/Avalonia.Themes.Fluent/FluentTheme.xaml.cs

@ -99,15 +99,15 @@ namespace Avalonia.Themes.Fluent
var themeVariantResource1 = Mode == FluentThemeMode.Dark ? _baseDark : _baseLight; var themeVariantResource1 = Mode == FluentThemeMode.Dark ? _baseDark : _baseLight;
var themeVariantResource2 = Mode == FluentThemeMode.Dark ? _fluentDark : _fluentLight; var themeVariantResource2 = Mode == FluentThemeMode.Dark ? _fluentDark : _fluentLight;
var dict = Resources.MergedDictionaries; var dict = Resources.MergedDictionaries;
if (dict.Count == 2) if (dict.Count == 0)
{ {
dict.Insert(1, themeVariantResource1); dict.Add(themeVariantResource1);
dict.Add(themeVariantResource2); dict.Add(themeVariantResource2);
} }
else else
{ {
dict[1] = themeVariantResource1; dict[0] = themeVariantResource1;
dict[3] = themeVariantResource2; dict[1] = themeVariantResource2;
} }
} }

128
src/Avalonia.Themes.Simple/Controls/SimpleControls.xaml

@ -3,70 +3,70 @@
<Styles.Resources> <Styles.Resources>
<ResourceDictionary> <ResourceDictionary>
<ResourceDictionary.MergedDictionaries> <ResourceDictionary.MergedDictionaries>
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/Button.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/Button.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/RadioButton.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/RadioButton.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/Expander.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/Expander.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/RepeatButton.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/RepeatButton.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/ToggleSwitch.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/ToggleSwitch.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/ButtonSpinner.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/ButtonSpinner.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/ToggleButton.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/ToggleButton.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/DropDownButton.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/DropDownButton.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/PathIcon.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/PathIcon.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/CheckBox.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/CheckBox.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/ToolTip.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/ToolTip.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/Label.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/Label.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/ComboBoxItem.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/ComboBoxItem.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/ComboBox.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/ComboBox.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/Window.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/Window.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/Carousel.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/Carousel.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/CaptionButtons.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/CaptionButtons.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/TitleBar.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/TitleBar.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/TextBox.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/TextBox.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/AutoCompleteBox.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/AutoCompleteBox.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/DataValidationErrors.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/DataValidationErrors.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/ContentControl.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/ContentControl.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/FlyoutPresenter.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/FlyoutPresenter.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/MenuFlyoutPresenter.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/MenuFlyoutPresenter.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/GridSplitter.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/GridSplitter.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/ItemsControl.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/ItemsControl.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/ListBoxItem.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/ListBoxItem.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/ListBox.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/ListBox.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/ScrollBar.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/ScrollBar.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/ScrollViewer.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/ScrollViewer.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/TabStrip.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/TabStrip.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/TabStripItem.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/TabStripItem.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/TabControl.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/TabControl.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/TabItem.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/TabItem.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/PopupRoot.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/PopupRoot.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/OverlayPopupHost.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/OverlayPopupHost.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/WindowNotificationManager.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/WindowNotificationManager.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/EmbeddableControlRoot.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/EmbeddableControlRoot.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/TransitioningContentControl.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/TransitioningContentControl.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/TreeView.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/TreeView.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/TreeViewItem.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/TreeViewItem.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/ProgressBar.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/ProgressBar.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/Separator.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/Separator.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/Menu.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/Menu.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/MenuItem.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/MenuItem.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/ContextMenu.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/ContextMenu.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/CalendarButton.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/CalendarButton.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/CalendarDayButton.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/CalendarDayButton.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/CalendarItem.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/CalendarItem.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/Calendar.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/Calendar.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/CalendarDatePicker.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/CalendarDatePicker.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/Slider.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/Slider.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/NotificationCard.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/NotificationCard.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/NativeMenuBar.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/NativeMenuBar.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/NumericUpDown.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/NumericUpDown.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/DateTimePickerShared.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/DateTimePickerShared.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/DatePicker.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/DatePicker.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/TimePicker.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/TimePicker.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/SplitView.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/SplitView.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/ManagedFileChooser.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/ManagedFileChooser.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/SplitButton.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/SplitButton.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/SelectableTextBlock.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/SelectableTextBlock.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/RefreshContainer.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/RefreshContainer.xaml" />
<ResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/RefreshVisualizer.xaml" /> <MergeResourceInclude Source="avares://Avalonia.Themes.Simple/Controls/RefreshVisualizer.xaml" />
</ResourceDictionary.MergedDictionaries> </ResourceDictionary.MergedDictionaries>
</ResourceDictionary> </ResourceDictionary>
</Styles.Resources> </Styles.Resources>

2
src/Avalonia.Themes.Simple/SimpleTheme.xaml

@ -4,7 +4,7 @@
<Styles.Resources> <Styles.Resources>
<ResourceDictionary> <ResourceDictionary>
<ResourceDictionary.MergedDictionaries> <ResourceDictionary.MergedDictionaries>
<ResourceInclude Source="/Accents/Base.xaml" /> <MergeResourceInclude Source="/Accents/Base.xaml" />
</ResourceDictionary.MergedDictionaries> </ResourceDictionary.MergedDictionaries>
<!-- These are not part of MergedDictionaries so we can add or remove them easier --> <!-- These are not part of MergedDictionaries so we can add or remove them easier -->

4
src/Avalonia.Themes.Simple/SimpleTheme.xaml.cs

@ -58,13 +58,13 @@ namespace Avalonia.Themes.Simple
{ {
var themeVariantResource = Mode == SimpleThemeMode.Dark ? _simpleDark : _simpleLight; var themeVariantResource = Mode == SimpleThemeMode.Dark ? _simpleDark : _simpleLight;
var dict = Resources.MergedDictionaries; var dict = Resources.MergedDictionaries;
if (dict.Count == 1) if (dict.Count == 0)
{ {
dict.Add(themeVariantResource); dict.Add(themeVariantResource);
} }
else else
{ {
dict[1] = themeVariantResource; dict[0] = themeVariantResource;
} }
} }
} }

4
src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaRuntimeXamlLoader.cs

@ -56,8 +56,8 @@ namespace Avalonia.Markup.Xaml
/// </summary> /// </summary>
/// <param name="documents">Collection of documents.</param> /// <param name="documents">Collection of documents.</param>
/// <param name="configuration">Xaml loader configuration.</param> /// <param name="configuration">Xaml loader configuration.</param>
/// <returns>The loaded objects per each input document.</returns> /// <returns>The loaded objects per each input document. If document was removed, the element by index is null.</returns>
public static IReadOnlyList<object> LoadGroup(IReadOnlyCollection<RuntimeXamlLoaderDocument> documents, RuntimeXamlLoaderConfiguration? configuration = null) public static IReadOnlyList<object?> LoadGroup(IReadOnlyCollection<RuntimeXamlLoaderDocument> documents, RuntimeXamlLoaderConfiguration? configuration = null)
=> AvaloniaXamlIlRuntimeCompiler.LoadGroup(documents, configuration ?? new RuntimeXamlLoaderConfiguration()); => AvaloniaXamlIlRuntimeCompiler.LoadGroup(documents, configuration ?? new RuntimeXamlLoaderConfiguration());
/// <summary> /// <summary>

1
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs

@ -85,6 +85,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
GroupTransformers = new() GroupTransformers = new()
{ {
new XamlMergeResourceGroupTransformer(),
new AvaloniaXamlIncludeTransformer() new AvaloniaXamlIncludeTransformer()
}; };
} }

2
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/IXamlAstGroupTransformer.cs

@ -18,7 +18,7 @@ internal class AstGroupTransformationContext : AstTransformationContext
public IXamlDocumentResource CurrentDocument { get; set; } public IXamlDocumentResource CurrentDocument { get; set; }
public IReadOnlyCollection<IXamlDocumentResource> Documents { get; } public IReadOnlyCollection<IXamlDocumentResource> Documents { get; }
public new IXamlAstNode ParseError(string message, IXamlAstNode node) => public new IXamlAstNode ParseError(string message, IXamlAstNode node) =>
Error(node, new XamlDocumentParseException(CurrentDocument?.FileSource?.FilePath, message, node)); Error(node, new XamlDocumentParseException(CurrentDocument?.FileSource?.FilePath, message, node));

75
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlIncludeGroupTransformer.cs

@ -15,6 +15,7 @@ using XamlX.TypeSystem;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.GroupTransformers; namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.GroupTransformers;
#nullable enable
internal class AvaloniaXamlIncludeTransformer : IXamlAstGroupTransformer internal class AvaloniaXamlIncludeTransformer : IXamlAstGroupTransformer
{ {
public IXamlAstNode Transform(AstGroupTransformationContext context, IXamlAstNode node) public IXamlAstNode Transform(AstGroupTransformationContext context, IXamlAstNode node)
@ -34,40 +35,26 @@ internal class AvaloniaXamlIncludeTransformer : IXamlAstGroupTransformer
{ {
throw new InvalidOperationException($"\"{nodeTypeName}\".Loaded property is expected to be defined"); throw new InvalidOperationException($"\"{nodeTypeName}\".Loaded property is expected to be defined");
} }
if (valueNode.Manipulation is not XamlObjectInitializationNode if (valueNode.Manipulation is not XamlObjectInitializationNode
{ {
Manipulation: XamlPropertyAssignmentNode { Property: { Name: "Source" } } sourceProperty Manipulation: XamlPropertyAssignmentNode { Property: { Name: "Source" } } sourceProperty
}) })
{ {
return context.ParseError($"Source property must be set on the \"{nodeTypeName}\" node.", node); throw new XamlDocumentParseException(context.CurrentDocument,
$"Source property must be set on the \"{nodeTypeName}\" node.", valueNode);
} }
// We expect that AvaloniaXamlIlLanguageParseIntrinsics has already parsed the Uri and created node like: `new Uri(assetPath, uriKind)`. var (assetPathUri, sourceUriNode) = ResolveSourceFromXamlInclude(context, nodeTypeName, sourceProperty, false);
if (sourceProperty.Values.OfType<XamlAstNewClrObjectNode>().FirstOrDefault() is not { } sourceUriNode if (assetPathUri is null)
|| sourceUriNode.Type.GetClrType() != context.GetAvaloniaTypes().Uri
|| sourceUriNode.Arguments.FirstOrDefault() is not XamlConstantNode { Constant: string originalAssetPath }
|| sourceUriNode.Arguments.Skip(1).FirstOrDefault() is not XamlConstantNode { Constant: int uriKind })
{ {
// TODO: make it a compiler warning
// Source value can be set with markup extension instead of the Uri object node, we don't support it here yet.
return node; return node;
} }
else
var uriPath = new Uri(originalAssetPath, (UriKind)uriKind);
if (!uriPath.IsAbsoluteUri)
{ {
var baseUrl = context.CurrentDocument.Uri ?? throw new InvalidOperationException("CurrentDocument URI is null."); sourceUriNode ??= valueNode;
uriPath = new Uri(new Uri(baseUrl, UriKind.Absolute), uriPath);
}
else if (!uriPath.Scheme.Equals("avares", StringComparison.CurrentCultureIgnoreCase))
{
return context.ParseError(
$"\"{nodeTypeName}.Source\" supports only \"avares://\" absolute or relative uri.",
sourceUriNode, node);
} }
var assetPathUri = Uri.UnescapeDataString(uriPath.AbsoluteUri);
var assetPath = assetPathUri.Replace("avares://", ""); var assetPath = assetPathUri.Replace("avares://", "");
var assemblyNameSeparator = assetPath.IndexOf('/'); var assemblyNameSeparator = assetPath.IndexOf('/');
var assembly = assetPath.Substring(0, assemblyNameSeparator); var assembly = assetPath.Substring(0, assemblyNameSeparator);
@ -119,7 +106,7 @@ internal class AvaloniaXamlIncludeTransformer : IXamlAstGroupTransformer
$"Unable to resolve XAML resource \"{assetPathUri}\" in the \"{assembly}\" assembly.", $"Unable to resolve XAML resource \"{assetPathUri}\" in the \"{assembly}\" assembly.",
sourceUriNode, node); sourceUriNode, node);
} }
private static IXamlAstNode FromType(AstTransformationContext context, IXamlType type, IXamlAstNode li, private static IXamlAstNode FromType(AstTransformationContext context, IXamlType type, IXamlAstNode li,
IXamlType expectedLoadedType, IXamlAstNode fallbackNode, string assetPathUri, string assembly) IXamlType expectedLoadedType, IXamlAstNode fallbackNode, string assetPathUri, string assembly)
{ {
@ -151,7 +138,49 @@ internal class AvaloniaXamlIncludeTransformer : IXamlAstGroupTransformer
new[] { new NewServiceProviderNode(sp, li) }); new[] { new NewServiceProviderNode(sp, li) });
} }
internal class NewServiceProviderNode : XamlAstNode, IXamlAstValueNode,IXamlAstNodeNeedsParentStack, internal static (string?, IXamlAstNode?) ResolveSourceFromXamlInclude(
AstGroupTransformationContext context, string nodeTypeName, XamlPropertyAssignmentNode sourceProperty,
bool strictSourceValueType)
{
// We expect that AvaloniaXamlIlLanguageParseIntrinsics has already parsed the Uri and created node like: `new Uri(assetPath, uriKind)`.
if (sourceProperty.Values.OfType<XamlAstNewClrObjectNode>().FirstOrDefault() is not { } sourceUriNode
|| sourceUriNode.Type.GetClrType() != context.GetAvaloniaTypes().Uri
|| sourceUriNode.Arguments.FirstOrDefault() is not XamlConstantNode { Constant: string originalAssetPath }
|| sourceUriNode.Arguments.Skip(1).FirstOrDefault() is not XamlConstantNode { Constant: int uriKind })
{
// Source value can be set with markup extension instead of the Uri object node, we don't support it here yet.
var anyPropValue = sourceProperty.Values.FirstOrDefault();
if (strictSourceValueType)
{
context.Error(anyPropValue,
new XamlDocumentParseException(context.CurrentDocument,
$"\"{nodeTypeName}.Source\" supports only \"avares://\" absolute or relative uri.", anyPropValue));
}
else
{
// TODO: make it a compiler warning
}
return (null, anyPropValue);
}
var uriPath = new Uri(originalAssetPath, (UriKind)uriKind);
if (!uriPath.IsAbsoluteUri)
{
var baseUrl = context.CurrentDocument.Uri ?? throw new InvalidOperationException("CurrentDocument URI is null.");
uriPath = new Uri(new Uri(baseUrl, UriKind.Absolute), uriPath);
}
else if (!uriPath.Scheme.Equals("avares", StringComparison.CurrentCultureIgnoreCase))
{
context.Error(sourceUriNode,
new XamlDocumentParseException(context.CurrentDocument,
$"\"{nodeTypeName}.Source\" supports only \"avares://\" absolute or relative uri.", sourceUriNode));
return (null, sourceUriNode);
}
return (Uri.UnescapeDataString(uriPath.AbsoluteUri), sourceUriNode);
}
private class NewServiceProviderNode : XamlAstNode, IXamlAstValueNode,IXamlAstNodeNeedsParentStack,
IXamlAstEmitableNode<IXamlILEmitter, XamlILNodeEmitResult> IXamlAstEmitableNode<IXamlILEmitter, XamlILNodeEmitResult>
{ {
public NewServiceProviderNode(IXamlType type, IXamlLineInfo lineInfo) : base(lineInfo) public NewServiceProviderNode(IXamlType type, IXamlLineInfo lineInfo) : base(lineInfo)

113
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlMergeResourceGroupTransformer.cs

@ -0,0 +1,113 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
using XamlX.Ast;
using XamlX.IL.Emitters;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.GroupTransformers;
#nullable enable
internal class XamlMergeResourceGroupTransformer : IXamlAstGroupTransformer
{
public IXamlAstNode Transform(AstGroupTransformationContext context, IXamlAstNode node)
{
var resourceDictionaryType = context.GetAvaloniaTypes().ResourceDictionary;
if (node is not XamlObjectInitializationNode resourceDictionaryNode
|| resourceDictionaryNode.Type != resourceDictionaryType
|| resourceDictionaryNode.Manipulation is not XamlManipulationGroupNode resourceDictionaryManipulation)
{
return node;
}
var mergeResourceIncludeType = context.GetAvaloniaTypes().MergeResourceInclude;
var mergeSourceNodes = new List<XamlPropertyAssignmentNode>();
var hasAnyNonMergedResource = false;
foreach (var manipulationNode in resourceDictionaryManipulation.Children.ToArray())
{
void ProcessXamlPropertyAssignmentNode(XamlManipulationGroupNode parent, XamlPropertyAssignmentNode assignmentNode)
{
if (assignmentNode.Property.Name == "MergedDictionaries"
&& assignmentNode.Values.FirstOrDefault() is XamlValueWithManipulationNode valueNode)
{
if (valueNode.Type.GetClrType() == mergeResourceIncludeType)
{
if (valueNode.Manipulation is XamlObjectInitializationNode objectInitialization
&& objectInitialization.Manipulation is XamlPropertyAssignmentNode sourceAssignmentNode)
{
parent.Children.Remove(assignmentNode);
mergeSourceNodes.Add(sourceAssignmentNode);
}
else
{
throw new XamlDocumentParseException(context.CurrentDocument,
"Invalid MergeResourceInclude node found. Make sure that Source property is set.",
valueNode);
}
}
else
{
hasAnyNonMergedResource = true;
}
if (hasAnyNonMergedResource && mergeSourceNodes.Any())
{
throw new XamlDocumentParseException(context.CurrentDocument,
"Mix of MergeResourceInclude and other dictionaries inside of the ResourceDictionary.MergedDictionaries is not allowed",
valueNode);
}
}
}
if (manipulationNode is XamlPropertyAssignmentNode singleValueAssignment)
{
ProcessXamlPropertyAssignmentNode(resourceDictionaryManipulation, singleValueAssignment);
}
else if (manipulationNode is XamlManipulationGroupNode groupNodeValues)
{
foreach (var groupNodeValue in groupNodeValues.Children.OfType<XamlPropertyAssignmentNode>().ToArray())
{
ProcessXamlPropertyAssignmentNode(groupNodeValues, groupNodeValue);
}
}
}
var manipulationGroup = new XamlManipulationGroupNode(node, new List<IXamlAstManipulationNode>());
foreach (var sourceNode in mergeSourceNodes)
{
var (originalAssetPath, propertyNode) =
AvaloniaXamlIncludeTransformer.ResolveSourceFromXamlInclude(context, "MergeResourceInclude", sourceNode, true);
if (originalAssetPath is null)
{
return node;
}
var targetDocument = context.Documents.FirstOrDefault(d =>
string.Equals(d.Uri, originalAssetPath, StringComparison.InvariantCultureIgnoreCase))
?.XamlDocument.Root as XamlValueWithManipulationNode;
if (targetDocument is null)
{
return context.ParseError(
$"Node MergeResourceInclude is unable to resolve \"{originalAssetPath}\" path.", propertyNode, node);
}
var singleRootObject = ((XamlManipulationGroupNode)targetDocument.Manipulation)
.Children.OfType<XamlObjectInitializationNode>().Single();
if (singleRootObject.Type != resourceDictionaryType)
{
return context.ParseError(
$"MergeResourceInclude can only include another ResourceDictionary", propertyNode, node);
}
manipulationGroup.Children.Add(singleRootObject.Manipulation);
}
if (manipulationGroup.Children.Any())
{
// MergedDictionaries are read first, so we need ot inject our merged values in the beginning.
resourceDictionaryManipulation.Children.Insert(0, manipulationGroup);
}
return node;
}
}

2
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs

@ -104,6 +104,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
public IXamlType IStyle { get; } public IXamlType IStyle { get; }
public IXamlType StyleInclude { get; } public IXamlType StyleInclude { get; }
public IXamlType ResourceInclude { get; } public IXamlType ResourceInclude { get; }
public IXamlType MergeResourceInclude { get; }
public IXamlType IResourceDictionary { get; } public IXamlType IResourceDictionary { get; }
public IXamlType ResourceDictionary { get; } public IXamlType ResourceDictionary { get; }
public IXamlMethod ResourceDictionaryDeferredAdd { get; } public IXamlMethod ResourceDictionaryDeferredAdd { get; }
@ -236,6 +237,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
IStyle = cfg.TypeSystem.GetType("Avalonia.Styling.IStyle"); IStyle = cfg.TypeSystem.GetType("Avalonia.Styling.IStyle");
StyleInclude = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.Styling.StyleInclude"); StyleInclude = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.Styling.StyleInclude");
ResourceInclude = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.Styling.ResourceInclude"); ResourceInclude = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.Styling.ResourceInclude");
MergeResourceInclude = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.Styling.MergeResourceInclude");
IResourceDictionary = cfg.TypeSystem.GetType("Avalonia.Controls.IResourceDictionary"); IResourceDictionary = cfg.TypeSystem.GetType("Avalonia.Controls.IResourceDictionary");
ResourceDictionary = cfg.TypeSystem.GetType("Avalonia.Controls.ResourceDictionary"); ResourceDictionary = cfg.TypeSystem.GetType("Avalonia.Controls.ResourceDictionary");
ResourceDictionaryDeferredAdd = ResourceDictionary.FindMethod("AddDeferred", XamlIlTypes.Void, true, XamlIlTypes.Object, ResourceDictionaryDeferredAdd = ResourceDictionary.FindMethod("AddDeferred", XamlIlTypes.Void, true, XamlIlTypes.Object,

5
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlDocumentParseException.cs

@ -18,4 +18,9 @@ internal class XamlDocumentParseException : XamlParseException
{ {
FilePath = path; FilePath = path;
} }
public XamlDocumentParseException(IXamlDocumentResource document, string message, IXamlLineInfo lineInfo)
: this(document.FileSource?.FilePath, message, lineInfo)
{
}
} }

1
src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj

@ -43,6 +43,7 @@
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RuntimeXamlLoaderConfiguration.cs" /> <Compile Include="RuntimeXamlLoaderConfiguration.cs" />
<Compile Include="RuntimeXamlLoaderDocument.cs" /> <Compile Include="RuntimeXamlLoaderDocument.cs" />
<Compile Include="Styling\MergeResourceInclude.cs" />
<Compile Include="Styling\ResourceInclude.cs" /> <Compile Include="Styling\ResourceInclude.cs" />
<Compile Include="Styling\StyleInclude.cs" /> <Compile Include="Styling\StyleInclude.cs" />
<Compile Include="Templates\ControlTemplate.cs" /> <Compile Include="Templates\ControlTemplate.cs" />

26
src/Markup/Avalonia.Markup.Xaml/Styling/MergeResourceInclude.cs

@ -0,0 +1,26 @@
using System;
using System.Diagnostics.CodeAnalysis;
using Avalonia.Controls;
#nullable enable
namespace Avalonia.Markup.Xaml.Styling;
/// <summary>
/// Loads a resource dictionary from a specified URL.
/// </summary>
/// <remarks>
/// If used from the XAML code, it is merged into the parent dictionary in the compile time.
/// When used in runtime, this type behaves like <see cref="ResourceInclude"/>.
/// </remarks>
[RequiresUnreferencedCode(TrimmingMessages.StyleResourceIncludeRequiresUnreferenceCodeMessage)]
public class MergeResourceInclude : ResourceInclude
{
public MergeResourceInclude(Uri? baseUri) : base(baseUri)
{
}
public MergeResourceInclude(IServiceProvider serviceProvider) : base(serviceProvider)
{
}
}

4
src/Markup/Avalonia.Markup.Xaml/Styling/ResourceInclude.cs

@ -9,6 +9,10 @@ namespace Avalonia.Markup.Xaml.Styling
/// <summary> /// <summary>
/// Loads a resource dictionary from a specified URL. /// Loads a resource dictionary from a specified URL.
/// </summary> /// </summary>
/// <remarks>
/// If used from the XAML code, it is replaced with direct resource dictionary reference.
/// When used in runtime, this type might be unsafe with trimming and AOT.
/// </remarks>
[RequiresUnreferencedCode(TrimmingMessages.StyleResourceIncludeRequiresUnreferenceCodeMessage)] [RequiresUnreferencedCode(TrimmingMessages.StyleResourceIncludeRequiresUnreferenceCodeMessage)]
public class ResourceInclude : IResourceProvider public class ResourceInclude : IResourceProvider
{ {

4
src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs

@ -11,6 +11,10 @@ namespace Avalonia.Markup.Xaml.Styling
/// <summary> /// <summary>
/// Includes a style from a URL. /// Includes a style from a URL.
/// </summary> /// </summary>
/// <remarks>
/// If used from the XAML code, it is replaced with direct style reference.
/// When used in runtime, this type might be unsafe with trimming and AOT.
/// </remarks>
[RequiresUnreferencedCode(TrimmingMessages.StyleResourceIncludeRequiresUnreferenceCodeMessage)] [RequiresUnreferencedCode(TrimmingMessages.StyleResourceIncludeRequiresUnreferenceCodeMessage)]
public class StyleInclude : IStyle, IResourceProvider public class StyleInclude : IStyle, IResourceProvider
{ {

22
tests/Avalonia.Benchmarks/Themes/ThemeBenchmark.cs

@ -16,6 +16,8 @@ namespace Avalonia.Benchmarks.Themes
public class ThemeBenchmark : IDisposable public class ThemeBenchmark : IDisposable
{ {
private IDisposable _app; private IDisposable _app;
private readonly FluentTheme _reusableFluentTheme = new FluentTheme();
private readonly SimpleTheme _reusableSimpleTheme = new SimpleTheme();
public ThemeBenchmark() public ThemeBenchmark()
{ {
@ -49,6 +51,26 @@ namespace Avalonia.Benchmarks.Themes
}; };
return ((IResourceHost)UnitTestApplication.Current).TryGetResource("ThemeAccentColor", out _); return ((IResourceHost)UnitTestApplication.Current).TryGetResource("ThemeAccentColor", out _);
} }
[Benchmark]
[Arguments(typeof(Button))]
[Arguments(typeof(TextBox))]
[Arguments(typeof(DatePicker))]
public object FindFluentControlTheme(Type type)
{
_reusableFluentTheme.TryGetResource(type, out var theme);
return theme;
}
[Benchmark]
[Arguments(typeof(Button))]
[Arguments(typeof(TextBox))]
[Arguments(typeof(DatePicker))]
public object FindSimpleControlTheme(Type type)
{
_reusableSimpleTheme.TryGetResource(type, out var theme);
return theme;
}
public void Dispose() public void Dispose()
{ {

124
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/MergeResourceIncludeTests.cs

@ -0,0 +1,124 @@
using System;
using System.Xml;
using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.Styling;
using Xunit;
namespace Avalonia.Markup.Xaml.UnitTests.Xaml;
public class MergeResourceIncludeTests
{
[Fact]
public void MergeResourceInclude_Works_With_Single_Resource()
{
var documents = new[]
{
new RuntimeXamlLoaderDocument(new Uri("avares://Tests/Resources.xaml"), @"
<ResourceDictionary xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<SolidColorBrush x:Key='brush2'>Red</SolidColorBrush>
</ResourceDictionary>"),
new RuntimeXamlLoaderDocument(@"
<UserControl xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<UserControl.Resources>
<ResourceDictionary>
<SolidColorBrush x:Key='brush1'>Blue</SolidColorBrush>
<ResourceDictionary.MergedDictionaries>
<MergeResourceInclude Source='avares://Tests/Resources.xaml'/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
</UserControl>")
};
var objects = AvaloniaRuntimeXamlLoader.LoadGroup(documents);
var contentControl = Assert.IsType<UserControl>(objects[1]);
var resources = Assert.IsType<ResourceDictionary>(contentControl.Resources);
Assert.Empty(resources.MergedDictionaries);
var initialResource = (ISolidColorBrush)resources["brush1"]!;
Assert.Equal(Colors.Blue, initialResource.Color);
var mergedResource = (ISolidColorBrush)resources["brush2"]!;
Assert.Equal(Colors.Red, mergedResource.Color);
}
[Fact]
public void Mixing_MergeResourceInclude_And_ResourceInclude_Is_Not_Allowed()
{
var documents = new[]
{
new RuntimeXamlLoaderDocument(new Uri("avares://Tests/Resources1.xaml"), @"
<ResourceDictionary xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<SolidColorBrush x:Key='brush1'>Red</SolidColorBrush>
</ResourceDictionary>"),
new RuntimeXamlLoaderDocument(new Uri("avares://Tests/Resources2.xaml"), @"
<ResourceDictionary xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<SolidColorBrush x:Key='brush2'>Blue</SolidColorBrush>
</ResourceDictionary>"),
new RuntimeXamlLoaderDocument(@"
<ResourceDictionary xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<ResourceDictionary.MergedDictionaries>
<MergeResourceInclude Source='avares://Tests/Resources1.xaml'/>
<ResourceInclude Source='avares://Tests/Resources2.xaml'/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>")
};
Assert.ThrowsAny<XmlException>(() => AvaloniaRuntimeXamlLoader.LoadGroup(documents));
}
[Fact]
public void MergeResourceInclude_Works_With_Multiple_Resources()
{
var documents = new[]
{
new RuntimeXamlLoaderDocument(new Uri("avares://Tests/Resources1.xaml"), @"
<ResourceDictionary xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<SolidColorBrush x:Key='brush1'>Red</SolidColorBrush>
<SolidColorBrush x:Key='brush2'>Blue</SolidColorBrush>
</ResourceDictionary>"),
new RuntimeXamlLoaderDocument(new Uri("avares://Tests/Resources2.xaml"), @"
<ResourceDictionary xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<SolidColorBrush x:Key='brush4'>Yellow</SolidColorBrush>
<ResourceDictionary.MergedDictionaries>
<MergeResourceInclude Source='avares://Tests/Resources1_2.xaml'/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>"),
new RuntimeXamlLoaderDocument(@"
<ResourceDictionary xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<ResourceDictionary.MergedDictionaries>
<MergeResourceInclude Source='avares://Tests/Resources1.xaml'/>
<MergeResourceInclude Source='avares://Tests/Resources2.xaml'/>
</ResourceDictionary.MergedDictionaries>
<SolidColorBrush x:Key='brush5'>Black</SolidColorBrush>
<SolidColorBrush x:Key='brush6'>White</SolidColorBrush>
</ResourceDictionary>"),
new RuntimeXamlLoaderDocument(new Uri("avares://Tests/Resources1_2.xaml"), @"
<ResourceDictionary xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<SolidColorBrush x:Key='brush3'>Green</SolidColorBrush>
</ResourceDictionary>"),
};
var objects = AvaloniaRuntimeXamlLoader.LoadGroup(documents);
var resources = Assert.IsType<ResourceDictionary>(objects[2]);
Assert.Empty(resources.MergedDictionaries);
Assert.Equal(Colors.Red, ((ISolidColorBrush)resources["brush1"]!).Color);
Assert.Equal(Colors.Blue, ((ISolidColorBrush)resources["brush2"]!).Color);
Assert.Equal(Colors.Green, ((ISolidColorBrush)resources["brush3"]!).Color);
Assert.Equal(Colors.Yellow, ((ISolidColorBrush)resources["brush4"]!).Color);
Assert.Equal(Colors.Black, ((ISolidColorBrush)resources["brush5"]!).Color);
Assert.Equal(Colors.White, ((ISolidColorBrush)resources["brush6"]!).Color);
}
}
Loading…
Cancel
Save