diff --git a/.gitignore b/.gitignore
index c36f64e5de..640725fa26 100644
--- a/.gitignore
+++ b/.gitignore
@@ -162,7 +162,8 @@ $RECYCLE.BIN/
#################
## Cake
#################
-tools/
+tools/*
+!tools/packages.config
.nuget
artifacts/
nuget
diff --git a/build.cake b/build.cake
index 6518431959..b3822271d4 100644
--- a/build.cake
+++ b/build.cake
@@ -11,7 +11,7 @@
// TOOLS
///////////////////////////////////////////////////////////////////////////////
-#tool "nuget:?package=xunit.runner.console&version=2.1.0"
+#tool "nuget:?package=xunit.runner.console&version=2.2.0"
#tool "nuget:?package=OpenCover"
///////////////////////////////////////////////////////////////////////////////
@@ -98,7 +98,6 @@ Task("Clean")
CleanDirectory(parameters.TestsRoot);
});
-
Task("Restore-NuGet-Packages")
.IsDependentOn("Clean")
.WithCriteria(parameters.IsRunningOnWindows)
@@ -171,23 +170,25 @@ void RunCoreTest(string dir, Parameters parameters, bool net461Only)
continue;
Information("Running for " + fw);
DotNetCoreTest(System.IO.Path.Combine(dir, System.IO.Path.GetFileName(dir)+".csproj"),
- new DotNetCoreTestSettings{Framework = fw});
+ new DotNetCoreTestSettings {
+ Configuration = parameters.Configuration,
+ Framework = fw
+ });
}
}
-
Task("Run-Net-Core-Unit-Tests")
.IsDependentOn("Clean")
.Does(() => {
RunCoreTest("./tests/Avalonia.Base.UnitTests", parameters, false);
- RunCoreTest("./tests/Avalonia.Controls.UnitTests", parameters, true);
- RunCoreTest("./tests/Avalonia.Input.UnitTests", parameters, true);
- RunCoreTest("./tests/Avalonia.Interactivity.UnitTests", parameters, true);
- RunCoreTest("./tests/Avalonia.Layout.UnitTests", parameters, true);
- //RunCoreTest("./tests/Avalonia.Markup.UnitTests", parameters, true);
- //RunCoreTest("./tests/Avalonia.Markup.Xaml.UnitTests", parameters, true);
- RunCoreTest("./tests/Avalonia.Styling.UnitTests", parameters, true);
- RunCoreTest("./tests/Avalonia.Visuals.UnitTests", parameters, true);
+ RunCoreTest("./tests/Avalonia.Controls.UnitTests", parameters, false);
+ RunCoreTest("./tests/Avalonia.Input.UnitTests", parameters, false);
+ RunCoreTest("./tests/Avalonia.Interactivity.UnitTests", parameters, false);
+ RunCoreTest("./tests/Avalonia.Layout.UnitTests", parameters, false);
+ RunCoreTest("./tests/Avalonia.Markup.UnitTests", parameters, false);
+ RunCoreTest("./tests/Avalonia.Markup.Xaml.UnitTests", parameters, false);
+ RunCoreTest("./tests/Avalonia.Styling.UnitTests", parameters, false);
+ RunCoreTest("./tests/Avalonia.Visuals.UnitTests", parameters, false);
});
Task("Run-Unit-Tests")
diff --git a/build/Moq.props b/build/Moq.props
index c8544b8309..55242d922e 100644
--- a/build/Moq.props
+++ b/build/Moq.props
@@ -1,5 +1,5 @@
-
+
diff --git a/build/XUnit.props b/build/XUnit.props
index 58df7e8d3c..27e0afc987 100644
--- a/build/XUnit.props
+++ b/build/XUnit.props
@@ -7,7 +7,9 @@
-
-
+
+
+
+
diff --git a/samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj b/samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj
index 54a816b0a9..d90a251173 100644
--- a/samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj
+++ b/samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj
@@ -22,6 +22,7 @@
+
diff --git a/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj b/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj
index 28e5e274d0..ac7d25a91e 100644
--- a/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj
+++ b/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj
@@ -181,5 +181,6 @@
+
\ No newline at end of file
diff --git a/src/Android/Avalonia.Android/AndroidPlatform.cs b/src/Android/Avalonia.Android/AndroidPlatform.cs
index c779031e6c..e9b4ad0a6d 100644
--- a/src/Android/Avalonia.Android/AndroidPlatform.cs
+++ b/src/Android/Avalonia.Android/AndroidPlatform.cs
@@ -51,7 +51,6 @@ namespace Avalonia.Android
.Bind().ToTransient()
.Bind().ToTransient()
.Bind().ToSingleton()
- .Bind().ToSingleton()
.Bind().ToConstant(Instance)
.Bind().ToConstant(ImmediateRenderer.Factory)
.Bind().ToConstant(new AndroidThreadingInterface())
diff --git a/src/Android/Avalonia.Android/Platform/Input/AndroidMouseDevice.cs b/src/Android/Avalonia.Android/Platform/Input/AndroidMouseDevice.cs
index 9ae79efb48..d52eeb15e4 100644
--- a/src/Android/Avalonia.Android/Platform/Input/AndroidMouseDevice.cs
+++ b/src/Android/Avalonia.Android/Platform/Input/AndroidMouseDevice.cs
@@ -4,6 +4,8 @@ namespace Avalonia.Android.Platform.Input
{
public class AndroidMouseDevice : MouseDevice
{
+ public static AndroidMouseDevice Instance { get; } = new AndroidMouseDevice();
+
public AndroidMouseDevice()
{
diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs
index 64dbeb89cc..051a058363 100644
--- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs
+++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs
@@ -44,7 +44,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
public int Width { get; }
public int Height { get; }
public int RowBytes { get; }
- public Size Dpi { get; } = new Size(96, 96);
+ public Vector Dpi { get; } = new Vector(96, 96);
public PixelFormat Format { get; }
[DllImport("android")]
diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
index 75772be171..0c62eb9060 100644
--- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
+++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
@@ -10,6 +10,7 @@ using Avalonia.Platform;
using System;
using System.Collections.Generic;
using System.Reactive.Disposables;
+using Avalonia.Android.Platform.Input;
using Avalonia.Controls;
using Avalonia.Controls.Platform.Surfaces;
@@ -65,6 +66,8 @@ namespace Avalonia.Android.Platform.SkiaPlatform
}
}
+ public IMouseDevice MouseDevice => AndroidMouseDevice.Instance;
+
public Action Closed { get; set; }
public Action Input { get; set; }
diff --git a/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidTouchEventsHelper.cs b/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidTouchEventsHelper.cs
index 702829b91c..0f90472bd0 100644
--- a/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidTouchEventsHelper.cs
+++ b/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidTouchEventsHelper.cs
@@ -71,7 +71,7 @@ namespace Avalonia.Android.Platform.Specific.Helpers
if (x <= _point.X && r >= _point.X && y <= _point.Y && b >= _point.Y)
{
var inputRoot = _getInputRoot();
- var mouseDevice = MouseDevice.Instance;
+ var mouseDevice = Avalonia.Android.Platform.Input.AndroidMouseDevice.Instance;
//in order the controls to work in a predictable way
//we need to generate mouse move before first mouse down event
diff --git a/src/Avalonia.Base/Collections/AvaloniaListExtensions.cs b/src/Avalonia.Base/Collections/AvaloniaListExtensions.cs
index ffb7199619..54cd132b95 100644
--- a/src/Avalonia.Base/Collections/AvaloniaListExtensions.cs
+++ b/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.
///
///
- /// An action called when the collection is reset.
+ /// An action called when the collection is reset. This will be followed by calls to
+ /// for each item present in the collection after the reset.
///
/// A disposable used to terminate the subscription.
public static IDisposable ForEachItem(
@@ -68,112 +70,38 @@ namespace Avalonia.Collections
Action 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);
- }
-
- ///
- /// Invokes an action for each item in a collection and subsequently each item added or
- /// removed from the collection.
- ///
- /// The type of the collection items.
- /// The collection.
- ///
- /// 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.
- ///
- ///
- /// 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.
- ///
- ///
- /// An action called when the collection is reset.
- ///
- /// A disposable used to terminate the subscription.
- public static IDisposable ForEachItem(
- this IAvaloniaReadOnlyList collection,
- Action> added,
- Action> removed,
- Action reset)
- {
NotifyCollectionChangedEventHandler handler = (_, e) =>
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
- added(e.NewStartingIndex, e.NewItems.Cast());
+ Add(e.NewStartingIndex, e.NewItems);
break;
+ case NotifyCollectionChangedAction.Move:
case NotifyCollectionChangedAction.Replace:
- removed(e.OldStartingIndex, e.OldItems.Cast());
- added(e.NewStartingIndex, e.NewItems.Cast());
+ Remove(e.OldStartingIndex, e.OldItems);
+ Add(e.NewStartingIndex, e.NewItems);
break;
case NotifyCollectionChangedAction.Remove:
- removed(e.OldStartingIndex, e.OldItems.Cast());
+ 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 CreateDerivedList(
+ this IAvaloniaReadOnlyList collection,
+ Func select)
+ {
+ var result = new AvaloniaList();
+
+ collection.ForEachItem(
+ (i, item) => result.Insert(i, select(item)),
+ (i, item) => result.RemoveAt(i),
+ () => result.Clear());
+
+ return result;
+ }
+
///
/// Listens for property changed events from all items in a collection.
///
diff --git a/src/Avalonia.Controls/Platform/ITopLevelImpl.cs b/src/Avalonia.Controls/Platform/ITopLevelImpl.cs
index 16f436fd45..ba3ecb99c5 100644
--- a/src/Avalonia.Controls/Platform/ITopLevelImpl.cs
+++ b/src/Avalonia.Controls/Platform/ITopLevelImpl.cs
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using Avalonia.Input;
using Avalonia.Input.Raw;
+using JetBrains.Annotations;
namespace Avalonia.Platform
{
@@ -93,5 +94,11 @@ namespace Avalonia.Platform
/// Gets or sets a method called when the underlying implementation is destroyed.
///
Action Closed { get; set; }
+
+ ///
+ /// Gets a mouse device associated with toplevel
+ ///
+ [CanBeNull]
+ IMouseDevice MouseDevice { get; }
}
}
diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs
index bb2a61c024..daea187a69 100644
--- a/src/Avalonia.Controls/Primitives/Popup.cs
+++ b/src/Avalonia.Controls/Primitives/Popup.cs
@@ -340,11 +340,11 @@ namespace Avalonia.Controls.Primitives
switch (mode)
{
case PlacementMode.Pointer:
- if (MouseDevice.Instance != null)
+ if(PopupRoot != null)
{
// Scales the Horizontal and Vertical offset to screen co-ordinates.
var screenOffset = new Point(HorizontalOffset * (PopupRoot as ILayoutRoot).LayoutScaling, VerticalOffset * (PopupRoot as ILayoutRoot).LayoutScaling);
- return MouseDevice.Instance.Position + screenOffset;
+ return (((IInputRoot)PopupRoot)?.MouseDevice?.Position ?? default(Point)) + screenOffset;
}
return default(Point);
diff --git a/src/Avalonia.Controls/ToolTip.cs b/src/Avalonia.Controls/ToolTip.cs
index cff3dab150..b8896a3acf 100644
--- a/src/Avalonia.Controls/ToolTip.cs
+++ b/src/Avalonia.Controls/ToolTip.cs
@@ -109,8 +109,7 @@ namespace Avalonia.Controls
{
throw new AvaloniaInternalException("Previous ToolTip not disposed.");
}
-
- var cp = MouseDevice.Instance?.GetPosition(control);
+ var cp = (control.GetVisualRoot() as IInputRoot)?.MouseDevice?.GetPosition(control);
var position = control.PointToScreen(cp ?? new Point(0, 0)) + new Vector(0, 22);
s_popup = new PopupRoot();
diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs
index e08ddeb14f..8b1b80169f 100644
--- a/src/Avalonia.Controls/TopLevel.cs
+++ b/src/Avalonia.Controls/TopLevel.cs
@@ -163,6 +163,9 @@ namespace Avalonia.Controls
set { SetValue(PointerOverElementProperty, value); }
}
+ ///
+ IMouseDevice IInputRoot.MouseDevice => PlatformImpl?.MouseDevice;
+
///
/// Gets or sets a value indicating whether access keys are shown in the window.
///
diff --git a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj
index a060827f27..e4752f6662 100644
--- a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj
+++ b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj
@@ -34,7 +34,6 @@
-
diff --git a/src/Avalonia.Diagnostics/DevTools.xaml.cs b/src/Avalonia.Diagnostics/DevTools.xaml.cs
index 5be252c5c3..b735372b59 100644
--- a/src/Avalonia.Diagnostics/DevTools.xaml.cs
+++ b/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(),
+ new ViewLocator(),
}
};
@@ -107,7 +106,8 @@ namespace Avalonia.Diagnostics
if ((e.Modifiers) == modifiers)
{
- var point = MouseDevice.Instance.GetPosition(Root);
+
+ var point = (Root.VisualRoot as IInputRoot)?.MouseDevice?.GetPosition(Root) ?? default(Point);
var control = Root.GetVisualsAt(point, x => (!(x is AdornerLayer) && x.IsVisible))
.FirstOrDefault();
diff --git a/src/Avalonia.Diagnostics/ViewModels/ControlDetailsViewModel.cs b/src/Avalonia.Diagnostics/ViewModels/ControlDetailsViewModel.cs
index 0339d724f7..d723890196 100644
--- a/src/Avalonia.Diagnostics/ViewModels/ControlDetailsViewModel.cs
+++ b/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)
{
diff --git a/src/Avalonia.Diagnostics/ViewModels/DevToolsViewModel.cs b/src/Avalonia.Diagnostics/ViewModels/DevToolsViewModel.cs
index dbee9ad624..2d3f978462 100644
--- a/src/Avalonia.Diagnostics/ViewModels/DevToolsViewModel.cs
+++ b/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 _focusedControl;
-
- private readonly ObservableAsPropertyHelper _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;
+ }
}
}
diff --git a/src/Avalonia.Diagnostics/ViewModels/LogicalTreeNode.cs b/src/Avalonia.Diagnostics/ViewModels/LogicalTreeNode.cs
index dcb4e79402..e8a8951f0d 100644
--- a/src/Avalonia.Diagnostics/ViewModels/LogicalTreeNode.cs
+++ b/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)
diff --git a/src/Avalonia.Diagnostics/ViewModels/PropertyDetails.cs b/src/Avalonia.Diagnostics/ViewModels/PropertyDetails.cs
index c06f2415cf..2609b74ce0 100644
--- a/src/Avalonia.Diagnostics/ViewModels/PropertyDetails.cs
+++ b/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); }
}
}
}
diff --git a/src/Avalonia.Diagnostics/ViewModels/TreeNode.cs b/src/Avalonia.Diagnostics/ViewModels/TreeNode.cs
index 0b0b7a94fe..7c403e1b04 100644
--- a/src/Avalonia.Diagnostics/ViewModels/TreeNode.cs
+++ b/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 Children
+ public IAvaloniaReadOnlyList 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
diff --git a/src/Avalonia.Diagnostics/ViewModels/TreePageViewModel.cs b/src/Avalonia.Diagnostics/ViewModels/TreePageViewModel.cs
index 97bd971e74..dba44c5d0c 100644
--- a/src/Avalonia.Diagnostics/ViewModels/TreePageViewModel.cs
+++ b/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 _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)
{
diff --git a/src/Avalonia.Diagnostics/ViewModels/ViewModelBase.cs b/src/Avalonia.Diagnostics/ViewModels/ViewModelBase.cs
new file mode 100644
index 0000000000..349404603a
--- /dev/null
+++ b/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(ref T field, T value, [CallerMemberName] string propertyName = null)
+ {
+ if (!EqualityComparer.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));
+ }
+ }
+}
diff --git a/src/Avalonia.Diagnostics/ViewModels/VisualTreeNode.cs b/src/Avalonia.Diagnostics/ViewModels/VisualTreeNode.cs
index f1b02a61c9..8c070261d9 100644
--- a/src/Avalonia.Diagnostics/ViewModels/VisualTreeNode.cs
+++ b/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(new[] { new VisualTreeNode(host.Root, this) });
+ Children = new AvaloniaList(new[] { new VisualTreeNode(host.Root, this) });
}
if ((Visual is IStyleable styleable))
diff --git a/src/Avalonia.Diagnostics/Views/ControlDetailsView.cs b/src/Avalonia.Diagnostics/Views/ControlDetailsView.cs
index 7cb74ebb33..d7bd6fd128 100644
--- a/src/Avalonia.Diagnostics/Views/ControlDetailsView.cs
+++ b/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 ViewModelProperty =
AvaloniaProperty.Register("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(nameof(property.Diagnostic)).ToBinding(),
};
yield return new TextBlock
{
TextWrapping = TextWrapping.NoWrap,
- [!TextBlock.TextProperty] = property
- .WhenAnyValue(v => v.Value)
+ [!TextBlock.TextProperty] = property.GetObservable