diff --git a/samples/ControlCatalog/Pages/DataGridPage.xaml b/samples/ControlCatalog/Pages/DataGridPage.xaml index 6817d0698e..323eaa3463 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); 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)) { 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 +} 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 diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index 287d2c49a5..c137926e4c 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -1438,15 +1438,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 {