Browse Source

Implement compile time MergedDictionaties

pull/9577/head
Max Katz 3 years ago
parent
commit
390221b3ce
  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. 126
      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. 124
      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. 69
      src/Markup/Avalonia.Markup.Xaml/Styling/MergeResourceInclude.cs
  24. 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: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}"
TargetType="ColorPicker">
<Setter Property="CornerRadius" Value="{DynamicResource ControlCornerRadius}" />

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

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

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

@ -3,10 +3,6 @@
xmlns:controls="using:Avalonia.Controls"
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}"
TargetType="ColorPicker">
<Setter Property="CornerRadius" Value="0" />

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -56,13 +56,13 @@ namespace Avalonia.Themes.Simple
{
var themeVariantResource = Mode == SimpleThemeMode.Dark ? _simpleDark : _simpleLight;
var dict = Resources.MergedDictionaries;
if (dict.Count == 1)
if (dict.Count == 0)
{
dict.Add(themeVariantResource);
}
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>
/// <param name="documents">Collection of documents.</param>
/// <param name="configuration">Xaml loader configuration.</param>
/// <returns>The loaded objects per each input document.</returns>
public static IReadOnlyList<object> LoadGroup(IReadOnlyCollection<RuntimeXamlLoaderDocument> documents, RuntimeXamlLoaderConfiguration? configuration = null)
/// <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)
=> AvaloniaXamlIlRuntimeCompiler.LoadGroup(documents, configuration ?? new RuntimeXamlLoaderConfiguration());
/// <summary>

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

@ -85,6 +85,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
GroupTransformers = new()
{
new XamlMergeResourceGroupTransformer(),
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 IReadOnlyCollection<IXamlDocumentResource> Documents { get; }
public new IXamlAstNode ParseError(string message, IXamlAstNode 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;
#nullable enable
internal class AvaloniaXamlIncludeTransformer : IXamlAstGroupTransformer
{
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");
}
if (valueNode.Manipulation is not XamlObjectInitializationNode
{
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)`.
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 })
var (assetPathUri, sourceUriNode) = ResolveSourceFromXamlInclude(context, nodeTypeName, sourceProperty, false);
if (assetPathUri is null)
{
// 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;
}
var uriPath = new Uri(originalAssetPath, (UriKind)uriKind);
if (!uriPath.IsAbsoluteUri)
else
{
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))
{
return context.ParseError(
$"\"{nodeTypeName}.Source\" supports only \"avares://\" absolute or relative uri.",
sourceUriNode, node);
sourceUriNode ??= valueNode;
}
var assetPathUri = Uri.UnescapeDataString(uriPath.AbsoluteUri);
var assetPath = assetPathUri.Replace("avares://", "");
var assemblyNameSeparator = assetPath.IndexOf('/');
var assembly = assetPath.Substring(0, assemblyNameSeparator);
@ -119,7 +106,7 @@ internal class AvaloniaXamlIncludeTransformer : IXamlAstGroupTransformer
$"Unable to resolve XAML resource \"{assetPathUri}\" in the \"{assembly}\" assembly.",
sourceUriNode, node);
}
private static IXamlAstNode FromType(AstTransformationContext context, IXamlType type, IXamlAstNode li,
IXamlType expectedLoadedType, IXamlAstNode fallbackNode, string assetPathUri, string assembly)
{
@ -151,7 +138,49 @@ internal class AvaloniaXamlIncludeTransformer : IXamlAstGroupTransformer
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>
{
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 StyleInclude { get; }
public IXamlType ResourceInclude { get; }
public IXamlType MergeResourceInclude { get; }
public IXamlType IResourceDictionary { get; }
public IXamlType ResourceDictionary { get; }
public IXamlMethod ResourceDictionaryDeferredAdd { get; }
@ -236,6 +237,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
IStyle = cfg.TypeSystem.GetType("Avalonia.Styling.IStyle");
StyleInclude = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.Styling.StyleInclude");
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");
ResourceDictionary = cfg.TypeSystem.GetType("Avalonia.Controls.ResourceDictionary");
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;
}
public XamlDocumentParseException(IXamlDocumentResource document, string message, IXamlLineInfo lineInfo)
: this(document.FileSource?.FilePath, message, lineInfo)
{
}
}

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

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

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

@ -0,0 +1,69 @@
using System;
using Avalonia.Controls;
namespace Avalonia.Markup.Xaml.Styling;
public class MergeResourceInclude : IResourceProvider
{
private readonly IServiceProvider _serviceProvider;
private readonly Uri? _baseUri;
private IResourceDictionary? _loaded;
private bool _isLoading;
/// <summary>
/// Initializes a new instance of the <see cref="ResourceInclude"/> class.
/// </summary>
/// <param name="serviceProvider">The XAML service provider.</param>
public MergeResourceInclude(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
_baseUri = serviceProvider.GetContextBaseUri();
}
/// <summary>
/// Gets the loaded resource dictionary.
/// </summary>
public IResourceDictionary Loaded
{
get
{
if (_loaded == null)
{
_isLoading = true;
_loaded = (IResourceDictionary)AvaloniaXamlLoader.Load(_serviceProvider, Source, _baseUri);
_isLoading = false;
}
return _loaded;
}
}
public IResourceHost? Owner => Loaded.Owner;
/// <summary>
/// Gets or sets the source URL.
/// </summary>
public Uri? Source { get; set; }
bool IResourceNode.HasResources => Loaded.HasResources;
public event EventHandler? OwnerChanged
{
add => Loaded.OwnerChanged += value;
remove => Loaded.OwnerChanged -= value;
}
bool IResourceNode.TryGetResource(object key, out object? value)
{
if (!_isLoading)
{
return Loaded.TryGetResource(key, out value);
}
value = null;
return false;
}
void IResourceProvider.AddOwner(IResourceHost owner) => Loaded.AddOwner(owner);
void IResourceProvider.RemoveOwner(IResourceHost owner) => Loaded.RemoveOwner(owner);
}

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