diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj
index dea9b35e24..61f2443eb7 100644
--- a/samples/ControlCatalog/ControlCatalog.csproj
+++ b/samples/ControlCatalog/ControlCatalog.csproj
@@ -35,4 +35,4 @@
-
\ No newline at end of file
+
diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml
index 87cb5e9c5c..ec3bf799b4 100644
--- a/samples/ControlCatalog/MainView.xaml
+++ b/samples/ControlCatalog/MainView.xaml
@@ -20,8 +20,9 @@
+
-
+
diff --git a/samples/ControlCatalog/Pages/ListBoxPage.xaml b/samples/ControlCatalog/Pages/ListBoxPage.xaml
new file mode 100644
index 0000000000..3dd8be91c2
--- /dev/null
+++ b/samples/ControlCatalog/Pages/ListBoxPage.xaml
@@ -0,0 +1,13 @@
+
+
+ ListBox
+ Hosts a collection of ListBoxItem.
+
+
+
+
+
+
diff --git a/samples/ControlCatalog/Pages/ListBoxPage.xaml.cs b/samples/ControlCatalog/Pages/ListBoxPage.xaml.cs
new file mode 100644
index 0000000000..dbe6c74800
--- /dev/null
+++ b/samples/ControlCatalog/Pages/ListBoxPage.xaml.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace ControlCatalog.Pages
+{
+ public class ListBoxPage : UserControl
+ {
+ public ListBoxPage()
+ {
+ this.InitializeComponent();
+ DataContext = Enumerable.Range(1, 10).Select(i => $"Item {i}" )
+ .ToArray();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+
+ }
+}
diff --git a/samples/ControlCatalog/Pages/NumericUpDownPage.xaml b/samples/ControlCatalog/Pages/NumericUpDownPage.xaml
index 305bcd177c..2bb6214b58 100644
--- a/samples/ControlCatalog/Pages/NumericUpDownPage.xaml
+++ b/samples/ControlCatalog/Pages/NumericUpDownPage.xaml
@@ -14,7 +14,7 @@
AllowSpin:
-
+
ClipValueToMinMax:
@@ -77,4 +77,4 @@
-
\ No newline at end of file
+
diff --git a/samples/ControlCatalog/Pages/ScreenPage.cs b/samples/ControlCatalog/Pages/ScreenPage.cs
index 34aa85b8aa..fd66185832 100644
--- a/samples/ControlCatalog/Pages/ScreenPage.cs
+++ b/samples/ControlCatalog/Pages/ScreenPage.cs
@@ -42,7 +42,11 @@ namespace ControlCatalog.Pages
context.DrawRectangle(p, boundsRect);
context.DrawRectangle(p, workingAreaRect);
- FormattedText text = new FormattedText();
+ FormattedText text = new FormattedText()
+ {
+ Typeface = Typeface.Default
+ };
+
text.Text = $"Bounds: {screen.Bounds.Width}:{screen.Bounds.Height}";
context.DrawText(Brushes.Black, boundsRect.Position.WithY(boundsRect.Size.Height), text);
@@ -59,4 +63,4 @@ namespace ControlCatalog.Pages
context.DrawRectangle(p, new Rect(w.Position.X / 10f + Math.Abs(_leftMost), w.Position.Y / 10, w.Bounds.Width / 10, w.Bounds.Height / 10));
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Avalonia.Controls/ContentControl.cs b/src/Avalonia.Controls/ContentControl.cs
index 6da6da54a5..f9241956bd 100644
--- a/src/Avalonia.Controls/ContentControl.cs
+++ b/src/Avalonia.Controls/ContentControl.cs
@@ -45,6 +45,8 @@ namespace Avalonia.Controls
static ContentControl()
{
ContentControlMixin.Attach(ContentProperty, x => x.LogicalChildren);
+ PseudoClass(ContentProperty, x => x != null, ":valid");
+ PseudoClass(ContentProperty, x => x == null, ":invalid");
}
///
diff --git a/src/Avalonia.Controls/Converters/MarginMultiplierConverter.cs b/src/Avalonia.Controls/Converters/MarginMultiplierConverter.cs
new file mode 100644
index 0000000000..54bd6bcf39
--- /dev/null
+++ b/src/Avalonia.Controls/Converters/MarginMultiplierConverter.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Globalization;
+using Avalonia.Data.Converters;
+
+namespace Avalonia.Controls.Converters
+{
+ public class MarginMultiplierConverter : IValueConverter
+ {
+ public double Indent { get; set; }
+
+ public bool Left { get; set; } = false;
+
+ public bool Top { get; set; } = false;
+
+ public bool Right { get; set; } = false;
+
+ public bool Bottom { get; set; } = false;
+
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (!(value is int depth))
+ return new Thickness(0);
+
+ return new Thickness(Left ? Indent * depth : 0, Top ? Indent * depth : 0, Right ? Indent * depth : 0, Bottom ? Indent * depth : 0);
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new System.NotImplementedException();
+ }
+ }
+}
diff --git a/src/Avalonia.Controls/TreeView.cs b/src/Avalonia.Controls/TreeView.cs
index 3003dab85e..c574799724 100644
--- a/src/Avalonia.Controls/TreeView.cs
+++ b/src/Avalonia.Controls/TreeView.cs
@@ -1,6 +1,8 @@
// 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 Avalonia.Controls.Generators;
using Avalonia.Controls.Primitives;
@@ -31,6 +33,14 @@ namespace Avalonia.Controls
o => o.SelectedItem,
(o, v) => o.SelectedItem = v);
+ ///
+ /// Defines the event.
+ ///
+ public static readonly RoutedEvent SelectedItemChangedEvent =
+ RoutedEvent.Register(
+ "SelectedItemChanged",
+ RoutingStrategies.Bubble);
+
private object _selectedItem;
///
@@ -41,6 +51,15 @@ namespace Avalonia.Controls
// HACK: Needed or SelectedItem property will not be found in Release build.
}
+ ///
+ /// Occurs when the control's selection changes.
+ ///
+ public event EventHandler SelectedItemChanged
+ {
+ add { AddHandler(SelectedItemChangedEvent, value); }
+ remove { RemoveHandler(SelectedItemChangedEvent, value); }
+ }
+
///
/// Gets the for the tree view.
///
@@ -74,6 +93,7 @@ namespace Avalonia.Controls
MarkContainerSelected(container, false);
}
+ var oldItem = _selectedItem;
SetAndRaise(SelectedItemProperty, ref _selectedItem, value);
if (_selectedItem != null)
@@ -86,6 +106,28 @@ namespace Avalonia.Controls
container.BringIntoView();
}
}
+
+ if (oldItem != _selectedItem)
+ {
+ // Fire the SelectionChanged event
+ List
public static readonly DirectProperty IsExpandedProperty =
AvaloniaProperty.RegisterDirect(
- "IsExpanded",
+ nameof(IsExpanded),
o => o.IsExpanded,
(o, v) => o.IsExpanded = v);
@@ -31,17 +31,25 @@ namespace Avalonia.Controls
public static readonly StyledProperty IsSelectedProperty =
ListBoxItem.IsSelectedProperty.AddOwner();
+ ///
+ /// Defines the property.
+ ///
+ public static readonly DirectProperty LevelProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(Level), o => o.Level);
+
private static readonly ITemplate DefaultPanel =
new FuncTemplate(() => new StackPanel());
private TreeView _treeView;
private bool _isExpanded;
+ private int _level;
///
/// Initializes static members of the class.
///
static TreeViewItem()
- {
+ {
SelectableMixin.Attach(IsSelectedProperty);
FocusableProperty.OverrideDefaultValue(true);
ItemsPanelProperty.OverrideDefaultValue(DefaultPanel);
@@ -65,6 +73,15 @@ namespace Avalonia.Controls
set { SetValue(IsSelectedProperty, value); }
}
+ ///
+ /// Gets the level/indentation of the item.
+ ///
+ public int Level
+ {
+ get { return _level; }
+ private set { SetAndRaise(LevelProperty, ref _level, value); }
+ }
+
///
/// Gets the for the tree view.
///
@@ -89,6 +106,8 @@ namespace Avalonia.Controls
base.OnAttachedToLogicalTree(e);
_treeView = this.GetLogicalAncestors().OfType().FirstOrDefault();
+ Level = CalculateDistanceFromLogicalParent(this) - 1;
+
if (ItemTemplate == null && _treeView?.ItemTemplate != null)
{
ItemTemplate = _treeView.ItemTemplate;
@@ -126,5 +145,18 @@ namespace Avalonia.Controls
// Don't call base.OnKeyDown - let events bubble up to containing TreeView.
}
+
+ private static int CalculateDistanceFromLogicalParent(ILogical logical, int @default = -1) where T : class
+ {
+ var result = 0;
+
+ while (logical != null && logical.GetType() != typeof(T))
+ {
+ ++result;
+ logical = logical.LogicalParent;
+ }
+
+ return logical != null ? result : @default;
+ }
}
}
diff --git a/src/Avalonia.Styling/Styling/PropertyEqualsSelector.cs b/src/Avalonia.Styling/Styling/PropertyEqualsSelector.cs
index 57d5ad9271..25f12ffa57 100644
--- a/src/Avalonia.Styling/Styling/PropertyEqualsSelector.cs
+++ b/src/Avalonia.Styling/Styling/PropertyEqualsSelector.cs
@@ -60,7 +60,7 @@ namespace Avalonia.Styling
builder.Append(_property.Name);
builder.Append('=');
- builder.Append(_value);
+ builder.Append(_value ?? string.Empty);
builder.Append(']');
_selectorString = builder.ToString();
@@ -78,11 +78,11 @@ namespace Avalonia.Styling
}
else if (subscribe)
{
- return new SelectorMatch(control.GetObservable(_property).Select(v => Equals(v, _value)));
+ return new SelectorMatch(control.GetObservable(_property).Select(v => Equals(v ?? string.Empty, _value)));
}
else
{
- return new SelectorMatch(control.GetValue(_property).Equals(_value));
+ return new SelectorMatch((control.GetValue(_property) ?? string.Empty).Equals(_value));
}
}
diff --git a/src/Avalonia.Themes.Default/Accents/BaseLight.xaml b/src/Avalonia.Themes.Default/Accents/BaseLight.xaml
index 4c85e172ff..46767feca0 100644
--- a/src/Avalonia.Themes.Default/Accents/BaseLight.xaml
+++ b/src/Avalonia.Themes.Default/Accents/BaseLight.xaml
@@ -1,30 +1,56 @@
\ No newline at end of file
+ #FF086F9E
+ #FFFF0000
+ #10FF0000
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+ 0.5
+
+ 10
+ 12
+ 16
+
+
diff --git a/src/Avalonia.Themes.Default/AutoCompleteBox.xaml b/src/Avalonia.Themes.Default/AutoCompleteBox.xaml
index 6a9af487cb..11d8a344d9 100644
--- a/src/Avalonia.Themes.Default/AutoCompleteBox.xaml
+++ b/src/Avalonia.Themes.Default/AutoCompleteBox.xaml
@@ -36,8 +36,4 @@
-
-
-
\ No newline at end of file
+
diff --git a/src/Avalonia.Themes.Default/CalendarButton.xaml b/src/Avalonia.Themes.Default/CalendarButton.xaml
index 84969c135f..b70740e0c8 100644
--- a/src/Avalonia.Themes.Default/CalendarButton.xaml
+++ b/src/Avalonia.Themes.Default/CalendarButton.xaml
@@ -7,6 +7,7 @@
-
\ No newline at end of file
+
diff --git a/src/Avalonia.Themes.Default/CalendarDayButton.xaml b/src/Avalonia.Themes.Default/CalendarDayButton.xaml
index ee7afdc73b..4a971ef0cb 100644
--- a/src/Avalonia.Themes.Default/CalendarDayButton.xaml
+++ b/src/Avalonia.Themes.Default/CalendarDayButton.xaml
@@ -42,7 +42,7 @@
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
RenderTransformOrigin="0.5,0.5"
- Fill="#FF000000"
+ Fill="{DynamicResource ThemeForegroundBrush}"
Stretch="Fill"
Data="M8.1772461,11.029181 L10.433105,11.029181 L11.700684,12.801641 L12.973633,11.029181 L15.191895,11.029181 L12.844727,13.999395 L15.21875,17.060919 L12.962891,17.060919 L11.673828,15.256231 L10.352539,17.060919 L8.1396484,17.060919 L10.519043,14.042364 z" />
@@ -103,7 +103,7 @@
-
\ No newline at end of file
+
diff --git a/src/Avalonia.Themes.Default/CalendarItem.xaml b/src/Avalonia.Themes.Default/CalendarItem.xaml
index 3d3d75a39a..b52b7acf53 100644
--- a/src/Avalonia.Themes.Default/CalendarItem.xaml
+++ b/src/Avalonia.Themes.Default/CalendarItem.xaml
@@ -153,7 +153,7 @@
@@ -180,4 +180,4 @@
-
\ No newline at end of file
+
diff --git a/src/Avalonia.Themes.Default/CheckBox.xaml b/src/Avalonia.Themes.Default/CheckBox.xaml
index 62a05a3525..3d14dd8e51 100644
--- a/src/Avalonia.Themes.Default/CheckBox.xaml
+++ b/src/Avalonia.Themes.Default/CheckBox.xaml
@@ -40,6 +40,9 @@
+
@@ -58,4 +61,4 @@
-
\ No newline at end of file
+
diff --git a/src/Avalonia.Themes.Default/DataValidationErrors.xaml b/src/Avalonia.Themes.Default/DataValidationErrors.xaml
index f7f28d90d0..16c2d6adef 100644
--- a/src/Avalonia.Themes.Default/DataValidationErrors.xaml
+++ b/src/Avalonia.Themes.Default/DataValidationErrors.xaml
@@ -21,10 +21,10 @@
-
\ No newline at end of file
+
diff --git a/src/Avalonia.Themes.Default/DatePicker.xaml b/src/Avalonia.Themes.Default/DatePicker.xaml
index b706b5b4e5..93bafdb56f 100644
--- a/src/Avalonia.Themes.Default/DatePicker.xaml
+++ b/src/Avalonia.Themes.Default/DatePicker.xaml
@@ -6,7 +6,8 @@
-->
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:sys="clr-namespace:System;assembly=mscorlib">
-
\ No newline at end of file
+
diff --git a/src/Avalonia.Themes.Default/DropDownItem.xaml b/src/Avalonia.Themes.Default/DropDownItem.xaml
index 257030d8af..f52608c0a8 100644
--- a/src/Avalonia.Themes.Default/DropDownItem.xaml
+++ b/src/Avalonia.Themes.Default/DropDownItem.xaml
@@ -18,10 +18,24 @@
+
+
+
-
+
+
+
+
-
\ No newline at end of file
+
diff --git a/src/Avalonia.Themes.Default/Expander.xaml b/src/Avalonia.Themes.Default/Expander.xaml
index 0bea0c9763..d12f704cc4 100644
--- a/src/Avalonia.Themes.Default/Expander.xaml
+++ b/src/Avalonia.Themes.Default/Expander.xaml
@@ -108,7 +108,7 @@
-
\ No newline at end of file
+
diff --git a/src/Avalonia.Themes.Default/ListBoxItem.xaml b/src/Avalonia.Themes.Default/ListBoxItem.xaml
index fc2600c1a9..19a6e3d4ec 100644
--- a/src/Avalonia.Themes.Default/ListBoxItem.xaml
+++ b/src/Avalonia.Themes.Default/ListBoxItem.xaml
@@ -1,6 +1,9 @@
+
+
+
+
+
+
+
+
-
\ No newline at end of file
+
diff --git a/src/Avalonia.Themes.Default/ScrollBar.xaml b/src/Avalonia.Themes.Default/ScrollBar.xaml
index b24c863be9..ef57474eaf 100644
--- a/src/Avalonia.Themes.Default/ScrollBar.xaml
+++ b/src/Avalonia.Themes.Default/ScrollBar.xaml
@@ -10,7 +10,7 @@
Grid.Column="0">
+ Fill="{DynamicResource ThemeForegroundLightBrush}" />
public class Typeface
{
+ public static Typeface Default = new Typeface(FontFamily.Default);
+
///
/// Initializes a new instance of the class.
///
diff --git a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs
index c49c343a45..1a913865cb 100644
--- a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs
@@ -166,6 +166,39 @@ namespace Avalonia.Controls.UnitTests
Assert.True(container.IsSelected);
}
+
+ [Fact]
+ public void Setting_SelectedItem_Should_Raise_SelectedItemChanged_Event()
+ {
+ var tree = CreateTestTreeData();
+ var target = new TreeView
+ {
+ Template = CreateTreeViewTemplate(),
+ Items = tree,
+ };
+
+ var visualRoot = new TestRoot();
+ visualRoot.Child = target;
+
+ CreateNodeDataTemplate(target);
+ ApplyTemplates(target);
+
+ var item = tree[0].Children[1].Children[0];
+
+ var called = false;
+ target.SelectedItemChanged += (s, e) =>
+ {
+ Assert.Empty(e.RemovedItems);
+ Assert.Equal(1, e.AddedItems.Count);
+ Assert.Same(item, e.AddedItems[0]);
+ called = true;
+ };
+
+ target.SelectedItem = item;
+ Assert.True(called);
+ }
+
+
[Fact]
public void LogicalChildren_Should_Be_Set()
{