Browse Source

Merge pull request #1008 from AvaloniaUI/fixes/1004-remove-devtools-reactiveui-depenency

Remove ReactiveUI dependency from DevTools.
pull/1010/head
Nikita Tsukanov 9 years ago
committed by GitHub
parent
commit
99393ab36d
  1. 1
      samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj
  2. 123
      src/Avalonia.Base/Collections/AvaloniaListExtensions.cs
  3. 1
      src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj
  4. 3
      src/Avalonia.Diagnostics/DevTools.xaml.cs
  5. 3
      src/Avalonia.Diagnostics/ViewModels/ControlDetailsViewModel.cs
  6. 74
      src/Avalonia.Diagnostics/ViewModels/DevToolsViewModel.cs
  7. 4
      src/Avalonia.Diagnostics/ViewModels/LogicalTreeNode.cs
  8. 11
      src/Avalonia.Diagnostics/ViewModels/PropertyDetails.cs
  9. 10
      src/Avalonia.Diagnostics/ViewModels/TreeNode.cs
  10. 24
      src/Avalonia.Diagnostics/ViewModels/TreePageViewModel.cs
  11. 32
      src/Avalonia.Diagnostics/ViewModels/ViewModelBase.cs
  12. 7
      src/Avalonia.Diagnostics/ViewModels/VisualTreeNode.cs
  13. 20
      src/Avalonia.Diagnostics/Views/ControlDetailsView.cs
  14. 30
      src/Avalonia.Diagnostics/Views/PropertyChangedExtenions.cs
  15. 3
      src/Avalonia.Input/IKeyboardDevice.cs
  16. 134
      tests/Avalonia.Base.UnitTests/Collections/AvaloniaListExtenionsTests.cs

1
samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj

@ -22,6 +22,7 @@
<ItemGroup>
<ProjectReference Include="..\..\..\src\Avalonia.DesignerSupport\Avalonia.DesignerSupport.csproj" />
<ProjectReference Include="..\..\..\src\Avalonia.DotNetFrameworkRuntime\Avalonia.DotNetFrameworkRuntime.csproj" />
<ProjectReference Include="..\..\..\src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj" />
<ProjectReference Include="..\..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" />
<ProjectReference Include="..\..\..\src\Windows\Avalonia.Direct2D1\Avalonia.Direct2D1.csproj" />
<ProjectReference Include="..\..\..\src\Windows\Avalonia.Win32\Avalonia.Win32.csproj" />

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

@ -2,6 +2,7 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
@ -59,7 +60,8 @@ namespace Avalonia.Collections
/// the index in the collection and the item.
/// </param>
/// <param name="reset">
/// An action called when the collection is reset.
/// An action called when the collection is reset. This will be followed by calls to
/// <paramref name="added"/> for each item present in the collection after the reset.
/// </param>
/// <returns>A disposable used to terminate the subscription.</returns>
public static IDisposable ForEachItem<T>(
@ -68,112 +70,38 @@ namespace Avalonia.Collections
Action<int, T> removed,
Action reset)
{
int index;
NotifyCollectionChangedEventHandler handler = (_, e) =>
void Add(int index, IList items)
{
switch (e.Action)
foreach (T item in items)
{
case NotifyCollectionChangedAction.Add:
index = e.NewStartingIndex;
foreach (T item in e.NewItems)
{
added(index++, item);
}
break;
case NotifyCollectionChangedAction.Replace:
index = e.OldStartingIndex;
foreach (T item in e.OldItems)
{
removed(index++, item);
}
index = e.NewStartingIndex;
foreach (T item in e.NewItems)
{
added(index++, item);
}
break;
case NotifyCollectionChangedAction.Remove:
index = e.OldStartingIndex;
foreach (T item in e.OldItems)
{
removed(index++, item);
}
break;
case NotifyCollectionChangedAction.Reset:
if (reset == null)
{
throw new InvalidOperationException(
"Reset called on collection without reset handler.");
}
reset();
break;
added(index++, item);
}
};
}
index = 0;
foreach (T i in collection)
void Remove(int index, IList items)
{
added(index++, i);
for (var i = items.Count - 1; i >= 0; --i)
{
removed(index + i, (T)items[i]);
}
}
collection.CollectionChanged += handler;
return Disposable.Create(() => collection.CollectionChanged -= handler);
}
/// <summary>
/// Invokes an action for each item in a collection and subsequently each item added or
/// removed from the collection.
/// </summary>
/// <typeparam name="T">The type of the collection items.</typeparam>
/// <param name="collection">The collection.</param>
/// <param name="added">
/// An action called initially with all items in the collection and subsequently with a
/// list of items added to the collection. The parameters passed are the index of the
/// first item added to the collection and the items added.
/// </param>
/// <param name="removed">
/// An action called with all items removed from the collection. The parameters passed
/// are the index of the first item removed from the collection and the items removed.
/// </param>
/// <param name="reset">
/// An action called when the collection is reset.
/// </param>
/// <returns>A disposable used to terminate the subscription.</returns>
public static IDisposable ForEachItem<T>(
this IAvaloniaReadOnlyList<T> collection,
Action<int, IEnumerable<T>> added,
Action<int, IEnumerable<T>> removed,
Action reset)
{
NotifyCollectionChangedEventHandler handler = (_, e) =>
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
added(e.NewStartingIndex, e.NewItems.Cast<T>());
Add(e.NewStartingIndex, e.NewItems);
break;
case NotifyCollectionChangedAction.Move:
case NotifyCollectionChangedAction.Replace:
removed(e.OldStartingIndex, e.OldItems.Cast<T>());
added(e.NewStartingIndex, e.NewItems.Cast<T>());
Remove(e.OldStartingIndex, e.OldItems);
Add(e.NewStartingIndex, e.NewItems);
break;
case NotifyCollectionChangedAction.Remove:
removed(e.OldStartingIndex, e.OldItems.Cast<T>());
Remove(e.OldStartingIndex, e.OldItems);
break;
case NotifyCollectionChangedAction.Reset:
@ -184,16 +112,31 @@ namespace Avalonia.Collections
}
reset();
Add(0, (IList)collection);
break;
}
};
added(0, collection);
Add(0, (IList)collection);
collection.CollectionChanged += handler;
return Disposable.Create(() => collection.CollectionChanged -= handler);
}
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>
/// Listens for property changed events from all items in a collection.
/// </summary>

1
src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj

@ -34,7 +34,6 @@
<ProjectReference Include="..\Avalonia.Input\Avalonia.Input.csproj" />
<ProjectReference Include="..\Avalonia.Interactivity\Avalonia.Interactivity.csproj" />
<ProjectReference Include="..\Avalonia.Layout\Avalonia.Layout.csproj" />
<ProjectReference Include="..\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj" />
<ProjectReference Include="..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
<ProjectReference Include="..\Avalonia.Styling\Avalonia.Styling.csproj" />
<ProjectReference Include="..\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" />

3
src/Avalonia.Diagnostics/DevTools.xaml.cs

@ -11,7 +11,6 @@ using Avalonia.Input.Raw;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using Avalonia.VisualTree;
using ReactiveUI;
namespace Avalonia
{
@ -74,7 +73,7 @@ namespace Avalonia.Diagnostics
Content = devTools,
DataTemplates = new DataTemplates
{
new ViewLocator<ReactiveObject>(),
new ViewLocator<ViewModelBase>(),
}
};

3
src/Avalonia.Diagnostics/ViewModels/ControlDetailsViewModel.cs

@ -4,11 +4,10 @@
using System.Collections.Generic;
using System.Linq;
using Avalonia.VisualTree;
using ReactiveUI;
namespace Avalonia.Diagnostics.ViewModels
{
internal class ControlDetailsViewModel : ReactiveObject
internal class ControlDetailsViewModel : ViewModelBase
{
public ControlDetailsViewModel(IVisual control)
{

74
src/Avalonia.Diagnostics/ViewModels/DevToolsViewModel.cs

@ -5,32 +5,50 @@ using System;
using System.Reactive.Linq;
using Avalonia.Controls;
using Avalonia.Input;
using ReactiveUI;
namespace Avalonia.Diagnostics.ViewModels
{
internal class DevToolsViewModel : ReactiveObject
internal class DevToolsViewModel : ViewModelBase
{
private ReactiveObject _content;
private ViewModelBase _content;
private int _selectedTab;
private TreePageViewModel _logicalTree;
private TreePageViewModel _visualTree;
private readonly ObservableAsPropertyHelper<string> _focusedControl;
private readonly ObservableAsPropertyHelper<string> _pointerOverElement;
private string _focusedControl;
private string _pointerOverElement;
public DevToolsViewModel(IControl root)
{
_logicalTree = new TreePageViewModel(LogicalTreeNode.Create(root));
_visualTree = new TreePageViewModel(VisualTreeNode.Create(root));
this.WhenAnyValue(x => x.SelectedTab).Subscribe(index =>
UpdateFocusedControl();
KeyboardDevice.Instance.PropertyChanged += (s, e) =>
{
switch (index)
if (e.PropertyName == nameof(KeyboardDevice.Instance.FocusedElement))
{
UpdateFocusedControl();
}
};
root.GetObservable(TopLevel.PointerOverElementProperty)
.Subscribe(x => PointerOverElement = x?.GetType().Name);
}
public ViewModelBase Content
{
get { return _content; }
private set { RaiseAndSetIfChanged(ref _content, value); }
}
public int SelectedTab
{
get { return _selectedTab; }
set
{
_selectedTab = value;
switch (value)
{
case 0:
Content = _logicalTree;
@ -39,34 +57,23 @@ namespace Avalonia.Diagnostics.ViewModels
Content = _visualTree;
break;
}
});
_focusedControl = KeyboardDevice.Instance
.WhenAnyValue(x => x.FocusedElement)
.Select(x => x?.GetType().Name)
.ToProperty(this, x => x.FocusedControl);
_pointerOverElement = root.GetObservable(TopLevel.PointerOverElementProperty)
.Select(x => x?.GetType().Name)
.ToProperty(this, x => x.PointerOverElement);
RaisePropertyChanged();
}
}
public ReactiveObject Content
public string FocusedControl
{
get { return _content; }
private set { this.RaiseAndSetIfChanged(ref _content, value); }
get { return _focusedControl; }
private set { RaiseAndSetIfChanged(ref _focusedControl, value); }
}
public int SelectedTab
public string PointerOverElement
{
get { return _selectedTab; }
set { this.RaiseAndSetIfChanged(ref _selectedTab, value); }
get { return _pointerOverElement; }
private set { RaiseAndSetIfChanged(ref _pointerOverElement, value); }
}
public string FocusedControl => _focusedControl.Value;
public string PointerOverElement => _pointerOverElement.Value;
public void SelectControl(IControl control)
{
var tree = Content as TreePageViewModel;
@ -76,5 +83,10 @@ namespace Avalonia.Diagnostics.ViewModels
tree.SelectControl(control);
}
}
private void UpdateFocusedControl()
{
_focusedControl = KeyboardDevice.Instance.FocusedElement?.GetType().Name;
}
}
}

4
src/Avalonia.Diagnostics/ViewModels/LogicalTreeNode.cs

@ -2,9 +2,9 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.LogicalTree;
using ReactiveUI;
namespace Avalonia.Diagnostics.ViewModels
{
@ -13,7 +13,7 @@ namespace Avalonia.Diagnostics.ViewModels
public LogicalTreeNode(ILogical logical, TreeNode parent)
: base((Control)logical, parent)
{
Children = logical.LogicalChildren.CreateDerivedCollection(x => new LogicalTreeNode(x, this));
Children = logical.LogicalChildren.CreateDerivedList(x => new LogicalTreeNode(x, this));
}
public static LogicalTreeNode[] Create(object control)

11
src/Avalonia.Diagnostics/ViewModels/PropertyDetails.cs

@ -3,16 +3,13 @@
using System;
using Avalonia.Data;
using ReactiveUI;
namespace Avalonia.Diagnostics.ViewModels
{
internal class PropertyDetails : ReactiveObject
internal class PropertyDetails : ViewModelBase
{
private object _value;
private string _priority;
private string _diagnostic;
public PropertyDetails(AvaloniaObject o, AvaloniaProperty property)
@ -41,19 +38,19 @@ namespace Avalonia.Diagnostics.ViewModels
public string Priority
{
get { return _priority; }
private set { this.RaiseAndSetIfChanged(ref _priority, value); }
private set { RaiseAndSetIfChanged(ref _priority, value); }
}
public string Diagnostic
{
get { return _diagnostic; }
private set { this.RaiseAndSetIfChanged(ref _diagnostic, value); }
private set { RaiseAndSetIfChanged(ref _diagnostic, value); }
}
public object Value
{
get { return _value; }
private set { this.RaiseAndSetIfChanged(ref _value, value); }
private set { RaiseAndSetIfChanged(ref _value, value); }
}
}
}

10
src/Avalonia.Diagnostics/ViewModels/TreeNode.cs

@ -5,13 +5,13 @@ using System;
using System.Collections.Specialized;
using System.Reactive;
using System.Reactive.Linq;
using Avalonia.Collections;
using Avalonia.Styling;
using Avalonia.VisualTree;
using ReactiveUI;
namespace Avalonia.Diagnostics.ViewModels
{
internal class TreeNode : ReactiveObject
internal class TreeNode : ViewModelBase
{
private string _classes;
private bool _isExpanded;
@ -47,7 +47,7 @@ namespace Avalonia.Diagnostics.ViewModels
}
}
public IReadOnlyReactiveList<TreeNode> Children
public IAvaloniaReadOnlyList<TreeNode> Children
{
get;
protected set;
@ -56,7 +56,7 @@ namespace Avalonia.Diagnostics.ViewModels
public string Classes
{
get { return _classes; }
private set { this.RaiseAndSetIfChanged(ref _classes, value); }
private set { RaiseAndSetIfChanged(ref _classes, value); }
}
public IVisual Visual
@ -67,7 +67,7 @@ namespace Avalonia.Diagnostics.ViewModels
public bool IsExpanded
{
get { return _isExpanded; }
set { this.RaiseAndSetIfChanged(ref _isExpanded, value); }
set { RaiseAndSetIfChanged(ref _isExpanded, value); }
}
public TreeNode Parent

24
src/Avalonia.Diagnostics/ViewModels/TreePageViewModel.cs

@ -1,25 +1,19 @@
// 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.
using System.Reactive.Linq;
using Avalonia.Controls;
using Avalonia.VisualTree;
using ReactiveUI;
namespace Avalonia.Diagnostics.ViewModels
{
internal class TreePageViewModel : ReactiveObject
internal class TreePageViewModel : ViewModelBase
{
private TreeNode _selected;
private readonly ObservableAsPropertyHelper<ControlDetailsViewModel> _details;
private ControlDetailsViewModel _details;
public TreePageViewModel(TreeNode[] nodes)
{
Nodes = nodes;
_details = this.WhenAnyValue(x => x.SelectedNode)
.Select(x => x != null ? new ControlDetailsViewModel(x.Visual) : null)
.ToProperty(this, x => x.Details);
}
public TreeNode[] Nodes { get; protected set; }
@ -27,10 +21,20 @@ namespace Avalonia.Diagnostics.ViewModels
public TreeNode SelectedNode
{
get { return _selected; }
set { this.RaiseAndSetIfChanged(ref _selected, value); }
set
{
if (RaiseAndSetIfChanged(ref _selected, value))
{
Details = value != null ? new ControlDetailsViewModel(value.Visual) : null;
}
}
}
public ControlDetailsViewModel Details => _details.Value;
public ControlDetailsViewModel Details
{
get { return _details; }
private set { RaiseAndSetIfChanged(ref _details, value); }
}
public TreeNode FindNode(IControl control)
{

32
src/Avalonia.Diagnostics/ViewModels/ViewModelBase.cs

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
namespace Avalonia.Diagnostics.ViewModels
{
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected bool RaiseAndSetIfChanged<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (!EqualityComparer<T>.Default.Equals(field, value))
{
field = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
return true;
}
return false;
}
[NotifyPropertyChangedInvocator]
protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

7
src/Avalonia.Diagnostics/ViewModels/VisualTreeNode.cs

@ -1,10 +1,9 @@
// 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.
using Avalonia.Controls;
using Avalonia.Collections;
using Avalonia.Styling;
using Avalonia.VisualTree;
using ReactiveUI;
namespace Avalonia.Diagnostics.ViewModels
{
@ -17,11 +16,11 @@ namespace Avalonia.Diagnostics.ViewModels
if (host?.Root == null)
{
Children = visual.VisualChildren.CreateDerivedCollection(x => new VisualTreeNode(x, this));
Children = visual.VisualChildren.CreateDerivedList(x => new VisualTreeNode(x, this));
}
else
{
Children = new ReactiveList<VisualTreeNode>(new[] { new VisualTreeNode(host.Root, this) });
Children = new AvaloniaList<VisualTreeNode>(new[] { new VisualTreeNode(host.Root, this) });
}
if ((Visual is IStyleable styleable))

20
src/Avalonia.Diagnostics/Views/ControlDetailsView.cs

@ -8,7 +8,6 @@ using Avalonia.Controls;
using Avalonia.Diagnostics.ViewModels;
using Avalonia.Media;
using Avalonia.Styling;
using ReactiveUI;
namespace Avalonia.Diagnostics.Views
{
@ -16,6 +15,7 @@ namespace Avalonia.Diagnostics.Views
{
private static readonly StyledProperty<ControlDetailsViewModel> ViewModelProperty =
AvaloniaProperty.Register<ControlDetailsView, ControlDetailsViewModel>("ViewModel");
private SimpleGrid _grid;
public ControlDetailsView()
{
@ -27,7 +27,11 @@ namespace Avalonia.Diagnostics.Views
public ControlDetailsViewModel ViewModel
{
get { return GetValue(ViewModelProperty); }
private set { SetValue(ViewModelProperty, value); }
private set
{
SetValue(ViewModelProperty, value);
_grid[GridRepeater.ItemsProperty] = value?.Properties;
}
}
private void InitializeComponent()
@ -36,7 +40,7 @@ namespace Avalonia.Diagnostics.Views
Content = new ScrollViewer
{
Content = new SimpleGrid
Content = _grid = new SimpleGrid
{
Styles = new Styles
{
@ -49,7 +53,6 @@ namespace Avalonia.Diagnostics.Views
},
},
[GridRepeater.TemplateProperty] = pt,
[!GridRepeater.ItemsProperty] = this.WhenAnyValue(x => x.ViewModel.Properties).ToBinding(),
}
};
}
@ -62,16 +65,13 @@ namespace Avalonia.Diagnostics.Views
{
Text = property.Name,
TextWrapping = TextWrapping.NoWrap,
[!ToolTip.TipProperty] = property
.WhenAnyValue(x => x.Diagnostic)
.ToBinding(),
[!ToolTip.TipProperty] = property.GetObservable<string>(nameof(property.Diagnostic)).ToBinding(),
};
yield return new TextBlock
{
TextWrapping = TextWrapping.NoWrap,
[!TextBlock.TextProperty] = property
.WhenAnyValue(v => v.Value)
[!TextBlock.TextProperty] = property.GetObservable<object>(nameof(property.Value))
.Select(v => v?.ToString())
.ToBinding(),
};
@ -79,7 +79,7 @@ namespace Avalonia.Diagnostics.Views
yield return new TextBlock
{
TextWrapping = TextWrapping.NoWrap,
[!TextBlock.TextProperty] = property.WhenAnyValue(x => x.Priority).ToBinding(),
[!TextBlock.TextProperty] = property.GetObservable<string>((nameof(property.Priority))).ToBinding(),
};
}
}

30
src/Avalonia.Diagnostics/Views/PropertyChangedExtenions.cs

@ -0,0 +1,30 @@
using System;
using System.ComponentModel;
using System.Reactive.Linq;
using System.Reflection;
namespace Avalonia.Diagnostics.Views
{
internal static class PropertyChangedExtenions
{
public static IObservable<T> GetObservable<T>(this INotifyPropertyChanged source, string propertyName)
{
Contract.Requires<ArgumentNullException>(source != null);
Contract.Requires<ArgumentNullException>(propertyName != null);
var property = source.GetType().GetTypeInfo().GetDeclaredProperty(propertyName);
if (property == null)
{
throw new ArgumentException($"Property '{propertyName}' not found on '{source}.");
}
return Observable.FromEventPattern<PropertyChangedEventHandler, PropertyChangedEventArgs>(
e => source.PropertyChanged += e,
e => source.PropertyChanged -= e)
.Where(e => e.EventArgs.PropertyName == propertyName)
.Select(_ => (T)property.GetValue(source))
.StartWith((T)property.GetValue(source));
}
}
}

3
src/Avalonia.Input/IKeyboardDevice.cs

@ -2,6 +2,7 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.ComponentModel;
namespace Avalonia.Input
{
@ -26,7 +27,7 @@ namespace Avalonia.Input
Toggled = 2,
}
public interface IKeyboardDevice : IInputDevice
public interface IKeyboardDevice : IInputDevice, INotifyPropertyChanged
{
IInputElement FocusedElement { get; }

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

@ -0,0 +1,134 @@
using System;
using System.Linq;
using Avalonia.Collections;
using Xunit;
namespace Avalonia.Base.UnitTests.Collections
{
public class AvaloniaListExtenionsTests
{
[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);
}
[Fact]
public void CreateDerivedList_Handles_MoveRange()
{
var source = new AvaloniaList<int>(new[] { 0, 1, 2, 3 });
var target = source.CreateDerivedList(x => new Wrapper(x));
source.MoveRange(1, 2, 0);
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);
}
private class Wrapper
{
public Wrapper(int value)
{
Value = value;
}
public int Value { get; }
}
}
}
Loading…
Cancel
Save