Browse Source

Merge branch 'master' into fix/relative-layout-crash-with-infinite-size

pull/8727/head
Dan Walmsley 4 years ago
committed by GitHub
parent
commit
1b02c94838
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      build/HarfBuzzSharp.props
  2. 2
      build/ReactiveUI.props
  3. 6
      build/SkiaSharp.props
  4. 2
      samples/ControlCatalog/DecoratedWindow.xaml
  5. 6
      samples/ControlCatalog/MainWindow.xaml
  6. 4
      samples/ControlCatalog/Pages/NumericUpDownPage.xaml
  7. 8
      samples/ControlCatalog/ViewModels/ContextPageViewModel.cs
  8. 7
      samples/ControlCatalog/ViewModels/MenuPageViewModel.cs
  9. 265
      samples/RenderDemo/Pages/DrawingPage.xaml
  10. 45
      src/Android/Avalonia.Android/AndroidThreadingInterface.cs
  11. 17
      src/Avalonia.Base/Animation/Animation.cs
  12. 13
      src/Avalonia.Base/Animation/Animators/SolidColorBrushAnimator.cs
  13. 35
      src/Avalonia.Base/AvaloniaObjectExtensions.cs
  14. 15
      src/Avalonia.Base/AvaloniaProperty`1.cs
  15. 15
      src/Avalonia.Base/Collections/AvaloniaListExtensions.cs
  16. 15
      src/Avalonia.Base/DirectPropertyBase.cs
  17. 4
      src/Avalonia.Base/EnumExtensions.cs
  18. 3
      src/Avalonia.Base/Input/PointerOverPreProcessor.cs
  19. 18
      src/Avalonia.Base/Interactivity/RoutedEvent.cs
  20. 11
      src/Avalonia.Base/Layout/ILayoutManager.cs
  21. 11
      src/Avalonia.Base/Layout/LayoutManager.cs
  22. 28
      src/Avalonia.Base/Layout/Layoutable.cs
  23. 16
      src/Avalonia.Base/Media/Imaging/Bitmap.cs
  24. 14
      src/Avalonia.Base/Media/Imaging/WriteableBitmap.cs
  25. 35
      src/Avalonia.Base/Utilities/MathUtilities.cs
  26. 25
      src/Avalonia.Base/Utilities/WeakObservable.cs
  27. 193
      src/Avalonia.Base/Utilities/WeakSubscriptionManager.cs
  28. 16
      src/Avalonia.Base/Visual.cs
  29. 19
      src/Avalonia.Base/VisualTree/IVisualTreeHost.cs
  30. 23
      src/Avalonia.Controls.ColorPicker/Themes/Fluent/Fluent.xaml
  31. 23
      src/Avalonia.Controls.ColorPicker/Themes/Simple/Simple.xaml
  32. 30
      src/Avalonia.Controls.DataGrid/DataGrid.cs
  33. 6
      src/Avalonia.Controls/Control.cs
  34. 2
      src/Avalonia.Controls/DesktopApplicationExtensions.cs
  35. 61
      src/Avalonia.Controls/DrawingPresenter.cs
  36. 13
      src/Avalonia.Controls/LoggingExtensions.cs
  37. 7
      src/Avalonia.Controls/NativeMenuItem.cs
  38. 13
      src/Avalonia.Controls/NativeMenuItemSeparator.cs
  39. 11
      src/Avalonia.Controls/Notifications/WindowNotificationManager.cs
  40. 53
      src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs
  41. 4
      src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs
  42. 46
      src/Avalonia.Controls/Primitives/Popup.cs
  43. 12
      src/Avalonia.Controls/Primitives/TemplatedControl.cs
  44. 2
      src/Avalonia.Controls/ProgressBar.cs
  45. 2
      src/Avalonia.Controls/Remote/RemoteWidget.cs
  46. 2
      src/Avalonia.Controls/RichTextBlock.cs
  47. 36
      src/Avalonia.Controls/Shapes/Arc.cs
  48. 97
      src/Avalonia.Controls/Shapes/Sector.cs
  49. 20
      src/Avalonia.Controls/SystemDialog.cs
  50. 2
      src/Avalonia.Controls/TextBox.cs
  51. 3
      src/Avalonia.Controls/TopLevel.cs
  52. 44
      src/Avalonia.Controls/Window.cs
  53. 9
      src/Avalonia.Controls/WindowBase.cs
  54. 23
      src/Avalonia.Diagnostics/Diagnostics/Screenshots/FilePickerHandler.cs
  55. 6
      src/Avalonia.Dialogs/AboutAvaloniaDialog.xaml
  56. 48
      src/Avalonia.Headless/HeadlessPlatformThreadingInterface.cs
  57. 3
      src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs
  58. 28
      src/Avalonia.ReactiveUI/AvaloniaActivationForViewFetcher.cs
  59. 4
      src/Avalonia.ReactiveUI/RoutedViewHost.cs
  60. 6
      src/Avalonia.ReactiveUI/ViewModelViewHost.cs
  61. 12
      src/Avalonia.Themes.Fluent/Controls/ManagedFileChooser.xaml
  62. 2
      src/Avalonia.Themes.Simple/Controls/CalendarDatePicker.xaml
  63. 2
      src/Avalonia.X11/X11IconLoader.cs
  64. 12
      src/Skia/Avalonia.Skia/Gpu/OpenGl/OpenGlBitmapImpl.cs
  65. 155
      tests/Avalonia.Base.UnitTests/Collections/AvaloniaListExtenionsTests.cs
  66. 70
      tests/Avalonia.Base.UnitTests/WeakSubscriptionManagerTests.cs
  67. 71
      tests/Avalonia.Controls.UnitTests/LoadedTests.cs
  68. 14
      tests/Avalonia.ReactiveUI.UnitTests/AutoSuspendHelperTest.cs
  69. 11
      tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs
  70. 5
      tests/Avalonia.ReactiveUI.UnitTests/ReactiveUserControlTest.cs
  71. 5
      tests/Avalonia.ReactiveUI.UnitTests/ReactiveWindowTest.cs
  72. 4
      tests/Avalonia.ReactiveUI.UnitTests/RoutedViewHostTest.cs
  73. 3
      tests/Avalonia.ReactiveUI.UnitTests/ViewModelViewHostTest.cs
  74. 2
      tests/Avalonia.RenderTests/Media/BitmapTests.cs

6
build/HarfBuzzSharp.props

@ -1,7 +1,7 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup> <ItemGroup>
<PackageReference Include="HarfBuzzSharp" Version="2.8.2" /> <PackageReference Include="HarfBuzzSharp" Version="2.8.2.1-preview.108" />
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.Linux" Version="2.8.2" /> <PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.Linux" Version="2.8.2.1-preview.108" />
<PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.WebAssembly" Version="2.8.2" /> <PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.WebAssembly" Version="2.8.2.1-preview.108" />
</ItemGroup> </ItemGroup>
</Project> </Project>

2
build/ReactiveUI.props

@ -1,5 +1,5 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup> <ItemGroup>
<PackageReference Include="ReactiveUI" Version="13.2.10" /> <PackageReference Include="ReactiveUI" Version="18.3.1" />
</ItemGroup> </ItemGroup>
</Project> </Project>

6
build/SkiaSharp.props

@ -1,7 +1,7 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup> <ItemGroup>
<PackageReference Include="SkiaSharp" Version="2.88.1-preview.1" /> <PackageReference Include="SkiaSharp" Version="2.88.1-preview.108" />
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="SkiaSharp.NativeAssets.Linux" Version="2.88.1-preview.1" /> <PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="SkiaSharp.NativeAssets.Linux" Version="2.88.1-preview.108" />
<PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="SkiaSharp.NativeAssets.WebAssembly" Version="2.88.1-preview.1" /> <PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="SkiaSharp.NativeAssets.WebAssembly" Version="2.88.1-preview.108" />
</ItemGroup> </ItemGroup>
</Project> </Project>

2
samples/ControlCatalog/DecoratedWindow.xaml

@ -2,7 +2,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.DecoratedWindow" x:Class="ControlCatalog.DecoratedWindow"
Title="Avalonia Control Gallery" Title="Avalonia Control Gallery"
xmlns:local="clr-namespace:ControlCatalog" HasSystemDecorations="False" Name="Window"> xmlns:local="clr-namespace:ControlCatalog" SystemDecorations="None" Name="Window">
<NativeMenu.Menu> <NativeMenu.Menu>
<NativeMenu> <NativeMenu>
<NativeMenuItem Header="Decorated"> <NativeMenuItem Header="Decorated">

6
samples/ControlCatalog/MainWindow.xaml

@ -18,15 +18,15 @@
<NativeMenu> <NativeMenu>
<NativeMenuItem Header="File"> <NativeMenuItem Header="File">
<NativeMenu> <NativeMenu>
<NativeMenuItem Icon="/Assets/test_icon.ico" Header="Open" Clicked="OnOpenClicked" Gesture="Ctrl+O"/> <NativeMenuItem Icon="/Assets/test_icon.ico" Header="Open" Click="OnOpenClicked" Gesture="Ctrl+O"/>
<NativeMenuItemSeperator/><!-- Uses incorrect spelling to demonstrate backwards compatibility --> <NativeMenuItemSeparator/>
<NativeMenuItem Icon="/Assets/github_icon.png" Header="Recent"> <NativeMenuItem Icon="/Assets/github_icon.png" Header="Recent">
<NativeMenu/> <NativeMenu/>
</NativeMenuItem> </NativeMenuItem>
<NativeMenuItemSeparator/> <NativeMenuItemSeparator/>
<NativeMenuItem Header="{x:Static local:MainWindow.MenuQuitHeader}" <NativeMenuItem Header="{x:Static local:MainWindow.MenuQuitHeader}"
Gesture="{x:Static local:MainWindow.MenuQuitGesture}" Gesture="{x:Static local:MainWindow.MenuQuitGesture}"
Clicked="OnCloseClicked" /> Click="OnCloseClicked" />
</NativeMenu> </NativeMenu>
</NativeMenuItem> </NativeMenuItem>
<NativeMenuItem Header="Edit"> <NativeMenuItem Header="Edit">

4
samples/ControlCatalog/Pages/NumericUpDownPage.xaml

@ -54,11 +54,11 @@
<Grid Grid.Row="0" Grid.Column="2" Margin="8" RowDefinitions="Auto,Auto,Auto,Auto,Auto" ColumnDefinitions="Auto, Auto"> <Grid Grid.Row="0" Grid.Column="2" Margin="8" RowDefinitions="Auto,Auto,Auto,Auto,Auto" ColumnDefinitions="Auto, Auto">
<TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Margin="10,2,2,2">Minimum:</TextBlock> <TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Margin="10,2,2,2">Minimum:</TextBlock>
<NumericUpDown Grid.Row="0" Grid.Column="1" Value="{Binding #upDown.Minimum}" <NumericUpDown Grid.Row="0" Grid.Column="1" Value="{Binding #upDown.Minimum}"
CultureInfo="{Binding #upDown.CultureInfo}" VerticalAlignment="Center" Margin="2" HorizontalAlignment="Center"/> NumberFormat="{Binding #upDown.NumberFormat}" VerticalAlignment="Center" Margin="2" HorizontalAlignment="Center"/>
<TextBlock Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Margin="10,2,2,2">Maximum:</TextBlock> <TextBlock Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Margin="10,2,2,2">Maximum:</TextBlock>
<NumericUpDown Grid.Row="1" Grid.Column="1" Value="{Binding #upDown.Maximum}" <NumericUpDown Grid.Row="1" Grid.Column="1" Value="{Binding #upDown.Maximum}"
CultureInfo="{Binding #upDown.CultureInfo}" VerticalAlignment="Center" Margin="2" HorizontalAlignment="Center"/> NumberFormat="{Binding #upDown.NumberFormat}" VerticalAlignment="Center" Margin="2" HorizontalAlignment="Center"/>
<TextBlock Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" Margin="10,2,2,2">Increment:</TextBlock> <TextBlock Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" Margin="10,2,2,2">Increment:</TextBlock>
<NumericUpDown Grid.Row="2" Grid.Column="1" Value="{Binding #upDown.Increment}" VerticalAlignment="Center" <NumericUpDown Grid.Row="2" Grid.Column="1" Value="{Binding #upDown.Increment}" VerticalAlignment="Center"

8
samples/ControlCatalog/ViewModels/ContextPageViewModel.cs

@ -53,14 +53,14 @@ namespace ControlCatalog.ViewModels
var window = View?.GetVisualRoot() as Window; var window = View?.GetVisualRoot() as Window;
if (window == null) if (window == null)
return; return;
var dialog = new OpenFileDialog();
var result = await dialog.ShowAsync(window); var result = await window.StorageProvider.OpenFilePickerAsync(new Avalonia.Platform.Storage.FilePickerOpenOptions() { AllowMultiple = true });
if (result != null) if (result != null)
{ {
foreach (var path in result) foreach (var file in result)
{ {
System.Diagnostics.Debug.WriteLine($"Opened: {path}"); System.Diagnostics.Debug.WriteLine($"Opened: {file.Name}");
} }
} }
} }

7
samples/ControlCatalog/ViewModels/MenuPageViewModel.cs

@ -74,14 +74,13 @@ namespace ControlCatalog.ViewModels
var window = View?.GetVisualRoot() as Window; var window = View?.GetVisualRoot() as Window;
if (window == null) if (window == null)
return; return;
var dialog = new OpenFileDialog(); var result = await window.StorageProvider.OpenFilePickerAsync(new Avalonia.Platform.Storage.FilePickerOpenOptions() { AllowMultiple = true });
var result = await dialog.ShowAsync(window);
if (result != null) if (result != null)
{ {
foreach (var path in result) foreach (var file in result)
{ {
System.Diagnostics.Debug.WriteLine($"Opened: {path}"); System.Diagnostics.Debug.WriteLine($"Opened: {file.Name}");
} }
} }
} }

265
samples/RenderDemo/Pages/DrawingPage.xaml

@ -1,134 +1,141 @@
<UserControl <UserControl
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="RenderDemo.Pages.DrawingPage"> x:Class="RenderDemo.Pages.DrawingPage">
<UserControl.Styles> <UserControl.Styles>
<Style> <Style>
<Style.Resources> <Style.Resources>
<DrawingGroup x:Key="Bulb"> <DrawingGroup x:Key="Bulb">
<DrawingGroup.Transform> <DrawingGroup.Transform>
<MatrixTransform Matrix="1,0,0,1,0,-1028.4" /> <MatrixTransform Matrix="1,0,0,1,0,-1028.4" />
</DrawingGroup.Transform> </DrawingGroup.Transform>
<DrawingGroup> <DrawingGroup>
<DrawingGroup.Transform> <DrawingGroup.Transform>
<MatrixTransform Matrix="1,0,0,1.25,-10,1031.4" /> <MatrixTransform Matrix="1,0,0,1.25,-10,1031.4" />
</DrawingGroup.Transform> </DrawingGroup.Transform>
<GeometryDrawing Brush="#FF7F8C8D" <GeometryDrawing Brush="#FF7F8C8D"
Geometry="F1 M24,14 A2,2,0,1,1,20,14 A2,2,0,1,1,24,14 z" /> Geometry="F1 M24,14 A2,2,0,1,1,20,14 A2,2,0,1,1,24,14 z" />
</DrawingGroup> </DrawingGroup>
<GeometryDrawing Brush="#FFF39C12" <GeometryDrawing Brush="#FFF39C12"
Geometry="F1 M12,1030.4 C8.134,1030.4 5,1033.6 5,1037.6 5,1040.7 8.125,1043.5 9,1045.4 9.875,1047.2 9,1050.4 9,1050.4 L12,1049.9 15,1050.4 C15,1050.4 14.125,1047.2 15,1045.4 15.875,1043.5 19,1040.7 19,1037.6 19,1033.6 15.866,1030.4 12,1030.4 z" /> Geometry="F1 M12,1030.4 C8.134,1030.4 5,1033.6 5,1037.6 5,1040.7 8.125,1043.5 9,1045.4 9.875,1047.2 9,1050.4 9,1050.4 L12,1049.9 15,1050.4 C15,1050.4 14.125,1047.2 15,1045.4 15.875,1043.5 19,1040.7 19,1037.6 19,1033.6 15.866,1030.4 12,1030.4 z" />
<GeometryDrawing Brush="#FFF1C40F" <GeometryDrawing Brush="#FFF1C40F"
Geometry="F1 M12,1030.4 C15.866,1030.4 19,1033.6 19,1037.6 19,1040.7 15.875,1043.5 15,1045.4 14.125,1047.2 15,1050.4 15,1050.4 L12,1049.9 12,1030.4 z" /> Geometry="F1 M12,1030.4 C15.866,1030.4 19,1033.6 19,1037.6 19,1040.7 15.875,1043.5 15,1045.4 14.125,1047.2 15,1050.4 15,1050.4 L12,1049.9 12,1030.4 z" />
<GeometryDrawing Brush="#FFE67E22" <GeometryDrawing Brush="#FFE67E22"
Geometry="F1 M9,1036.4 L8,1037.4 12,1049.4 16,1037.4 15,1036.4 14,1037.4 13,1036.4 12,1037.4 11,1036.4 10,1037.4 9,1036.4 z M9,1037.4 L10,1038.4 10.5,1037.9 11,1037.4 11.5,1037.9 12,1038.4 12.5,1037.9 13,1037.4 13.5,1037.9 14,1038.4 15,1037.4 15.438,1037.8 12,1048.1 8.5625,1037.8 9,1037.4 z" /> Geometry="F1 M9,1036.4 L8,1037.4 12,1049.4 16,1037.4 15,1036.4 14,1037.4 13,1036.4 12,1037.4 11,1036.4 10,1037.4 9,1036.4 z M9,1037.4 L10,1038.4 10.5,1037.9 11,1037.4 11.5,1037.9 12,1038.4 12.5,1037.9 13,1037.4 13.5,1037.9 14,1038.4 15,1037.4 15.438,1037.8 12,1048.1 8.5625,1037.8 9,1037.4 z" />
<DrawingGroup> <DrawingGroup>
<DrawingGroup.Transform> <DrawingGroup.Transform>
<MatrixTransform Matrix="1,0,0,1,9,1045.4" /> <MatrixTransform Matrix="1,0,0,1,9,1045.4" />
</DrawingGroup.Transform> </DrawingGroup.Transform>
<GeometryDrawing Brush="#FFBDC3C7"> <GeometryDrawing Brush="#FFBDC3C7">
<GeometryDrawing.Geometry> <GeometryDrawing.Geometry>
<RectangleGeometry Rect="0,0,6,5" /> <RectangleGeometry Rect="0,0,6,5" />
</GeometryDrawing.Geometry> </GeometryDrawing.Geometry>
</GeometryDrawing> </GeometryDrawing>
</DrawingGroup> </DrawingGroup>
<GeometryDrawing Brush="#FF95A5A6" <GeometryDrawing Brush="#FF95A5A6"
Geometry="F1 M9,1045.4 L9,1050.4 12,1050.4 12,1049.4 15,1049.4 15,1048.4 12,1048.4 12,1047.4 15,1047.4 15,1046.4 12,1046.4 12,1045.4 9,1045.4 z" /> Geometry="F1 M9,1045.4 L9,1050.4 12,1050.4 12,1049.4 15,1049.4 15,1048.4 12,1048.4 12,1047.4 15,1047.4 15,1046.4 12,1046.4 12,1045.4 9,1045.4 z" />
<GeometryDrawing Brush="#FF7F8C8D" <GeometryDrawing Brush="#FF7F8C8D"
Geometry="F1 M9,1046.4 L9,1047.4 12,1047.4 12,1046.4 9,1046.4 z M9,1048.4 L9,1049.4 12,1049.4 12,1048.4 9,1048.4 z" /> Geometry="F1 M9,1046.4 L9,1047.4 12,1047.4 12,1046.4 9,1046.4 z M9,1048.4 L9,1049.4 12,1049.4 12,1048.4 9,1048.4 z" />
</DrawingGroup> </DrawingGroup>
</Style.Resources> </Style.Resources>
</Style> </Style>
</UserControl.Styles> </UserControl.Styles>
<Grid RowDefinitions="Auto,Auto,Auto" <Grid RowDefinitions="Auto,Auto,Auto"
ColumnDefinitions="Auto,Auto,Auto,Auto"> ColumnDefinitions="Auto,Auto,Auto,Auto">
<TextBlock Text="None" <TextBlock Text="None"
Margin="3" /> Margin="3" />
<Border Grid.Column="0" <Border Grid.Column="0"
Grid.Row="1" Grid.Row="1"
VerticalAlignment="Top" VerticalAlignment="Top"
HorizontalAlignment="Left" HorizontalAlignment="Left"
BorderThickness="1" BorderThickness="1"
BorderBrush="Gray" BorderBrush="Gray"
Margin="5"> Margin="5">
<DrawingPresenter Drawing="{DynamicResource Bulb}" /> <Image>
</Border> <Image.Source>
<TextBlock Text="Fill" <DrawingImage Drawing="{DynamicResource Bulb}" />
Margin="3" </Image.Source>
Grid.Column="1" /> </Image>
<Border Grid.Column="1" </Border>
Grid.Row="1" <TextBlock Text="Fill"
VerticalAlignment="Top" Margin="3"
HorizontalAlignment="Left" Grid.Column="1" />
BorderThickness="1" <Border Grid.Column="1"
BorderBrush="Gray" Grid.Row="1"
Margin="5"> VerticalAlignment="Top"
<DrawingPresenter Drawing="{DynamicResource Bulb}" HorizontalAlignment="Left"
Width="100" BorderThickness="1"
Height="50" BorderBrush="Gray"
Stretch="Fill" /> Margin="5">
</Border> <Image Width="100" Height="50" Stretch="Fill">
<TextBlock Text="Uniform" <Image.Source>
Margin="3" <DrawingImage Drawing="{DynamicResource Bulb}" />
Grid.Column="2" /> </Image.Source>
<Border Grid.Column="2" </Image>
Grid.Row="1" </Border>
VerticalAlignment="Top" <TextBlock Text="Uniform"
HorizontalAlignment="Left" Margin="3"
BorderThickness="1" Grid.Column="2" />
BorderBrush="Gray" <Border Grid.Column="2"
Margin="5"> Grid.Row="1"
<DrawingPresenter Drawing="{DynamicResource Bulb}" VerticalAlignment="Top"
Width="100" HorizontalAlignment="Left"
Height="50" BorderThickness="1"
Stretch="Uniform" /> BorderBrush="Gray"
</Border> Margin="5">
<TextBlock Text="UniformToFill" <Image Width="100" Height="50" Stretch="Uniform">
Margin="3" <Image.Source>
Grid.Column="3" /> <DrawingImage Drawing="{DynamicResource Bulb}" />
<Border Grid.Column="3" </Image.Source>
Grid.Row="1" </Image>
VerticalAlignment="Top" </Border>
HorizontalAlignment="Left" <TextBlock Text="UniformToFill"
BorderThickness="1" Margin="3"
BorderBrush="Gray" Grid.Column="3" />
Margin="5"> <Border Grid.Column="3"
<DrawingPresenter Drawing="{DynamicResource Bulb}" Grid.Row="1"
Width="100" VerticalAlignment="Top"
Height="50" HorizontalAlignment="Left"
Stretch="UniformToFill" /> BorderThickness="1"
</Border> BorderBrush="Gray"
Margin="5">
<Image Width="100" Height="50" Stretch="UniformToFill">
<Image.Source>
<DrawingImage Drawing="{DynamicResource Bulb}" />
</Image.Source>
</Image>
</Border>
<!-- For comparison --> <!-- For comparison -->
<Ellipse Grid.Row="2" <Ellipse Grid.Row="2"
Grid.Column="0" Grid.Column="0"
Width="100" Width="100"
Height="50" Height="50"
Stretch="None" Stretch="None"
Fill="Blue" Fill="Blue"
Margin="5"/> Margin="5"/>
<Ellipse Grid.Row="2" <Ellipse Grid.Row="2"
Grid.Column="1" Grid.Column="1"
Width="100" Width="100"
Height="50" Height="50"
Stretch="Fill" Stretch="Fill"
Fill="Blue" Fill="Blue"
Margin="5" /> Margin="5" />
<Ellipse Grid.Row="2" <Ellipse Grid.Row="2"
Grid.Column="2" Grid.Column="2"
Width="100" Width="100"
Height="50" Height="50"
Stretch="Uniform" Stretch="Uniform"
Fill="Blue" Fill="Blue"
Margin="5" /> Margin="5" />
<Ellipse Grid.Row="2" <Ellipse Grid.Row="2"
Grid.Column="3" Grid.Column="3"
Width="100" Width="100"
Height="50" Height="50"
Stretch="UniformToFill" Stretch="UniformToFill"
Fill="Blue" Fill="Blue"
Margin="5" /> Margin="5" />
</Grid> </Grid>
</UserControl> </UserControl>

45
src/Android/Avalonia.Android/AndroidThreadingInterface.cs

@ -27,46 +27,33 @@ namespace Avalonia.Android
{ {
if (interval.TotalMilliseconds < 10) if (interval.TotalMilliseconds < 10)
interval = TimeSpan.FromMilliseconds(10); interval = TimeSpan.FromMilliseconds(10);
object l = new object();
var stopped = false; var stopped = false;
Timer timer = null; Timer timer = null;
var scheduled = false;
timer = new Timer(_ => timer = new Timer(_ =>
{ {
lock (l) if (stopped)
return;
EnsureInvokeOnMainThread(() =>
{ {
if (stopped) try
{ {
timer.Dispose(); tick();
return;
} }
if (scheduled) finally
return;
scheduled = true;
EnsureInvokeOnMainThread(() =>
{ {
try if (!stopped)
{ timer.Change(interval, Timeout.InfiniteTimeSpan);
tick(); }
} });
finally },
{ null, interval, Timeout.InfiniteTimeSpan);
lock (l)
{
scheduled = false;
}
}
});
}
}, null, TimeSpan.Zero, interval);
return Disposable.Create(() => return Disposable.Create(() =>
{ {
lock (l) stopped = true;
{ timer.Dispose();
stopped = true;
timer.Dispose();
}
}); });
} }

17
src/Avalonia.Base/Animation/Animation.cs

@ -172,23 +172,6 @@ namespace Avalonia.Animation
set { SetAndRaise(SpeedRatioProperty, ref _speedRatio, value); } set { SetAndRaise(SpeedRatioProperty, ref _speedRatio, value); }
} }
/// <summary>
/// Obsolete: Do not use this property, use <see cref="IterationCount"/> instead.
/// </summary>
/// <value></value>
[Obsolete("This property has been superceded by IterationCount.")]
public string RepeatCount
{
get { return IterationCount.ToString(); }
set
{
var val = value.ToUpper();
val = val.Replace("LOOP", "INFINITE");
val = val.Replace("NONE", "1");
IterationCount = IterationCount.Parse(val);
}
}
/// <summary> /// <summary>
/// Gets the children of the <see cref="Animation"/>. /// Gets the children of the <see cref="Animation"/>.
/// </summary> /// </summary>

13
src/Avalonia.Base/Animation/Animators/SolidColorBrushAnimator.cs

@ -37,17 +37,4 @@ namespace Avalonia.Animation.Animators
} }
} }
[Obsolete("Use ISolidColorBrushAnimator instead")]
public class SolidColorBrushAnimator : Animator<SolidColorBrush?>
{
public override SolidColorBrush? Interpolate(double progress, SolidColorBrush? oldValue, SolidColorBrush? newValue)
{
if (oldValue is null || newValue is null)
{
return progress >= 0.5 ? newValue : oldValue;
}
return new SolidColorBrush(ColorAnimator.InterpolateCore(progress, oldValue.Color, newValue.Color));
}
}
} }

35
src/Avalonia.Base/AvaloniaObjectExtensions.cs

@ -468,41 +468,6 @@ namespace Avalonia
}); });
} }
/// <summary>
/// Subscribes to a property changed notifications for changes that originate from a
/// <typeparamref name="TTarget"/>.
/// </summary>
/// <typeparam name="TTarget">The type of the property change sender.</typeparam>
/// <param name="observable">The property changed observable.</param>
/// <param name="handler">Given a TTarget, returns the handler.</param>
/// <returns>A disposable that can be used to terminate the subscription.</returns>
[Obsolete("Use overload taking Action<TTarget, AvaloniaPropertyChangedEventArgs>.")]
public static IDisposable AddClassHandler<TTarget>(
this IObservable<AvaloniaPropertyChangedEventArgs> observable,
Func<TTarget, Action<AvaloniaPropertyChangedEventArgs>> handler)
where TTarget : class
{
return observable.Subscribe(e => SubscribeAdapter(e, handler));
}
/// <summary>
/// Observer method for <see cref="AddClassHandler{TTarget}(IObservable{AvaloniaPropertyChangedEventArgs},
/// Func{TTarget, Action{AvaloniaPropertyChangedEventArgs}})"/>.
/// </summary>
/// <typeparam name="TTarget">The sender type to accept.</typeparam>
/// <param name="e">The event args.</param>
/// <param name="handler">Given a TTarget, returns the handler.</param>
private static void SubscribeAdapter<TTarget>(
AvaloniaPropertyChangedEventArgs e,
Func<TTarget, Action<AvaloniaPropertyChangedEventArgs>> handler)
where TTarget : class
{
if (e.Sender is TTarget target)
{
handler(target)(e);
}
}
private class BindingAdaptor : IBinding private class BindingAdaptor : IBinding
{ {
private IObservable<object?> _source; private IObservable<object?> _source;

15
src/Avalonia.Base/AvaloniaProperty`1.cs

@ -30,21 +30,6 @@ namespace Avalonia
_changed = new Subject<AvaloniaPropertyChangedEventArgs<TValue>>(); _changed = new Subject<AvaloniaPropertyChangedEventArgs<TValue>>();
} }
/// <summary>
/// Initializes a new instance of the <see cref="AvaloniaProperty{TValue}"/> class.
/// </summary>
/// <param name="source">The property to copy.</param>
/// <param name="ownerType">The new owner type.</param>
/// <param name="metadata">Optional overridden metadata.</param>
[Obsolete("Use constructor with AvaloniaProperty<TValue> instead.", true)]
protected AvaloniaProperty(
AvaloniaProperty source,
Type ownerType,
AvaloniaPropertyMetadata? metadata)
: this(source as AvaloniaProperty<TValue> ?? throw new InvalidOperationException(), ownerType, metadata)
{
}
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="AvaloniaProperty{TValue}"/> class. /// Initializes a new instance of the <see cref="AvaloniaProperty{TValue}"/> class.
/// </summary> /// </summary>

15
src/Avalonia.Base/Collections/AvaloniaListExtensions.cs

@ -140,21 +140,6 @@ namespace Avalonia.Collections
} }
} }
[Obsolete("Causes memory leaks. Use DynamicData or similar instead.")]
public static IAvaloniaReadOnlyList<TDerived> CreateDerivedList<TSource, TDerived>(
this IAvaloniaReadOnlyList<TSource> collection,
Func<TSource, TDerived> select)
{
var result = new AvaloniaList<TDerived>();
collection.ForEachItem(
(i, item) => result.Insert(i, select(item)),
(i, item) => result.RemoveAt(i),
() => result.Clear());
return result;
}
/// <summary> /// <summary>
/// Listens for property changed events from all items in a collection. /// Listens for property changed events from all items in a collection.
/// </summary> /// </summary>

15
src/Avalonia.Base/DirectPropertyBase.cs

@ -29,21 +29,6 @@ namespace Avalonia
{ {
} }
/// <summary>
/// Initializes a new instance of the <see cref="DirectPropertyBase{TValue}"/> class.
/// </summary>
/// <param name="source">The property to copy.</param>
/// <param name="ownerType">The new owner type.</param>
/// <param name="metadata">Optional overridden metadata.</param>
[Obsolete("Use constructor with DirectPropertyBase<TValue> instead.", true)]
protected DirectPropertyBase(
AvaloniaProperty source,
Type ownerType,
AvaloniaPropertyMetadata metadata)
: this(source as DirectPropertyBase<TValue> ?? throw new InvalidOperationException(), ownerType, metadata)
{
}
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="DirectPropertyBase{TValue}"/> class. /// Initializes a new instance of the <see cref="DirectPropertyBase{TValue}"/> class.
/// </summary> /// </summary>

4
src/Avalonia.Base/EnumExtensions.cs

@ -8,10 +8,6 @@ namespace Avalonia
/// </summary> /// </summary>
public static class EnumExtensions public static class EnumExtensions
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[Obsolete("This method is obsolete. Use HasAllFlags instead.")]
public static bool HasFlagCustom<T>(this T value, T flag) where T : unmanaged, Enum
=> value.HasAllFlags(flag);
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe bool HasAllFlags<T>(this T value, T flags) where T : unmanaged, Enum public static unsafe bool HasAllFlags<T>(this T value, T flags) where T : unmanaged, Enum

3
src/Avalonia.Base/Input/PointerOverPreProcessor.cs

@ -158,6 +158,8 @@ namespace Avalonia.Input
ClearPointerOver(pointer, root, timestamp, position, properties, inputModifiers); ClearPointerOver(pointer, root, timestamp, position, properties, inputModifiers);
} }
} }
_lastPointer = (pointer, root.PointToScreen(position));
} }
private void SetPointerOverToElement(IPointer pointer, IInputRoot root, IInputElement element, private void SetPointerOverToElement(IPointer pointer, IInputRoot root, IInputElement element,
@ -195,7 +197,6 @@ namespace Avalonia.Input
} }
el = root.PointerOverElement = element; el = root.PointerOverElement = element;
_lastPointer = (pointer, root.PointToScreen(position));
e.RoutedEvent = InputElement.PointerEnteredEvent; e.RoutedEvent = InputElement.PointerEnteredEvent;

18
src/Avalonia.Base/Interactivity/RoutedEvent.cs

@ -113,24 +113,6 @@ namespace Avalonia.Interactivity
{ {
} }
[Obsolete("Use overload taking Action<TTarget, TEventArgs>.")]
public IDisposable AddClassHandler<TTarget>(
Func<TTarget, Action<TEventArgs>> handler,
RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble,
bool handledEventsToo = false)
where TTarget : class, IInteractive
{
void Adapter(object? sender, RoutedEventArgs e)
{
if (sender is TTarget target && e is TEventArgs args)
{
handler(target)(args);
}
}
return AddClassHandler(typeof(TTarget), Adapter, routes, handledEventsToo);
}
public IDisposable AddClassHandler<TTarget>( public IDisposable AddClassHandler<TTarget>(
Action<TTarget, TEventArgs> handler, Action<TTarget, TEventArgs> handler,
RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble, RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble,

11
src/Avalonia.Base/Layout/ILayoutManager.cs

@ -44,17 +44,6 @@ namespace Avalonia.Layout
/// </remarks> /// </remarks>
void ExecuteInitialLayoutPass(); void ExecuteInitialLayoutPass();
/// <summary>
/// Executes the initial layout pass on a layout root.
/// </summary>
/// <param name="root">The control to lay out.</param>
/// <remarks>
/// You should not usually need to call this method explictly, the layout root will call
/// it to carry out the initial layout of the control.
/// </remarks>
[Obsolete("Call ExecuteInitialLayoutPass without parameter")]
void ExecuteInitialLayoutPass(ILayoutRoot root);
/// <summary> /// <summary>
/// Registers a control as wanting to receive effective viewport notifications. /// Registers a control as wanting to receive effective viewport notifications.
/// </summary> /// </summary>

11
src/Avalonia.Base/Layout/LayoutManager.cs

@ -196,17 +196,6 @@ namespace Avalonia.Layout
ExecuteLayoutPass(); ExecuteLayoutPass();
} }
[Obsolete("Call ExecuteInitialLayoutPass without parameter")]
public void ExecuteInitialLayoutPass(ILayoutRoot root)
{
if (root != _owner)
{
throw new ArgumentException("ExecuteInitialLayoutPass called with incorrect root.");
}
ExecuteInitialLayoutPass();
}
public void Dispose() public void Dispose()
{ {
_disposed = true; _disposed = true;

28
src/Avalonia.Base/Layout/Layoutable.cs

@ -460,20 +460,6 @@ namespace Avalonia.Layout
_effectiveViewportChanged?.Invoke(this, e); _effectiveViewportChanged?.Invoke(this, e);
} }
/// <summary>
/// Marks a property as affecting the control's measurement.
/// </summary>
/// <param name="properties">The properties.</param>
/// <remarks>
/// After a call to this method in a control's static constructor, any change to the
/// property will cause <see cref="InvalidateMeasure"/> to be called on the element.
/// </remarks>
[Obsolete("Use AffectsMeasure<T> and specify the control type.")]
protected static void AffectsMeasure(params AvaloniaProperty[] properties)
{
AffectsMeasure<Layoutable>(properties);
}
/// <summary> /// <summary>
/// Marks a property as affecting the control's measurement. /// Marks a property as affecting the control's measurement.
/// </summary> /// </summary>
@ -497,20 +483,6 @@ namespace Avalonia.Layout
} }
} }
/// <summary>
/// Marks a property as affecting the control's arrangement.
/// </summary>
/// <param name="properties">The properties.</param>
/// <remarks>
/// After a call to this method in a control's static constructor, any change to the
/// property will cause <see cref="InvalidateArrange"/> to be called on the element.
/// </remarks>
[Obsolete("Use AffectsArrange<T> and specify the control type.")]
protected static void AffectsArrange(params AvaloniaProperty[] properties)
{
AffectsArrange<Layoutable>(properties);
}
/// <summary> /// <summary>
/// Marks a property as affecting the control's arrangement. /// Marks a property as affecting the control's arrangement.
/// </summary> /// </summary>

16
src/Avalonia.Base/Media/Imaging/Bitmap.cs

@ -89,22 +89,6 @@ namespace Avalonia.Media.Imaging
PlatformImpl.Dispose(); PlatformImpl.Dispose();
} }
/// <summary>
/// Initializes a new instance of the <see cref="Bitmap"/> class.
/// </summary>
/// <param name="format">The pixel format.</param>
/// <param name="data">The pointer to the source bytes.</param>
/// <param name="size">The size of the bitmap in device pixels.</param>
/// <param name="dpi">The DPI of the bitmap.</param>
/// <param name="stride">The number of bytes per row.</param>
[Obsolete("Use overload taking an AlphaFormat.")]
public Bitmap(PixelFormat format, IntPtr data, PixelSize size, Vector dpi, int stride)
{
var ri = GetFactory();
PlatformImpl = RefCountable.Create(ri
.LoadBitmap(format, ri.DefaultAlphaFormat, data, size, dpi, stride));
}
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Bitmap"/> class. /// Initializes a new instance of the <see cref="Bitmap"/> class.
/// </summary> /// </summary>

14
src/Avalonia.Base/Media/Imaging/WriteableBitmap.cs

@ -9,18 +9,6 @@ namespace Avalonia.Media.Imaging
/// </summary> /// </summary>
public class WriteableBitmap : Bitmap public class WriteableBitmap : Bitmap
{ {
/// <summary>
/// Initializes a new instance of the <see cref="WriteableBitmap"/> class.
/// </summary>
/// <param name="size">The size of the bitmap in device pixels.</param>
/// <param name="dpi">The DPI of the bitmap.</param>
/// <param name="format">The pixel format (optional).</param>
/// <returns>An <see cref="IWriteableBitmapImpl"/>.</returns>
[Obsolete("Use overload taking an AlphaFormat.")]
public WriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null)
: base(CreatePlatformImpl(size, dpi, format, null))
{
}
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="WriteableBitmap"/> class. /// Initializes a new instance of the <see cref="WriteableBitmap"/> class.
@ -30,7 +18,7 @@ namespace Avalonia.Media.Imaging
/// <param name="format">The pixel format (optional).</param> /// <param name="format">The pixel format (optional).</param>
/// <param name="alphaFormat">The alpha format (optional).</param> /// <param name="alphaFormat">The alpha format (optional).</param>
/// <returns>An <see cref="IWriteableBitmapImpl"/>.</returns> /// <returns>An <see cref="IWriteableBitmapImpl"/>.</returns>
public WriteableBitmap(PixelSize size, Vector dpi, PixelFormat format, AlphaFormat alphaFormat) public WriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null, AlphaFormat? alphaFormat = null)
: base(CreatePlatformImpl(size, dpi, format, alphaFormat)) : base(CreatePlatformImpl(size, dpi, format, alphaFormat))
{ {
} }

35
src/Avalonia.Base/Utilities/MathUtilities.cs

@ -324,6 +324,41 @@ namespace Avalonia.Utilities
return angle * 2 * Math.PI; return angle * 2 * Math.PI;
} }
/// <summary>
/// Calculates the point of an angle on an ellipse.
/// </summary>
/// <param name="centre">The centre point of the ellipse.</param>
/// <param name="radiusX">The x radius of the ellipse.</param>
/// <param name="radiusY">The y radius of the ellipse.</param>
/// <param name="angle">The angle in radians.</param>
/// <returns>A point on the ellipse.</returns>
public static Point GetEllipsePoint(Point centre, double radiusX, double radiusY, double angle)
{
return new Point(radiusX * Math.Cos(angle) + centre.X, radiusY * Math.Sin(angle) + centre.Y);
}
/// <summary>
/// Gets the minimum and maximum from the specified numbers.
/// </summary>
/// <param name="a">The first number.</param>
/// <param name="b">The second number.</param>
/// <returns>A tuple containing the minimum and maximum of the two specified numbers.</returns>
public static (double min, double max) GetMinMax(double a, double b)
{
return a < b ? (a, b) : (b, a);
}
/// <summary>
/// Gets the minimum and maximum from the specified number and the difference with that number.
/// </summary>
/// <param name="initialValue">The initial value to use.</param>
/// <param name="delta">The difference for <paramref name="initialValue"/>.</param>
/// <returns>A tuple containing the minimum and maximum of the specified number and the difference with that number.</returns>
public static (double min, double max) GetMinMaxFromDelta(double initialValue, double delta)
{
return GetMinMax(initialValue, initialValue + delta);
}
private static void ThrowCannotBeGreaterThanException<T>(T min, T max) private static void ThrowCannotBeGreaterThanException<T>(T min, T max)
{ {
throw new ArgumentException($"{min} cannot be greater than {max}."); throw new ArgumentException($"{min} cannot be greater than {max}.");

25
src/Avalonia.Base/Utilities/WeakObservable.cs

@ -9,31 +9,6 @@ namespace Avalonia.Utilities
/// </summary> /// </summary>
public static class WeakObservable public static class WeakObservable
{ {
/// <summary>
/// Converts a .NET event conforming to the standard .NET event pattern into an observable
/// sequence, subscribing weakly.
/// </summary>
/// <typeparam name="TTarget">The type of target.</typeparam>
/// <typeparam name="TEventArgs">The type of the event args.</typeparam>
/// <param name="target">Object instance that exposes the event to convert.</param>
/// <param name="eventName">Name of the event to convert.</param>
/// <returns></returns>
[Obsolete("Use WeakEvent-based overload")]
public static IObservable<EventPattern<object, TEventArgs>> FromEventPattern<TTarget, TEventArgs>(
TTarget target,
string eventName)
where TEventArgs : EventArgs
{
_ = target ?? throw new ArgumentNullException(nameof(target));
_ = eventName ?? throw new ArgumentNullException(nameof(eventName));
return Observable.Create<EventPattern<object, TEventArgs>>(observer =>
{
var handler = new Handler<TEventArgs>(observer);
WeakSubscriptionManager.Subscribe(target, eventName, handler);
return () => WeakSubscriptionManager.Unsubscribe(target, eventName, handler);
}).Publish().RefCount();
}
private class Handler<TEventArgs> private class Handler<TEventArgs>
: IWeakSubscriber<TEventArgs>, : IWeakSubscriber<TEventArgs>,

193
src/Avalonia.Base/Utilities/WeakSubscriptionManager.cs

@ -1,193 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
namespace Avalonia.Utilities
{
/// <summary>
/// Manages subscriptions to events using weak listeners.
/// </summary>
public static class WeakSubscriptionManager
{
/// <summary>
/// Subscribes to an event on an object using a weak subscription.
/// </summary>
/// <typeparam name="TTarget">The type of the target.</typeparam>
/// <typeparam name="TEventArgs">The type of the event arguments.</typeparam>
/// <param name="target">The event source.</param>
/// <param name="eventName">The name of the event.</param>
/// <param name="subscriber">The subscriber.</param>
[Obsolete("Use WeakEvent")]
public static void Subscribe<TTarget, TEventArgs>(TTarget target, string eventName, IWeakSubscriber<TEventArgs> subscriber)
where TEventArgs : EventArgs
{
_ = target ?? throw new ArgumentNullException(nameof(target));
var dic = SubscriptionTypeStorage<TEventArgs>.Subscribers.GetOrCreateValue(target);
if (!dic.TryGetValue(eventName, out var sub))
{
dic[eventName] = sub = new Subscription<TEventArgs>(dic, typeof(TTarget), target, eventName);
}
sub.Add(new WeakReference<IWeakSubscriber<TEventArgs>>(subscriber));
}
/// <summary>
/// Unsubscribes from an event.
/// </summary>
/// <typeparam name="T">The type of the event arguments.</typeparam>
/// <param name="target">The event source.</param>
/// <param name="eventName">The name of the event.</param>
/// <param name="subscriber">The subscriber.</param>
public static void Unsubscribe<T>(object target, string eventName, IWeakSubscriber<T> subscriber)
where T : EventArgs
{
if (SubscriptionTypeStorage<T>.Subscribers.TryGetValue(target, out var dic))
{
if (dic.TryGetValue(eventName, out var sub))
{
sub.Remove(subscriber);
}
}
}
private static class SubscriptionTypeStorage<T>
where T : EventArgs
{
public static readonly ConditionalWeakTable<object, SubscriptionDic<T>> Subscribers
= new ConditionalWeakTable<object, SubscriptionDic<T>>();
}
private class SubscriptionDic<T> : Dictionary<string, Subscription<T>>
where T : EventArgs
{
}
private static readonly Dictionary<Type, Dictionary<string, EventInfo>> Accessors
= new Dictionary<Type, Dictionary<string, EventInfo>>();
private class Subscription<T> where T : EventArgs
{
private readonly EventInfo _info;
private readonly SubscriptionDic<T> _sdic;
private readonly object _target;
private readonly string _eventName;
private readonly Delegate _delegate;
private WeakReference<IWeakSubscriber<T>>?[] _data = new WeakReference<IWeakSubscriber<T>>?[16];
private int _count = 0;
public Subscription(SubscriptionDic<T> sdic, Type targetType, object target, string eventName)
{
_sdic = sdic;
_target = target;
_eventName = eventName;
if (!Accessors.TryGetValue(targetType, out var evDic))
Accessors[targetType] = evDic = new Dictionary<string, EventInfo>();
if (evDic.TryGetValue(eventName, out var info))
{
_info = info;
}
else
{
var ev = targetType.GetRuntimeEvents().FirstOrDefault(x => x.Name == eventName);
if (ev == null)
{
throw new ArgumentException(
$"The event {eventName} was not found on {target.GetType()}.");
}
evDic[eventName] = _info = ev;
}
var del = new Action<object, T>(OnEvent);
_delegate = del.GetMethodInfo().CreateDelegate(_info.EventHandlerType!, del.Target);
_info.AddMethod!.Invoke(target, new[] { _delegate });
}
void Destroy()
{
_info.RemoveMethod!.Invoke(_target, new[] { _delegate });
_sdic.Remove(_eventName);
}
public void Add(WeakReference<IWeakSubscriber<T>> s)
{
if (_count == _data.Length)
{
//Extend capacity
var ndata = new WeakReference<IWeakSubscriber<T>>?[_data.Length*2];
Array.Copy(_data, ndata, _data.Length);
_data = ndata;
}
_data[_count] = s!;
_count++;
}
public void Remove(IWeakSubscriber<T> s)
{
var removed = false;
for (int c = 0; c < _count; ++c)
{
var reference = _data[c];
IWeakSubscriber<T>? instance;
if (reference != null && reference.TryGetTarget(out instance) && instance == s)
{
_data[c] = null;
removed = true;
}
}
if (removed)
{
Compact();
}
}
void Compact()
{
int empty = -1;
for (int c = 0; c < _count; c++)
{
var r = _data[c];
//Mark current index as first empty
if (r == null && empty == -1)
empty = c;
//If current element isn't null and we have an empty one
if (r != null && empty != -1)
{
_data[c] = null;
_data[empty] = r;
empty++;
}
}
if (empty != -1)
_count = empty;
if (_count == 0)
Destroy();
}
void OnEvent(object sender, T eventArgs)
{
var needCompact = false;
for(var c=0; c<_count; c++)
{
var r = _data[c];
if (r?.TryGetTarget(out var sub) == true)
sub!.OnEvent(sender, eventArgs);
else
needCompact = true;
}
if (needCompact)
Compact();
}
}
}
}

16
src/Avalonia.Base/Visual.cs

@ -338,22 +338,6 @@ namespace Avalonia
Contract.Requires<ArgumentNullException>(context != null); Contract.Requires<ArgumentNullException>(context != null);
} }
/// <summary>
/// Indicates that a property change should cause <see cref="InvalidateVisual"/> to be
/// called.
/// </summary>
/// <param name="properties">The properties.</param>
/// <remarks>
/// This method should be called in a control's static constructor with each property
/// on the control which when changed should cause a redraw. This is similar to WPF's
/// FrameworkPropertyMetadata.AffectsRender flag.
/// </remarks>
[Obsolete("Use AffectsRender<T> and specify the control type.")]
protected static void AffectsRender(params AvaloniaProperty[] properties)
{
AffectsRender<Visual>(properties);
}
/// <summary> /// <summary>
/// Indicates that a property change should cause <see cref="InvalidateVisual"/> to be /// Indicates that a property change should cause <see cref="InvalidateVisual"/> to be
/// called. /// called.

19
src/Avalonia.Base/VisualTree/IVisualTreeHost.cs

@ -1,19 +0,0 @@
using System;
namespace Avalonia.VisualTree
{
/// <summary>
/// Interface for controls that host their own separate visual tree, such as popups.
/// </summary>
[Obsolete]
public interface IVisualTreeHost
{
/// <summary>
/// Gets the root of the hosted visual tree.
/// </summary>
/// <value>
/// The root of the hosted visual tree.
/// </value>
IVisual? Root { get; }
}
}

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

@ -11,15 +11,20 @@
Stretch="Uniform" Stretch="Uniform"
DestinationRect="0,0,8,8"> DestinationRect="0,0,8,8">
<VisualBrush.Visual> <VisualBrush.Visual>
<DrawingPresenter Width="8" <Image Width="8" Height="8">
Height="8"> <Image.Source>
<DrawingGroup> <DrawingImage>
<GeometryDrawing Geometry="M0,0 L2,0 2,2, 0,2Z" <DrawingImage.Drawing>
Brush="Transparent" /> <DrawingGroup>
<GeometryDrawing Geometry="M0,1 L2,1 2,2, 1,2 1,0 0,0Z" <GeometryDrawing Geometry="M0,0 L2,0 2,2, 0,2Z"
Brush="#19808080" /> Brush="Transparent" />
</DrawingGroup> <GeometryDrawing Geometry="M0,1 L2,1 2,2, 1,2 1,0 0,0Z"
</DrawingPresenter> Brush="#19808080" />
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
</VisualBrush.Visual> </VisualBrush.Visual>
</VisualBrush> </VisualBrush>

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

@ -11,15 +11,20 @@
Stretch="Uniform" Stretch="Uniform"
DestinationRect="0,0,8,8"> DestinationRect="0,0,8,8">
<VisualBrush.Visual> <VisualBrush.Visual>
<DrawingPresenter Width="8" <Image Width="8" Height="8">
Height="8"> <Image.Source>
<DrawingGroup> <DrawingImage>
<GeometryDrawing Geometry="M0,0 L2,0 2,2, 0,2Z" <DrawingImage.Drawing>
Brush="Transparent" /> <DrawingGroup>
<GeometryDrawing Geometry="M0,1 L2,1 2,2, 1,2 1,0 0,0Z" <GeometryDrawing Geometry="M0,0 L2,0 2,2, 0,2Z"
Brush="#19808080" /> Brush="Transparent" />
</DrawingGroup> <GeometryDrawing Geometry="M0,1 L2,1 2,2, 1,2 1,0 0,0Z"
</DrawingPresenter> Brush="#19808080" />
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
</VisualBrush.Visual> </VisualBrush.Visual>
</VisualBrush> </VisualBrush>

30
src/Avalonia.Controls.DataGrid/DataGrid.cs

@ -2167,7 +2167,23 @@ namespace Avalonia.Controls
return desiredSize; return desiredSize;
} }
/// <inheritdoc/>
protected override void OnDataContextBeginUpdate()
{
base.OnDataContextBeginUpdate();
NotifyDataContextPropertyForAllRowCells(GetAllRows(), true);
}
/// <inheritdoc/>
protected override void OnDataContextEndUpdate()
{
base.OnDataContextEndUpdate();
NotifyDataContextPropertyForAllRowCells(GetAllRows(), false);
}
/// <summary> /// <summary>
/// Raises the BeginningEdit event. /// Raises the BeginningEdit event.
/// </summary> /// </summary>
@ -3165,6 +3181,20 @@ namespace Avalonia.Controls
} }
} }
private static void NotifyDataContextPropertyForAllRowCells(IEnumerable<DataGridRow> rowSource, bool arg2)
{
foreach (DataGridRow row in rowSource)
{
foreach (DataGridCell cell in row.Cells)
{
if (cell.Content is StyledElement cellContent)
{
DataContextProperty.Notifying?.Invoke(cellContent, arg2);
}
}
}
}
private void UpdateRowDetailsVisibilityMode(DataGridRowDetailsVisibilityMode newDetailsMode) private void UpdateRowDetailsVisibilityMode(DataGridRowDetailsVisibilityMode newDetailsMode)
{ {
int itemCount = DataConnection.Count; int itemCount = DataConnection.Count;

6
src/Avalonia.Controls/Control.cs

@ -8,6 +8,7 @@ using Avalonia.Controls.Templates;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Input.Platform; using Avalonia.Input.Platform;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.LogicalTree;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Rendering; using Avalonia.Rendering;
using Avalonia.Styling; using Avalonia.Styling;
@ -104,7 +105,6 @@ namespace Avalonia.Controls
private static readonly HashSet<Control> _loadedQueue = new HashSet<Control>(); private static readonly HashSet<Control> _loadedQueue = new HashSet<Control>();
private static readonly HashSet<Control> _loadedProcessingQueue = new HashSet<Control>(); private static readonly HashSet<Control> _loadedProcessingQueue = new HashSet<Control>();
private bool _isAttachedToVisualTree = false;
private bool _isLoaded = false; private bool _isLoaded = false;
private DataTemplates? _dataTemplates; private DataTemplates? _dataTemplates;
private IControl? _focusAdorner; private IControl? _focusAdorner;
@ -347,7 +347,7 @@ namespace Avalonia.Controls
internal void OnLoadedCore() internal void OnLoadedCore()
{ {
if (_isLoaded == false && if (_isLoaded == false &&
_isAttachedToVisualTree) ((ILogical)this).IsAttachedToLogicalTree)
{ {
_isLoaded = true; _isLoaded = true;
OnLoaded(); OnLoaded();
@ -395,7 +395,6 @@ namespace Avalonia.Controls
protected sealed override void OnAttachedToVisualTreeCore(VisualTreeAttachmentEventArgs e) protected sealed override void OnAttachedToVisualTreeCore(VisualTreeAttachmentEventArgs e)
{ {
base.OnAttachedToVisualTreeCore(e); base.OnAttachedToVisualTreeCore(e);
_isAttachedToVisualTree = true;
InitializeIfNeeded(); InitializeIfNeeded();
@ -406,7 +405,6 @@ namespace Avalonia.Controls
protected sealed override void OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) protected sealed override void OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e)
{ {
base.OnDetachedFromVisualTreeCore(e); base.OnDetachedFromVisualTreeCore(e);
_isAttachedToVisualTree = false;
OnUnloadedCore(); OnUnloadedCore();
} }

2
src/Avalonia.Controls/DesktopApplicationExtensions.cs

@ -8,8 +8,6 @@ namespace Avalonia.Controls
{ {
public static class DesktopApplicationExtensions public static class DesktopApplicationExtensions
{ {
[Obsolete("Running application without a cancellation token and a lifetime is no longer supported, see https://github.com/AvaloniaUI/Avalonia/wiki/Application-lifetimes for details", true)]
public static void Run(this Application app) => throw new NotSupportedException();
/// <summary> /// <summary>
/// On desktop-style platforms runs the application's main loop until closable is closed /// On desktop-style platforms runs the application's main loop until closable is closed

61
src/Avalonia.Controls/DrawingPresenter.cs

@ -1,61 +0,0 @@
using System;
using Avalonia.Controls.Shapes;
using Avalonia.Media;
using Avalonia.Metadata;
namespace Avalonia.Controls
{
[Obsolete("Use Image control with DrawingImage source")]
public class DrawingPresenter : Control
{
static DrawingPresenter()
{
AffectsMeasure<DrawingPresenter>(DrawingProperty);
AffectsRender<DrawingPresenter>(DrawingProperty);
}
public static readonly StyledProperty<Drawing> DrawingProperty =
AvaloniaProperty.Register<DrawingPresenter, Drawing>(nameof(Drawing));
public static readonly StyledProperty<Stretch> StretchProperty =
AvaloniaProperty.Register<DrawingPresenter, Stretch>(nameof(Stretch), Stretch.Uniform);
[Content]
public Drawing Drawing
{
get => GetValue(DrawingProperty);
set => SetValue(DrawingProperty, value);
}
public Stretch Stretch
{
get => GetValue(StretchProperty);
set => SetValue(StretchProperty, value);
}
private Matrix _transform = Matrix.Identity;
protected override Size MeasureOverride(Size availableSize)
{
if (Drawing == null) return new Size();
var (size, transform) = Shape.CalculateSizeAndTransform(availableSize, Drawing.GetBounds(), Stretch);
_transform = transform;
return size;
}
public override void Render(DrawingContext context)
{
if (Drawing != null)
{
using (context.PushPreTransform(_transform))
using (context.PushClip(new Rect(Bounds.Size)))
{
Drawing.Draw(context);
}
}
}
}
}

13
src/Avalonia.Controls/LoggingExtensions.cs

@ -1,21 +1,10 @@
using System; using Avalonia.Controls;
using Avalonia.Controls;
using Avalonia.Logging; using Avalonia.Logging;
namespace Avalonia namespace Avalonia
{ {
public static class LoggingExtensions public static class LoggingExtensions
{ {
[Obsolete("Use LogToTrace")]
public static T LogToDebug<T>(
this T builder,
LogEventLevel level = LogEventLevel.Warning,
params string[] areas)
where T : AppBuilderBase<T>, new()
{
return LogToTrace(builder, level, areas);
}
/// <summary> /// <summary>
/// Logs Avalonia events to the <see cref="System.Diagnostics.Trace"/> sink. /// Logs Avalonia events to the <see cref="System.Diagnostics.Trace"/> sink.
/// </summary> /// </summary>

7
src/Avalonia.Controls/NativeMenuItem.cs

@ -186,13 +186,6 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
public event EventHandler? Click; public event EventHandler? Click;
[Obsolete("Use Click event.")]
public event EventHandler Clicked
{
add => Click += value;
remove => Click -= value;
}
void INativeMenuItemExporterEventsImplBridge.RaiseClicked() void INativeMenuItemExporterEventsImplBridge.RaiseClicked()
{ {
Click?.Invoke(this, new EventArgs()); Click?.Invoke(this, new EventArgs());

13
src/Avalonia.Controls/NativeMenuItemSeparator.cs

@ -1,16 +1,7 @@
using System; namespace Avalonia.Controls
namespace Avalonia.Controls
{ {
[Obsolete("This class exists to maintain backwards compatibility with existing code. Use NativeMenuItemSeparator instead")]
public class NativeMenuItemSeperator : NativeMenuItemSeparator
{
}
public class NativeMenuItemSeparator : NativeMenuItemBase public class NativeMenuItemSeparator : NativeMenuItemBase
{ {
[Obsolete("This is a temporary hack to make our MenuItem recognize this as a separator, don't use", true)]
public string Header => "-";
} }
} }

11
src/Avalonia.Controls/Notifications/WindowNotificationManager.cs

@ -102,15 +102,12 @@ namespace Avalonia.Controls.Notifications
Content = content Content = content
}; };
if (notification != null) notificationControl.NotificationClosed += (sender, args) =>
{ {
notificationControl.NotificationClosed += (sender, args) => notification?.OnClose?.Invoke();
{
notification.OnClose?.Invoke();
_items?.Remove(sender); _items?.Remove(sender);
}; };
}
notificationControl.PointerPressed += (sender, args) => notificationControl.PointerPressed += (sender, args) =>
{ {

53
src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs

@ -45,14 +45,6 @@ namespace Avalonia.Controls
AvaloniaProperty.RegisterDirect<NumericUpDown, bool>(nameof(ClipValueToMinMax), AvaloniaProperty.RegisterDirect<NumericUpDown, bool>(nameof(ClipValueToMinMax),
updown => updown.ClipValueToMinMax, (updown, b) => updown.ClipValueToMinMax = b); updown => updown.ClipValueToMinMax, (updown, b) => updown.ClipValueToMinMax = b);
/// <summary>
/// Defines the <see cref="CultureInfo"/> property.
/// </summary>
[Obsolete]
public static readonly DirectProperty<NumericUpDown, CultureInfo?> CultureInfoProperty =
AvaloniaProperty.RegisterDirect<NumericUpDown, CultureInfo?>(nameof(CultureInfo), o => o.CultureInfo,
(o, v) => o.CultureInfo = v, CultureInfo.CurrentCulture);
/// <summary> /// <summary>
/// Defines the <see cref="NumberFormat"/> property. /// Defines the <see cref="NumberFormat"/> property.
/// </summary> /// </summary>
@ -187,21 +179,6 @@ namespace Avalonia.Controls
set { SetAndRaise(ClipValueToMinMaxProperty, ref _clipValueToMinMax, value); } set { SetAndRaise(ClipValueToMinMaxProperty, ref _clipValueToMinMax, value); }
} }
/// <summary>
/// Gets or sets the current CultureInfo.
/// </summary>
[Obsolete("CultureInfo is obsolete, please use NumberFormat instead.")]
public CultureInfo? CultureInfo
{
get { return _cultureInfo; }
set
{
SetAndRaise(CultureInfoProperty, ref _cultureInfo, value);
//Set and Raise the NumberFormatProperty when CultureInfo is changed.
SetAndRaise(NumberFormatProperty, ref _numberFormat, value?.NumberFormat);
}
}
/// <summary> /// <summary>
/// Gets or sets the current NumberFormatInfo /// Gets or sets the current NumberFormatInfo
/// </summary> /// </summary>
@ -335,9 +312,6 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
static NumericUpDown() static NumericUpDown()
{ {
#pragma warning disable CS0612 // Type or member is obsolete
CultureInfoProperty.Changed.Subscribe(OnCultureInfoChanged);
#pragma warning restore CS0612 // Type or member is obsolete
NumberFormatProperty.Changed.Subscribe(OnNumberFormatChanged); NumberFormatProperty.Changed.Subscribe(OnNumberFormatChanged);
FormatStringProperty.Changed.Subscribe(FormatStringChanged); FormatStringProperty.Changed.Subscribe(FormatStringChanged);
IncrementProperty.Changed.Subscribe(IncrementChanged); IncrementProperty.Changed.Subscribe(IncrementChanged);
@ -416,19 +390,6 @@ namespace Avalonia.Controls
} }
} }
/// <summary>
/// Called when the <see cref="CultureInfo"/> property value changed.
/// </summary>
/// <param name="oldValue">The old value.</param>
/// <param name="newValue">The new value.</param>
protected virtual void OnCultureInfoChanged(CultureInfo? oldValue, CultureInfo? newValue)
{
if (IsInitialized)
{
SyncTextAndValueProperties(false, null);
}
}
/// <summary> /// <summary>
/// Called when the <see cref="NumberFormat"/> property value changed. /// Called when the <see cref="NumberFormat"/> property value changed.
/// </summary> /// </summary>
@ -729,20 +690,6 @@ namespace Avalonia.Controls
} }
} }
/// <summary>
/// Called when the <see cref="CultureInfo"/> property value changed.
/// </summary>
/// <param name="e">The event args.</param>
private static void OnCultureInfoChanged(AvaloniaPropertyChangedEventArgs e)
{
if (e.Sender is NumericUpDown upDown)
{
var oldValue = (CultureInfo?)e.OldValue;
var newValue = (CultureInfo?)e.NewValue;
upDown.OnCultureInfoChanged(oldValue, newValue);
}
}
/// <summary> /// <summary>
/// Called when the <see cref="NumberFormat"/> property value changed. /// Called when the <see cref="NumberFormat"/> property value changed.
/// </summary> /// </summary>

4
src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs

@ -43,7 +43,7 @@ namespace Avalonia.Controls.Platform
_priority = priority; _priority = priority;
_interval = interval; _interval = interval;
_tick = tick; _tick = tick;
_timer = new Timer(OnTimer, null, interval, TimeSpan.FromMilliseconds(-1)); _timer = new Timer(OnTimer, null, interval, Timeout.InfiniteTimeSpan);
_handle = GCHandle.Alloc(_timer); _handle = GCHandle.Alloc(_timer);
} }
@ -57,7 +57,7 @@ namespace Avalonia.Controls.Platform
if (_timer == null) if (_timer == null)
return; return;
_tick(); _tick();
_timer?.Change(_interval, TimeSpan.FromMilliseconds(-1)); _timer?.Change(_interval, Timeout.InfiniteTimeSpan);
}); });
} }

46
src/Avalonia.Controls/Primitives/Popup.cs

@ -22,9 +22,7 @@ namespace Avalonia.Controls.Primitives
/// <summary> /// <summary>
/// Displays a popup window. /// Displays a popup window.
/// </summary> /// </summary>
#pragma warning disable CS0612 // Type or member is obsolete public class Popup : Control, IPopupHostProvider
public class Popup : Control, IVisualTreeHost, IPopupHostProvider
#pragma warning restore CS0612 // Type or member is obsolete
{ {
public static readonly StyledProperty<bool> WindowManagerAddShadowHintProperty = public static readonly StyledProperty<bool> WindowManagerAddShadowHintProperty =
AvaloniaProperty.Register<Popup, bool>(nameof(WindowManagerAddShadowHint), false); AvaloniaProperty.Register<Popup, bool>(nameof(WindowManagerAddShadowHint), false);
@ -90,14 +88,6 @@ namespace Avalonia.Controls.Primitives
public static readonly StyledProperty<Control?> PlacementTargetProperty = public static readonly StyledProperty<Control?> PlacementTargetProperty =
AvaloniaProperty.Register<Popup, Control?>(nameof(PlacementTarget)); AvaloniaProperty.Register<Popup, Control?>(nameof(PlacementTarget));
#pragma warning disable 618
/// <summary>
/// Defines the <see cref="ObeyScreenEdges"/> property.
/// </summary>
public static readonly StyledProperty<bool> ObeyScreenEdgesProperty =
AvaloniaProperty.Register<Popup, bool>(nameof(ObeyScreenEdges), true);
#pragma warning restore 618
public static readonly StyledProperty<bool> OverlayDismissEventPassThroughProperty = public static readonly StyledProperty<bool> OverlayDismissEventPassThroughProperty =
AvaloniaProperty.Register<Popup, bool>(nameof(OverlayDismissEventPassThrough)); AvaloniaProperty.Register<Popup, bool>(nameof(OverlayDismissEventPassThrough));
@ -125,17 +115,6 @@ namespace Avalonia.Controls.Primitives
public static readonly StyledProperty<double> VerticalOffsetProperty = public static readonly StyledProperty<double> VerticalOffsetProperty =
AvaloniaProperty.Register<Popup, double>(nameof(VerticalOffset)); AvaloniaProperty.Register<Popup, double>(nameof(VerticalOffset));
/// <summary>
/// Defines the <see cref="StaysOpen"/> property.
/// </summary>
[Obsolete("Use IsLightDismissEnabledProperty")]
public static readonly DirectProperty<Popup, bool> StaysOpenProperty =
AvaloniaProperty.RegisterDirect<Popup, bool>(
nameof(StaysOpen),
o => o.StaysOpen,
(o, v) => o.StaysOpen = v,
true);
/// <summary> /// <summary>
/// Defines the <see cref="Topmost"/> property. /// Defines the <see cref="Topmost"/> property.
/// </summary> /// </summary>
@ -301,13 +280,6 @@ namespace Avalonia.Controls.Primitives
set { SetValue(PlacementTargetProperty, value); } set { SetValue(PlacementTargetProperty, value); }
} }
[Obsolete("This property has no effect")]
public bool ObeyScreenEdges
{
get => GetValue(ObeyScreenEdgesProperty);
set => SetValue(ObeyScreenEdgesProperty, value);
}
/// <summary> /// <summary>
/// Gets or sets a value indicating whether the event that closes the popup is passed /// Gets or sets a value indicating whether the event that closes the popup is passed
/// through to the parent window. /// through to the parent window.
@ -352,17 +324,6 @@ namespace Avalonia.Controls.Primitives
set { SetValue(VerticalOffsetProperty, value); } set { SetValue(VerticalOffsetProperty, value); }
} }
/// <summary>
/// Gets or sets a value indicating whether the popup should stay open when the popup is
/// pressed or loses focus.
/// </summary>
[Obsolete("Use IsLightDismissEnabled")]
public bool StaysOpen
{
get => !IsLightDismissEnabled;
set => IsLightDismissEnabled = !value;
}
/// <summary> /// <summary>
/// Gets or sets whether this popup appears on top of all other windows /// Gets or sets whether this popup appears on top of all other windows
/// </summary> /// </summary>
@ -372,11 +333,6 @@ namespace Avalonia.Controls.Primitives
set { SetValue(TopmostProperty, value); } set { SetValue(TopmostProperty, value); }
} }
/// <summary>
/// Gets the root of the popup window.
/// </summary>
IVisual? IVisualTreeHost.Root => _openState?.PopupHost.HostedVisualTreeRoot;
IPopupHost? IPopupHostProvider.PopupHost => Host; IPopupHost? IPopupHostProvider.PopupHost => Host;
event Action<IPopupHost?>? IPopupHostProvider.PopupHostChanged event Action<IPopupHost?>? IPopupHostProvider.PopupHostChanged

12
src/Avalonia.Controls/Primitives/TemplatedControl.cs

@ -297,9 +297,6 @@ namespace Avalonia.Controls.Primitives
var e = new TemplateAppliedEventArgs(nameScope); var e = new TemplateAppliedEventArgs(nameScope);
OnApplyTemplate(e); OnApplyTemplate(e);
#pragma warning disable CS0618 // Type or member is obsolete
OnTemplateApplied(e);
#pragma warning restore CS0618 // Type or member is obsolete
RaiseEvent(e); RaiseEvent(e);
} }
@ -378,15 +375,6 @@ namespace Avalonia.Controls.Primitives
} }
} }
/// <summary>
/// Called when the control's template is applied.
/// </summary>
/// <param name="e">The event args.</param>
[Obsolete("Use OnApplyTemplate")]
protected virtual void OnTemplateApplied(TemplateAppliedEventArgs e)
{
}
/// <summary> /// <summary>
/// Called when the <see cref="Template"/> property changes. /// Called when the <see cref="Template"/> property changes.
/// </summary> /// </summary>

2
src/Avalonia.Controls/ProgressBar.cs

@ -251,11 +251,9 @@ namespace Avalonia.Controls
TemplateProperties.Container2AnimationEndPosition = barIndicatorWidth2 * 1.66; // Position at 166% TemplateProperties.Container2AnimationEndPosition = barIndicatorWidth2 * 1.66; // Position at 166%
#pragma warning disable CS0618 // Type or member is obsolete
// Remove these properties when we switch to fluent as default and removed the old one. // Remove these properties when we switch to fluent as default and removed the old one.
IndeterminateStartingOffset = -dim; IndeterminateStartingOffset = -dim;
IndeterminateEndingOffset = dim; IndeterminateEndingOffset = dim;
#pragma warning restore CS0618 // Type or member is obsolete
var padding = Padding; var padding = Padding;
var rectangle = new RectangleGeometry( var rectangle = new RectangleGeometry(

2
src/Avalonia.Controls/Remote/RemoteWidget.cs

@ -77,10 +77,8 @@ namespace Avalonia.Controls.Remote
_bitmap.PixelSize.Height != _lastFrame.Height) _bitmap.PixelSize.Height != _lastFrame.Height)
{ {
_bitmap?.Dispose(); _bitmap?.Dispose();
#pragma warning disable CS0618 // Type or member is obsolete
_bitmap = new WriteableBitmap(new PixelSize(_lastFrame.Width, _lastFrame.Height), _bitmap = new WriteableBitmap(new PixelSize(_lastFrame.Width, _lastFrame.Height),
new Vector(96, 96), fmt); new Vector(96, 96), fmt);
#pragma warning restore CS0618 // Type or member is obsolete
} }
using (var l = _bitmap.Lock()) using (var l = _bitmap.Lock())
{ {

2
src/Avalonia.Controls/RichTextBlock.cs

@ -381,9 +381,7 @@ namespace Avalonia.Controls
var hit = TextLayout.HitTestPoint(point); var hit = TextLayout.HitTestPoint(point);
var index = hit.TextPosition; var index = hit.TextPosition;
#pragma warning disable CS0618 // Type or member is obsolete
switch (e.ClickCount) switch (e.ClickCount)
#pragma warning restore CS0618 // Type or member is obsolete
{ {
case 1: case 1:
if (clickToSelect) if (clickToSelect)

36
src/Avalonia.Controls/Shapes/Arc.cs

@ -1,8 +1,12 @@
using System; using System;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Utilities;
namespace Avalonia.Controls.Shapes namespace Avalonia.Controls.Shapes
{ {
/// <summary>
/// Represents a circular or elliptical arc (a segment of a curve).
/// </summary>
public class Arc : Shape public class Arc : Shape
{ {
/// <summary> /// <summary>
@ -19,8 +23,12 @@ namespace Avalonia.Controls.Shapes
static Arc() static Arc()
{ {
StrokeThicknessProperty.OverrideDefaultValue<Arc>(1); StrokeThicknessProperty.OverrideDefaultValue<Arc>(1.0d);
AffectsGeometry<Arc>(BoundsProperty, StrokeThicknessProperty, StartAngleProperty, SweepAngleProperty); AffectsGeometry<Arc>(
BoundsProperty,
StrokeThicknessProperty,
StartAngleProperty,
SweepAngleProperty);
} }
/// <summary> /// <summary>
@ -42,10 +50,11 @@ namespace Avalonia.Controls.Shapes
set => SetValue(SweepAngleProperty, value); set => SetValue(SweepAngleProperty, value);
} }
/// <inheritdoc/>
protected override Geometry CreateDefiningGeometry() protected override Geometry CreateDefiningGeometry()
{ {
var angle1 = DegreesToRad(StartAngle); var angle1 = MathUtilities.Deg2Rad(StartAngle);
var angle2 = angle1 + DegreesToRad(SweepAngle); var angle2 = angle1 + MathUtilities.Deg2Rad(SweepAngle);
var startAngle = Math.Min(angle1, angle2); var startAngle = Math.Min(angle1, angle2);
var sweepAngle = Math.Max(angle1, angle2); var sweepAngle = Math.Max(angle1, angle2);
@ -80,24 +89,25 @@ namespace Avalonia.Controls.Shapes
var arcGeometry = new StreamGeometry(); var arcGeometry = new StreamGeometry();
using (var ctx = arcGeometry.Open()) using (StreamGeometryContext context = arcGeometry.Open())
{ {
ctx.BeginFigure(startPoint, false); context.BeginFigure(startPoint, false);
ctx.ArcTo(endPoint, new Size(radiusX, radiusY), angleGap, angleGap >= Math.PI, context.ArcTo(
endPoint,
new Size(radiusX, radiusY),
rotationAngle: angleGap,
isLargeArc: angleGap >= Math.PI,
SweepDirection.Clockwise); SweepDirection.Clockwise);
ctx.EndFigure(false); context.EndFigure(false);
} }
return arcGeometry; return arcGeometry;
} }
} }
static double DegreesToRad(double inAngle) => private static double RadToNormRad(double inAngle) => ((inAngle % (Math.PI * 2)) + (Math.PI * 2)) % (Math.PI * 2);
inAngle * Math.PI / 180;
static double RadToNormRad(double inAngle) => ((inAngle % (Math.PI * 2)) + (Math.PI * 2)) % (Math.PI * 2); private static Point GetRingPoint(double radiusX, double radiusY, double centerX, double centerY, double angle) =>
static Point GetRingPoint(double radiusX, double radiusY, double centerX, double centerY, double angle) =>
new Point((radiusX * Math.Cos(angle)) + centerX, (radiusY * Math.Sin(angle)) + centerY); new Point((radiusX * Math.Cos(angle)) + centerX, (radiusY * Math.Sin(angle)) + centerY);
} }
} }

97
src/Avalonia.Controls/Shapes/Sector.cs

@ -0,0 +1,97 @@
using System;
using Avalonia.Media;
using Avalonia.Utilities;
namespace Avalonia.Controls.Shapes
{
/// <summary>
/// Represents a circular or elliptical sector (a pie-shaped closed region of a circle or ellipse).
/// </summary>
public class Sector : Shape
{
/// <summary>
/// Defines the <see cref="StartAngle"/> property.
/// </summary>
public static readonly StyledProperty<double> StartAngleProperty =
AvaloniaProperty.Register<Sector, double>(nameof(StartAngle), 0.0d);
/// <summary>
/// Defines the <see cref="SweepAngle"/> property.
/// </summary>
public static readonly StyledProperty<double> SweepAngleProperty =
AvaloniaProperty.Register<Sector, double>(nameof(SweepAngle), 0.0d);
/// <summary>
/// Gets or sets the angle at which the sector's arc starts, in degrees.
/// </summary>
public double StartAngle
{
get => GetValue(StartAngleProperty);
set => SetValue(StartAngleProperty, value);
}
/// <summary>
/// Gets or sets the angle, in degrees, added to the <see cref="StartAngle"/> defining where the sector's arc ends.
/// A positive value is clockwise, negative is counter-clockwise.
/// </summary>
public double SweepAngle
{
get => GetValue(SweepAngleProperty);
set => SetValue(SweepAngleProperty, value);
}
static Sector()
{
StrokeThicknessProperty.OverrideDefaultValue<Sector>(1.0d);
AffectsGeometry<Sector>(
BoundsProperty,
StrokeThicknessProperty,
StartAngleProperty,
SweepAngleProperty);
}
/// <inheritdoc/>
protected override Geometry? CreateDefiningGeometry()
{
Rect rect = new Rect(Bounds.Size);
Rect deflatedRect = rect.Deflate(StrokeThickness * 0.5d);
if (SweepAngle >= 360.0d || SweepAngle <= -360.0d)
{
return new EllipseGeometry(deflatedRect);
}
if (SweepAngle == 0.0d)
{
return new StreamGeometry();
}
(double startAngle, double endAngle) = MathUtilities.GetMinMaxFromDelta(
MathUtilities.Deg2Rad(StartAngle),
MathUtilities.Deg2Rad(SweepAngle));
Point centre = new Point(rect.Width * 0.5d, rect.Height * 0.5d);
double radiusX = deflatedRect.Width * 0.5d;
double radiusY = deflatedRect.Height * 0.5d;
Point startCurvePoint = MathUtilities.GetEllipsePoint(centre, radiusX, radiusY, startAngle);
Point endCurvePoint = MathUtilities.GetEllipsePoint(centre, radiusX, radiusY, endAngle);
Size size = new Size(radiusX, radiusY);
var streamGeometry = new StreamGeometry();
using (StreamGeometryContext context = streamGeometry.Open())
{
context.BeginFigure(startCurvePoint, false);
context.ArcTo(
endCurvePoint,
size,
rotationAngle: 0.0d,
isLargeArc: Math.Abs(SweepAngle) > 180.0d,
SweepDirection.Clockwise);
context.LineTo(centre);
context.EndFigure(true);
}
return streamGeometry;
}
}
}

20
src/Avalonia.Controls/SystemDialog.cs

@ -32,13 +32,6 @@ namespace Avalonia.Controls
[Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API")] [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API")]
public abstract class FileSystemDialog : SystemDialog public abstract class FileSystemDialog : SystemDialog
{ {
[Obsolete("Use Directory")]
public string? InitialDirectory
{
get => Directory;
set => Directory = value;
}
/// <summary> /// <summary>
/// Gets or sets the initial directory that will be displayed when the file system dialog /// Gets or sets the initial directory that will be displayed when the file system dialog
/// is opened. /// is opened.
@ -87,7 +80,7 @@ namespace Avalonia.Controls
DefaultExtension = DefaultExtension, DefaultExtension = DefaultExtension,
FileTypeChoices = Filters?.Select(f => new FilePickerFileType(f.Name!) { Patterns = f.Extensions.Select(e => $"*.{e}").ToArray() }).ToArray(), FileTypeChoices = Filters?.Select(f => new FilePickerFileType(f.Name!) { Patterns = f.Extensions.Select(e => $"*.{e}").ToArray() }).ToArray(),
Title = Title, Title = Title,
SuggestedStartLocation = InitialDirectory is { } directory SuggestedStartLocation = Directory is { } directory
? new BclStorageFolder(new System.IO.DirectoryInfo(directory)) ? new BclStorageFolder(new System.IO.DirectoryInfo(directory))
: null, : null,
ShowOverwritePrompt = ShowOverwritePrompt ShowOverwritePrompt = ShowOverwritePrompt
@ -129,7 +122,7 @@ namespace Avalonia.Controls
AllowMultiple = AllowMultiple, AllowMultiple = AllowMultiple,
FileTypeFilter = Filters?.Select(f => new FilePickerFileType(f.Name!) { Patterns = f.Extensions.Select(e => $"*.{e}").ToArray() }).ToArray(), FileTypeFilter = Filters?.Select(f => new FilePickerFileType(f.Name!) { Patterns = f.Extensions.Select(e => $"*.{e}").ToArray() }).ToArray(),
Title = Title, Title = Title,
SuggestedStartLocation = InitialDirectory is { } directory SuggestedStartLocation = Directory is { } directory
? new BclStorageFolder(new System.IO.DirectoryInfo(directory)) ? new BclStorageFolder(new System.IO.DirectoryInfo(directory))
: null : null
}; };
@ -142,13 +135,6 @@ namespace Avalonia.Controls
[Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API")] [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API")]
public class OpenFolderDialog : FileSystemDialog public class OpenFolderDialog : FileSystemDialog
{ {
[Obsolete("Use Directory")]
public string? DefaultDirectory
{
get => Directory;
set => Directory = value;
}
/// <summary> /// <summary>
/// Shows the open folder dialog. /// Shows the open folder dialog.
/// </summary> /// </summary>
@ -170,7 +156,7 @@ namespace Avalonia.Controls
return new FolderPickerOpenOptions return new FolderPickerOpenOptions
{ {
Title = Title, Title = Title,
SuggestedStartLocation = InitialDirectory is { } directory SuggestedStartLocation = Directory is { } directory
? new BclStorageFolder(new System.IO.DirectoryInfo(directory)) ? new BclStorageFolder(new System.IO.DirectoryInfo(directory))
: null : null
}; };

2
src/Avalonia.Controls/TextBox.cs

@ -1165,9 +1165,7 @@ namespace Avalonia.Controls
SetAndRaise(CaretIndexProperty, ref _caretIndex, index); SetAndRaise(CaretIndexProperty, ref _caretIndex, index);
#pragma warning disable CS0618 // Type or member is obsolete
switch (e.ClickCount) switch (e.ClickCount)
#pragma warning restore CS0618 // Type or member is obsolete
{ {
case 1: case 1:
if (clickToSelect) if (clickToSelect)

3
src/Avalonia.Controls/TopLevel.cs

@ -404,9 +404,6 @@ namespace Avalonia.Controls
LayoutManager?.Dispose(); LayoutManager?.Dispose();
} }
[Obsolete("Use HandleResized(Size, PlatformResizeReason)")]
protected virtual void HandleResized(Size clientSize) => HandleResized(clientSize, PlatformResizeReason.Unspecified);
/// <summary> /// <summary>
/// Handles a resize notification from <see cref="ITopLevelImpl.Resized"/>. /// Handles a resize notification from <see cref="ITopLevelImpl.Resized"/>.
/// </summary> /// </summary>

44
src/Avalonia.Controls/Window.cs

@ -79,16 +79,6 @@ namespace Avalonia.Controls
public static readonly StyledProperty<SizeToContent> SizeToContentProperty = public static readonly StyledProperty<SizeToContent> SizeToContentProperty =
AvaloniaProperty.Register<Window, SizeToContent>(nameof(SizeToContent)); AvaloniaProperty.Register<Window, SizeToContent>(nameof(SizeToContent));
/// <summary>
/// Enables or disables system window decorations (title bar, buttons, etc)
/// </summary>
[Obsolete("Use SystemDecorationsProperty instead")]
public static readonly DirectProperty<Window, bool> HasSystemDecorationsProperty =
AvaloniaProperty.RegisterDirect<Window, bool>(
nameof(HasSystemDecorations),
o => o.HasSystemDecorations,
(o, v) => o.HasSystemDecorations = v);
/// <summary> /// <summary>
/// Defines the <see cref="ExtendClientAreaToDecorationsHint"/> property. /// Defines the <see cref="ExtendClientAreaToDecorationsHint"/> property.
/// </summary> /// </summary>
@ -289,25 +279,6 @@ namespace Avalonia.Controls
set { SetValue(TitleProperty, value); } set { SetValue(TitleProperty, value); }
} }
/// <summary>
/// Enables or disables system window decorations (title bar, buttons, etc)
/// </summary>
[Obsolete("Use SystemDecorations instead")]
public bool HasSystemDecorations
{
get => SystemDecorations == SystemDecorations.Full;
set
{
var oldValue = HasSystemDecorations;
if (oldValue != value)
{
SystemDecorations = value ? SystemDecorations.Full : SystemDecorations.None;
RaisePropertyChanged(HasSystemDecorationsProperty, oldValue, value);
}
}
}
/// <summary> /// <summary>
/// Gets or sets if the ClientArea is Extended into the Window Decorations (chrome or border). /// Gets or sets if the ClientArea is Extended into the Window Decorations (chrome or border).
/// </summary> /// </summary>
@ -985,9 +956,6 @@ namespace Avalonia.Controls
Owner = null; Owner = null;
} }
[Obsolete("Use HandleResized(Size, PlatformResizeReason)")]
protected sealed override void HandleResized(Size clientSize) => HandleResized(clientSize, PlatformResizeReason.Unspecified);
/// <inheritdoc/> /// <inheritdoc/>
protected sealed override void HandleResized(Size clientSize, PlatformResizeReason reason) protected sealed override void HandleResized(Size clientSize, PlatformResizeReason reason)
{ {
@ -1033,19 +1001,9 @@ namespace Avalonia.Controls
base.OnPropertyChanged(change); base.OnPropertyChanged(change);
if (change.Property == SystemDecorationsProperty) if (change.Property == SystemDecorationsProperty)
{ {
var (typedOldValue, typedNewValue) = change.GetOldAndNewValue<SystemDecorations>(); var (_, typedNewValue) = change.GetOldAndNewValue<SystemDecorations>();
PlatformImpl?.SetSystemDecorations(typedNewValue); PlatformImpl?.SetSystemDecorations(typedNewValue);
var o = typedOldValue == SystemDecorations.Full;
var n = typedNewValue == SystemDecorations.Full;
if (o != n)
{
#pragma warning disable CS0618 // Type or member is obsolete
RaisePropertyChanged(HasSystemDecorationsProperty, o, n);
#pragma warning restore CS0618 // Type or member is obsolete
}
} }
} }

9
src/Avalonia.Controls/WindowBase.cs

@ -94,9 +94,6 @@ namespace Avalonia.Controls
public Screens Screens { get; private set; } public Screens Screens { get; private set; }
[Obsolete("No longer used. Always returns false.")]
protected bool AutoSizing => false;
/// <summary> /// <summary>
/// Gets or sets the owner of the window. /// Gets or sets the owner of the window.
/// </summary> /// </summary>
@ -169,9 +166,6 @@ namespace Avalonia.Controls
} }
} }
[Obsolete("No longer used. Has no effect.")]
protected IDisposable BeginAutoSizing() => Disposable.Empty;
/// <summary> /// <summary>
/// Ensures that the window is initialized. /// Ensures that the window is initialized.
/// </summary> /// </summary>
@ -226,9 +220,6 @@ namespace Avalonia.Controls
} }
} }
[Obsolete("Use HandleResized(Size, PlatformResizeReason)")]
protected override void HandleResized(Size clientSize) => HandleResized(clientSize, PlatformResizeReason.Unspecified);
/// <summary> /// <summary>
/// Handles a resize notification from <see cref="ITopLevelImpl.Resized"/>. /// Handles a resize notification from <see cref="ITopLevelImpl.Resized"/>.
/// </summary> /// </summary>

23
src/Avalonia.Diagnostics/Diagnostics/Screenshots/FilePickerHandler.cs

@ -3,6 +3,8 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Platform.Storage;
using Avalonia.Platform.Storage.FileIO;
using Lifetimes = Avalonia.Controls.ApplicationLifetimes; using Lifetimes = Avalonia.Controls.ApplicationLifetimes;
namespace Avalonia.Diagnostics.Screenshots namespace Avalonia.Diagnostics.Screenshots
@ -59,24 +61,25 @@ namespace Avalonia.Diagnostics.Screenshots
protected async override Task<Stream?> GetStream(IControl control) protected async override Task<Stream?> GetStream(IControl control)
{ {
Stream? output = default; Stream? output = default;
var result = await new SaveFileDialog() var result = await GetWindow(control).StorageProvider.SaveFilePickerAsync(new FilePickerSaveOptions
{ {
SuggestedStartLocation = new BclStorageFolder(new DirectoryInfo(ScreenshotsRoot)),
Title = Title, Title = Title,
Filters = new() { new FileDialogFilter() { Name = "PNG", Extensions = new() { "png" } } }, FileTypeChoices = new FilePickerFileType[] { new FilePickerFileType("PNG") { Patterns = new string[] { "*.png" } } }
Directory = ScreenshotsRoot, });
}.ShowAsync(GetWindow(control));
if (!string.IsNullOrWhiteSpace(result)) if (result!=null && !string.IsNullOrWhiteSpace(result.Name))
{ {
var foldler = Path.GetDirectoryName(result); var folder = Path.GetDirectoryName(result.Name);
// Directory information for path, or null if path denotes a root directory or is // Directory information for path, or null if path denotes a root directory or is
// null. Returns System.String.Empty if path does not contain directory information. // null. Returns System.String.Empty if path does not contain directory information.
if (!string.IsNullOrWhiteSpace(foldler)) if (!string.IsNullOrWhiteSpace(folder))
{ {
if (!Directory.Exists(foldler)) if (!Directory.Exists(folder))
{ {
Directory.CreateDirectory(foldler); Directory.CreateDirectory(folder);
} }
output = new FileStream(result, FileMode.Create); output = new FileStream(result.Name, FileMode.Create);
} }
} }
return output; return output;

6
src/Avalonia.Dialogs/AboutAvaloniaDialog.xaml

@ -76,7 +76,11 @@
</Canvas> </Canvas>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="18"> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="18">
<Border Height="70" Width="70"> <Border Height="70" Width="70">
<DrawingPresenter Drawing="{DynamicResource AvaloniaLogo}" /> <Image>
<Image.Source>
<DrawingImage Drawing="{DynamicResource AvaloniaLogo}"/>
</Image.Source>
</Image>
</Border> </Border>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Margin="10,-10,0,0"> <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Margin="10,-10,0,0">
<TextBlock Text="{Binding Version, StringFormat=Avalonia {0}}" FontSize="40" Foreground="White" /> <TextBlock Text="{Binding Version, StringFormat=Avalonia {0}}" FontSize="40" Foreground="White" />

48
src/Avalonia.Headless/HeadlessPlatformThreadingInterface.cs

@ -36,35 +36,35 @@ namespace Avalonia.Headless
public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick) public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick)
{ {
var cancelled = false; if (interval.TotalMilliseconds < 10)
var enqueued = false; interval = TimeSpan.FromMilliseconds(10);
var l = new object();
var timer = new Timer(_ => var stopped = false;
Timer timer = null;
timer = new Timer(_ =>
{ {
lock (l) if (stopped)
return;
Dispatcher.UIThread.Post(() =>
{ {
if (cancelled || enqueued) try
return;
enqueued = true;
Dispatcher.UIThread.Post(() =>
{ {
lock (l) tick();
{ }
enqueued = false; finally
if (cancelled) {
return; if (!stopped)
tick(); timer.Change(interval, Timeout.InfiniteTimeSpan);
} }
}, priority); });
} },
}, null, interval, interval); null, interval, Timeout.InfiniteTimeSpan);
return Disposable.Create(() => return Disposable.Create(() =>
{ {
lock (l) stopped = true;
{ timer.Dispose();
timer.Dispose();
cancelled = true;
}
}); });
} }

3
src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs

@ -34,7 +34,7 @@ namespace Avalonia.OpenGL.Controls
_attachment.Present(); _attachment.Present();
} }
context.DrawImage(_bitmap, new Rect(_bitmap.Size), Bounds); context.DrawImage(_bitmap, new Rect(_bitmap.Size), new Rect(Bounds.Size));
base.Render(context); base.Render(context);
} }
@ -84,6 +84,7 @@ namespace Avalonia.OpenGL.Controls
using (_context.MakeCurrent()) using (_context.MakeCurrent())
{ {
var gl = _context.GlInterface; var gl = _context.GlInterface;
gl.ActiveTexture(GL_TEXTURE0);
gl.BindTexture(GL_TEXTURE_2D, 0); gl.BindTexture(GL_TEXTURE_2D, 0);
gl.BindFramebuffer(GL_FRAMEBUFFER, 0); gl.BindFramebuffer(GL_FRAMEBUFFER, 0);
gl.DeleteFramebuffer(_fb); gl.DeleteFramebuffer(_fb);

28
src/Avalonia.ReactiveUI/AvaloniaActivationForViewFetcher.cs

@ -2,6 +2,7 @@ using System;
using System.Reactive.Linq; using System.Reactive.Linq;
using Avalonia.VisualTree; using Avalonia.VisualTree;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Interactivity;
using ReactiveUI; using ReactiveUI;
namespace Avalonia.ReactiveUI namespace Avalonia.ReactiveUI
@ -25,27 +26,28 @@ namespace Avalonia.ReactiveUI
public IObservable<bool> GetActivationForView(IActivatableView view) public IObservable<bool> GetActivationForView(IActivatableView view)
{ {
if (!(view is IVisual visual)) return Observable.Return(false); if (!(view is IVisual visual)) return Observable.Return(false);
if (view is WindowBase window) return GetActivationForWindowBase(window); if (view is Control control) return GetActivationForControl(control);
return GetActivationForVisual(visual); return GetActivationForVisual(visual);
} }
/// <summary> /// <summary>
/// Listens to Opened and Closed events for Avalonia windows. /// Listens to Loaded and Unloaded
/// events for Avalonia Control.
/// </summary> /// </summary>
private IObservable<bool> GetActivationForWindowBase(WindowBase window) private IObservable<bool> GetActivationForControl(Control control)
{ {
var windowLoaded = Observable var controlLoaded = Observable
.FromEventPattern( .FromEventPattern<RoutedEventArgs>(
x => window.Opened += x, x => control.Loaded += x,
x => window.Opened -= x) x => control.Loaded -= x)
.Select(args => true); .Select(args => true);
var windowUnloaded = Observable var controlUnloaded = Observable
.FromEventPattern( .FromEventPattern<RoutedEventArgs>(
x => window.Closed += x, x => control.Unloaded += x,
x => window.Closed -= x) x => control.Unloaded -= x)
.Select(args => false); .Select(args => false);
return windowLoaded return controlLoaded
.Merge(windowUnloaded) .Merge(controlUnloaded)
.DistinctUntilChanged(); .DistinctUntilChanged();
} }

4
src/Avalonia.ReactiveUI/RoutedViewHost.cs

@ -50,7 +50,7 @@ namespace Avalonia.ReactiveUI
/// ReactiveUI routing documentation website</see> for more info. /// ReactiveUI routing documentation website</see> for more info.
/// </para> /// </para>
/// </remarks> /// </remarks>
public class RoutedViewHost : TransitioningContentControl, IActivatableView, IEnableLogger public class RoutedViewHost : TransitioningContentControl, IActivatableView, IEnableLogger, IStyleable
{ {
/// <summary> /// <summary>
/// <see cref="AvaloniaProperty"/> for the <see cref="Router"/> property. /// <see cref="AvaloniaProperty"/> for the <see cref="Router"/> property.
@ -126,6 +126,8 @@ namespace Avalonia.ReactiveUI
/// </summary> /// </summary>
public IViewLocator? ViewLocator { get; set; } public IViewLocator? ViewLocator { get; set; }
Type IStyleable.StyleKey => typeof(TransitioningContentControl);
/// <summary> /// <summary>
/// Invoked when ReactiveUI router navigates to a view model. /// Invoked when ReactiveUI router navigates to a view model.
/// </summary> /// </summary>

6
src/Avalonia.ReactiveUI/ViewModelViewHost.cs

@ -2,7 +2,7 @@ using System;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Styling;
using ReactiveUI; using ReactiveUI;
using Splat; using Splat;
@ -13,7 +13,7 @@ namespace Avalonia.ReactiveUI
/// the ViewModel property and display it. This control is very useful /// the ViewModel property and display it. This control is very useful
/// inside a DataTemplate to display the View associated with a ViewModel. /// inside a DataTemplate to display the View associated with a ViewModel.
/// </summary> /// </summary>
public class ViewModelViewHost : TransitioningContentControl, IViewFor, IEnableLogger public class ViewModelViewHost : TransitioningContentControl, IViewFor, IEnableLogger, IStyleable
{ {
/// <summary> /// <summary>
/// <see cref="AvaloniaProperty"/> for the <see cref="ViewModel"/> property. /// <see cref="AvaloniaProperty"/> for the <see cref="ViewModel"/> property.
@ -78,6 +78,8 @@ namespace Avalonia.ReactiveUI
/// </summary> /// </summary>
public IViewLocator? ViewLocator { get; set; } public IViewLocator? ViewLocator { get; set; }
Type IStyleable.StyleKey => typeof(TransitioningContentControl);
/// <summary> /// <summary>
/// Invoked when ReactiveUI router navigates to a view model. /// Invoked when ReactiveUI router navigates to a view model.
/// </summary> /// </summary>

12
src/Avalonia.Themes.Fluent/Controls/ManagedFileChooser.xaml

@ -138,7 +138,11 @@
<ListBox.ItemTemplate> <ListBox.ItemTemplate>
<DataTemplate> <DataTemplate>
<StackPanel Spacing="4" Orientation="Horizontal" Background="Transparent"> <StackPanel Spacing="4" Orientation="Horizontal" Background="Transparent">
<DrawingPresenter Width="16" Height="16" Drawing="{Binding IconKey, Converter={StaticResource Icons}}"/> <Image Width="16" Height="16">
<Image.Source>
<DrawingImage Drawing="{Binding IconKey, Converter={StaticResource Icons}}"/>
</Image.Source>
</Image>
<TextBlock Text="{Binding DisplayName}"/> <TextBlock Text="{Binding DisplayName}"/>
</StackPanel> </StackPanel>
</DataTemplate> </DataTemplate>
@ -231,7 +235,11 @@
<ColumnDefinition SharedSizeGroup="Splitter" /> <ColumnDefinition SharedSizeGroup="Splitter" />
<ColumnDefinition SharedSizeGroup="Size" /> <ColumnDefinition SharedSizeGroup="Size" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<DrawingPresenter Width="16" Height="16" Drawing="{Binding IconKey, Converter={StaticResource Icons}}"/> <Image Width="16" Height="16">
<Image.Source>
<DrawingImage Drawing="{Binding IconKey, Converter={StaticResource Icons}}"/>
</Image.Source>
</Image>
<TextBlock Grid.Column="2" Text="{Binding DisplayName}"/> <TextBlock Grid.Column="2" Text="{Binding DisplayName}"/>
<TextBlock Grid.Column="4" Text="{Binding Modified}" /> <TextBlock Grid.Column="4" Text="{Binding Modified}" />
<TextBlock Grid.Column="6" Text="{Binding Type}" /> <TextBlock Grid.Column="6" Text="{Binding Type}" />

2
src/Avalonia.Themes.Simple/Controls/CalendarDatePicker.xaml

@ -118,7 +118,7 @@
<Popup Name="PART_Popup" <Popup Name="PART_Popup"
PlacementTarget="{TemplateBinding}" PlacementTarget="{TemplateBinding}"
StaysOpen="False"> IsLightDismissEnabled="False">
<Calendar Name="PART_Calendar" <Calendar Name="PART_Calendar"
DisplayDate="{TemplateBinding DisplayDate}" DisplayDate="{TemplateBinding DisplayDate}"
DisplayDateEnd="{TemplateBinding DisplayDateEnd}" DisplayDateEnd="{TemplateBinding DisplayDateEnd}"

2
src/Avalonia.X11/X11IconLoader.cs

@ -68,9 +68,7 @@ namespace Avalonia.X11
public void Save(Stream outputStream) public void Save(Stream outputStream)
{ {
using (var wr = using (var wr =
#pragma warning disable CS0618 // Type or member is obsolete
new WriteableBitmap(new PixelSize(_width, _height), new Vector(96, 96), PixelFormat.Bgra8888)) new WriteableBitmap(new PixelSize(_width, _height), new Vector(96, 96), PixelFormat.Bgra8888))
#pragma warning restore CS0618 // Type or member is obsolete
{ {
using (var fb = wr.Lock()) using (var fb = wr.Lock())
{ {

12
src/Skia/Avalonia.Skia/Gpu/OpenGl/OpenGlBitmapImpl.cs

@ -118,7 +118,7 @@ namespace Avalonia.Skia
{ {
var gl = _context.GlInterface; var gl = _context.GlInterface;
var textures = new int[2]; Span<int> textures = stackalloc int[2];
fixed (int* ptex = textures) fixed (int* ptex = textures)
gl.GenTextures(2, ptex); gl.GenTextures(2, ptex);
_texture = textures[0]; _texture = textures[0];
@ -139,7 +139,6 @@ namespace Avalonia.Skia
gl.FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture, 0); gl.FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture, 0);
gl.BindTexture(GL_TEXTURE_2D, oldTexture); gl.BindTexture(GL_TEXTURE_2D, oldTexture);
} }
} }
} }
@ -161,15 +160,15 @@ namespace Avalonia.Skia
gl.GetIntegerv(GL_ACTIVE_TEXTURE, out var oldActive); gl.GetIntegerv(GL_ACTIVE_TEXTURE, out var oldActive);
gl.BindFramebuffer(GL_FRAMEBUFFER, _fbo); gl.BindFramebuffer(GL_FRAMEBUFFER, _fbo);
gl.BindTexture(GL_TEXTURE_2D, _frontBuffer);
gl.ActiveTexture(GL_TEXTURE0); gl.ActiveTexture(GL_TEXTURE0);
gl.BindTexture(GL_TEXTURE_2D, _frontBuffer);
gl.CopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, _bitmap.PixelSize.Width, gl.CopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, _bitmap.PixelSize.Width,
_bitmap.PixelSize.Height); _bitmap.PixelSize.Height);
gl.BindFramebuffer(GL_FRAMEBUFFER, oldFbo); gl.BindFramebuffer(GL_FRAMEBUFFER, oldFbo);
gl.BindTexture(GL_TEXTURE_2D, oldTexture);
gl.ActiveTexture(oldActive); gl.ActiveTexture(oldActive);
gl.BindTexture(GL_TEXTURE_2D, oldTexture);
gl.Finish(); gl.Finish();
} }
@ -192,9 +191,8 @@ namespace Avalonia.Skia
if(_disposed) if(_disposed)
return; return;
_disposed = true; _disposed = true;
var tex = new[] { _texture, _frontBuffer }; var ptex = stackalloc[] { _texture, _frontBuffer };
fixed (int* ptex = tex) gl.DeleteTextures(2, ptex);
gl.DeleteTextures(2, ptex);
} }
} }

155
tests/Avalonia.Base.UnitTests/Collections/AvaloniaListExtenionsTests.cs

@ -1,155 +0,0 @@
using System.Linq;
using Avalonia.Collections;
using Xunit;
namespace Avalonia.Base.UnitTests.Collections
{
public class AvaloniaListExtenionsTests
{
#pragma warning disable CS0618 // Type or member is obsolete
[Fact]
public void CreateDerivedList_Creates_Initial_Items()
{
var source = new AvaloniaList<int>(new[] { 0, 1, 2, 3 });
var target = source.CreateDerivedList(x => new Wrapper(x));
var result = target.Select(x => x.Value).ToList();
Assert.Equal(source, result);
}
[Fact]
public void CreateDerivedList_Handles_Add()
{
var source = new AvaloniaList<int>(new[] { 0, 1, 2, 3 });
var target = source.CreateDerivedList(x => new Wrapper(x));
source.Add(4);
var result = target.Select(x => x.Value).ToList();
Assert.Equal(source, result);
}
[Fact]
public void CreateDerivedList_Handles_Insert()
{
var source = new AvaloniaList<int>(new[] { 0, 1, 2, 3 });
var target = source.CreateDerivedList(x => new Wrapper(x));
source.Insert(1, 4);
var result = target.Select(x => x.Value).ToList();
Assert.Equal(source, result);
}
[Fact]
public void CreateDerivedList_Handles_Remove()
{
var source = new AvaloniaList<int>(new[] { 0, 1, 2, 3 });
var target = source.CreateDerivedList(x => new Wrapper(x));
source.Remove(2);
var result = target.Select(x => x.Value).ToList();
Assert.Equal(source, result);
}
[Fact]
public void CreateDerivedList_Handles_RemoveRange()
{
var source = new AvaloniaList<int>(new[] { 0, 1, 2, 3 });
var target = source.CreateDerivedList(x => new Wrapper(x));
source.RemoveRange(1, 2);
var result = target.Select(x => x.Value).ToList();
Assert.Equal(source, result);
}
[Fact]
public void CreateDerivedList_Handles_Move()
{
var source = new AvaloniaList<int>(new[] { 0, 1, 2, 3 });
var target = source.CreateDerivedList(x => new Wrapper(x));
source.Move(2, 0);
var result = target.Select(x => x.Value).ToList();
Assert.Equal(source, result);
}
[Theory]
[InlineData(0, 2, 3)]
[InlineData(0, 2, 4)]
[InlineData(0, 2, 5)]
[InlineData(0, 4, 4)]
[InlineData(1, 2, 0)]
[InlineData(1, 2, 4)]
[InlineData(1, 2, 5)]
[InlineData(1, 4, 0)]
[InlineData(2, 2, 0)]
[InlineData(2, 2, 1)]
[InlineData(2, 2, 3)]
[InlineData(2, 2, 4)]
[InlineData(2, 2, 5)]
[InlineData(4, 2, 0)]
[InlineData(4, 2, 1)]
[InlineData(4, 2, 3)]
[InlineData(5, 1, 0)]
[InlineData(5, 1, 3)]
public void CreateDerivedList_Handles_MoveRange(int oldIndex, int count, int newIndex)
{
var source = new AvaloniaList<int>(new[] { 0, 1, 2, 3, 4, 5 });
var target = source.CreateDerivedList(x => new Wrapper(x));
source.MoveRange(oldIndex, count, newIndex);
var result = target.Select(x => x.Value).ToList();
Assert.Equal(source, result);
}
[Fact]
public void CreateDerivedList_Handles_Replace()
{
var source = new AvaloniaList<int>(new[] { 0, 1, 2, 3 });
var target = source.CreateDerivedList(x => new Wrapper(x));
source[1] = 4;
var result = target.Select(x => x.Value).ToList();
Assert.Equal(source, result);
}
[Fact]
public void CreateDerivedList_Handles_Clear()
{
var source = new AvaloniaList<int>(new[] { 0, 1, 2, 3 });
var target = source.CreateDerivedList(x => new Wrapper(x));
source.Clear();
var result = target.Select(x => x.Value).ToList();
Assert.Equal(source, result);
}
#pragma warning restore CS0618 // Type or member is obsolete
private class Wrapper
{
public Wrapper(int value)
{
Value = value;
}
public int Value { get; }
}
}
}

70
tests/Avalonia.Base.UnitTests/WeakSubscriptionManagerTests.cs

@ -1,70 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Utilities;
using Xunit;
namespace Avalonia.Base.UnitTests
{
public class WeakSubscriptionManagerTests
{
class EventSource
{
public event EventHandler<EventArgs> Event;
public void Fire()
{
Event?.Invoke(this, new EventArgs());
}
}
class Subscriber : IWeakSubscriber<EventArgs>
{
private readonly Action _onEvent;
public Subscriber(Action onEvent)
{
_onEvent = onEvent;
}
public void OnEvent(object sender, EventArgs ev)
{
_onEvent?.Invoke();
}
}
[Fact]
public void EventShoudBePassedToSubscriber()
{
bool handled = false;
var subscriber = new Subscriber(() => handled = true);
var source = new EventSource();
WeakSubscriptionManager.Subscribe(source, "Event", subscriber);
source.Fire();
Assert.True(handled);
}
[Fact]
public void EventHandlerShouldNotBeKeptAlive()
{
bool handled = false;
var source = new EventSource();
AddSubscriber(source, "Event", () => handled = true);
for (int c = 0; c < 10; c++)
{
GC.Collect();
GC.Collect(3, GCCollectionMode.Forced, true);
}
source.Fire();
Assert.False(handled);
}
private void AddSubscriber(EventSource source, string name, Action func)
{
WeakSubscriptionManager.Subscribe(source, name, new Subscriber(func));
}
}
}

71
tests/Avalonia.Controls.UnitTests/LoadedTests.cs

@ -0,0 +1,71 @@
using Avalonia.Platform;
using Avalonia.Threading;
using Avalonia.UnitTests;
using Moq;
using Xunit;
namespace Avalonia.Controls.UnitTests;
public class LoadedTests
{
[Fact]
public void Window_Loads_And_Unloads()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
int loadedCount = 0, unloadedCount = 0;
var target = new Window();
target.Loaded += (_, _) => loadedCount++;
target.Unloaded += (_, _) => unloadedCount++;
Assert.Equal(0, loadedCount);
Assert.Equal(0, unloadedCount);
target.Show();
Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded);
Assert.True(target.IsLoaded);
Assert.Equal(1, loadedCount);
Assert.Equal(0, unloadedCount);
target.Close();
Assert.Equal(1, loadedCount);
Assert.Equal(1, unloadedCount);
Assert.False(target.IsLoaded);
}
}
[Fact]
public void Control_Loads_And_Unloads()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
int loadedCount = 0, unloadedCount = 0;
var window = new Window();
window.Show();
var target = new Button();
target.Loaded += (_, _) => loadedCount++;
target.Unloaded += (_, _) => unloadedCount++;
Assert.Equal(0, loadedCount);
Assert.Equal(0, unloadedCount);
window.Content = target;
Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded);
Assert.True(target.IsLoaded);
Assert.Equal(1, loadedCount);
Assert.Equal(0, unloadedCount);
window.Content = null;
Assert.Equal(1, loadedCount);
Assert.Equal(1, unloadedCount);
Assert.False(target.IsLoaded);
}
}
}

14
tests/Avalonia.ReactiveUI.UnitTests/AutoSuspendHelperTest.cs

@ -7,6 +7,7 @@ using System.Reactive;
using System.Reactive.Subjects; using System.Reactive.Subjects;
using System.Reactive.Linq; using System.Reactive.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using System.Threading; using System.Threading;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
@ -17,6 +18,7 @@ using Avalonia.UnitTests;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
using Avalonia; using Avalonia;
using Avalonia.Threading;
using ReactiveUI; using ReactiveUI;
using DynamicData; using DynamicData;
using Xunit; using Xunit;
@ -93,13 +95,23 @@ namespace Avalonia.ReactiveUI.UnitTests
var suspension = new AutoSuspendHelper(application.ApplicationLifetime); var suspension = new AutoSuspendHelper(application.ApplicationLifetime);
RxApp.SuspensionHost.CreateNewAppState = () => new AppState { Example = "Foo" }; RxApp.SuspensionHost.CreateNewAppState = () => new AppState { Example = "Foo" };
RxApp.SuspensionHost.ShouldPersistState.Subscribe(_ => shouldPersistReceived = true); RxApp.SuspensionHost.ShouldPersistState.Subscribe(_ => shouldPersistReceived = true);
RxApp.SuspensionHost.SetupDefaultSuspendResume(new DummySuspensionDriver()); RxApp.SuspensionHost.SetupDefaultSuspendResume(new FakeSuspensionDriver());
suspension.OnFrameworkInitializationCompleted(); suspension.OnFrameworkInitializationCompleted();
lifetime.Shutdown(); lifetime.Shutdown();
Assert.True(shouldPersistReceived); Assert.True(shouldPersistReceived);
Assert.Equal("Foo", RxApp.SuspensionHost.GetAppState<AppState>().Example); Assert.Equal("Foo", RxApp.SuspensionHost.GetAppState<AppState>().Example);
} }
} }
private class FakeSuspensionDriver : ISuspensionDriver
{
public IObservable<object> LoadState() => Observable.Empty<object>();
public IObservable<Unit> SaveState(object state) => Observable.Empty<Unit>();
public IObservable<Unit> InvalidateState() => Observable.Empty<Unit>();
}
} }
} }

11
tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs

@ -12,6 +12,7 @@ using Xunit;
using Splat; using Splat;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
using Avalonia.Threading;
namespace Avalonia.ReactiveUI.UnitTests namespace Avalonia.ReactiveUI.UnitTests
{ {
@ -109,10 +110,12 @@ namespace Avalonia.ReactiveUI.UnitTests
var fakeRenderedDecorator = new TestRoot(); var fakeRenderedDecorator = new TestRoot();
fakeRenderedDecorator.Child = userControl; fakeRenderedDecorator.Child = userControl;
Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded);
Assert.True(activated[0]); Assert.True(activated[0]);
Assert.Equal(1, activated.Count); Assert.Equal(1, activated.Count);
fakeRenderedDecorator.Child = null; fakeRenderedDecorator.Child = null;
Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded);
Assert.True(activated[0]); Assert.True(activated[0]);
Assert.False(activated[1]); Assert.False(activated[1]);
Assert.Equal(2, activated.Count); Assert.Equal(2, activated.Count);
@ -139,9 +142,11 @@ namespace Avalonia.ReactiveUI.UnitTests
var fakeRenderedDecorator = new TestRoot(); var fakeRenderedDecorator = new TestRoot();
fakeRenderedDecorator.Child = userControl; fakeRenderedDecorator.Child = userControl;
Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded);
Assert.True(userControl.Active); Assert.True(userControl.Active);
fakeRenderedDecorator.Child = null; fakeRenderedDecorator.Child = null;
Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded);
Assert.False(userControl.Active); Assert.False(userControl.Active);
} }
@ -154,9 +159,11 @@ namespace Avalonia.ReactiveUI.UnitTests
Assert.False(window.Active); Assert.False(window.Active);
window.Show(); window.Show();
Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded);
Assert.True(window.Active); Assert.True(window.Active);
window.Close(); window.Close();
Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded);
Assert.False(window.Active); Assert.False(window.Active);
} }
} }
@ -171,9 +178,11 @@ namespace Avalonia.ReactiveUI.UnitTests
Assert.False(viewModel.IsActivated); Assert.False(viewModel.IsActivated);
window.Show(); window.Show();
Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded);
Assert.True(viewModel.IsActivated); Assert.True(viewModel.IsActivated);
window.Close(); window.Close();
Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded);
Assert.False(viewModel.IsActivated); Assert.False(viewModel.IsActivated);
} }
} }
@ -187,9 +196,11 @@ namespace Avalonia.ReactiveUI.UnitTests
Assert.False(viewModel.IsActivated); Assert.False(viewModel.IsActivated);
root.Child = control; root.Child = control;
Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded);
Assert.True(viewModel.IsActivated); Assert.True(viewModel.IsActivated);
root.Child = null; root.Child = null;
Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded);
Assert.False(viewModel.IsActivated); Assert.False(viewModel.IsActivated);
} }
} }

5
tests/Avalonia.ReactiveUI.UnitTests/ReactiveUserControlTest.cs

@ -1,5 +1,6 @@
using System.Reactive.Disposables; using System.Reactive.Disposables;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Threading;
using Avalonia.UnitTests; using Avalonia.UnitTests;
using ReactiveUI; using ReactiveUI;
using Splat; using Splat;
@ -69,12 +70,14 @@ namespace Avalonia.ReactiveUI.UnitTests
Assert.False(view.ViewModel.IsActive); Assert.False(view.ViewModel.IsActive);
root.Child = view; root.Child = view;
Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded);
Assert.NotNull(view.ViewModel); Assert.NotNull(view.ViewModel);
Assert.NotNull(view.DataContext); Assert.NotNull(view.DataContext);
Assert.True(view.ViewModel.IsActive); Assert.True(view.ViewModel.IsActive);
root.Child = null; root.Child = null;
Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded);
Assert.NotNull(view.ViewModel); Assert.NotNull(view.ViewModel);
Assert.NotNull(view.DataContext); Assert.NotNull(view.DataContext);
@ -90,12 +93,14 @@ namespace Avalonia.ReactiveUI.UnitTests
Assert.False(view.ViewModel.IsActive); Assert.False(view.ViewModel.IsActive);
root.Child = view; root.Child = view;
Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded);
Assert.NotNull(view.ViewModel); Assert.NotNull(view.ViewModel);
Assert.NotNull(view.DataContext); Assert.NotNull(view.DataContext);
Assert.True(view.ViewModel.IsActive); Assert.True(view.ViewModel.IsActive);
root.Child = null; root.Child = null;
Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded);
Assert.NotNull(view.ViewModel); Assert.NotNull(view.ViewModel);
Assert.NotNull(view.DataContext); Assert.NotNull(view.DataContext);

5
tests/Avalonia.ReactiveUI.UnitTests/ReactiveWindowTest.cs

@ -1,4 +1,5 @@
using System.Reactive.Disposables; using System.Reactive.Disposables;
using Avalonia.Threading;
using Avalonia.UnitTests; using Avalonia.UnitTests;
using ReactiveUI; using ReactiveUI;
using Splat; using Splat;
@ -72,12 +73,14 @@ namespace Avalonia.ReactiveUI.UnitTests
Assert.False(view.ViewModel.IsActive); Assert.False(view.ViewModel.IsActive);
view.Show(); view.Show();
Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded);
Assert.NotNull(view.ViewModel); Assert.NotNull(view.ViewModel);
Assert.NotNull(view.DataContext); Assert.NotNull(view.DataContext);
Assert.True(view.ViewModel.IsActive); Assert.True(view.ViewModel.IsActive);
view.Close(); view.Close();
Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded);
Assert.NotNull(view.ViewModel); Assert.NotNull(view.ViewModel);
Assert.NotNull(view.DataContext); Assert.NotNull(view.DataContext);
@ -96,12 +99,14 @@ namespace Avalonia.ReactiveUI.UnitTests
view.Show(); view.Show();
Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded);
Assert.NotNull(view.ViewModel); Assert.NotNull(view.ViewModel);
Assert.NotNull(view.DataContext); Assert.NotNull(view.DataContext);
Assert.True(view.ViewModel.IsActive); Assert.True(view.ViewModel.IsActive);
view.Close(); view.Close();
Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded);
Assert.NotNull(view.ViewModel); Assert.NotNull(view.ViewModel);
Assert.NotNull(view.DataContext); Assert.NotNull(view.DataContext);
Assert.False(view.ViewModel.IsActive); Assert.False(view.ViewModel.IsActive);

4
tests/Avalonia.ReactiveUI.UnitTests/RoutedViewHostTest.cs

@ -15,6 +15,7 @@ using System.ComponentModel;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Reactive; using System.Reactive;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
using Avalonia.Threading;
namespace Avalonia.ReactiveUI.UnitTests namespace Avalonia.ReactiveUI.UnitTests
{ {
@ -75,6 +76,7 @@ namespace Avalonia.ReactiveUI.UnitTests
Child = host Child = host
}; };
Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded);
Assert.NotNull(host.Content); Assert.NotNull(host.Content);
Assert.IsType<TextBlock>(host.Content); Assert.IsType<TextBlock>(host.Content);
Assert.Equal(defaultContent, host.Content); Assert.Equal(defaultContent, host.Content);
@ -126,6 +128,7 @@ namespace Avalonia.ReactiveUI.UnitTests
Child = host Child = host
}; };
Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded);
Assert.NotNull(host.Content); Assert.NotNull(host.Content);
Assert.IsType<TextBlock>(host.Content); Assert.IsType<TextBlock>(host.Content);
Assert.Equal(defaultContent, host.Content); Assert.Equal(defaultContent, host.Content);
@ -191,6 +194,7 @@ namespace Avalonia.ReactiveUI.UnitTests
Child = host Child = host
}; };
Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded);
Assert.NotNull(host.Content); Assert.NotNull(host.Content);
Assert.Equal(defaultContent, host.Content); Assert.Equal(defaultContent, host.Content);

3
tests/Avalonia.ReactiveUI.UnitTests/ViewModelViewHostTest.cs

@ -1,4 +1,5 @@
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Threading;
using Avalonia.UnitTests; using Avalonia.UnitTests;
using ReactiveUI; using ReactiveUI;
using Splat; using Splat;
@ -46,6 +47,7 @@ namespace Avalonia.ReactiveUI.UnitTests
Child = host Child = host
}; };
Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded);
Assert.NotNull(host.Content); Assert.NotNull(host.Content);
Assert.Equal(typeof(TextBlock), host.Content.GetType()); Assert.Equal(typeof(TextBlock), host.Content.GetType());
Assert.Equal(defaultContent, host.Content); Assert.Equal(defaultContent, host.Content);
@ -91,6 +93,7 @@ namespace Avalonia.ReactiveUI.UnitTests
Child = host Child = host
}; };
Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded);
Assert.NotNull(host.Content); Assert.NotNull(host.Content);
Assert.Equal(typeof(TextBlock), host.Content.GetType()); Assert.Equal(typeof(TextBlock), host.Content.GetType());
Assert.Equal(defaultContent, host.Content); Assert.Equal(defaultContent, host.Content);

2
tests/Avalonia.RenderTests/Media/BitmapTests.cs

@ -102,9 +102,7 @@ namespace Avalonia.Direct2D1.RenderTests.Media
[InlineData(PixelFormat.Bgra8888), InlineData(PixelFormat.Rgba8888)] [InlineData(PixelFormat.Bgra8888), InlineData(PixelFormat.Rgba8888)]
public void WriteableBitmapShouldBeUsable(PixelFormat fmt) public void WriteableBitmapShouldBeUsable(PixelFormat fmt)
{ {
#pragma warning disable CS0618 // Type or member is obsolete
var writeableBitmap = new WriteableBitmap(new PixelSize(256, 256), new Vector(96, 96), fmt); var writeableBitmap = new WriteableBitmap(new PixelSize(256, 256), new Vector(96, 96), fmt);
#pragma warning restore CS0618 // Type or member is obsolete
var data = new int[256 * 256]; var data = new int[256 * 256];
for (int y = 0; y < 256; y++) for (int y = 0; y < 256; y++)

Loading…
Cancel
Save