Browse Source

tmp: devtools improvements

Andrey Kunchev 6 years ago
parent
commit
740028f13a
  1. 185
      src/Avalonia.Diagnostics/ViewModels/ControlDetailsViewModel.cs
  2. 3
      src/Avalonia.Diagnostics/ViewModels/LogicalTreeNode.cs
  3. 168
      src/Avalonia.Diagnostics/ViewModels/PropertyDetails.cs
  4. 40
      src/Avalonia.Diagnostics/ViewModels/StylesNode.cs
  5. 8
      src/Avalonia.Diagnostics/ViewModels/TreeNode.cs
  6. 2
      src/Avalonia.Diagnostics/ViewModels/VisualTreeNode.cs
  7. 10
      src/Avalonia.Diagnostics/Views/ControlDetailsView.cs
  8. 24
      src/Avalonia.Diagnostics/Views/ControlDetailsView.xaml
  9. 4
      src/Avalonia.Diagnostics/Views/TreePageView.xaml
  10. 8
      src/Avalonia.Diagnostics/Views/TreePageView.xaml.cs

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

@ -7,52 +7,189 @@ using System.Linq;
using System.Reactive.Linq;
using Avalonia.Controls;
using Avalonia.Layout;
using Avalonia.Threading;
using Avalonia.VisualTree;
using Avalonia.Markup.Xaml.Styling;
using Avalonia.Styling;
namespace Avalonia.Diagnostics.ViewModels
{
using WellKNownProperties = List<WellKnownProperty>;
using WellKNownPropertiesReg = Dictionary<Func<object, bool>, List<WellKnownProperty>>;
internal class WellKnownProperty
{
private static IObservable<object> LayoutUpdatedEvent(object ctrl) => Observable.FromEventPattern(ctrl, nameof(Layoutable.LayoutUpdated));
private static WellKNownPropertiesReg _registry = new WellKNownPropertiesReg
{
{
o => true,
new WellKNownProperties
{
new WellKnownProperty()
{
Name = "ToString()",
Type = typeof(string),
Getter = o => o.ToString(),
Changed = o => Observable.Never<object>()
}
}
},
{
o => o is Style,
new WellKNownProperties
{
new WellKnownProperty()
{
Name = "Selector",
Type = typeof(Selector),
Getter = o => (o as Style).Selector.ToString(),
Changed = o => Observable.Never<object>()
}
}
},
{
o => o is Control,
new WellKNownProperties
{
new WellKnownProperty()
{
Name = nameof(StyledElement.Classes),
Type = typeof(string),
Getter = o => string.Join(" ", (o as StyledElement).Classes),
Setter = (o, v) => (o as StyledElement).Classes = Classes.Parse((v??"").ToString()),
Changed = o => Observable.FromEventPattern((o as StyledElement).Classes, nameof(StyledElement.Classes.CollectionChanged))
},
new WellKnownProperty()
{
Name = "Layout State",
Type = typeof(string),
Getter = o =>
{
var l = o as ILayoutable;
return $"measured: {l.IsMeasureValid} -> {l.PreviousMeasure} arranged: {l.IsArrangeValid} -> {l.PreviousArrange}";
},
Changed = o => LayoutUpdatedEvent(o)
},
}
},
{
o => o is Grid,
new WellKNownProperties
{
new WellKnownProperty()
{
Name = nameof(Grid.ColumnDefinitions),
Type = typeof(string),
Getter = o => string.Join(",", (o as Grid).ColumnDefinitions.Select(c=>c.Width.ToString())),
Setter = (o,v) => (o as Grid).ColumnDefinitions = ColumnDefinitions.Parse(v?.ToString()??""),
Changed = o => Observable.Never<object>()
},
new WellKnownProperty()
{
Name = nameof(Grid.ColumnDefinitions) + " (Actual)",
Type = typeof(string),
Getter = o => string.Join(",", (o as Grid).ColumnDefinitions.Select(c=>c.ActualWidth.ToString())),
Changed = o => LayoutUpdatedEvent(o)
},
new WellKnownProperty()
{
Name = nameof(Grid.RowDefinitions),
Type = typeof(string),
Getter = o => string.Join(",", (o as Grid).RowDefinitions.Select(c=>c.Height.ToString())),
Setter = (o,v) => (o as Grid).RowDefinitions = RowDefinitions.Parse(v?.ToString()??""),
Changed = o => Observable.Never<object>()
},
new WellKnownProperty()
{
Name = nameof(Grid.RowDefinitions) + " (Actual)",
Type = typeof(string),
Getter = o => string.Join(",", (o as Grid).RowDefinitions.Select(c=>c.ActualHeight.ToString())),
Changed = o => LayoutUpdatedEvent(o)
},
}
},
};
public static IEnumerable<WellKnownProperty> Get(AvaloniaObject obj)
{
return _registry.Where(v => v.Key(obj)).SelectMany(v => v.Value);
}
public string Name;
public Type Type;
public Func<object, object> Getter;
public Action<object, object> Setter;
public Func<object, IObservable<object>> Changed;
}
internal class ControlDetailsViewModel : ViewModelBase, IDisposable
{
private IVisual _control;
private object _control;
public ControlDetailsViewModel(IVisual control)
public ControlDetailsViewModel(object avObject)
{
if (control is AvaloniaObject avaloniaObject)
if (avObject is AvaloniaObject avaloniaObject)
{
var props = AvaloniaPropertyRegistry.Instance.GetRegistered(avaloniaObject)
var props = WellKnownProperty.Get(avaloniaObject)
.Select(x => new PropertyDetails(avaloniaObject, x))
.ToList();
var avProps = AvaloniaPropertyRegistry.Instance.GetRegistered(avaloniaObject)
.Select(x => new PropertyDetails(avaloniaObject, x))
.OrderBy(x => x.IsAttached)
.ThenBy(x => x.Name)
.ToList();
if (control is Control c)
props.AddRange(avProps);
if (avObject is Control c)
{
var classesProp = new PropertyDetails(c, nameof(c.Classes),
() => string.Join(" ", c.Classes),
v => c.Classes.Replace(Classes.Parse(v as string)),
Observable.FromEventPattern(c.Classes, nameof(c.Classes.CollectionChanged))
);
if (c.Parent != null)
{
var attached = AvaloniaPropertyRegistry.Instance.GetRegistered((AvaloniaObject)c.Parent)
.Where(p => p.IsAttached)
.Select(x => new PropertyDetails(avaloniaObject, x))
.OrderBy(x => x.Name)
.ToList();
props.Insert(0, classesProp);
props.AddRange(attached);
}
}
var l = c as ILayoutable;
DateTime? last = null;
var layoutProps = new[]
if (avObject is Style style)
{
WellKnownProperty forSetter(ISetter setter)
{
new PropertyDetails(c, "Layout Props",
() => $"measured: {l.IsMeasureValid} -> {l.PreviousMeasure} arranged: {l.IsArrangeValid} -> {l.PreviousArrange} ({last?.TimeOfDay})",
null,
Observable.FromEventPattern(c, nameof(c.LayoutUpdated)).Select(_=>(object)(last=DateTime.Now))
),
};
props.InsertRange(0, layoutProps);
if (setter is Setter sett)
{
return new WellKnownProperty()
{
Name = sett.Property.Name,
Type = sett.Property.PropertyType,
Getter = o => sett.Value,
Setter = (o,v) => sett.Value = v,
Changed = o => Observable.Never<object>()
};
}
return new WellKnownProperty()
{
Name = setter.GetType().Name,
Type = setter.GetType(),
Getter = o => setter.ToString(),
Changed = o => Observable.Never<object>()
};
}
var setters = style.Setters.Select(s => new PropertyDetails(style, forSetter(s))).ToList();
props.AddRange(setters);
}
Properties = props;
}
_control = control;
_control = avObject;
}
public IEnumerable<PropertyDetails> Properties

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

@ -12,7 +12,8 @@ namespace Avalonia.Diagnostics.ViewModels
public LogicalTreeNode(ILogical logical, TreeNode parent)
: base((Control)logical, parent)
{
Children = logical.LogicalChildren.CreateDerivedList(x => new LogicalTreeNode(x, this));
var children = logical.LogicalChildren.CreateDerivedList(x => new LogicalTreeNode(x, this));
Children = StyleTreeNode.WithStyles(this, children);
}
public static LogicalTreeNode[] Create(object control)

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

@ -11,7 +11,10 @@ using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Avalonia.Data;
using Avalonia.Diagnostics.Views;
using Avalonia.Input;
using Avalonia.Media;
using Avalonia.Platform;
namespace Avalonia.Diagnostics.ViewModels
{
@ -19,8 +22,46 @@ namespace Avalonia.Diagnostics.ViewModels
{
public class ParseTypeConverter : TypeConverter
{
private static Dictionary<IPlatformHandle, string> _standardCursors;
private static string TryGetCursorName(Cursor cursor)
{
if (cursor?.PlatformCursor == null)
return "";
if (_standardCursors == null)
{
_standardCursors = new Dictionary<IPlatformHandle, string>();
try
{
var platform = AvaloniaLocator.Current.GetService<IStandardCursorFactory>();
foreach (StandardCursorType c in Enum.GetValues(typeof(StandardCursorType)))
{
_standardCursors[platform.GetCursor(c)] = c.ToString();
}
}
catch
{
}
}
return cursor?.PlatformCursor != null && _standardCursors.TryGetValue(cursor.PlatformCursor, out string r) ? r : cursor?.PlatformCursor?.ToString();
}
private readonly Func<string, CultureInfo, object> _parse;
private static Dictionary<Type, Func<object, string>> _customToString = new Dictionary<Type, Func<object, string>>()
{
//TODO: may be override ToString and remove this hardcoded functionality
{ typeof(RelativePoint), o =>
{
var rp = (RelativePoint)o;
return rp.Unit== RelativeUnit.Absolute?rp.Point.ToString():$"{rp.Point.X*100}%,{rp.Point.Y*100}%";
} },
{ typeof(Cursor), o => TryGetCursorName(o as Cursor) },
};
public static Func<string, CultureInfo, object> TryGetParse(Type type)
{
var bf = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
@ -39,6 +80,17 @@ namespace Avalonia.Diagnostics.ViewModels
return (s, c) => parse.Invoke(null, new object[] { s });
}
parse = type.GetMethod("Parse", bf);
if (parse?.ReturnParameter?.ParameterType == type)
{
var pars = parse.GetParameters();
//parse with string parameter and default second argument
if (pars.Length == 2 && pars[0].ParameterType == typeof(string) && pars[1].IsOptional)
{
return (s, c) => parse.Invoke(null, new object[] { s, Type.Missing });
}
}
return null;
}
@ -48,23 +100,21 @@ namespace Avalonia.Diagnostics.ViewModels
}
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string);
}
=> sourceType == typeof(string);
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
return _parse((string)value, culture);
}
=> _parse((string)value, culture);
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
=> _customToString.TryGetValue(value?.GetType() ?? typeof(object), out var ts) ? ts(value) : value?.ToString();
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
=> destinationType == typeof(string);
}
private static Dictionary<Type, TypeConverter> _converters = new Dictionary<Type, TypeConverter>()
{
{typeof(TimeSpan), new Markup.Xaml.Converters.TimeSpanTypeConverter() },
{typeof(FontFamily), new Markup.Xaml.Converters.FontFamilyTypeConverter() },
{typeof(int), null},
{typeof(double), null},
{typeof(string), null}
//here hard coded type converters if needed
};
static public TypeConverter TryGetTypeConverter(Type type)
@ -97,33 +147,36 @@ namespace Avalonia.Diagnostics.ViewModels
private object _originalValue;
private object _value;
private bool _isChanged = false;
private string _priority;
private string _priority = "";
private string _diagnostic;
private AvaloniaObject _object;
private AvaloniaProperty _property;
private WellKnownProperty _wellKnownProperty;
private TypeConverter _typeConverter;
private IEnumerable<string> _possibleValues;
private Func<object> _getter;
private Action<object> _setter;
private IEnumerable<string> _hintValues;
private bool _setActive = false;
private static TypeConverter TryGetTypeConverter(AvaloniaProperty property)
private static TypeConverter TryGetTypeConverter(Type propertyType)
{
if (propertyType == null)
{
return null;
}
TypeConverter result;
if (_typeConverters.TryGetValue(property.PropertyType, out result))
if (_typeConverters.TryGetValue(propertyType, out result))
return result;
result = AvaloniaTypeConverters.TryGetTypeConverter(property.PropertyType) ??
TypeDescriptor.GetConverter(property.PropertyType);
result = AvaloniaTypeConverters.TryGetTypeConverter(propertyType) ??
TypeDescriptor.GetConverter(propertyType);
if (result?.CanConvertFrom(typeof(string)) == false)
{
result = null;
}
return _typeConverters[property.PropertyType] = result;
return _typeConverters[propertyType] = result;
}
public PropertyDetails(AvaloniaObject o, AvaloniaProperty property)
@ -132,11 +185,10 @@ namespace Avalonia.Diagnostics.ViewModels
$"[{property.OwnerType.Name}.{property.Name}]" :
property.Name;
_typeConverter = property.IsReadOnly ? null : TryGetTypeConverter(property);
_typeConverter = TryGetTypeConverter(property.PropertyType);
IsAttached = property.IsAttached;
IsReadOnly = property.IsReadOnly || _typeConverter == null;
IsReadOnly = property.IsReadOnly || !(_typeConverter?.CanConvertFrom(typeof(string)) ?? false);
bool first = true;
// TODO: Unsubscribe when view model is deactivated.
_disposable = o.GetObservable(property).Where(_ => !_setActive).Subscribe(x =>
{
var diagnostic = o.GetDiagnostic(property);
@ -160,20 +212,26 @@ namespace Avalonia.Diagnostics.ViewModels
_property = property;
}
public PropertyDetails(AvaloniaObject o, string propertyName, Func<object> getter, Action<object> setter, IObservable<object> changed)
public PropertyDetails(AvaloniaObject o, WellKnownProperty property)
{
_wellKnownProperty = property;
_object = o;
Name = propertyName;
_getter = getter;
_setter = setter;
IsReadOnly = setter == null;
_originalValue = _getter();
SetValue(_getter(), false);
Name = _wellKnownProperty.Name;
_typeConverter = TryGetTypeConverter(_wellKnownProperty.Type);
IsReadOnly = !(_wellKnownProperty.Setter != null && (_typeConverter?.CanConvertFrom(typeof(string)) ?? true));
var getter = _wellKnownProperty.Getter;
if (_typeConverter != null)
{
getter = x => _typeConverter.ConvertTo(_wellKnownProperty.Getter(x), typeof(string)) ?? "(null)";
}
_originalValue = getter(o);
SetValue(_originalValue, false);
var inpc = o as INotifyPropertyChanged;
changed = changed ?? Observable.FromEventPattern<PropertyChangedEventHandler, PropertyChangedEventArgs>
(v => inpc.PropertyChanged += v, v => inpc.PropertyChanged -= v)
.Where(v => v.EventArgs.PropertyName == propertyName);
_disposable = changed.Where(_ => !_setActive).Subscribe(_ => SetValue(_getter() ?? "(null)", false));
var changed = _wellKnownProperty.Changed(_object) ?? inpc.GetObservable<object>(Name);
_disposable = changed.Where(_ => !_setActive).Subscribe(_ => SetValue(getter(o) ?? "(null)", false));
}
private static Dictionary<Type, string[]> _typespossibleValues = new Dictionary<Type, string[]>();
@ -182,26 +240,32 @@ namespace Avalonia.Diagnostics.ViewModels
{
string[] result;
if (_typespossibleValues.TryGetValue(_property.PropertyType, out result))
var propertyType = _property?.PropertyType ?? _wellKnownProperty?.Type ?? typeof(object);
if (_typespossibleValues.TryGetValue(propertyType, out result))
return result;
if (_property != null)
{
if (_property.PropertyType.IsEnum)
if (propertyType.IsEnum)
{
result = Enum.GetNames(_property.PropertyType);
result = Enum.GetNames(propertyType);
}
else if (_property.PropertyType == typeof(IBrush))
else if (propertyType == typeof(IBrush))
{
result = typeof(Brushes).GetProperties().Select(p => p.Name).ToArray();
}
else if (_property.PropertyType == typeof(bool))
else if (propertyType == typeof(bool) || propertyType == typeof(bool?))
{
result = new[] { "True", "False" };
}
else if (propertyType == typeof(Cursor))
{
result = Enum.GetNames(typeof(StandardCursorType));
}
}
return _typespossibleValues[_property.PropertyType] = result;
return _typespossibleValues[propertyType] = result ?? Array.Empty<string>();
}
public string Name { get; }
@ -236,7 +300,7 @@ namespace Avalonia.Diagnostics.ViewModels
{
string stringValue = (value as string)?.TrimStart(' ');
if (setback && !IsReadOnly)
if (setback && !IsReadOnly && _disposable != null)
{
try
{
@ -244,9 +308,9 @@ namespace Avalonia.Diagnostics.ViewModels
null : (_typeConverter?.ConvertFrom(stringValue) ?? stringValue);
_setActive = true;
if (_setter != null)
if (_wellKnownProperty != null)
{
_setter(propValue);
_wellKnownProperty?.Setter?.Invoke(_object, propValue);
}
else
{
@ -304,24 +368,30 @@ namespace Avalonia.Diagnostics.ViewModels
set { this.RaiseAndSetIfChanged(ref _hasValueError, value); }
}
public async Task<IEnumerable<object>> PossibleValuesPopulator(string text, CancellationToken token)
public async Task<IEnumerable<object>> HintValuesPopulator(string text, CancellationToken token)
{
if (text.Equals(Value))
return Enumerable.Empty<string>();
return Array.Empty<string>();
await Task.Delay(100, token);
return PossibleValues;
if (token.IsCancellationRequested)
{
return Array.Empty<string>();
}
return HintValues;
}
public void Dispose()
{
_disposable?.Dispose();
_disposable = null;
}
public IEnumerable<string> PossibleValues
public IEnumerable<string> HintValues
{
get => _possibleValues ?? (_possibleValues = GetPossibleValues());
get => _hintValues ?? (_hintValues = GetPossibleValues());
}
}
}

40
src/Avalonia.Diagnostics/ViewModels/StylesNode.cs

@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.Styling;
namespace Avalonia.Diagnostics.ViewModels
{
internal class StyleTreeNode : TreeNode
{
public static IAvaloniaReadOnlyList<TreeNode> WithStyles<T>(TreeNode node, IAvaloniaReadOnlyList<T> children) where T : TreeNode
{
if (node.Visual is Control ctrl && (ctrl.Styles.Count > 0 || ctrl is TopLevel))
{
var result = new AvaloniaList<TreeNode>();
if (ctrl is TopLevel)
{
result.Add(new StyleTreeNode(Application.Current.Styles, node));
}
if (ctrl.Styles.Count > 0)
{
result.Add(new StyleTreeNode(ctrl.Styles, node));
}
var cnt = result.Count;
children.ForEachItem((i, v) => result.Insert(i + cnt, v),
(i, v) => result.RemoveAt(i + cnt),
() => result.RemoveRange(cnt, result.Count - cnt),
true);
return result;
}
return children;
}
public StyleTreeNode(IStyle style, TreeNode parent) : base((IAvaloniaObject)style, parent)
{
Children = new AvaloniaList<TreeNode>(((style as Styles)?.OfType<Style>().Select(s => new StyleTreeNode(s, this))) ?? Array.Empty<StyleTreeNode>());
}
}
}

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

@ -16,7 +16,7 @@ namespace Avalonia.Diagnostics.ViewModels
private string _classes;
private bool _isExpanded;
public TreeNode(IVisual visual, TreeNode parent)
public TreeNode(object visual, TreeNode parent)
{
Parent = parent;
Type = visual.GetType().Name;
@ -45,6 +45,10 @@ namespace Avalonia.Diagnostics.ViewModels
}
});
}
else if (visual is Style s)
{
Classes = $"({s.Selector?.ToString()})";
}
}
public IAvaloniaReadOnlyList<TreeNode> Children
@ -59,7 +63,7 @@ namespace Avalonia.Diagnostics.ViewModels
private set => RaiseAndSetIfChanged(ref _classes, value);
}
public IVisual Visual
public object Visual
{
get;
}

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

@ -10,7 +10,7 @@ namespace Avalonia.Diagnostics.ViewModels
internal class VisualTreeNode : TreeNode
{
public VisualTreeNode(IVisual visual, TreeNode parent)
: base(visual, parent)
: base((IAvaloniaObject)visual, parent)
{
var host = visual as IVisualTreeHost;

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

@ -8,6 +8,7 @@ using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Diagnostics.ViewModels;
using Avalonia.Input;
using Avalonia.Markup.Xaml;
namespace Avalonia.Diagnostics.Views
@ -57,7 +58,14 @@ namespace Avalonia.Diagnostics.Views
if (value != null)
{
var evt = Observable.Merge(value.EventNames.Select(v => Observable.FromEventPattern(c, v)));
var evt = Observable.Merge(value.EventNames.Select(eventName =>
{
if (eventName == "EnterKeyUp")
{
return Observable.FromEventPattern<KeyEventArgs>(c, "KeyUp").Where(v => v.EventArgs.Key == Key.Enter).OfType<object>();
}
return Observable.FromEventPattern(c, eventName);
}));
var avp = AvaloniaPropertyRegistry.Instance.FindRegistered(c, value.PropertyName);
c.Bind(Control.TagProperty, value.Binding);
c.GetObservable(Control.TagProperty).Subscribe(v => c.SetValue(avp, v));

24
src/Avalonia.Diagnostics/Views/ControlDetailsView.xaml

@ -19,7 +19,7 @@
</Style>
</UserControl.Styles>
<Grid RowDefinitions="Auto,*">
<Grid x:Name="headerGrid" ColumnDefinitions="200,4,1*,4,100,20" Grid.Row="0" Background="{DynamicResource ThemeBorderMidBrush}">
<Grid x:Name="headerGrid" ColumnDefinitions="200,auto,1*,auto,100,20" Grid.Row="0" Background="{DynamicResource ThemeBorderMidBrush}">
<Grid.Styles>
<Style Selector="TextBlock.header">
<Setter Property="Foreground" Value="{DynamicResource ThemeControlMidBrush}" />
@ -34,17 +34,19 @@
<GridSplitter Grid.Column="1"/>
<GridSplitter Grid.Column="3"/>
</Grid>
<ScrollViewer Grid.Row="1" HorizontalScrollBarVisibility="Disabled">
<ItemsRepeater Items="{Binding Properties}" Tag="{Binding #headerGrid.ColumnDefinitions}">
<ItemsRepeater.ItemTemplate>
<!--<ScrollViewer Grid.Row="1" HorizontalScrollBarVisibility="Disabled">
<ItemsRepeater Name="itemsParent" Items="{Binding Properties}">
<ItemsRepeater.ItemTemplate>-->
<ListBox Grid.Row="1" Name="itemsParent" ScrollViewer.HorizontalScrollBarVisibility="Disabled" Items="{Binding Properties}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding $parent[ItemsRepeater].Tag[0].Width}" />
<ColumnDefinition Width="{Binding #headerGrid.ColumnDefinitions[0].Width}" />
<ColumnDefinition Width="4" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="4" />
<ColumnDefinition Width="{Binding $parent[ItemsRepeater].Tag[4].Width}" />
<ColumnDefinition Width="{Binding #headerGrid.ColumnDefinitions[4].Width}" />
</Grid.ColumnDefinitions>
<Panel Background="Transparent" Grid.Column="0" ToolTip.Tip="{Binding Diagnostic}" >
<TextBlock Text="{Binding Name}" Margin="5 0 0 0" VerticalAlignment="Center" />
@ -55,10 +57,10 @@
<AutoCompleteBox IsEnabled="{Binding !IsReadOnly}" Classes="editor" Margin="1"
DataValidationErrors.Errors="{Binding ValueErrors}"
IsTextCompletionEnabled="{Binding !IsReadOnly}"
AsyncPopulator="{Binding PossibleValuesPopulator}"
AsyncPopulator="{Binding HintValuesPopulator}"
>
<v:BindingHelper.BindBackOnEvent>
<v:BindingHelper Binding="{Binding Value, Mode=TwoWay}" EventNames="LostFocus,DropDownClosed" PropertyName="Text" />
<v:BindingHelper Binding="{Binding Value, Mode=TwoWay}" EventNames="LostFocus,DropDownClosed,SelectionChanged,EnterKeyUp" PropertyName="Text" />
</v:BindingHelper.BindBackOnEvent>
</AutoCompleteBox>
</Panel>
@ -70,8 +72,10 @@
<TextBlock Text="{Binding Priority}" Margin="5 0 0 0" Grid.Column="4" VerticalAlignment="Center" />
</Grid>
</DataTemplate>
</ItemsRepeater.ItemTemplate>
</ListBox.ItemTemplate>
</ListBox>
<!--</ItemsRepeater.ItemTemplate>
</ItemsRepeater>
</ScrollViewer>
</ScrollViewer>-->
</Grid>
</UserControl>

4
src/Avalonia.Diagnostics/Views/TreePageView.xaml

@ -3,7 +3,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:v="clr-namespace:Avalonia.Diagnostics.Views;assembly=Avalonia.Diagnostics"
x:Class="Avalonia.Diagnostics.Views.TreePageView">
<Grid ColumnDefinitions="*,4,3*">
<Grid ColumnDefinitions="*,auto,3*">
<TreeView Name="tree" Items="{Binding Nodes}" SelectedItem="{Binding SelectedNode, Mode=TwoWay}">
<TreeView.DataTemplates>
<TreeDataTemplate DataType="vm:TreeNode"
@ -21,7 +21,7 @@
</TreeView.Styles>
</TreeView>
<GridSplitter Width="4" Grid.Column="1"/>
<GridSplitter Grid.Column="1"/>
<v:ControlDetailsView Grid.Column="2" DataContext="{Binding Details}" />
<!--<ContentControl Content="{Binding Details}" Grid.Column="2"/>-->

8
src/Avalonia.Diagnostics/Views/TreePageView.xaml.cs

@ -9,6 +9,7 @@ using Avalonia.Diagnostics.ViewModels;
using Avalonia.Input;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
using Avalonia.VisualTree;
namespace Avalonia.Diagnostics.Views
{
@ -26,7 +27,12 @@ namespace Avalonia.Diagnostics.Views
protected void AddAdorner(object sender, PointerEventArgs e)
{
var node = (TreeNode)((Control)sender).DataContext;
var layer = AdornerLayer.GetAdornerLayer(node.Visual);
var visual = node.Visual as IVisual;
if(visual == null)
{
return;
}
var layer = AdornerLayer.GetAdornerLayer(visual);
if (layer != null)
{

Loading…
Cancel
Save