Browse Source

Merge branch 'master' into infra/remove_ReactiveUI_Events

Steven Kirk 4 years ago
parent
commit
ffc21d9b97
33 changed files with 821 additions and 197 deletions
  1. 1 0
      samples/ControlCatalog/Pages/ContextMenuPage.xaml
  2. 14 0
      src/Avalonia.Base/Data/Optional.cs
  3. 2 2
      src/Avalonia.Base/PropertyStore/BindingEntry.cs
  4. 2 2
      src/Avalonia.Base/PropertyStore/ConstantValueEntry.cs
  5. 2 2
      src/Avalonia.Base/PropertyStore/LocalValueEntry.cs
  6. 2 2
      src/Avalonia.Base/PropertyStore/PriorityValue.cs
  7. 37 0
      src/Avalonia.Controls.DataGrid/Collections/DataGridSortDescription.cs
  8. 1 1
      src/Avalonia.Controls.DataGrid/DataGrid.cs
  9. 16 0
      src/Avalonia.Controls.DataGrid/DataGridColumn.cs
  10. 6 0
      src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs
  11. 4 1
      src/Avalonia.Controls/ApiCompatBaseline.txt
  12. 1 1
      src/Avalonia.Controls/AutoCompleteBox.cs
  13. 6 0
      src/Avalonia.Controls/IMenuItem.cs
  14. 17 8
      src/Avalonia.Controls/ItemsControl.cs
  15. 16 0
      src/Avalonia.Controls/MenuItem.cs
  16. 5 1
      src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs
  17. 4 8
      src/Avalonia.Controls/TextBox.cs
  18. 0 3
      src/Avalonia.Dialogs/Avalonia.Dialogs.csproj
  19. 1 1
      src/Avalonia.Dialogs/ChildFitter.cs
  20. 1 1
      src/Avalonia.Dialogs/FileSizeStringConverter.cs
  21. 22 15
      src/Avalonia.Dialogs/ManagedFileChooser.cs
  22. 0 144
      src/Avalonia.Dialogs/ManagedFileChooser.xaml
  23. 1 1
      src/Avalonia.Dialogs/ResourceSelectorConverter.cs
  24. 1 0
      src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj
  25. 1 0
      src/Avalonia.Themes.Default/DefaultTheme.xaml
  26. 153 0
      src/Avalonia.Themes.Default/ManagedFileChooser.xaml
  27. 2 1
      src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj
  28. 2 0
      src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml
  29. 324 0
      src/Avalonia.Themes.Fluent/Controls/ManagedFileChooser.xaml
  30. 4 2
      src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs
  31. 49 0
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_BatchUpdate.cs
  32. 123 0
      tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs
  33. 1 1
      tests/Avalonia.Controls.UnitTests/TextBoxTests.cs

+ 1 - 0
samples/ControlCatalog/Pages/ContextMenuPage.xaml

@@ -31,6 +31,7 @@
                                 <CheckBox BorderThickness="0" IsHitTestVisible="False" IsChecked="True"/>
                             </MenuItem.Icon>
                         </MenuItem>
+                        <MenuItem Header="Menu Item that won't close on click" StaysOpenOnClick="True" />
                     </ContextMenu>
                 </Border.ContextMenu>
                 <TextBlock Text="Defined in XAML"/>

+ 14 - 0
src/Avalonia.Base/Data/Optional.cs

@@ -153,4 +153,18 @@ namespace Avalonia.Data
         /// </summary>
         public static Optional<T> Empty => default;
     }
+
+    public static class OptionalExtensions
+    {
+        /// <summary>
+        /// Casts the type of an <see cref="Optional{T}"/> using only the C# cast operator.
+        /// </summary>
+        /// <typeparam name="T">The target type.</typeparam>
+        /// <param name="value">The binding value.</param>
+        /// <returns>The cast value.</returns>
+        public static Optional<T> Cast<T>(this Optional<object> value)
+        {
+            return value.HasValue ? new Optional<T>((T)value.Value) : Optional<T>.Empty;
+        }
+    }
 }

+ 2 - 2
src/Avalonia.Base/PropertyStore/BindingEntry.cs

@@ -127,8 +127,8 @@ namespace Avalonia.PropertyStore
             sink.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>(
                 owner,
                 (AvaloniaProperty<T>)property,
-                oldValue.GetValueOrDefault<T>(),
-                newValue.GetValueOrDefault<T>(),
+                oldValue.Cast<T>(),
+                newValue.Cast<T>(),
                 Priority));
         }
 

+ 2 - 2
src/Avalonia.Base/PropertyStore/ConstantValueEntry.cs

@@ -65,8 +65,8 @@ namespace Avalonia.PropertyStore
             sink.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>(
                 owner,
                 (AvaloniaProperty<T>)property,
-                oldValue.GetValueOrDefault<T>(),
-                newValue.GetValueOrDefault<T>(),
+                oldValue.Cast<T>(),
+                newValue.Cast<T>(),
                 Priority));
         }
     }

+ 2 - 2
src/Avalonia.Base/PropertyStore/LocalValueEntry.cs

@@ -36,8 +36,8 @@ namespace Avalonia.PropertyStore
             sink.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>(
                 owner,
                 (AvaloniaProperty<T>)property,
-                oldValue.GetValueOrDefault<T>(),
-                newValue.GetValueOrDefault<T>(),
+                oldValue.Cast<T>(),
+                newValue.Cast<T>(),
                 BindingPriority.LocalValue));
         }
     }

+ 2 - 2
src/Avalonia.Base/PropertyStore/PriorityValue.cs

@@ -197,8 +197,8 @@ namespace Avalonia.PropertyStore
             sink.ValueChanged(new AvaloniaPropertyChangedEventArgs<T>(
                 owner,
                 (AvaloniaProperty<T>)property,
-                oldValue.GetValueOrDefault<T>(),
-                newValue.GetValueOrDefault<T>(),
+                oldValue.Cast<T>(),
+                newValue.Cast<T>(),
                 Priority));
         }
 

+ 37 - 0
src/Avalonia.Controls.DataGrid/Collections/DataGridSortDescription.cs

@@ -265,6 +265,43 @@ namespace Avalonia.Collections
         {
             return new DataGridPathSortDescription(propertyPath, direction, comparer, null);
         }
+
+        public static DataGridSortDescription FromComparer(IComparer comparer, ListSortDirection direction = ListSortDirection.Ascending)
+        {
+            return new DataGridComparerSortDesctiption(comparer, direction);
+        }
+    }
+
+    public class DataGridComparerSortDesctiption : DataGridSortDescription
+    {
+        private readonly IComparer _innerComparer;
+        private readonly ListSortDirection _direction;
+        private readonly IComparer<object> _comparer;
+
+        public IComparer SourceComparer => _innerComparer;
+        public override IComparer<object> Comparer => _comparer;
+        public override ListSortDirection Direction => _direction;
+        public DataGridComparerSortDesctiption(IComparer comparer, ListSortDirection direction)
+        {
+            _innerComparer = comparer;
+            _direction = direction;
+            _comparer = Comparer<object>.Create((x, y) => Compare(x, y));
+        }
+
+        private int Compare(object x, object y)
+        {
+            int result = _innerComparer.Compare(x, y);
+
+            if (Direction == ListSortDirection.Descending)
+                return -result;
+            else
+                return result;
+        }
+        public override DataGridSortDescription SwitchSortDirection()
+        {
+            var newDirection = _direction == ListSortDirection.Ascending ? ListSortDirection.Descending : ListSortDirection.Ascending;
+            return new DataGridComparerSortDesctiption(_innerComparer, newDirection);
+        }
     }
 
     public class DataGridSortDescriptionCollection : AvaloniaList<DataGridSortDescription>

+ 1 - 1
src/Avalonia.Controls.DataGrid/DataGrid.cs

@@ -5357,7 +5357,7 @@ namespace Avalonia.Controls
             _focusedRow = null;
         }
 
-        private void SelectAll()
+        public void SelectAll()
         {
             SetRowsSelection(0, SlotCount - 1);
         }

+ 16 - 0
src/Avalonia.Controls.DataGrid/DataGridColumn.cs

@@ -1009,6 +1009,14 @@ namespace Avalonia.Controls
             get;
             set;
         }
+        /// <summary>
+        /// Holds a Comparer to use for sorting, if not using the default.
+        /// </summary>
+        public System.Collections.IComparer CustomSortComparer
+        {
+            get;
+            set;
+        }
 
         /// <summary>
         /// We get the sort description from the data source.  We don't worry whether we can modify sort -- perhaps the sort description
@@ -1020,6 +1028,14 @@ namespace Avalonia.Controls
                 && OwningGrid.DataConnection != null
                 && OwningGrid.DataConnection.SortDescriptions != null)
             {
+                if(CustomSortComparer != null)
+                {
+                    return
+                        OwningGrid.DataConnection.SortDescriptions
+                                  .OfType<DataGridComparerSortDesctiption>()
+                                  .FirstOrDefault(s => s.SourceComparer == CustomSortComparer);
+                }
+
                 string propertyName = GetSortPropertyName();
 
                 return OwningGrid.DataConnection.SortDescriptions.FirstOrDefault(s => s.HasPropertyPath && s.PropertyPath == propertyName);

+ 6 - 0
src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs

@@ -274,6 +274,12 @@ namespace Avalonia.Controls
                                     owningGrid.DataConnection.SortDescriptions.Add(newSort);
                                 }
                             }
+                            else if (OwningColumn.CustomSortComparer != null)
+                            {
+                                newSort = DataGridSortDescription.FromComparer(OwningColumn.CustomSortComparer);
+
+                                owningGrid.DataConnection.SortDescriptions.Add(newSort);
+                            }
                             else
                             {
                                 string propertyName = OwningColumn.GetSortPropertyName();

+ 4 - 1
src/Avalonia.Controls/ApiCompatBaseline.txt

@@ -1,4 +1,7 @@
 Compat issues with assembly Avalonia.Controls:
+InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Controls.IMenuItem.StaysOpenOnClick' is present in the implementation but not in the contract.
+InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Controls.IMenuItem.StaysOpenOnClick.get()' is present in the implementation but not in the contract.
+InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.IMenuItem.StaysOpenOnClick.set(System.Boolean)' is present in the implementation but not in the contract.
 InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.INativeMenuExporterEventsImplBridge.RaiseClosed()' is present in the implementation but not in the contract.
 InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.INativeMenuExporterEventsImplBridge.RaiseOpening()' is present in the implementation but not in the contract.
 MembersMustExist : Member 'public void Avalonia.Controls.Embedding.Offscreen.OffscreenTopLevelImplBase.SetCursor(Avalonia.Platform.IPlatformHandle)' does not exist in the implementation but it does exist in the contract.
@@ -7,4 +10,4 @@ EnumValuesMustMatch : Enum value 'Avalonia.Platform.ExtendClientAreaChromeHints
 InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.ICursorImpl)' is present in the implementation but not in the contract.
 InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.IPlatformHandle)' is present in the contract but not in the implementation.
 MembersMustExist : Member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.IPlatformHandle)' does not exist in the implementation but it does exist in the contract.
-Total Issues: 7
+Total Issues: 11

+ 1 - 1
src/Avalonia.Controls/AutoCompleteBox.cs

@@ -31,7 +31,6 @@ namespace Avalonia.Controls
     /// <see cref="E:Avalonia.Controls.AutoCompleteBox.Populated" />
     /// event.
     /// </summary>
-    [PseudoClasses(":dropdownopen")]
     public class PopulatedEventArgs : EventArgs
     {
         /// <summary>
@@ -253,6 +252,7 @@ namespace Avalonia.Controls
     /// drop-down that contains possible matches based on the input in the text
     /// box.
     /// </summary>
+    [PseudoClasses(":dropdownopen")]
     public class AutoCompleteBox : TemplatedControl
     {
         /// <summary>

+ 6 - 0
src/Avalonia.Controls/IMenuItem.cs

@@ -23,6 +23,12 @@ namespace Avalonia.Controls
         /// </summary>
         bool IsSubMenuOpen { get; set; }
 
+        /// <summary>
+        /// Gets or sets a value that indicates the submenu that this <see cref="MenuItem"/> is
+        /// within should not close when this item is clicked.
+        /// </summary>
+        bool StaysOpenOnClick { get; set; }
+
         /// <summary>
         /// Gets a value that indicates whether the <see cref="MenuItem"/> is a top-level main menu item.
         /// </summary>

+ 17 - 8
src/Avalonia.Controls/ItemsControl.cs

@@ -70,7 +70,7 @@ namespace Avalonia.Controls
         /// </summary>
         public ItemsControl()
         {
-            PseudoClasses.Add(":empty");
+            UpdatePseudoClasses(0);
             SubscribeToItems(_items);
         }
 
@@ -323,6 +323,16 @@ namespace Avalonia.Controls
             base.OnKeyDown(e);
         }
 
+        protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
+        {
+            base.OnPropertyChanged(change);
+
+            if (change.Property == ItemCountProperty)
+            {
+                UpdatePseudoClasses(change.NewValue.GetValueOrDefault<int>());
+            }
+        }
+
         /// <summary>
         /// Called when the <see cref="Items"/> property changes.
         /// </summary>
@@ -371,10 +381,6 @@ namespace Avalonia.Controls
             }
 
             Presenter?.ItemsChanged(e);
-
-            var collection = sender as ICollection;
-            PseudoClasses.Set(":empty", collection == null || collection.Count == 0);
-            PseudoClasses.Set(":singleitem", collection != null && collection.Count == 1);
         }
 
         /// <summary>
@@ -431,9 +437,6 @@ namespace Avalonia.Controls
         /// <param name="items">The items collection.</param>
         private void SubscribeToItems(IEnumerable items)
         {
-            PseudoClasses.Set(":empty", items == null || items.Count() == 0);
-            PseudoClasses.Set(":singleitem", items != null && items.Count() == 1);
-
             if (items is INotifyCollectionChanged incc)
             {
                 CollectionChangedEventManager.Instance.AddListener(incc, this);
@@ -469,6 +472,12 @@ namespace Avalonia.Controls
             }
         }
 
+        private void UpdatePseudoClasses(int itemCount)
+        {
+            PseudoClasses.Set(":empty", itemCount == 0);
+            PseudoClasses.Set(":singleitem", itemCount == 1);
+        }
+
         protected static IInputElement GetNextControl(
             INavigableContainer container,
             NavigationDirection direction,

+ 16 - 0
src/Avalonia.Controls/MenuItem.cs

@@ -69,6 +69,12 @@ namespace Avalonia.Controls
         public static readonly StyledProperty<bool> IsSubMenuOpenProperty =
             AvaloniaProperty.Register<MenuItem, bool>(nameof(IsSubMenuOpen));
 
+        /// <summary>
+        /// Defines the <see cref="StaysOpenOnClick"/> property.
+        /// </summary>
+        public static readonly StyledProperty<bool> StaysOpenOnClickProperty =
+            AvaloniaProperty.Register<MenuItem, bool>(nameof(StaysOpenOnClick));
+
         /// <summary>
         /// Defines the <see cref="Click"/> event.
         /// </summary>
@@ -265,6 +271,16 @@ namespace Avalonia.Controls
             set { SetValue(IsSubMenuOpenProperty, value); }
         }
 
+        /// <summary>
+        /// Gets or sets a value that indicates the submenu that this <see cref="MenuItem"/> is
+        /// within should not close when this item is clicked.
+        /// </summary>
+        public bool StaysOpenOnClick
+        {
+            get { return GetValue(StaysOpenOnClickProperty); }
+            set { SetValue(StaysOpenOnClickProperty, value); }
+        }
+
         /// <summary>
         /// Gets or sets a value that indicates whether the <see cref="MenuItem"/> has a submenu.
         /// </summary>

+ 5 - 1
src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs

@@ -449,7 +449,11 @@ namespace Avalonia.Controls.Platform
         protected void Click(IMenuItem item)
         {
             item.RaiseClick();
-            CloseMenu(item);
+
+            if (!item.StaysOpenOnClick)
+            {
+                CloseMenu(item);
+            }
         }
 
         protected void CloseMenu(IMenuItem item)

+ 4 - 8
src/Avalonia.Controls/TextBox.cs

@@ -175,16 +175,12 @@ namespace Avalonia.Controls
                 this.GetObservable(TextWrappingProperty),
                 (acceptsReturn, wrapping) =>
                 {
-                    if (acceptsReturn)
+                    if (wrapping != TextWrapping.NoWrap)
                     {
-                        return wrapping != TextWrapping.Wrap ?
-                            ScrollBarVisibility.Auto :
-                            ScrollBarVisibility.Disabled;
-                    }
-                    else
-                    {
-                        return ScrollBarVisibility.Hidden;
+                        return ScrollBarVisibility.Disabled;
                     }
+
+                    return acceptsReturn ? ScrollBarVisibility.Auto : ScrollBarVisibility.Hidden;
                 });
             this.Bind(
                 ScrollViewer.HorizontalScrollBarVisibilityProperty,

+ 0 - 3
src/Avalonia.Dialogs/Avalonia.Dialogs.csproj

@@ -4,9 +4,6 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <AvaloniaResource Include="**\*.xaml">
-      <SubType>Designer</SubType>
-    </AvaloniaResource>
     <AvaloniaResource Include="Assets\*" />
   </ItemGroup>
 

+ 1 - 1
src/Avalonia.Dialogs/ChildFitter.cs

@@ -4,7 +4,7 @@ using Avalonia.Layout;
 
 namespace Avalonia.Dialogs
 {
-    internal class ChildFitter : Decorator
+    public class ChildFitter : Decorator
     {
         protected override Size MeasureOverride(Size availableSize)
         {

+ 1 - 1
src/Avalonia.Dialogs/FileSizeStringConverter.cs

@@ -6,7 +6,7 @@ using System.Text;
 
 namespace Avalonia.Dialogs
 {
-    internal class FileSizeStringConverter : IValueConverter
+    public class FileSizeStringConverter : IValueConverter
     {
         public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
         {

+ 22 - 15
src/Avalonia.Dialogs/ManagedFileChooser.xaml.cs → src/Avalonia.Dialogs/ManagedFileChooser.cs

@@ -3,6 +3,7 @@ using System.Linq;
 using System.Threading.Tasks;
 using Avalonia;
 using Avalonia.Controls;
+using Avalonia.Controls.Primitives;
 using Avalonia.Input;
 using Avalonia.Interactivity;
 using Avalonia.LogicalTree;
@@ -10,17 +11,14 @@ using Avalonia.Markup.Xaml;
 
 namespace Avalonia.Dialogs
 {
-    internal class ManagedFileChooser : UserControl
+    public class ManagedFileChooser : TemplatedControl
     {
         private Control _quickLinksRoot;
         private ListBox _filesView;
 
         public ManagedFileChooser()
         {
-            AvaloniaXamlLoader.Load(this);
             AddHandler(PointerPressedEvent, OnPointerPressed, RoutingStrategies.Tunnel);
-            _quickLinksRoot = this.FindControl<Control>("QuickLinks");
-            _filesView = this.FindControl<ListBox>("Files");
         }
 
         ManagedFileChooserViewModel Model => DataContext as ManagedFileChooserViewModel;
@@ -34,19 +32,22 @@ namespace Avalonia.Dialogs
                 return;
             }
 
-            var isQuickLink = _quickLinksRoot.IsLogicalAncestorOf(e.Source as Control);
-            if (e.ClickCount == 2 || isQuickLink)
+            if (_quickLinksRoot != null)
             {
-                if (model.ItemType == ManagedFileChooserItemType.File)
+                var isQuickLink = _quickLinksRoot.IsLogicalAncestorOf(e.Source as Control);
+                if (e.ClickCount == 2 || isQuickLink)
                 {
-                    Model?.SelectSingleFile(model);
+                    if (model.ItemType == ManagedFileChooserItemType.File)
+                    {
+                        Model?.SelectSingleFile(model);
+                    }
+                    else
+                    {
+                        Model?.Navigate(model.Path);
+                    }
+
+                    e.Handled = true;
                 }
-                else
-                {
-                    Model?.Navigate(model.Path);
-                }
-
-                e.Handled = true;
             }
         }
 
@@ -79,10 +80,16 @@ namespace Avalonia.Dialogs
             // Workaround for ListBox bug, scroll to the previous file
             var indexOfPreselected = model.Items.IndexOf(preselected);
 
-            if (indexOfPreselected > 1)
+            if ((_filesView != null) && (indexOfPreselected > 1))
             {
                 _filesView.ScrollIntoView(indexOfPreselected - 1);
             }
         }
+
+        protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
+        {
+            _quickLinksRoot = e.NameScope.Get<Control>("QuickLinks");
+            _filesView = e.NameScope.Get<ListBox>("Files");
+        }
     }
 }

+ 0 - 144
src/Avalonia.Dialogs/ManagedFileChooser.xaml

@@ -1,144 +0,0 @@
-<UserControl xmlns="https://github.com/avaloniaui"
-             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
-             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
-             xmlns:dialogs="clr-namespace:Avalonia.Dialogs"
-             xmlns:internal="clr-namespace:Avalonia.Dialogs"
-             x:Class="Avalonia.Dialogs.ManagedFileChooser" Margin="10">
-    <UserControl.Resources>
-        <internal:FileSizeStringConverter x:Key="FileSizeConverter" />
-        <DrawingGroup x:Key="LevelUp">
-            <GeometryDrawing Brush="#00FFFFFF" Geometry="F1M16,16L0,16 0,0 16,0z" />
-            <GeometryDrawing Brush="#FFF6F6F6" Geometry="F1M14.5,0L6.39,0 5.39,2 2.504,2C1.677,2,1,2.673,1,3.5L1,10.582 1,10.586 1,15.414 3,13.414 3,16 7,16 7,13.414 9,15.414 9,13 14.5,13C15.327,13,16,12.327,16,11.5L16,1.5C16,0.673,15.327,0,14.5,0" />
-            <GeometryDrawing Brush="#FFDCB679" Geometry="F1M14,3L7.508,3 8.008,2 8.012,2 14,2z M14.5,1L7.008,1 6.008,3 2.504,3C2.227,3,2,3.224,2,3.5L2,9.582 4.998,6.586 9,10.586 9,12 14.5,12C14.775,12,15,11.776,15,11.5L15,1.5C15,1.224,14.775,1,14.5,1" />
-            <GeometryDrawing Brush="#FF00529C" Geometry="F1M8,11L5,8 2,11 2,13 4,11 4,15 6,15 6,11 8,13z" />
-            <GeometryDrawing Brush="#FFF0EFF1" Geometry="F1M8.0001,1.9996L7.5001,3.0006 14.0001,3.0006 14.0001,1.9996z" />
-        </DrawingGroup>
-        <dialogs:ResourceSelectorConverter x:Key="Icons">
-            <DrawingGroup x:Key="Icon_Folder">
-                <GeometryDrawing Brush="#00FFFFFF" Geometry="F1M0,0L16,0 16,16 0,16z" />
-                <GeometryDrawing Brush="#FFF6F6F6" Geometry="F1M1.5,1L9.61,1 10.61,3 13.496,3C14.323,3,14.996,3.673,14.996,4.5L14.996,12.5C14.996,13.327,14.323,14,13.496,14L1.5,14C0.673,14,0,13.327,0,12.5L0,2.5C0,1.673,0.673,1,1.5,1" />
-                <GeometryDrawing Brush="#FFDCB67A" Geometry="F1M2,3L8.374,3 8.874,4 2,4z M13.496,4L10,4 9.992,4 8.992,2 1.5,2C1.225,2,1,2.224,1,2.5L1,12.5C1,12.776,1.225,13,1.5,13L13.496,13C13.773,13,13.996,12.776,13.996,12.5L13.996,4.5C13.996,4.224,13.773,4,13.496,4" />
-                <GeometryDrawing Brush="#FFEFEFF0" Geometry="F1M2,3L8.374,3 8.874,4 2,4z" />
-            </DrawingGroup>
-            <DrawingGroup x:Key="Icon_File">
-                <GeometryDrawing Brush="#00FFFFFF" Geometry="F1M16,16L0,16 0,0 16,0z" />
-                <GeometryDrawing Brush="#FFF6F6F6" Geometry="F1M4,15C3.03,15,2,14.299,2,13L2,3C2,1.701,3.03,1,4,1L10.061,1 14,4.556 14,13C14,13.97,13.299,15,12,15z" />
-                <GeometryDrawing Brush="#FF9B4E96" Geometry="F1M12,13L4,13 4,3 9,3 9,6 12,6z M9.641,2L3.964,2C3.964,2,3,2,3,3L3,13C3,14,3.964,14,3.964,14L11.965,14C12.965,14,13,13,13,13L13,5z" />
-                <GeometryDrawing Brush="#FFF0EFF1" Geometry="F1M4,3L9,3 9,6 12,6 12,13 4,13z" />
-            </DrawingGroup>
-            <DrawingGroup x:Key="Icon_Volume">
-                <GeometryDrawing Brush="#00FFFFFF" Geometry="F1M16,16L0,16 0,0 16,0z" />
-                <GeometryDrawing Brush="#FFF6F6F6" Geometry="F1M0,12L0,6.5C0,5.122,1.122,4,2.5,4L13.5,4C14.879,4,16,5.122,16,6.5L16,12z" />
-                <GeometryDrawing Brush="#FFEFEFF0" Geometry="F1M13,8L12,8 12,7 13,7z M11,8L10,8 10,7 11,7z M13.5,6L2.5,6C2.224,6,2,6.224,2,6.5L2,10 14,10 14,6.5C14,6.224,13.775,6,13.5,6" />
-                <GeometryDrawing Brush="#FF424242" Geometry="F1M13,7L12,7 12,8 13,8z M11,7L10,7 10,8 11,8z M2,10L14,10 14,6.5C14,6.224,13.775,6,13.5,6L2.5,6C2.224,6,2,6.224,2,6.5z M15,11L1,11 1,6.5C1,5.673,1.673,5,2.5,5L13.5,5C14.327,5,15,5.673,15,6.5z" />
-            </DrawingGroup>
-        </dialogs:ResourceSelectorConverter>
-    </UserControl.Resources>
-    <DockPanel>
-        <DockPanel DockPanel.Dock="Top" Margin="0 0 0 5">
-            <dialogs:ChildFitter DockPanel.Dock="Right" Width="{Binding ElementName=Location, Path=Bounds.Height}">
-                <Button Command="{Binding GoUp}" >
-
-                    <DrawingPresenter Drawing="{StaticResource LevelUp}" Stretch="Fill"/>
-                </Button>
-            </dialogs:ChildFitter>
-            <TextBox x:Name="Location" Text="{Binding Location}" Margin="0 0 5 0">
-                <TextBox.KeyBindings>
-                    <KeyBinding Command="{Binding EnterPressed}" Gesture="Enter"/>
-                </TextBox.KeyBindings>
-            </TextBox>
-        </DockPanel>
-        <DockPanel Margin="0 5 0 0"  DockPanel.Dock="Bottom">
-            <StackPanel Orientation="Horizontal" DockPanel.Dock="Left">
-                <CheckBox IsChecked="{Binding ShowHiddenFiles}">
-                    <TextBlock>Show hidden files</TextBlock>
-                </CheckBox>
-            </StackPanel>
-            <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Spacing="10">
-                <StackPanel.Styles>
-                    <Style Selector="Button">
-                        <Setter Property="Margin" Value="4"/>
-                    </Style>
-                </StackPanel.Styles>
-                <Button Command="{Binding Ok}">OK</Button>
-                <Button Command="{Binding Cancel}">Cancel</Button>
-            </StackPanel>
-        </DockPanel>
-
-        <ComboBox DockPanel.Dock="Bottom" 
-                  IsVisible="{Binding ShowFilters}" 
-                  Items="{Binding Filters}"
-                  SelectedItem="{Binding SelectedFilter}"
-                  Margin="0 5 0 0" />
-
-        <TextBox Text="{Binding FileName}" Watermark="File name" DockPanel.Dock="Bottom" IsVisible="{Binding !SelectingFolder}" />
-
-        <ListBox Margin="0 0 5 5" BorderBrush="Transparent" x:Name="QuickLinks" Items="{Binding QuickLinks}"
-                 SelectedIndex="{Binding QuickLinksSelectedIndex}"
-                 DockPanel.Dock="Left" Focusable="False">
-            <ListBox.ItemTemplate>
-                <DataTemplate>
-                    <StackPanel Spacing="4" Orientation="Horizontal" Background="Transparent">
-                        <DrawingPresenter Width="16" Height="16" Drawing="{Binding IconKey, Converter={StaticResource Icons}}"/>
-                        <TextBlock Text="{Binding DisplayName}"/>
-                    </StackPanel>
-                </DataTemplate>
-            </ListBox.ItemTemplate>
-        </ListBox>
-        <DockPanel Grid.IsSharedSizeScope="True">
-            <Grid DockPanel.Dock="Top" Margin="15 5 0 0" HorizontalAlignment="Stretch">
-                <Grid.ColumnDefinitions>
-                    <ColumnDefinition Width="20" SharedSizeGroup="Icon" />
-                    <ColumnDefinition Width="16" SharedSizeGroup="Splitter"  />
-                    <ColumnDefinition Width="400" SharedSizeGroup="Name" />
-                    <ColumnDefinition Width="16" SharedSizeGroup="Splitter" />
-                    <ColumnDefinition Width="200" SharedSizeGroup="Modified" />
-                    <ColumnDefinition Width="16" SharedSizeGroup="Splitter" />
-                    <ColumnDefinition Width="150" SharedSizeGroup="Type" />
-                    <ColumnDefinition Width="16" SharedSizeGroup="Splitter" />
-                    <ColumnDefinition Width="200" SharedSizeGroup="Size" />
-                </Grid.ColumnDefinitions>
-                <GridSplitter Grid.Column="1" />
-                <TextBlock Grid.Column="2" Text="Name" />
-                <GridSplitter Grid.Column="3" />
-                <TextBlock Grid.Column="4" Text="Date Modified" />
-                <GridSplitter Grid.Column="5" />
-                <TextBlock Grid.Column="6" Text="Type" />
-                <GridSplitter Grid.Column="7" />
-                <TextBlock Grid.Column="8" Text="Size" />
-            </Grid>
-            <ListBox x:Name="Files"
-                 VirtualizationMode="Simple"
-                 Items="{Binding Items}"
-                 Margin="0 5"
-                 SelectionMode="{Binding SelectionMode}"
-                 SelectedItems="{Binding SelectedItems}"
-                 ScrollViewer.HorizontalScrollBarVisibility="Disabled">
-                <ListBox.ItemTemplate>
-                    <DataTemplate>
-                        <Grid Background="Transparent">
-                            <Grid.ColumnDefinitions>
-                                <ColumnDefinition SharedSizeGroup="Icon" />
-                                <ColumnDefinition SharedSizeGroup="Splitter"  />
-                                <ColumnDefinition SharedSizeGroup="Name" />
-                                <ColumnDefinition SharedSizeGroup="Splitter" />
-                                <ColumnDefinition SharedSizeGroup="Modified" />
-                                <ColumnDefinition SharedSizeGroup="Splitter" />
-                                <ColumnDefinition SharedSizeGroup="Type" />
-                                <ColumnDefinition SharedSizeGroup="Splitter" />
-                                <ColumnDefinition SharedSizeGroup="Size" />
-                            </Grid.ColumnDefinitions>
-                            <DrawingPresenter Width="16" Height="16" Drawing="{Binding IconKey, Converter={StaticResource Icons}}"/>
-                            <TextBlock Grid.Column="2" Text="{Binding DisplayName}"/>
-                            <TextBlock Grid.Column="4" Text="{Binding Modified}" />
-                            <TextBlock Grid.Column="6" Text="{Binding Type}" />
-                            <TextBlock Grid.Column="8" Text="{Binding Size, Converter={StaticResource FileSizeConverter}}" />
-                        </Grid>
-                    </DataTemplate>
-                </ListBox.ItemTemplate>
-            </ListBox>
-        </DockPanel>
-    </DockPanel>
-
-</UserControl>

+ 1 - 1
src/Avalonia.Dialogs/ResourceSelectorConverter.cs

@@ -5,7 +5,7 @@ using Avalonia.Data.Converters;
 
 namespace Avalonia.Dialogs
 {
-    internal class ResourceSelectorConverter : ResourceDictionary, IValueConverter
+    public class ResourceSelectorConverter : ResourceDictionary, IValueConverter
     {
         public object Convert(object key, Type targetType, object parameter, CultureInfo culture)
         {

+ 1 - 0
src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj

@@ -12,6 +12,7 @@
     <ProjectReference Include="..\Avalonia.Layout\Avalonia.Layout.csproj" />
     <ProjectReference Include="..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
     <ProjectReference Include="..\Avalonia.Styling\Avalonia.Styling.csproj" />
+    <ProjectReference Include="..\Avalonia.Dialogs\Avalonia.Dialogs.csproj" />
     <AvaloniaResource Include="DefaultTheme.xaml" />
     <AvaloniaResource Include="Accents/*.xaml" />
     <!-- Compatibility with old apps, probably need to replace with AvaloniaResource -->

+ 1 - 0
src/Avalonia.Themes.Default/DefaultTheme.xaml

@@ -62,4 +62,5 @@
   <StyleInclude Source="resm:Avalonia.Themes.Default.TimePicker.xaml?assembly=Avalonia.Themes.Default"/>
   <StyleInclude Source="resm:Avalonia.Themes.Default.FlyoutPresenter.xaml?assembly=Avalonia.Themes.Default"/>
   <StyleInclude Source="resm:Avalonia.Themes.Default.MenuFlyoutPresenter.xaml?assembly=Avalonia.Themes.Default"/>
+  <StyleInclude Source="resm:Avalonia.Themes.Default.ManagedFileChooser.xaml?assembly=Avalonia.Themes.Default"/>
 </Styles>

+ 153 - 0
src/Avalonia.Themes.Default/ManagedFileChooser.xaml

@@ -0,0 +1,153 @@
+<Styles xmlns="https://github.com/avaloniaui"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+        xmlns:dialogs="clr-namespace:Avalonia.Dialogs;assembly=Avalonia.Dialogs">
+  <Style Selector="dialogs|ManagedFileChooser">
+    <Style.Resources>
+        <ResourceDictionary>
+            <DrawingGroup x:Key="LevelUp">
+                <GeometryDrawing Brush="#00FFFFFF" Geometry="F1M16,16L0,16 0,0 16,0z" />
+                <GeometryDrawing Brush="#FFF6F6F6" Geometry="F1M14.5,0L6.39,0 5.39,2 2.504,2C1.677,2,1,2.673,1,3.5L1,10.582 1,10.586 1,15.414 3,13.414 3,16 7,16 7,13.414 9,15.414 9,13 14.5,13C15.327,13,16,12.327,16,11.5L16,1.5C16,0.673,15.327,0,14.5,0" />
+                <GeometryDrawing Brush="#FFDCB679" Geometry="F1M14,3L7.508,3 8.008,2 8.012,2 14,2z M14.5,1L7.008,1 6.008,3 2.504,3C2.227,3,2,3.224,2,3.5L2,9.582 4.998,6.586 9,10.586 9,12 14.5,12C14.775,12,15,11.776,15,11.5L15,1.5C15,1.224,14.775,1,14.5,1" />
+                <GeometryDrawing Brush="#FF00529C" Geometry="F1M8,11L5,8 2,11 2,13 4,11 4,15 6,15 6,11 8,13z" />
+                <GeometryDrawing Brush="#FFF0EFF1" Geometry="F1M8.0001,1.9996L7.5001,3.0006 14.0001,3.0006 14.0001,1.9996z" />
+            </DrawingGroup>
+            <dialogs:ResourceSelectorConverter x:Key="Icons">
+                <DrawingGroup x:Key="Icon_Folder">
+                    <GeometryDrawing Brush="#00FFFFFF" Geometry="F1M0,0L16,0 16,16 0,16z" />
+                    <GeometryDrawing Brush="#FFF6F6F6" Geometry="F1M1.5,1L9.61,1 10.61,3 13.496,3C14.323,3,14.996,3.673,14.996,4.5L14.996,12.5C14.996,13.327,14.323,14,13.496,14L1.5,14C0.673,14,0,13.327,0,12.5L0,2.5C0,1.673,0.673,1,1.5,1" />
+                    <GeometryDrawing Brush="#FFDCB67A" Geometry="F1M2,3L8.374,3 8.874,4 2,4z M13.496,4L10,4 9.992,4 8.992,2 1.5,2C1.225,2,1,2.224,1,2.5L1,12.5C1,12.776,1.225,13,1.5,13L13.496,13C13.773,13,13.996,12.776,13.996,12.5L13.996,4.5C13.996,4.224,13.773,4,13.496,4" />
+                    <GeometryDrawing Brush="#FFEFEFF0" Geometry="F1M2,3L8.374,3 8.874,4 2,4z" />
+                </DrawingGroup>
+                <DrawingGroup x:Key="Icon_File">
+                    <GeometryDrawing Brush="#00FFFFFF" Geometry="F1M16,16L0,16 0,0 16,0z" />
+                    <GeometryDrawing Brush="#FFF6F6F6" Geometry="F1M4,15C3.03,15,2,14.299,2,13L2,3C2,1.701,3.03,1,4,1L10.061,1 14,4.556 14,13C14,13.97,13.299,15,12,15z" />
+                    <GeometryDrawing Brush="#FF9B4E96" Geometry="F1M12,13L4,13 4,3 9,3 9,6 12,6z M9.641,2L3.964,2C3.964,2,3,2,3,3L3,13C3,14,3.964,14,3.964,14L11.965,14C12.965,14,13,13,13,13L13,5z" />
+                    <GeometryDrawing Brush="#FFF0EFF1" Geometry="F1M4,3L9,3 9,6 12,6 12,13 4,13z" />
+                </DrawingGroup>
+                <DrawingGroup x:Key="Icon_Volume">
+                    <GeometryDrawing Brush="#00FFFFFF" Geometry="F1M16,16L0,16 0,0 16,0z" />
+                    <GeometryDrawing Brush="#FFF6F6F6" Geometry="F1M0,12L0,6.5C0,5.122,1.122,4,2.5,4L13.5,4C14.879,4,16,5.122,16,6.5L16,12z" />
+                    <GeometryDrawing Brush="#FFEFEFF0" Geometry="F1M13,8L12,8 12,7 13,7z M11,8L10,8 10,7 11,7z M13.5,6L2.5,6C2.224,6,2,6.224,2,6.5L2,10 14,10 14,6.5C14,6.224,13.775,6,13.5,6" />
+                    <GeometryDrawing Brush="#FF424242" Geometry="F1M13,7L12,7 12,8 13,8z M11,7L10,7 10,8 11,8z M2,10L14,10 14,6.5C14,6.224,13.775,6,13.5,6L2.5,6C2.224,6,2,6.224,2,6.5z M15,11L1,11 1,6.5C1,5.673,1.673,5,2.5,5L13.5,5C14.327,5,15,5.673,15,6.5z" />
+                </DrawingGroup>
+            </dialogs:ResourceSelectorConverter>
+        </ResourceDictionary>
+    </Style.Resources>
+    <Setter Property="Template">
+      <ControlTemplate>
+        <DockPanel>
+          <DockPanel DockPanel.Dock="Top" Margin="0 0 0 5">
+              <dialogs:ChildFitter DockPanel.Dock="Right" Width="{Binding ElementName=Location, Path=Bounds.Height}">
+                  <Button Command="{Binding GoUp}">
+                      <DrawingPresenter Drawing="{StaticResource LevelUp}" Stretch="Fill"/>
+                  </Button>
+              </dialogs:ChildFitter>
+              <TextBox x:Name="Location" Text="{Binding Location}" Margin="0 0 5 0">
+                  <TextBox.KeyBindings>
+                      <KeyBinding Command="{Binding EnterPressed}" Gesture="Enter"/>
+                  </TextBox.KeyBindings>
+              </TextBox>
+          </DockPanel>
+          <DockPanel Margin="0 5 0 0"  DockPanel.Dock="Bottom">
+              <StackPanel Orientation="Horizontal" DockPanel.Dock="Left">
+                  <CheckBox IsChecked="{Binding ShowHiddenFiles}">
+                      <TextBlock>Show hidden files</TextBlock>
+                  </CheckBox>
+              </StackPanel>
+              <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Spacing="10">
+                  <StackPanel.Styles>
+                      <Style Selector="Button">
+                          <Setter Property="Margin" Value="4"/>
+                      </Style>
+                  </StackPanel.Styles>
+                  <Button Command="{Binding Ok}">OK</Button>
+                  <Button Command="{Binding Cancel}">Cancel</Button>
+              </StackPanel>
+          </DockPanel>
+
+          <ComboBox DockPanel.Dock="Bottom" 
+                    IsVisible="{Binding ShowFilters}" 
+                    Items="{Binding Filters}"
+                    SelectedItem="{Binding SelectedFilter}"
+                    Margin="0 5 0 0" />
+
+          <TextBox Text="{Binding FileName}" Watermark="File name" DockPanel.Dock="Bottom" IsVisible="{Binding !SelectingFolder}" />
+
+          <ListBox Margin="0 0 5 5" BorderBrush="Transparent" x:Name="QuickLinks" Items="{Binding QuickLinks}"
+                  SelectedIndex="{Binding QuickLinksSelectedIndex}"
+                  DockPanel.Dock="Left" Focusable="False">
+              <ListBox.ItemTemplate>
+                  <DataTemplate>
+                      <StackPanel Spacing="4" Orientation="Horizontal" Background="Transparent">
+                          <DrawingPresenter Width="16" Height="16" Drawing="{Binding IconKey, Converter={StaticResource Icons}}"/>
+                          <TextBlock Text="{Binding DisplayName}"/>
+                      </StackPanel>
+                  </DataTemplate>
+              </ListBox.ItemTemplate>
+          </ListBox>
+          <DockPanel Grid.IsSharedSizeScope="True">
+              <Grid DockPanel.Dock="Top" Margin="15 5 0 0" HorizontalAlignment="Stretch">
+                  <Grid.ColumnDefinitions>
+                      <ColumnDefinition Width="20" SharedSizeGroup="Icon" />
+                      <ColumnDefinition Width="16" SharedSizeGroup="Splitter"  />
+                      <ColumnDefinition Width="400" SharedSizeGroup="Name" />
+                      <ColumnDefinition Width="16" SharedSizeGroup="Splitter" />
+                      <ColumnDefinition Width="200" SharedSizeGroup="Modified" />
+                      <ColumnDefinition Width="16" SharedSizeGroup="Splitter" />
+                      <ColumnDefinition Width="150" SharedSizeGroup="Type" />
+                      <ColumnDefinition Width="16" SharedSizeGroup="Splitter" />
+                      <ColumnDefinition Width="200" SharedSizeGroup="Size" />
+                  </Grid.ColumnDefinitions>
+                  <GridSplitter Grid.Column="1" />
+                  <TextBlock Grid.Column="2" Text="Name" />
+                  <GridSplitter Grid.Column="3" />
+                  <TextBlock Grid.Column="4" Text="Date Modified" />
+                  <GridSplitter Grid.Column="5" />
+                  <TextBlock Grid.Column="6" Text="Type" />
+                  <GridSplitter Grid.Column="7" />
+                  <TextBlock Grid.Column="8" Text="Size" />
+              </Grid>
+              <ListBox x:Name="Files"
+                  VirtualizationMode="Simple"
+                  Items="{Binding Items}"
+                  Margin="0 5"
+                  SelectionMode="{Binding SelectionMode}"
+                  SelectedItems="{Binding SelectedItems}"
+                  ScrollViewer.HorizontalScrollBarVisibility="Disabled">
+                  <ListBox.ItemTemplate>
+                      <DataTemplate>
+                          <Grid Background="Transparent">
+                              <Grid.ColumnDefinitions>
+                                  <ColumnDefinition SharedSizeGroup="Icon" />
+                                  <ColumnDefinition SharedSizeGroup="Splitter"  />
+                                  <ColumnDefinition SharedSizeGroup="Name" />
+                                  <ColumnDefinition SharedSizeGroup="Splitter" />
+                                  <ColumnDefinition SharedSizeGroup="Modified" />
+                                  <ColumnDefinition SharedSizeGroup="Splitter" />
+                                  <ColumnDefinition SharedSizeGroup="Type" />
+                                  <ColumnDefinition SharedSizeGroup="Splitter" />
+                                  <ColumnDefinition SharedSizeGroup="Size" />
+                              </Grid.ColumnDefinitions>
+                              <DrawingPresenter Width="16" Height="16" Drawing="{Binding IconKey, Converter={StaticResource Icons}}"/>
+                              <TextBlock Grid.Column="2" Text="{Binding DisplayName}"/>
+                              <TextBlock Grid.Column="4" Text="{Binding Modified}" />
+                              <TextBlock Grid.Column="6" Text="{Binding Type}" />
+                              <TextBlock Grid.Column="8">
+                                <TextBlock.Text>
+                                    <Binding Path="Size">
+                                        <Binding.Converter>
+                                            <dialogs:FileSizeStringConverter/>
+                                        </Binding.Converter>
+                                    </Binding>
+                                </TextBlock.Text>
+                              </TextBlock>
+                          </Grid>
+                      </DataTemplate>
+                  </ListBox.ItemTemplate>
+              </ListBox>
+          </DockPanel>
+        </DockPanel>
+      </ControlTemplate>
+    </Setter>
+  </Style>
+</Styles>

+ 2 - 1
src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj

@@ -11,7 +11,8 @@
     <ProjectReference Include="..\Avalonia.Interactivity\Avalonia.Interactivity.csproj" />
     <ProjectReference Include="..\Avalonia.Layout\Avalonia.Layout.csproj" />
     <ProjectReference Include="..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
-    <ProjectReference Include="..\Avalonia.Styling\Avalonia.Styling.csproj" />    
+    <ProjectReference Include="..\Avalonia.Styling\Avalonia.Styling.csproj" />  
+    <ProjectReference Include="..\Avalonia.Dialogs\Avalonia.Dialogs.csproj" />
     <AvaloniaResource Include="**/*.xaml" />
     <AvaloniaResource Include="Assets\*" />  
   </ItemGroup>

+ 2 - 0
src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml

@@ -61,4 +61,6 @@
   <StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/TimePicker.xaml"/>  
   <StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/FlyoutPresenter.xaml"/>
   <StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/MenuFlyoutPresenter.xaml"/>
+  <!-- ManagedFileChooser comes last because it uses (and overrides) styles for a multitude of other controls...the dialogs were originally UserControls, after all-->
+  <StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/ManagedFileChooser.xaml"/>
 </Styles>

+ 324 - 0
src/Avalonia.Themes.Fluent/Controls/ManagedFileChooser.xaml

@@ -0,0 +1,324 @@
+<Styles xmlns="https://github.com/avaloniaui"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+        xmlns:dialogs="clr-namespace:Avalonia.Dialogs;assembly=Avalonia.Dialogs">
+    <Style Selector="dialogs|ManagedFileChooser">
+        <Style.Resources>
+            <ResourceDictionary>
+                <GradientStops x:Key="IconRes.FolderBackGradientStops">
+                </GradientStops>
+                <GradientStops x:Key="IconRes.FolderFrontGradientStops">
+                    <GradientStop Offset="0" Color="#FFFFDA6F"/>
+                    <GradientStop Offset="1" Color="#FFFEC326"/>
+                </GradientStops>
+                <DrawingGroup x:Key="LevelUp">
+                    <GeometryDrawing Brush="#00FFFFFF" Geometry="F1M16,16L0,16 0,0 16,0z" />
+                    <GeometryDrawing Brush="#FFF6F6F6" Geometry="F1M14.5,0L6.39,0 5.39,2 2.504,2C1.677,2,1,2.673,1,3.5L1,10.582 1,10.586 1,15.414 3,13.414 3,16 7,16 7,13.414 9,15.414 9,13 14.5,13C15.327,13,16,12.327,16,11.5L16,1.5C16,0.673,15.327,0,14.5,0" />
+                    <GeometryDrawing Brush="#FFDCB679" Geometry="F1M14,3L7.508,3 8.008,2 8.012,2 14,2z M14.5,1L7.008,1 6.008,3 2.504,3C2.227,3,2,3.224,2,3.5L2,9.582 4.998,6.586 9,10.586 9,12 14.5,12C14.775,12,15,11.776,15,11.5L15,1.5C15,1.224,14.775,1,14.5,1" />
+                    <GeometryDrawing Brush="#FF00529C" Geometry="F1M8,11L5,8 2,11 2,13 4,11 4,15 6,15 6,11 8,13z" />
+                    <GeometryDrawing Brush="#FFF0EFF1" Geometry="F1M8.0001,1.9996L7.5001,3.0006 14.0001,3.0006 14.0001,1.9996z" />
+                </DrawingGroup>
+                <dialogs:ResourceSelectorConverter x:Key="Icons">
+                    <DrawingGroup x:Key="Icon_Folder">
+                        <GeometryDrawing Geometry="M 0 0 L 16 16"/>
+                        <GeometryDrawing Geometry="M 0 3 C 0,1 0,1 2,1 L 5 1 C 5.5,1 6,1 6.5,1.5 L 8 3 L 14 3 C 16,3 16,3 16,5
+                        L 16,12 C 16,14 16,14 14,14
+                        L 2,14 C 0,14 0,14 0,12 Z">
+                            <GeometryDrawing.Brush>
+                                <LinearGradientBrush StartPoint="1,4" EndPoint="23,20">
+                                    <GradientStop Offset="0" Color="#FFFFC018"/>
+                                    <GradientStop Offset="1" Color="#FFDFA32D"/>
+                                </LinearGradientBrush>
+                            </GeometryDrawing.Brush>
+                        </GeometryDrawing>
+                        <GeometryDrawing Geometry="M 0 4.5 L 8 4.5 L 8 9 L 0 9 Z">
+                            <GeometryDrawing.Brush>
+                                <LinearGradientBrush StartPoint="0,4.5" EndPoint="0,5">
+                                    <GradientStop Offset="0" Color="#00D7A018"/>
+                                    <GradientStop Offset="1" Color="#7FD7A018"/>
+                                </LinearGradientBrush>
+                            </GeometryDrawing.Brush>
+                        </GeometryDrawing>
+                        <GeometryDrawing Geometry="M 0 9 C 0,5 0,5 2,5 L 5 5 C 5.5,5 6,5 6.5,4.75 L 8 4 L 14 4 C 16,4 16,4 16,6
+                                                    L 16,11 C 16,13 16,13 14,13
+                                                    L 2,13 C 0,13 0,13 0,11 Z">
+                            <GeometryDrawing.Brush>
+                                <LinearGradientBrush StartPoint="1,6" EndPoint="23,19">
+                                    <GradientStop Offset="0" Color="#FFFFE69D"/>
+                                    <GradientStop Offset="1" Color="#FFFFC937"/>
+                                </LinearGradientBrush>
+                            </GeometryDrawing.Brush>
+                        </GeometryDrawing>
+                        <GeometryDrawing Geometry="M 0 9 C 0,5 0,5 2,5 L 5 5 C 5.5,5 6,5 6.5,4.75 L 8 4 L 14 4
+                                                    L 8 4.25 C 6,5.25 5.5,5.25 5.125,5.25 L 2 5.25 C 0,5.25 0,5.25 0,9.25 z" Brush="#7FFFFFFF"/>
+                    </DrawingGroup>
+                    <DrawingGroup x:Key="Icon_File">
+                        <GeometryDrawing Geometry="M 0 0 L 16 16"/>
+                        <GeometryDrawing Geometry="M 2 0 L 10 0 L 14 4 L 14 16 L 2 16 Z" Brush="#FF797774"/>
+                        <GeometryDrawing Geometry="M 3 1 L 9.7 1 L 13 4.3 L 13 15 L 3 15 Z" Brush="#FFFAFAFA"/>
+                        <GeometryDrawing Geometry="L 9 1 L 9 5 L 14 5 L 14 4 L 10 4 L 10 1 Z" Brush="#FF797774"/>
+                    </DrawingGroup>
+                    <DrawingGroup x:Key="Icon_Volume">
+                        <GeometryDrawing Geometry="M 0 0 L 16 16"/>
+                        <GeometryDrawing Geometry="M 4 5 L 12 5 L 14.5 7.5 C 15,8 15,8 15,9 L 1 9 C 1,8 1,8 1.5 7.5 Z" Brush="#FFE1E3E6"/>
+                        <GeometryDrawing Geometry="M 12 5 L 14.5 7.5 C 15,8 15,8 15,9 L 10 9 L 10 5 Z">
+                            <GeometryDrawing.Brush>
+                                <LinearGradientBrush StartPoint="12,5" EndPoint="11.5,5.5">
+                                    <GradientStop Offset="0" Color="#FFCDCFD1"/>
+                                    <GradientStop Offset="1" Color="#00CDCFD1"/>
+                                </LinearGradientBrush>
+                            </GeometryDrawing.Brush>
+                        </GeometryDrawing>
+                        <GeometryDrawing Geometry="M 4 5 L 1.5 7.5 C 1,8 1,8 1,9 L 4 9 L 6 9 L 6 5 Z">
+                            <GeometryDrawing.Brush>
+                                <LinearGradientBrush StartPoint="4,5" EndPoint="4.5,5.5">
+                                    <GradientStop Offset="0" Color="#FFCDCFD1"/>
+                                    <GradientStop Offset="1" Color="#00CDCFD1"/>
+                                </LinearGradientBrush>
+                            </GeometryDrawing.Brush>
+                        </GeometryDrawing>
+                        <GeometryDrawing Geometry="M 1 9 C 1,8 1,8 2,8 L 14 8
+                                                    C 15,8 15,8 15,9 L 15 11
+                                                    C 15,12 15,12 14,12 L 2 12
+                                                    C 1,12 1,12 1,11 Z">
+                            <GeometryDrawing.Brush>
+                                <LinearGradientBrush StartPoint="0,8" EndPoint="0,12">
+                                    <GradientStop Offset="0" Color="#FF737374"/>
+                                    <GradientStop Offset="1" Color="#FFA8A8A8"/>
+                                </LinearGradientBrush>
+                            </GeometryDrawing.Brush>
+                        </GeometryDrawing>
+                        <GeometryDrawing Geometry="M 2 9 C 2,8 2,8 3,8 L 13 8
+                                                    C 14,8 14,8 14,9 L 14 10
+                                                    C 14,11 14,11 13,11 L 3 11
+                                                    C 2,11 2,11 2,10 Z">
+                            <GeometryDrawing.Brush>
+                                <LinearGradientBrush StartPoint="0,8" EndPoint="0,11">
+                                    <GradientStop Offset="0" Color="#FF333333"/>
+                                    <GradientStop Offset="1" Color="#FF5A5A5A"/>
+                                </LinearGradientBrush>
+                            </GeometryDrawing.Brush>
+                        </GeometryDrawing>
+                        <GeometryDrawing>
+                            <GeometryDrawing.Geometry>
+                                <EllipseGeometry Rect="2.5,8.5,2,2"/>
+                            </GeometryDrawing.Geometry>
+                            <GeometryDrawing.Brush>
+                                <RadialGradientBrush GradientOrigin="3.5,9.5" Center="3.5,9.5">
+                                    <GradientStop Offset="0.8" Color="#4001FF01"/>
+                                    <GradientStop Offset="1" Color="#0001FF01"/>
+                                </RadialGradientBrush>
+                            </GeometryDrawing.Brush>
+                        </GeometryDrawing>
+                        <GeometryDrawing>
+                            <GeometryDrawing.Geometry>
+                                <EllipseGeometry Rect="3,9,1,1"/>
+                            </GeometryDrawing.Geometry>
+                            <GeometryDrawing.Brush>
+                                <RadialGradientBrush GradientOrigin="3.5,9.5" Center="3.25,9.75">
+                                    <GradientStop Offset="0" Color="#FFB6FFB6"/>
+                                    <GradientStop Offset="1" Color="#FF01FF01"/>
+                                </RadialGradientBrush>
+                            </GeometryDrawing.Brush>
+                        </GeometryDrawing>
+                        <GeometryDrawing Geometry="M 3.23483495705 9.76516504295 A 0.375,0.375 180 1 0 3.76516504295,9.23483495705 A 0.4375,0.4375 135 0 1 3.23483495705,9.76516504295 Z" Brush="#FF00B300"/>
+                    </DrawingGroup>
+                </dialogs:ResourceSelectorConverter>
+            </ResourceDictionary>
+        </Style.Resources>
+        <Setter Property="Template">
+            <ControlTemplate>
+                <DockPanel>
+                    <ListBox x:Name="QuickLinks" DockPanel.Dock="Left" Items="{Binding QuickLinks}" SelectedIndex="{Binding QuickLinksSelectedIndex}" Focusable="False">
+                        <ListBox.ItemTemplate>
+                            <DataTemplate>
+                                <StackPanel Spacing="4" Orientation="Horizontal" Background="Transparent">
+                                    <DrawingPresenter Width="16" Height="16" Drawing="{Binding IconKey, Converter={StaticResource Icons}}"/>
+                                    <TextBlock Text="{Binding DisplayName}"/>
+                                </StackPanel>
+                            </DataTemplate>
+                        </ListBox.ItemTemplate>
+                    </ListBox>
+                    <DockPanel x:Name="NavBar" DockPanel.Dock="Top" Margin="8,5,8,0" VerticalAlignment="Center">
+                        <Rectangle Fill="{DynamicResource SystemControlHighlightAltBaseMediumLowBrush}" Height="1" Margin="0,5,0,0" DockPanel.Dock="Bottom"/>
+                        <DockPanel Margin="4,0">
+                            <Button Command="{Binding GoUp}" DockPanel.Dock="Left">
+                                <Path Data="M 0 7 L 7 0 L 14 7 M 7 0 L 7 16" Stroke="{Binding $parent[Button].Foreground}" StrokeThickness="1" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,1,0,-1"/>
+                            </Button>
+                            <TextBox x:Name="Location" Text="{Binding Location}">
+                                <TextBox.KeyBindings>
+                                    <KeyBinding Command="{Binding EnterPressed}" Gesture="Enter"/>
+                                </TextBox.KeyBindings>
+                            </TextBox>
+                        </DockPanel>
+                    </DockPanel>
+                    <DockPanel Margin="8,0,8,5" DockPanel.Dock="Bottom">
+                        <Rectangle Fill="{DynamicResource SystemControlHighlightAltBaseMediumLowBrush}" Height="1" Margin="0,0,0,5" DockPanel.Dock="Top"/>
+                        <DockPanel Margin="4,0">
+                            <DockPanel DockPanel.Dock="Top" Margin="0,0,0,4">
+                                <ComboBox DockPanel.Dock="Right"
+                                    IsVisible="{Binding ShowFilters}" 
+                                    Items="{Binding Filters}"
+                                    SelectedItem="{Binding SelectedFilter}" />
+                                <TextBox Text="{Binding FileName}" Watermark="File name" IsVisible="{Binding !SelectingFolder}" />
+                            </DockPanel>
+                            <CheckBox IsChecked="{Binding ShowHiddenFiles}" Content="Show hidden files" DockPanel.Dock="Left"/>
+                            <UniformGrid x:Name="Finalize" HorizontalAlignment="Right" Rows="1">
+                                <Button Command="{Binding Ok}">OK</Button>
+                                <Button Command="{Binding Cancel}">Cancel</Button>
+                            </UniformGrid>
+                        </DockPanel>
+                    </DockPanel>
+
+                    <DockPanel Grid.IsSharedSizeScope="True">
+                        <Grid DockPanel.Dock="Top" Margin="15 5 0 0" HorizontalAlignment="Stretch">
+                            <Grid.ColumnDefinitions>
+                                <ColumnDefinition Width="20" SharedSizeGroup="Icon" />
+                                <ColumnDefinition Width="16" SharedSizeGroup="Splitter"  />
+                                <ColumnDefinition Width="275" SharedSizeGroup="Name" />
+                                <ColumnDefinition Width="16" SharedSizeGroup="Splitter" />
+                                <ColumnDefinition Width="200" SharedSizeGroup="Modified" />
+                                <ColumnDefinition Width="16" SharedSizeGroup="Splitter" />
+                                <ColumnDefinition Width="150" SharedSizeGroup="Type" />
+                                <ColumnDefinition Width="16" SharedSizeGroup="Splitter" />
+                                <ColumnDefinition Width="200" SharedSizeGroup="Size" />
+                            </Grid.ColumnDefinitions>
+                            <Grid.Styles>
+                                <Style Selector="GridSplitter">
+                                    <Setter Property="Background" Value="{DynamicResource SystemControlHighlightAltBaseMediumLowBrush}"/>
+                                    <Setter Property="Template">
+                                        <ControlTemplate>
+                                            <Border VerticalAlignment="Stretch" BorderThickness="0" Background="#01000000">
+                                                <Rectangle Width="1" VerticalAlignment="Stretch" Fill="{TemplateBinding Background}"/>
+                                            </Border>
+                                        </ControlTemplate>
+                                    </Setter>
+                                </Style>
+                            </Grid.Styles>
+                            <GridSplitter Grid.Column="1" />
+                            <TextBlock Grid.Column="2" Text="Name" />
+                            <GridSplitter Grid.Column="3" />
+                            <TextBlock Grid.Column="4" Text="Date Modified" />
+                            <GridSplitter Grid.Column="5" />
+                            <TextBlock Grid.Column="6" Text="Type" />
+                            <GridSplitter Grid.Column="7" />
+                            <TextBlock Grid.Column="8" Text="Size" />
+                        </Grid>
+                        <ListBox x:Name="Files"
+                            VirtualizationMode="Simple"
+                            Items="{Binding Items}"
+                            Margin="0 5"
+                            SelectionMode="{Binding SelectionMode}"
+                            SelectedItems="{Binding SelectedItems}"
+                            Background="Transparent"
+                            ScrollViewer.HorizontalScrollBarVisibility="Disabled">
+                            <ListBox.ItemTemplate>
+                                <DataTemplate>
+                                    <Grid Background="Transparent">
+                                        <Grid.ColumnDefinitions>
+                                            <ColumnDefinition SharedSizeGroup="Icon" />
+                                            <ColumnDefinition SharedSizeGroup="Splitter"  />
+                                            <ColumnDefinition SharedSizeGroup="Name" />
+                                            <ColumnDefinition SharedSizeGroup="Splitter" />
+                                            <ColumnDefinition SharedSizeGroup="Modified" />
+                                            <ColumnDefinition SharedSizeGroup="Splitter" />
+                                            <ColumnDefinition SharedSizeGroup="Type" />
+                                            <ColumnDefinition SharedSizeGroup="Splitter" />
+                                            <ColumnDefinition SharedSizeGroup="Size" />
+                                        </Grid.ColumnDefinitions>
+                                        <DrawingPresenter Width="16" Height="16" Drawing="{Binding IconKey, Converter={StaticResource Icons}}"/>
+                                        <TextBlock Grid.Column="2" Text="{Binding DisplayName}"/>
+                                        <TextBlock Grid.Column="4" Text="{Binding Modified}" />
+                                        <TextBlock Grid.Column="6" Text="{Binding Type}" />
+                                        <TextBlock Grid.Column="8">
+                                            <TextBlock.Text>
+                                                <Binding Path="Size">
+                                                    <Binding.Converter>
+                                                        <dialogs:FileSizeStringConverter/>
+                                                    </Binding.Converter>
+                                                </Binding>
+                                            </TextBlock.Text>
+                                        </TextBlock>
+                                    </Grid>
+                                </DataTemplate>
+                            </ListBox.ItemTemplate>
+                        </ListBox>
+                    </DockPanel>
+                </DockPanel>
+            </ControlTemplate>
+        </Setter>
+    </Style>
+
+
+    <Style Selector="dialogs|ManagedFileChooser /template/ ListBox#QuickLinks">
+        <Setter Property="Margin" Value="0"/>
+        <Setter Property="Background" Value="{DynamicResource SystemControlBackgroundChromeMediumBrush}"/>
+        <Setter Property="BorderThickness" Value="0"/>
+        <Setter Property="Width" Value="240"/>
+        <Setter Property="Padding" Value="0,20"/>
+        <Setter Property="Template">
+            <ControlTemplate>
+                <Border Name="border" BoxShadow="inset -6 0 3 -3 #20000000" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
+                    <ScrollViewer Name="PART_ScrollViewer" HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}" VerticalScrollBarVisibility="{TemplateBinding (ScrollViewer.VerticalScrollBarVisibility)}">
+                        <ItemsPresenter Name="PART_ItemsPresenter"
+                                        Items="{TemplateBinding Items}"
+                                        ItemsPanel="{TemplateBinding ItemsPanel}"
+                                        ItemTemplate="{TemplateBinding ItemTemplate}"
+                                        Margin="{TemplateBinding Padding}"
+                                        VirtualizationMode="{TemplateBinding VirtualizationMode}"/>
+                    </ScrollViewer>
+                </Border>
+            </ControlTemplate>
+        </Setter>
+    </Style>
+    <Style Selector="dialogs|ManagedFileChooser /template/ ListBox#QuickLinks ListBoxItem">
+        <Setter Property="Height" Value="32"/>
+        <Setter Property="Padding" Value="30,6"/>
+        <Setter Property="Template">
+            <ControlTemplate>
+                <Border x:Name="LayoutRoot" CornerRadius="2" Margin="10,0">
+                    <Panel>
+                        <Border x:Name="SelectedLine" HorizontalAlignment="Left" Margin="2,6" CornerRadius="0.5" Width="3" Background="{DynamicResource SystemControlHighlightAccentBrush}" IsVisible="{TemplateBinding IsSelected}"/>
+                        <ContentPresenter Name="PART_ContentPresenter"
+                                    Background="Transparent"
+                                    BorderBrush="Transparent"
+                                    BorderThickness="0"
+                                    ContentTemplate="{TemplateBinding ContentTemplate}"
+                                    Content="{TemplateBinding Content}"
+                                    Padding="{TemplateBinding Padding}"
+                                    VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
+                                    HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"/>
+
+                    </Panel>
+                </Border>
+            </ControlTemplate>
+        </Setter>
+    </Style>
+    <Style Selector="dialogs|ManagedFileChooser /template/ ListBox#QuickLinks ListBoxItem:pointerover /template/ Border#LayoutRoot">
+        <Setter Property="Background" Value="{DynamicResource SystemControlBackgroundAltMediumBrush}"/>
+    </Style>
+    <Style Selector="dialogs|ManagedFileChooser /template/ ListBox#QuickLinks ListBoxItem:selected /template/ Border#LayoutRoot">
+        <Setter Property="Background" Value="{DynamicResource SystemControlBackgroundAltMediumHighBrush}"/>
+    </Style>
+    <Style Selector="dialogs|ManagedFileChooser /template/ DockPanel#NavBar Button,
+                    dialogs|ManagedFileChooser /template/ DockPanel#NavBar TextBox">
+        <Setter Property="Height" Value="30"/>
+    </Style>
+    <Style Selector="dialogs|ManagedFileChooser /template/ DockPanel#NavBar Button">
+        <Setter Property="VerticalAlignment" Value="Stretch"/>
+        <Setter Property="Width" Value="40"/>
+        <Setter Property="Margin" Value="0,0,8,0"/>
+        <Setter Property="BorderThickness" Value="0"/>
+    </Style>
+    <Style Selector="dialogs|ManagedFileChooser /template/ DockPanel#NavBar Button:not(:pointerover):not(:pressed)">
+        <Setter Property="Background" Value="Transparent"/>
+    </Style>
+
+    <Style Selector="dialogs|ManagedFileChooser /template/ UniformGrid#Finalize > Button">
+            <Setter Property="HorizontalContentAlignment" Value="Center"/>
+            <Setter Property="HorizontalAlignment" Value="Stretch"/>
+            <Setter Property="Margin" Value="4,0,0,0"/>
+    </Style>
+    <Style Selector="dialogs|ManagedFileChooser /template/ UniformGrid#Finalize > Button /template/ ContentPresenter#PART_ContentPresenter">
+            <Setter Property="HorizontalAlignment" Value="Stretch"/>
+    </Style>
+</Styles>

+ 4 - 2
src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs

@@ -305,9 +305,11 @@ namespace Avalonia.Media.TextFormatting
         /// <param name="height">The current height.</param>
         private static void UpdateBounds(TextLine textLine, ref double width, ref double height)
         {
-            if (width < textLine.Width)
+            var lineWidth = textLine.Width + textLine.Start * 2;
+            
+            if (width < lineWidth)
             {
-                width = textLine.Width;
+                width = lineWidth;
             }
 
             height += textLine.Height;

+ 49 - 0
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_BatchUpdate.cs

@@ -5,6 +5,7 @@ using System.Reactive.Disposables;
 using System.Reactive.Linq;
 using System.Text;
 using Avalonia.Data;
+using Avalonia.Layout;
 using Xunit;
 
 namespace Avalonia.Base.UnitTests
@@ -104,6 +105,25 @@ namespace Avalonia.Base.UnitTests
             Assert.Equal("baz", target.Foo);
         }
 
+        [Fact]
+        public void SetValue_Change_Should_Be_Raised_After_Batch_Update_3()
+        {
+            var target = new TestClass();
+            var raised = new List<AvaloniaPropertyChangedEventArgs>();
+
+            target.PropertyChanged += (s, e) => raised.Add(e);
+
+            target.BeginBatchUpdate();
+            target.SetValue(TestClass.BazProperty, Orientation.Horizontal, BindingPriority.LocalValue);
+            target.EndBatchUpdate();
+
+            Assert.Equal(1, raised.Count);
+            Assert.Equal(TestClass.BazProperty, raised[0].Property);
+            Assert.Equal(Orientation.Vertical, raised[0].OldValue);
+            Assert.Equal(Orientation.Horizontal, raised[0].NewValue);
+            Assert.Equal(Orientation.Horizontal, target.Baz);
+        }
+
         [Fact]
         public void SetValue_Changes_Should_Be_Raised_In_Correct_Order_After_Batch_Update()
         {
@@ -234,6 +254,26 @@ namespace Avalonia.Base.UnitTests
             Assert.Equal("baz", raised[0].NewValue);
         }
 
+        [Fact]
+        public void Binding_Change_Should_Be_Raised_After_Batch_Update_3()
+        {
+            var target = new TestClass();
+            var observable = new TestObservable<Orientation>(Orientation.Horizontal);
+            var raised = new List<AvaloniaPropertyChangedEventArgs>();
+
+            target.PropertyChanged += (s, e) => raised.Add(e);
+
+            target.BeginBatchUpdate();
+            target.Bind(TestClass.BazProperty, observable, BindingPriority.LocalValue);
+            target.EndBatchUpdate();
+
+            Assert.Equal(1, raised.Count);
+            Assert.Equal(TestClass.BazProperty, raised[0].Property);
+            Assert.Equal(Orientation.Vertical, raised[0].OldValue);
+            Assert.Equal(Orientation.Horizontal, raised[0].NewValue);
+            Assert.Equal(Orientation.Horizontal, target.Baz);
+        }
+
         [Fact]
         public void Binding_Completion_Should_Be_Raised_After_Batch_Update()
         {
@@ -579,6 +619,9 @@ namespace Avalonia.Base.UnitTests
             public static readonly StyledProperty<string> BarProperty =
                 AvaloniaProperty.Register<TestClass, string>(nameof(Bar));
 
+            public static readonly StyledProperty<Orientation> BazProperty =
+                AvaloniaProperty.Register<TestClass, Orientation>(nameof(Bar), Orientation.Vertical);
+
             public string Foo
             {
                 get => GetValue(FooProperty);
@@ -590,6 +633,12 @@ namespace Avalonia.Base.UnitTests
                 get => GetValue(BarProperty);
                 set => SetValue(BarProperty, value);
             }
+
+            public Orientation Baz
+            {
+                get => GetValue(BazProperty);
+                set => SetValue(BazProperty, value);
+            }
         }
 
         public class TestObservable<T> : ObservableBase<BindingValue<T>>

+ 123 - 0
tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs

@@ -379,6 +379,17 @@ namespace Avalonia.Controls.UnitTests
             Assert.DoesNotContain(":empty", target.Classes);
         }
 
+        [Fact]
+        public void Empty_Class_Should_Be_Set_When_Items_Not_Set()
+        {
+            var target = new ItemsControl()
+            {
+                Template = GetTemplate(),
+            };
+
+            Assert.Contains(":empty", target.Classes);
+        }
+
         [Fact]
         public void Empty_Class_Should_Be_Set_When_Empty_Collection_Set()
         {
@@ -393,6 +404,118 @@ namespace Avalonia.Controls.UnitTests
             Assert.Contains(":empty", target.Classes);
         }
 
+        [Fact]
+        public void Item_Count_Should_Be_Set_When_Items_Added()
+        {
+            var target = new ItemsControl()
+            {
+                Template = GetTemplate(),
+                Items = new[] { 1, 2, 3 },
+            };
+
+            Assert.Equal(3, target.ItemCount);
+        }
+
+        [Fact]
+        public void Item_Count_Should_Be_Set_When_Items_Changed()
+        {
+            var items = new ObservableCollection<int>() { 1, 2, 3 };
+
+            var target = new ItemsControl()
+            {
+                Template = GetTemplate(),
+                Items = items,
+            };
+
+            items.Add(4);
+
+            Assert.Equal(4, target.ItemCount);
+
+            items.Clear();
+
+            Assert.Equal(0, target.ItemCount);
+        }
+
+        [Fact]
+        public void Empty_Class_Should_Be_Set_When_Items_Collection_Cleared()
+        {
+            var items = new ObservableCollection<int>() { 1, 2, 3 };
+
+            var target = new ItemsControl()
+            {
+                Template = GetTemplate(),
+                Items = items,
+            };
+
+            items.Clear();
+
+            Assert.Contains(":empty", target.Classes);
+        }
+
+        [Fact]
+        public void Empty_Class_Should_Not_Be_Set_When_Items_Collection_Count_Increases()
+        {
+            var items = new ObservableCollection<int>() { };
+
+            var target = new ItemsControl()
+            {
+                Template = GetTemplate(),
+                Items = items,
+            };
+
+            items.Add(1);
+
+            Assert.DoesNotContain(":empty", target.Classes);
+        }
+
+        [Fact]
+        public void Single_Item_Class_Should_Be_Set_When_Items_Collection_Count_Increases_To_One()
+        {
+            var items = new ObservableCollection<int>() { };
+
+            var target = new ItemsControl()
+            {
+                Template = GetTemplate(),
+                Items = items,
+            };
+
+            items.Add(1);
+
+            Assert.Contains(":singleitem", target.Classes);
+        }
+
+        [Fact]
+        public void Empty_Class_Should_Not_Be_Set_When_Items_Collection_Cleared()
+        {
+            var items = new ObservableCollection<int>() { 1, 2, 3 };
+
+            var target = new ItemsControl()
+            {
+                Template = GetTemplate(),
+                Items = items,
+            };
+
+            items.Clear();
+
+            Assert.DoesNotContain(":singleitem", target.Classes);
+        }
+
+        [Fact]
+        public void Single_Item_Class_Should_Not_Be_Set_When_Items_Collection_Count_Increases_Beyond_One()
+        {
+            var items = new ObservableCollection<int>() { 1 };
+
+            var target = new ItemsControl()
+            {
+                Template = GetTemplate(),
+                Items = items,
+            };
+
+            items.Add(2);
+
+            Assert.DoesNotContain(":singleitem", target.Classes);
+        }
+
         [Fact]
         public void Setting_Presenter_Explicitly_Should_Set_Item_Parent()
         {

+ 1 - 1
tests/Avalonia.Controls.UnitTests/TextBoxTests.cs

@@ -378,7 +378,7 @@ namespace Avalonia.Controls.UnitTests
 
         [Theory]
         [InlineData(new object[] { false, TextWrapping.NoWrap, ScrollBarVisibility.Hidden })]
-        [InlineData(new object[] { false, TextWrapping.Wrap, ScrollBarVisibility.Hidden })]
+        [InlineData(new object[] { false, TextWrapping.Wrap, ScrollBarVisibility.Disabled })]
         [InlineData(new object[] { true, TextWrapping.NoWrap, ScrollBarVisibility.Auto })]
         [InlineData(new object[] { true, TextWrapping.Wrap, ScrollBarVisibility.Disabled })]
         public void Has_Correct_Horizontal_ScrollBar_Visibility(