diff --git a/build/CoreLibraries.props b/build/CoreLibraries.props
index 3923bdeeda..d61268173d 100644
--- a/build/CoreLibraries.props
+++ b/build/CoreLibraries.props
@@ -4,7 +4,6 @@
-
diff --git a/samples/BindingDemo/BindingDemo.csproj b/samples/BindingDemo/BindingDemo.csproj
index 3005fdbc51..a3f16dc222 100644
--- a/samples/BindingDemo/BindingDemo.csproj
+++ b/samples/BindingDemo/BindingDemo.csproj
@@ -4,6 +4,7 @@
netcoreapp2.0;net461
+
diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj
index 4c2769bcc4..11c0a4fa68 100644
--- a/samples/ControlCatalog/ControlCatalog.csproj
+++ b/samples/ControlCatalog/ControlCatalog.csproj
@@ -21,6 +21,7 @@
+
diff --git a/samples/RenderDemo/RenderDemo.csproj b/samples/RenderDemo/RenderDemo.csproj
index 3005fdbc51..a3f16dc222 100644
--- a/samples/RenderDemo/RenderDemo.csproj
+++ b/samples/RenderDemo/RenderDemo.csproj
@@ -4,6 +4,7 @@
netcoreapp2.0;net461
+
diff --git a/samples/VirtualizationDemo/VirtualizationDemo.csproj b/samples/VirtualizationDemo/VirtualizationDemo.csproj
index 3005fdbc51..a3f16dc222 100644
--- a/samples/VirtualizationDemo/VirtualizationDemo.csproj
+++ b/samples/VirtualizationDemo/VirtualizationDemo.csproj
@@ -4,6 +4,7 @@
netcoreapp2.0;net461
+
diff --git a/src/Avalonia.Base/Utilities/TypeUtilities.cs b/src/Avalonia.Base/Utilities/TypeUtilities.cs
index d1393a9c0d..8a25d29c3f 100644
--- a/src/Avalonia.Base/Utilities/TypeUtilities.cs
+++ b/src/Avalonia.Base/Utilities/TypeUtilities.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;
using System.Globalization;
using System.Linq;
using System.Reflection;
@@ -185,6 +186,14 @@ namespace Avalonia.Utilities
}
}
+ var typeConverter = TypeDescriptor.GetConverter(to);
+
+ if (typeConverter.CanConvertFrom(from) == true)
+ {
+ result = typeConverter.ConvertFrom(null, culture, value);
+ return true;
+ }
+
var cast = FindTypeConversionOperatorMethod(from, to, OperatorType.Implicit | OperatorType.Explicit);
if (cast != null)
diff --git a/src/Avalonia.Base/ValueStore.cs b/src/Avalonia.Base/ValueStore.cs
index e9118af9f1..e310be0f0a 100644
--- a/src/Avalonia.Base/ValueStore.cs
+++ b/src/Avalonia.Base/ValueStore.cs
@@ -173,7 +173,7 @@ namespace Avalonia
{
return new Diagnostics.AvaloniaPropertyValue(
property,
- slot.Value.HasValue ? (object)slot.Value : AvaloniaProperty.UnsetValue,
+ slot.Value.HasValue ? slot.Value.Value : AvaloniaProperty.UnsetValue,
slot.ValuePriority,
null);
}
diff --git a/src/Avalonia.Controls.DataGrid/Themes/Default.xaml b/src/Avalonia.Controls.DataGrid/Themes/Default.xaml
index 0e039f01cb..17e7ecba43 100644
--- a/src/Avalonia.Controls.DataGrid/Themes/Default.xaml
+++ b/src/Avalonia.Controls.DataGrid/Themes/Default.xaml
@@ -202,7 +202,7 @@
-
+
@@ -215,11 +215,11 @@
-
+
-
-
+
+
netstandard2.0
+ Avalonia
+
+ %(Filename)
+
+
+
+
@@ -14,7 +21,10 @@
-
+
+
+
+
diff --git a/src/Avalonia.Diagnostics/DevTools.xaml b/src/Avalonia.Diagnostics/DevTools.xaml
deleted file mode 100644
index 1df0f3a097..0000000000
--- a/src/Avalonia.Diagnostics/DevTools.xaml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
- Hold Ctrl+Shift over a control to inspect.
-
- Focused:
-
-
- Pointer Over:
-
-
-
-
diff --git a/src/Avalonia.Diagnostics/DevTools.xaml.cs b/src/Avalonia.Diagnostics/DevTools.xaml.cs
deleted file mode 100644
index 037e80e372..0000000000
--- a/src/Avalonia.Diagnostics/DevTools.xaml.cs
+++ /dev/null
@@ -1,159 +0,0 @@
-// 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;
-using System.Collections.Generic;
-using System.Linq;
-using System.Reactive.Linq;
-using Avalonia.Controls;
-using Avalonia.Controls.Primitives;
-using Avalonia.Diagnostics.ViewModels;
-using Avalonia.Input;
-using Avalonia.Input.Raw;
-using Avalonia.Interactivity;
-using Avalonia.Markup.Xaml;
-using Avalonia.Rendering;
-using Avalonia.VisualTree;
-
-namespace Avalonia
-{
- public static class DevToolsExtensions
- {
- public static void AttachDevTools(this TopLevel control)
- {
- Diagnostics.DevTools.Attach(control, new KeyGesture(Key.F12));
- }
-
- public static void AttachDevTools(this TopLevel control, KeyGesture gesture)
- {
- Diagnostics.DevTools.Attach(control, gesture);
- }
-
- public static void OpenDevTools(this TopLevel control)
- {
- Diagnostics.DevTools.OpenDevTools(control);
- }
- }
-}
-
-namespace Avalonia.Diagnostics
-{
- public class DevTools : UserControl
- {
- private static readonly Dictionary s_open = new Dictionary();
- private static readonly HashSet s_visualTreeRoots = new HashSet();
- private readonly IDisposable _keySubscription;
-
- public DevTools(IControl root)
- {
- InitializeComponent();
- Root = root;
- DataContext = new DevToolsViewModel(root);
-
- _keySubscription = InputManager.Instance.Process
- .OfType()
- .Subscribe(RawKeyDown);
- }
-
- // HACK: needed for XAMLIL, will fix that later
- public DevTools()
- {
- }
-
- public IControl Root { get; }
-
- public static IDisposable Attach(TopLevel control, KeyGesture gesture)
- {
- void PreviewKeyDown(object sender, KeyEventArgs e)
- {
- if (gesture.Matches(e))
- {
- OpenDevTools(control);
- }
- }
-
- return control.AddHandler(
- KeyDownEvent,
- PreviewKeyDown,
- RoutingStrategies.Tunnel);
- }
-
- internal static void OpenDevTools(TopLevel control)
- {
- if (s_open.TryGetValue(control, out var devToolsWindow))
- {
- devToolsWindow.Activate();
- }
- else
- {
- var devTools = new DevTools(control);
-
- devToolsWindow = new Window
- {
- Width = 1024,
- Height = 512,
- Content = devTools,
- DataTemplates = { new ViewLocator() },
- Title = "Avalonia DevTools"
- };
-
- devToolsWindow.Closed += devTools.DevToolsClosed;
- s_open.Add(control, devToolsWindow);
- MarkAsDevTool(devToolsWindow);
- devToolsWindow.Show();
- }
- }
-
- private void DevToolsClosed(object sender, EventArgs e)
- {
- var devToolsWindow = (Window)sender;
- var devTools = (DevTools)devToolsWindow.Content;
- s_open.Remove((TopLevel)devTools.Root);
- RemoveDevTool(devToolsWindow);
- _keySubscription.Dispose();
- devToolsWindow.Closed -= DevToolsClosed;
- }
-
- private void InitializeComponent()
- {
- AvaloniaXamlLoader.Load(this);
- }
-
- private void RawKeyDown(RawKeyEventArgs e)
- {
- const RawInputModifiers modifiers = RawInputModifiers.Control | RawInputModifiers.Shift;
-
- if (e.Modifiers == modifiers)
- {
- var point = (Root.VisualRoot as IInputRoot)?.MouseDevice?.GetPosition(Root) ?? default(Point);
- var control = Root.GetVisualsAt(point, x => (!(x is AdornerLayer) && x.IsVisible))
- .FirstOrDefault();
-
- if (control != null)
- {
- var vm = (DevToolsViewModel)DataContext;
- vm.SelectControl((IControl)control);
- }
- }
- }
-
- ///
- /// Marks a visual as part of the DevTools, so it can be excluded from event tracking.
- ///
- /// The visual whose root is to be marked.
- public static void MarkAsDevTool(IVisual visual)
- {
- s_visualTreeRoots.Add(visual.GetVisualRoot());
- }
-
- public static void RemoveDevTool(IVisual visual)
- {
- s_visualTreeRoots.Remove(visual.GetVisualRoot());
- }
-
- public static bool BelongsToDevTool(IVisual visual)
- {
- return s_visualTreeRoots.Contains(visual.GetVisualRoot());
- }
- }
-}
diff --git a/src/Avalonia.Diagnostics/DevToolsExtensions.cs b/src/Avalonia.Diagnostics/DevToolsExtensions.cs
new file mode 100644
index 0000000000..4bc2ca313f
--- /dev/null
+++ b/src/Avalonia.Diagnostics/DevToolsExtensions.cs
@@ -0,0 +1,31 @@
+using Avalonia.Controls;
+using Avalonia.Diagnostics;
+using Avalonia.Input;
+
+namespace Avalonia
+{
+ ///
+ /// Extension methods for attaching DevTools..
+ ///
+ public static class DevToolsExtensions
+ {
+ ///
+ /// Attaches DevTools to a window, to be opened with the F12 key.
+ ///
+ /// The window to attach DevTools to.
+ public static void AttachDevTools(this TopLevel root)
+ {
+ DevTools.Attach(root, new KeyGesture(Key.F12));
+ }
+
+ ///
+ /// Attaches DevTools to a window, to be opened with the specified key gesture.
+ ///
+ /// The window to attach DevTools to.
+ /// The key gesture to open DevTools.
+ public static void AttachDevTools(this TopLevel root, KeyGesture gesture)
+ {
+ DevTools.Attach(root, gesture);
+ }
+ }
+}
diff --git a/src/Avalonia.Diagnostics/Diagnostics/Converters/BoolToBrushConverter.cs b/src/Avalonia.Diagnostics/Diagnostics/Converters/BoolToBrushConverter.cs
new file mode 100644
index 0000000000..37ba5155fd
--- /dev/null
+++ b/src/Avalonia.Diagnostics/Diagnostics/Converters/BoolToBrushConverter.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Globalization;
+using Avalonia.Data.Converters;
+using Avalonia.Media;
+
+namespace Avalonia.Diagnostics.Converters
+{
+ internal class BoolToBrushConverter : IValueConverter
+ {
+ public IBrush Brush { get; set; }
+
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return (bool)value ? Brush : Brushes.Transparent;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/src/Avalonia.Diagnostics/Diagnostics/DevTools.cs b/src/Avalonia.Diagnostics/Diagnostics/DevTools.cs
new file mode 100644
index 0000000000..0464047273
--- /dev/null
+++ b/src/Avalonia.Diagnostics/Diagnostics/DevTools.cs
@@ -0,0 +1,61 @@
+using System;
+using System.Collections.Generic;
+using System.Reactive.Disposables;
+using Avalonia.Controls;
+using Avalonia.Diagnostics.Views;
+using Avalonia.Input;
+using Avalonia.Interactivity;
+
+namespace Avalonia.Diagnostics
+{
+ public static class DevTools
+ {
+ private static readonly Dictionary s_open = new Dictionary();
+
+ public static IDisposable Attach(TopLevel root, KeyGesture gesture)
+ {
+ void PreviewKeyDown(object sender, KeyEventArgs e)
+ {
+ if (gesture.Matches(e))
+ {
+ Open(root);
+ }
+ }
+
+ return root.AddHandler(
+ InputElement.KeyDownEvent,
+ PreviewKeyDown,
+ RoutingStrategies.Tunnel);
+ }
+
+ public static IDisposable Open(TopLevel root)
+ {
+ if (s_open.TryGetValue(root, out var window))
+ {
+ window.Activate();
+ }
+ else
+ {
+ window = new MainWindow
+ {
+ Width = 1024,
+ Height = 512,
+ Root = root,
+ };
+
+ window.Closed += DevToolsClosed;
+ s_open.Add(root, window);
+ window.Show();
+ }
+
+ return Disposable.Create(() => window?.Close());
+ }
+
+ private static void DevToolsClosed(object sender, EventArgs e)
+ {
+ var window = (MainWindow)sender;
+ s_open.Remove(window.Root);
+ window.Closed -= DevToolsClosed;
+ }
+ }
+}
diff --git a/src/Avalonia.Diagnostics/Diagnostics/Models/ConsoleContext.cs b/src/Avalonia.Diagnostics/Diagnostics/Models/ConsoleContext.cs
new file mode 100644
index 0000000000..5927bd785e
--- /dev/null
+++ b/src/Avalonia.Diagnostics/Diagnostics/Models/ConsoleContext.cs
@@ -0,0 +1,36 @@
+#pragma warning disable IDE1006 // Naming Styles
+
+using Avalonia.Diagnostics.ViewModels;
+
+namespace Avalonia.Diagnostics.Models
+{
+ public class ConsoleContext
+ {
+ private readonly ConsoleViewModel _owner;
+
+ internal ConsoleContext(ConsoleViewModel owner) => _owner = owner;
+
+ public readonly string help = @"Welcome to Avalonia DevTools. Here you can execute arbitrary C# code using Roslyn scripting.
+
+The following variables are available:
+
+e: The control currently selected in the logical or visual tree view
+root: The root of the visual tree
+
+The following commands are available:
+
+clear(): Clear the output history
+";
+
+ public dynamic e { get; internal set; }
+ public dynamic root { get; internal set; }
+
+ internal static object NoOutput { get; } = new object();
+
+ public object clear()
+ {
+ _owner.History.Clear();
+ return NoOutput;
+ }
+ }
+}
diff --git a/src/Avalonia.Diagnostics/Diagnostics/Models/ConsoleHistoryItem.cs b/src/Avalonia.Diagnostics/Diagnostics/Models/ConsoleHistoryItem.cs
new file mode 100644
index 0000000000..86da18145a
--- /dev/null
+++ b/src/Avalonia.Diagnostics/Diagnostics/Models/ConsoleHistoryItem.cs
@@ -0,0 +1,19 @@
+using System;
+using Avalonia.Media;
+
+namespace Avalonia.Diagnostics.Models
+{
+ internal class ConsoleHistoryItem
+ {
+ public ConsoleHistoryItem(string input, object output)
+ {
+ Input = input;
+ Output = output;
+ Foreground = output is Exception ? Brushes.Red : Brushes.Green;
+ }
+
+ public string Input { get; }
+ public object Output { get; }
+ public IBrush Foreground { get; }
+ }
+}
diff --git a/src/Avalonia.Diagnostics/Models/EventChainLink.cs b/src/Avalonia.Diagnostics/Diagnostics/Models/EventChainLink.cs
similarity index 100%
rename from src/Avalonia.Diagnostics/Models/EventChainLink.cs
rename to src/Avalonia.Diagnostics/Diagnostics/Models/EventChainLink.cs
diff --git a/src/Avalonia.Diagnostics/ViewLocator.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewLocator.cs
similarity index 70%
rename from src/Avalonia.Diagnostics/ViewLocator.cs
rename to src/Avalonia.Diagnostics/Diagnostics/ViewLocator.cs
index a66703301d..c06fbec801 100644
--- a/src/Avalonia.Diagnostics/ViewLocator.cs
+++ b/src/Avalonia.Diagnostics/Diagnostics/ViewLocator.cs
@@ -1,13 +1,11 @@
-// 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;
using Avalonia.Controls;
using Avalonia.Controls.Templates;
+using Avalonia.Diagnostics.ViewModels;
namespace Avalonia.Diagnostics
{
- internal class ViewLocator : IDataTemplate
+ internal class ViewLocator : IDataTemplate
{
public bool SupportsRecycling => false;
@@ -28,7 +26,7 @@ namespace Avalonia.Diagnostics
public bool Match(object data)
{
- return data is TViewModel;
+ return data is ViewModelBase;
}
}
}
diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs
new file mode 100644
index 0000000000..a9353eba8b
--- /dev/null
+++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs
@@ -0,0 +1,95 @@
+using System.ComponentModel;
+using Avalonia.Collections;
+
+namespace Avalonia.Diagnostics.ViewModels
+{
+ internal class AvaloniaPropertyViewModel : PropertyViewModel
+ {
+ private readonly AvaloniaObject _target;
+ private string _type;
+ private object _value;
+ private string _priority;
+ private string _group;
+
+ public AvaloniaPropertyViewModel(AvaloniaObject o, AvaloniaProperty property)
+ {
+ _target = o;
+ Property = property;
+
+ Name = property.IsAttached ?
+ $"[{property.OwnerType.Name}.{property.Name}]" :
+ property.Name;
+
+ if (property.IsDirect)
+ {
+ _group = "Properties";
+ Priority = "Direct";
+ }
+
+ Update();
+ }
+
+ public AvaloniaProperty Property { get; }
+ public override object Key => Property;
+ public override string Name { get; }
+ public bool IsAttached => Property.IsAttached;
+
+ public string Priority
+ {
+ get => _priority;
+ private set => RaiseAndSetIfChanged(ref _priority, value);
+ }
+
+ public override string Type => _type;
+
+ public override string Value
+ {
+ get => ConvertToString(_value);
+ set
+ {
+ try
+ {
+ var convertedValue = ConvertFromString(value, Property.PropertyType);
+ _target.SetValue(Property, convertedValue);
+ }
+ catch { }
+ }
+ }
+
+ public override string Group
+ {
+ get => _group;
+ }
+
+ public override void Update()
+ {
+ if (Property.IsDirect)
+ {
+ RaiseAndSetIfChanged(ref _value, _target.GetValue(Property), nameof(Value));
+ RaiseAndSetIfChanged(ref _type, _value?.GetType().Name, nameof(Type));
+ }
+ else
+ {
+ var val = _target.GetDiagnostic(Property);
+
+ RaiseAndSetIfChanged(ref _value, val?.Value, nameof(Value));
+ RaiseAndSetIfChanged(ref _type, _value?.GetType().Name, nameof(Type));
+
+ if (val != null)
+ {
+ SetGroup(IsAttached ? "Attached Properties" : "Properties");
+ Priority = val.Priority.ToString();
+ }
+ else
+ {
+ SetGroup(Priority = "Unset");
+ }
+ }
+ }
+
+ private void SetGroup(string group)
+ {
+ RaiseAndSetIfChanged(ref _group, group, nameof(Group));
+ }
+ }
+}
diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs
new file mode 100644
index 0000000000..af5e254204
--- /dev/null
+++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs
@@ -0,0 +1,57 @@
+using System.ComponentModel;
+using System.Reflection;
+
+namespace Avalonia.Diagnostics.ViewModels
+{
+ internal class ClrPropertyViewModel : PropertyViewModel
+ {
+ private readonly object _target;
+ private string _type;
+ private object _value;
+
+ public ClrPropertyViewModel(object o, PropertyInfo property)
+ {
+ _target = o;
+ Property = property;
+
+ if (!property.DeclaringType.IsInterface)
+ {
+ Name = property.Name;
+ }
+ else
+ {
+ Name = property.DeclaringType.Name + '.' + property.Name;
+ }
+
+ Update();
+ }
+
+ public PropertyInfo Property { get; }
+ public override object Key => Name;
+ public override string Name { get; }
+ public override string Group => "CLR Properties";
+
+ public override string Type => _type;
+
+ public override string Value
+ {
+ get => ConvertToString(_value);
+ set
+ {
+ try
+ {
+ var convertedValue = ConvertFromString(value, Property.PropertyType);
+ Property.SetValue(_target, convertedValue);
+ }
+ catch { }
+ }
+ }
+
+ public override void Update()
+ {
+ var val = Property.GetValue(_target);
+ RaiseAndSetIfChanged(ref _value, val, nameof(Value));
+ RaiseAndSetIfChanged(ref _type, _value?.GetType().Name, nameof(Type));
+ }
+ }
+}
diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ConsoleViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ConsoleViewModel.cs
new file mode 100644
index 0000000000..0e0c44ded8
--- /dev/null
+++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ConsoleViewModel.cs
@@ -0,0 +1,112 @@
+using System;
+using System.Reflection;
+using System.Threading.Tasks;
+using Avalonia.Collections;
+using Avalonia.Diagnostics.Models;
+using Microsoft.CodeAnalysis.CSharp.Scripting;
+using Microsoft.CodeAnalysis.Scripting;
+
+namespace Avalonia.Diagnostics.ViewModels
+{
+ internal class ConsoleViewModel : ViewModelBase
+ {
+ private readonly ConsoleContext _context;
+ private readonly Action _updateContext;
+ private int _historyIndex = -1;
+ private string _input;
+ private bool _isVisible;
+ private ScriptState