From bb0a8b698567addeb835341504c2a8aff2125bc2 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Mon, 23 Nov 2020 23:19:16 -0500 Subject: [PATCH 1/5] Fix compiled PropertyElement.ToString() for first Property element --- .../CompiledBindings/CompiledBindingPath.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs index f6636664c1..11489c39aa 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs @@ -72,7 +72,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings internal object RawSource { get; } public override string ToString() - => string.Concat(_elements.Select(e => e.ToString())); + => string.Concat(_elements); } public class CompiledBindingPathBuilder @@ -88,7 +88,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings public CompiledBindingPathBuilder Property(IPropertyInfo info, Func, IPropertyInfo, IPropertyAccessor> accessorFactory) { - _elements.Add(new PropertyElement(info, accessorFactory)); + _elements.Add(new PropertyElement(info, accessorFactory, _elements.Count == 0)); return this; } @@ -161,10 +161,13 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings internal class PropertyElement : ICompiledBindingPathElement { - public PropertyElement(IPropertyInfo property, Func, IPropertyInfo, IPropertyAccessor> accessorFactory) + private readonly bool _isFirstElement; + + public PropertyElement(IPropertyInfo property, Func, IPropertyInfo, IPropertyAccessor> accessorFactory, bool isFirstElement) { Property = property; AccessorFactory = accessorFactory; + _isFirstElement = isFirstElement; } public IPropertyInfo Property { get; } @@ -172,7 +175,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings public Func, IPropertyInfo, IPropertyAccessor> AccessorFactory { get; } public override string ToString() - => $".{Property.Name}"; + => _isFirstElement ? Property.Name : $".{Property.Name}"; } internal interface IStronglyTypedStreamElement : ICompiledBindingPathElement From 14e3799641fc55a8c7fd9dd183bd3a718fa570f6 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Mon, 23 Nov 2020 23:20:51 -0500 Subject: [PATCH 2/5] Use BindingBase in DataGrid instead of Binding and support CompiledBinding --- .../DataGridBoundColumn.cs | 21 ++++++++++++------- .../DataGridColumn.cs | 16 ++++++++------ .../DataGridColumns.cs | 5 +++-- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/Avalonia.Controls.DataGrid/DataGridBoundColumn.cs b/src/Avalonia.Controls.DataGrid/DataGridBoundColumn.cs index 1e72a07760..90401a00a2 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridBoundColumn.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridBoundColumn.cs @@ -10,7 +10,8 @@ using System.Reactive.Disposables; using System.Reactive.Subjects; using Avalonia.Reactive; using System.Diagnostics; -using Avalonia.Controls.Utils; +using Avalonia.Controls.Utils; +using Avalonia.Markup.Xaml.MarkupExtensions; namespace Avalonia.Controls { @@ -47,14 +48,15 @@ namespace Avalonia.Controls if (_binding != null) { - if(_binding is Avalonia.Data.Binding binding) + if(_binding is BindingBase binding) { if (binding.Mode == BindingMode.OneWayToSource) { throw new InvalidOperationException("DataGridColumn doesn't support BindingMode.OneWayToSource. Use BindingMode.TwoWay instead."); } - if (!String.IsNullOrEmpty(binding.Path) && binding.Mode == BindingMode.Default) + var path = (binding as Binding)?.Path ?? (binding as CompiledBindingExtension)?.Path.ToString(); + if (!string.IsNullOrEmpty(path) && binding.Mode == BindingMode.Default) { binding.Mode = BindingMode.TwoWay; } @@ -136,13 +138,16 @@ namespace Avalonia.Controls internal void SetHeaderFromBinding() { if (OwningGrid != null && OwningGrid.DataConnection.DataType != null - && Header == null && Binding != null && Binding is Binding binding - && !String.IsNullOrWhiteSpace(binding.Path)) + && Header == null && Binding != null && Binding is BindingBase binding) { - string header = OwningGrid.DataConnection.DataType.GetDisplayName(binding.Path); - if (header != null) + var path = (binding as Binding)?.Path ?? (binding as CompiledBindingExtension)?.Path.ToString(); + if (!string.IsNullOrWhiteSpace(path)) { - Header = header; + var header = OwningGrid.DataConnection.DataType.GetDisplayName(path); + if (header != null) + { + Header = header; + } } } } diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumn.cs b/src/Avalonia.Controls.DataGrid/DataGridColumn.cs index 92ddd4e736..407d6ff058 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridColumn.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridColumn.cs @@ -12,6 +12,7 @@ using System; using System.Linq; using System.Diagnostics; using Avalonia.Controls.Utils; +using Avalonia.Markup.Xaml.MarkupExtensions; namespace Avalonia.Controls { @@ -1033,13 +1034,16 @@ namespace Avalonia.Controls if (String.IsNullOrEmpty(result)) { - - if(this is DataGridBoundColumn boundColumn && - boundColumn.Binding != null && - boundColumn.Binding is Binding binding && - binding.Path != null) + if (this is DataGridBoundColumn boundColumn) { - result = binding.Path; + if (boundColumn.Binding is Binding binding) + { + result = binding.Path; + } + else if (boundColumn.Binding is CompiledBindingExtension compiledBinding) + { + result = compiledBinding.Path.ToString(); + } } } diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumns.cs b/src/Avalonia.Controls.DataGrid/DataGridColumns.cs index 46bcd0d347..a4577ee952 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridColumns.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridColumns.cs @@ -5,6 +5,7 @@ using Avalonia.Controls.Utils; using Avalonia.Data; +using Avalonia.Markup.Xaml.MarkupExtensions; using Avalonia.Utilities; using System; using System.Collections.Generic; @@ -141,9 +142,9 @@ namespace Avalonia.Controls Debug.Assert(dataGridColumn != null); if (dataGridColumn is DataGridBoundColumn dataGridBoundColumn && - dataGridBoundColumn.Binding is Binding binding) + dataGridBoundColumn.Binding is BindingBase binding) { - string path = binding.Path; + var path = (binding as Binding)?.Path ?? (binding as CompiledBindingExtension)?.Path.ToString(); if (string.IsNullOrWhiteSpace(path)) { From 26fa71bddce33848565dfd4c42df314364d7cec3 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Mon, 23 Nov 2020 23:23:55 -0500 Subject: [PATCH 3/5] DataGrid add CompiledBinding example --- samples/ControlCatalog/Pages/DataGridPage.xaml | 5 +++-- samples/ControlCatalog/Pages/DataGridPage.xaml.cs | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/samples/ControlCatalog/Pages/DataGridPage.xaml b/samples/ControlCatalog/Pages/DataGridPage.xaml index cacc2204bd..6dc50087f8 100644 --- a/samples/ControlCatalog/Pages/DataGridPage.xaml +++ b/samples/ControlCatalog/Pages/DataGridPage.xaml @@ -1,5 +1,5 @@ @@ -26,7 +26,8 @@ - + + diff --git a/samples/ControlCatalog/Pages/DataGridPage.xaml.cs b/samples/ControlCatalog/Pages/DataGridPage.xaml.cs index 2a30f4d91b..dc5cc49a90 100644 --- a/samples/ControlCatalog/Pages/DataGridPage.xaml.cs +++ b/samples/ControlCatalog/Pages/DataGridPage.xaml.cs @@ -24,8 +24,10 @@ namespace ControlCatalog.Pages dg1.LoadingRow += Dg1_LoadingRow; dg1.Sorting += (s, a) => { - var property = ((a.Column as DataGridBoundColumn)?.Binding as Binding).Path; - if (property == dataGridSortDescription.PropertyPath + var binding = (a.Column as DataGridBoundColumn)?.Binding as Binding; + + if (binding?.Path is string property + && property == dataGridSortDescription.PropertyPath && !collectionView1.SortDescriptions.Contains(dataGridSortDescription)) { collectionView1.SortDescriptions.Add(dataGridSortDescription); From b8d3f85636d15b39c05e5b3cd2869dfd20532047 Mon Sep 17 00:00:00 2001 From: MichalPetryka <35800402+MichalPetryka@users.noreply.github.com> Date: Thu, 28 Jan 2021 19:57:49 +0100 Subject: [PATCH 4/5] Correct RtlGetVersion usage --- src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index b21741453c..1559c7e794 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -1432,15 +1432,15 @@ namespace Avalonia.Win32.Interop } [DllImport("ntdll")] - private static extern int RtlGetVersion(out RTL_OSVERSIONINFOEX lpVersionInformation); + private static extern int RtlGetVersion(ref RTL_OSVERSIONINFOEX lpVersionInformation); internal static Version RtlGetVersion() { RTL_OSVERSIONINFOEX v = new RTL_OSVERSIONINFOEX(); v.dwOSVersionInfoSize = (uint)Marshal.SizeOf(v); - if (RtlGetVersion(out v) == 0) + if (RtlGetVersion(ref v) == 0) { - return new Version((int)v.dwMajorVersion, (int)v.dwMinorVersion, (int)v.dwBuildNumber, (int)v.dwPlatformId); + return new Version((int)v.dwMajorVersion, (int)v.dwMinorVersion, (int)v.dwBuildNumber); } else { From 2a8b7302ce4520e7e7f9cde2788e8890310a4c13 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Mon, 15 Feb 2021 16:43:34 -0500 Subject: [PATCH 5/5] Enable nullable reference types on Avalonia.ReactiveUI project --- .../AutoDataTemplateBindingHook.cs | 2 +- .../Avalonia.ReactiveUI.csproj | 2 ++ src/Avalonia.ReactiveUI/ReactiveUserControl.cs | 12 ++++++------ src/Avalonia.ReactiveUI/ReactiveWindow.cs | 14 +++++++------- src/Avalonia.ReactiveUI/RoutedViewHost.cs | 16 ++++++++-------- .../TransitioningContentControl.cs | 16 ++++++++-------- src/Avalonia.ReactiveUI/ViewModelViewHost.cs | 12 ++++++------ 7 files changed, 38 insertions(+), 36 deletions(-) diff --git a/src/Avalonia.ReactiveUI/AutoDataTemplateBindingHook.cs b/src/Avalonia.ReactiveUI/AutoDataTemplateBindingHook.cs index fe4e9dd94a..b6cdb77f81 100644 --- a/src/Avalonia.ReactiveUI/AutoDataTemplateBindingHook.cs +++ b/src/Avalonia.ReactiveUI/AutoDataTemplateBindingHook.cs @@ -29,7 +29,7 @@ namespace Avalonia.ReactiveUI /// public bool ExecuteHook( - object source, object target, + object? source, object target, Func[]> getCurrentViewModelProperties, Func[]> getCurrentViewProperties, BindingDirection direction) diff --git a/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj b/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj index 624c5772a9..2792ae1c91 100644 --- a/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj +++ b/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj @@ -3,6 +3,8 @@ netstandard2.0 Avalonia.ReactiveUI false + enable + nullable diff --git a/src/Avalonia.ReactiveUI/ReactiveUserControl.cs b/src/Avalonia.ReactiveUI/ReactiveUserControl.cs index f4824ade0a..4987349162 100644 --- a/src/Avalonia.ReactiveUI/ReactiveUserControl.cs +++ b/src/Avalonia.ReactiveUI/ReactiveUserControl.cs @@ -17,8 +17,8 @@ namespace Avalonia.ReactiveUI /// ViewModel type. public class ReactiveUserControl : UserControl, IViewFor where TViewModel : class { - public static readonly StyledProperty ViewModelProperty = AvaloniaProperty - .Register, TViewModel>(nameof(ViewModel)); + public static readonly StyledProperty ViewModelProperty = AvaloniaProperty + .Register, TViewModel?>(nameof(ViewModel)); /// /// Initializes a new instance of the class. @@ -34,16 +34,16 @@ namespace Avalonia.ReactiveUI /// /// The ViewModel. /// - public TViewModel ViewModel + public TViewModel? ViewModel { get => GetValue(ViewModelProperty); set => SetValue(ViewModelProperty, value); } - object IViewFor.ViewModel + object? IViewFor.ViewModel { get => ViewModel; - set => ViewModel = (TViewModel)value; + set => ViewModel = (TViewModel?)value; } protected override void OnDataContextChanged(EventArgs e) @@ -51,7 +51,7 @@ namespace Avalonia.ReactiveUI ViewModel = DataContext as TViewModel; } - private void OnViewModelChanged(object value) + private void OnViewModelChanged(object? value) { if (value == null) { diff --git a/src/Avalonia.ReactiveUI/ReactiveWindow.cs b/src/Avalonia.ReactiveUI/ReactiveWindow.cs index 758a807bfc..726fb3d661 100644 --- a/src/Avalonia.ReactiveUI/ReactiveWindow.cs +++ b/src/Avalonia.ReactiveUI/ReactiveWindow.cs @@ -17,8 +17,8 @@ namespace Avalonia.ReactiveUI /// ViewModel type. public class ReactiveWindow : Window, IViewFor where TViewModel : class { - public static readonly StyledProperty ViewModelProperty = AvaloniaProperty - .Register, TViewModel>(nameof(ViewModel)); + public static readonly StyledProperty ViewModelProperty = AvaloniaProperty + .Register, TViewModel?>(nameof(ViewModel)); /// /// Initializes a new instance of the class. @@ -35,19 +35,19 @@ namespace Avalonia.ReactiveUI /// /// The ViewModel. /// - public TViewModel ViewModel + public TViewModel? ViewModel { get => GetValue(ViewModelProperty); set => SetValue(ViewModelProperty, value); } - object IViewFor.ViewModel + object? IViewFor.ViewModel { get => ViewModel; - set => ViewModel = (TViewModel)value; + set => ViewModel = (TViewModel?)value; } - private void OnDataContextChanged(object value) + private void OnDataContextChanged(object? value) { if (value is TViewModel viewModel) { @@ -59,7 +59,7 @@ namespace Avalonia.ReactiveUI } } - private void OnViewModelChanged(object value) + private void OnViewModelChanged(object? value) { if (value == null) { diff --git a/src/Avalonia.ReactiveUI/RoutedViewHost.cs b/src/Avalonia.ReactiveUI/RoutedViewHost.cs index 421633cd58..63456bc13a 100644 --- a/src/Avalonia.ReactiveUI/RoutedViewHost.cs +++ b/src/Avalonia.ReactiveUI/RoutedViewHost.cs @@ -55,8 +55,8 @@ namespace Avalonia.ReactiveUI /// /// for the property. /// - public static readonly StyledProperty RouterProperty = - AvaloniaProperty.Register(nameof(Router)); + public static readonly StyledProperty RouterProperty = + AvaloniaProperty.Register(nameof(Router)); /// /// Initializes a new instance of the class. @@ -67,12 +67,12 @@ namespace Avalonia.ReactiveUI { var routerRemoved = this .WhenAnyValue(x => x.Router) - .Where(router => router == null) - .Cast(); + .Where(router => router == null)! + .Cast(); this.WhenAnyValue(x => x.Router) .Where(router => router != null) - .SelectMany(router => router.CurrentViewModel) + .SelectMany(router => router!.CurrentViewModel) .Merge(routerRemoved) .Subscribe(NavigateToViewModel) .DisposeWith(disposables); @@ -82,7 +82,7 @@ namespace Avalonia.ReactiveUI /// /// Gets or sets the of the view model stack. /// - public RoutingState Router + public RoutingState? Router { get => GetValue(RouterProperty); set => SetValue(RouterProperty, value); @@ -91,13 +91,13 @@ namespace Avalonia.ReactiveUI /// /// Gets or sets the ReactiveUI view locator used by this router. /// - public IViewLocator ViewLocator { get; set; } + public IViewLocator? ViewLocator { get; set; } /// /// Invoked when ReactiveUI router navigates to a view model. /// /// ViewModel to which the user navigates. - private void NavigateToViewModel(object viewModel) + private void NavigateToViewModel(object? viewModel) { if (Router == null) { diff --git a/src/Avalonia.ReactiveUI/TransitioningContentControl.cs b/src/Avalonia.ReactiveUI/TransitioningContentControl.cs index cc1613ce19..9685ecbe91 100644 --- a/src/Avalonia.ReactiveUI/TransitioningContentControl.cs +++ b/src/Avalonia.ReactiveUI/TransitioningContentControl.cs @@ -13,20 +13,20 @@ namespace Avalonia.ReactiveUI /// /// for the property. /// - public static readonly StyledProperty PageTransitionProperty = - AvaloniaProperty.Register(nameof(PageTransition), + public static readonly StyledProperty PageTransitionProperty = + AvaloniaProperty.Register(nameof(PageTransition), new CrossFade(TimeSpan.FromSeconds(0.5))); /// /// for the property. /// - public static readonly StyledProperty DefaultContentProperty = - AvaloniaProperty.Register(nameof(DefaultContent)); + public static readonly StyledProperty DefaultContentProperty = + AvaloniaProperty.Register(nameof(DefaultContent)); /// /// Gets or sets the animation played when content appears and disappears. /// - public IPageTransition PageTransition + public IPageTransition? PageTransition { get => GetValue(PageTransitionProperty); set => SetValue(PageTransitionProperty, value); @@ -35,7 +35,7 @@ namespace Avalonia.ReactiveUI /// /// Gets or sets the content displayed whenever there is no page currently routed. /// - public object DefaultContent + public object? DefaultContent { get => GetValue(DefaultContentProperty); set => SetValue(DefaultContentProperty, value); @@ -44,7 +44,7 @@ namespace Avalonia.ReactiveUI /// /// Gets or sets the content with animation. /// - public new object Content + public new object? Content { get => base.Content; set => UpdateContentWithTransition(value); @@ -60,7 +60,7 @@ namespace Avalonia.ReactiveUI /// Updates the content with transitions. /// /// New content to set. - private async void UpdateContentWithTransition(object content) + private async void UpdateContentWithTransition(object? content) { if (PageTransition != null) await PageTransition.Start(this, null, true); diff --git a/src/Avalonia.ReactiveUI/ViewModelViewHost.cs b/src/Avalonia.ReactiveUI/ViewModelViewHost.cs index 0169b268ca..c88323d674 100644 --- a/src/Avalonia.ReactiveUI/ViewModelViewHost.cs +++ b/src/Avalonia.ReactiveUI/ViewModelViewHost.cs @@ -15,8 +15,8 @@ namespace Avalonia.ReactiveUI /// /// for the property. /// - public static readonly AvaloniaProperty ViewModelProperty = - AvaloniaProperty.Register(nameof(ViewModel)); + public static readonly AvaloniaProperty ViewModelProperty = + AvaloniaProperty.Register(nameof(ViewModel)); /// /// Initializes a new instance of the class. @@ -34,7 +34,7 @@ namespace Avalonia.ReactiveUI /// /// Gets or sets the ViewModel to display. /// - public object ViewModel + public object? ViewModel { get => GetValue(ViewModelProperty); set => SetValue(ViewModelProperty, value); @@ -43,13 +43,13 @@ namespace Avalonia.ReactiveUI /// /// Gets or sets the view locator. /// - public IViewLocator ViewLocator { get; set; } + public IViewLocator? ViewLocator { get; set; } /// /// Invoked when ReactiveUI router navigates to a view model. /// /// ViewModel to which the user navigates. - private void NavigateToViewModel(object viewModel) + private void NavigateToViewModel(object? viewModel) { if (viewModel == null) { @@ -74,4 +74,4 @@ namespace Avalonia.ReactiveUI Content = viewInstance; } } -} \ No newline at end of file +}