diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml index c99a6b117b..874560a294 100644 --- a/samples/ControlCatalog/MainView.xaml +++ b/samples/ControlCatalog/MainView.xaml @@ -34,7 +34,8 @@ + ScrollViewer.VerticalScrollBarVisibility="Disabled" + ScrollViewer.HorizontalScrollBarVisibility="Disabled"> diff --git a/samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml b/samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml index d0631d2cbd..103c016298 100644 --- a/samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml +++ b/samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml @@ -14,12 +14,19 @@ UniformGrid - Horizontal + - + + + + + + + diff --git a/samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs b/samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs index 1a607342f3..decd849adc 100644 --- a/samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs +++ b/samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs @@ -74,7 +74,7 @@ namespace ControlCatalog.Pages private void RepeaterClick(object sender, PointerPressedEventArgs e) { - var item = (e.Source as TextBlock)?.DataContext as string; + var item = (e.Source as TextBlock)?.DataContext as ItemsRepeaterPageViewModel.Item; ((ItemsRepeaterPageViewModel)DataContext).SelectedItem = item; } } diff --git a/samples/ControlCatalog/Pages/ListBoxPage.xaml.cs b/samples/ControlCatalog/Pages/ListBoxPage.xaml.cs index cdbf8fd2b6..0f2d4461bf 100644 --- a/samples/ControlCatalog/Pages/ListBoxPage.xaml.cs +++ b/samples/ControlCatalog/Pages/ListBoxPage.xaml.cs @@ -78,7 +78,7 @@ namespace ControlCatalog.Pages } } - private string GenerateItem() => $"Item {_counter++}"; + private string GenerateItem() => $"Item {_counter++.ToString()}"; } } } diff --git a/samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs b/samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs index 436a479441..bc2ce80714 100644 --- a/samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs +++ b/samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs @@ -1,4 +1,5 @@ -using System.Collections.ObjectModel; +using System; +using System.Collections.ObjectModel; using System.Linq; using ReactiveUI; @@ -10,18 +11,44 @@ namespace ControlCatalog.ViewModels public ItemsRepeaterPageViewModel() { - Items = new ObservableCollection( - Enumerable.Range(1, 100000).Select(i => $"Item {i}")); + Items = new ObservableCollection( + Enumerable.Range(1, 100000).Select(i => new Item + { + Text = $"Item {i.ToString()}", + })); } - public ObservableCollection Items { get; } + public ObservableCollection Items { get; } - public string SelectedItem { get; set; } + public Item SelectedItem { get; set; } public void AddItem() { var index = SelectedItem != null ? Items.IndexOf(SelectedItem) : -1; - Items.Insert(index + 1, $"New Item {newItemIndex++}"); + Items.Insert(index + 1, new Item { Text = $"New Item {newItemIndex++}" }); + } + + public void RandomizeHeights() + { + var random = new Random(); + + foreach (var i in Items) + { + i.Height = random.Next(240) + 10; + } + } + + public class Item : ReactiveObject + { + private double _height = double.NaN; + + public string Text { get; set; } + + public double Height + { + get => _height; + set => this.RaiseAndSetIfChanged(ref _height, value); + } } } } diff --git a/src/Avalonia.Animation/DisposeAnimationInstanceSubject.cs b/src/Avalonia.Animation/DisposeAnimationInstanceSubject.cs index b44f392ce3..0c15524362 100644 --- a/src/Avalonia.Animation/DisposeAnimationInstanceSubject.cs +++ b/src/Avalonia.Animation/DisposeAnimationInstanceSubject.cs @@ -2,14 +2,7 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; -using System.Collections.Generic; -using System.Linq; -using System.Reactive.Linq; using Avalonia.Animation.Animators; -using Avalonia.Animation.Utils; -using Avalonia.Collections; -using Avalonia.Data; -using Avalonia.Reactive; namespace Avalonia.Animation { @@ -46,6 +39,7 @@ namespace Avalonia.Animation public void OnError(Exception error) { _lastInstance?.Dispose(); + _lastInstance = null; } void IObserver.OnNext(bool matchVal) @@ -53,12 +47,18 @@ namespace Avalonia.Animation if (matchVal != _lastMatch) { _lastInstance?.Dispose(); + if (matchVal) { _lastInstance = _animator.Run(_animation, _control, _clock, _onComplete); } + else + { + _lastInstance = null; + } + _lastMatch = matchVal; } } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Base/PriorityLevel.cs b/src/Avalonia.Base/PriorityLevel.cs index 6366911e77..a2364083ea 100644 --- a/src/Avalonia.Base/PriorityLevel.cs +++ b/src/Avalonia.Base/PriorityLevel.cs @@ -3,7 +3,8 @@ using System; using System.Collections.Generic; -using System.Reactive.Disposables; +using System.Diagnostics; +using System.Threading; using Avalonia.Data; namespace Avalonia @@ -181,9 +182,9 @@ namespace Avalonia private sealed class RemoveBindingDisposable : IDisposable { - private readonly LinkedListNode _binding; private readonly LinkedList _bindings; private readonly PriorityLevel _priorityLevel; + private LinkedListNode _binding; public RemoveBindingDisposable( LinkedListNode binding, @@ -197,11 +198,21 @@ namespace Avalonia public void Dispose() { - PriorityBindingEntry entry = _binding.Value; + LinkedListNode binding = Interlocked.Exchange(ref _binding, null); + + if (binding == null) + { + // Some system is trying to remove binding twice. + Debug.Assert(false); + + return; + } + + PriorityBindingEntry entry = binding.Value; if (!entry.HasCompleted) { - _bindings.Remove(_binding); + _bindings.Remove(binding); entry.Dispose(); diff --git a/src/Avalonia.Base/Utilities/SingleOrDictionary.cs b/src/Avalonia.Base/Utilities/SingleOrDictionary.cs index bda85f90e2..140da6750d 100644 --- a/src/Avalonia.Base/Utilities/SingleOrDictionary.cs +++ b/src/Avalonia.Base/Utilities/SingleOrDictionary.cs @@ -38,7 +38,7 @@ namespace Avalonia.Utilities { if (dictionary == null) { - if (!_singleValue.HasValue || !_singleValue.Value.Key.Equals(key)) + if (!_singleValue.HasValue || !EqualityComparer.Default.Equals(_singleValue.Value.Key, key)) { value = default(TValue); return false; diff --git a/src/Avalonia.Base/Utilities/WeakEventHandlerManager.cs b/src/Avalonia.Base/Utilities/WeakEventHandlerManager.cs index f4cec98628..37e25d0fac 100644 --- a/src/Avalonia.Base/Utilities/WeakEventHandlerManager.cs +++ b/src/Avalonia.Base/Utilities/WeakEventHandlerManager.cs @@ -142,10 +142,12 @@ namespace Avalonia.Utilities _data = ndata; } + MethodInfo method = s.Method; + var subscriber = (TSubscriber)s.Target; - if (!s_Callers.TryGetValue(s.Method, out var caller)) - s_Callers[s.Method] = caller = - (CallerDelegate)Delegate.CreateDelegate(typeof(CallerDelegate), null, s.Method); + if (!s_Callers.TryGetValue(method, out var caller)) + s_Callers[method] = caller = + (CallerDelegate)Delegate.CreateDelegate(typeof(CallerDelegate), null, method); _data[_count] = new Descriptor { Caller = caller, diff --git a/src/Avalonia.Controls/Calendar/DatePicker.cs b/src/Avalonia.Controls/Calendar/DatePicker.cs index 70de8bec16..55797ae1e3 100644 --- a/src/Avalonia.Controls/Calendar/DatePicker.cs +++ b/src/Avalonia.Controls/Calendar/DatePicker.cs @@ -1042,7 +1042,8 @@ namespace Avalonia.Controls } } DateTime? d = SetTextBoxValue(s); - if (!SelectedDate.Equals(d)) + + if (SelectedDate != d) { SelectedDate = d; } @@ -1058,7 +1059,8 @@ namespace Avalonia.Controls else { DateTime? d = SetTextBoxValue(_defaultText); - if (!SelectedDate.Equals(d)) + + if (SelectedDate != d) { SelectedDate = d; } diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index debbb81264..5931fec350 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -244,7 +244,7 @@ namespace Avalonia.Controls.Presenters var rect = FormattedText.HitTestTextPosition(caretIndex); this.BringIntoView(rect); }, - DispatcherPriority.Normal); + DispatcherPriority.Render); } } } diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index a50175bdee..6869ea0822 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -1088,7 +1088,9 @@ namespace Avalonia.Controls.Primitives } else { - SelectedIndex = _updateSelectedIndex != int.MinValue ? _updateSelectedIndex : 0; + SelectedIndex = _updateSelectedIndex != int.MinValue ? + _updateSelectedIndex : + AlwaysSelected ? 0 : -1; } } } diff --git a/src/Avalonia.Controls/Primitives/Track.cs b/src/Avalonia.Controls/Primitives/Track.cs index 21a7dd68f8..a569808b35 100644 --- a/src/Avalonia.Controls/Primitives/Track.cs +++ b/src/Avalonia.Controls/Primitives/Track.cs @@ -1,10 +1,13 @@ -// Copyright (c) The Avalonia Project. All rights reserved. -// Licensed under the MIT license. See licence.md file in the project root for full license information. +// Portions of this source file are adapted from the Windows Presentation Foundation project. +// (https://github.com/dotnet/wpf/) +// +// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation. using System; using Avalonia.Input; using Avalonia.Layout; using Avalonia.Metadata; +using Avalonia.Utilities; namespace Avalonia.Controls.Primitives { @@ -34,6 +37,9 @@ namespace Avalonia.Controls.Primitives public static readonly StyledProperty