diff --git a/samples/ControlCatalog/Pages/ContextMenuPage.xaml b/samples/ControlCatalog/Pages/ContextMenuPage.xaml index 260162ddb9..25f305151d 100644 --- a/samples/ControlCatalog/Pages/ContextMenuPage.xaml +++ b/samples/ControlCatalog/Pages/ContextMenuPage.xaml @@ -31,6 +31,7 @@ + diff --git a/src/Avalonia.Base/Data/Optional.cs b/src/Avalonia.Base/Data/Optional.cs index 8e044d7896..9dec399e35 100644 --- a/src/Avalonia.Base/Data/Optional.cs +++ b/src/Avalonia.Base/Data/Optional.cs @@ -153,4 +153,18 @@ namespace Avalonia.Data /// public static Optional Empty => default; } + + public static class OptionalExtensions + { + /// + /// Casts the type of an using only the C# cast operator. + /// + /// The target type. + /// The binding value. + /// The cast value. + public static Optional Cast(this Optional value) + { + return value.HasValue ? new Optional((T)value.Value) : Optional.Empty; + } + } } diff --git a/src/Avalonia.Base/PropertyStore/BindingEntry.cs b/src/Avalonia.Base/PropertyStore/BindingEntry.cs index 3e17a81dd8..1b29338f07 100644 --- a/src/Avalonia.Base/PropertyStore/BindingEntry.cs +++ b/src/Avalonia.Base/PropertyStore/BindingEntry.cs @@ -127,8 +127,8 @@ namespace Avalonia.PropertyStore sink.ValueChanged(new AvaloniaPropertyChangedEventArgs( owner, (AvaloniaProperty)property, - oldValue.GetValueOrDefault(), - newValue.GetValueOrDefault(), + oldValue.Cast(), + newValue.Cast(), Priority)); } diff --git a/src/Avalonia.Base/PropertyStore/ConstantValueEntry.cs b/src/Avalonia.Base/PropertyStore/ConstantValueEntry.cs index d39fc3bb1e..dc4a1d88c1 100644 --- a/src/Avalonia.Base/PropertyStore/ConstantValueEntry.cs +++ b/src/Avalonia.Base/PropertyStore/ConstantValueEntry.cs @@ -65,8 +65,8 @@ namespace Avalonia.PropertyStore sink.ValueChanged(new AvaloniaPropertyChangedEventArgs( owner, (AvaloniaProperty)property, - oldValue.GetValueOrDefault(), - newValue.GetValueOrDefault(), + oldValue.Cast(), + newValue.Cast(), Priority)); } } diff --git a/src/Avalonia.Base/PropertyStore/LocalValueEntry.cs b/src/Avalonia.Base/PropertyStore/LocalValueEntry.cs index f49b74f4a8..8fe2ad7794 100644 --- a/src/Avalonia.Base/PropertyStore/LocalValueEntry.cs +++ b/src/Avalonia.Base/PropertyStore/LocalValueEntry.cs @@ -36,8 +36,8 @@ namespace Avalonia.PropertyStore sink.ValueChanged(new AvaloniaPropertyChangedEventArgs( owner, (AvaloniaProperty)property, - oldValue.GetValueOrDefault(), - newValue.GetValueOrDefault(), + oldValue.Cast(), + newValue.Cast(), BindingPriority.LocalValue)); } } diff --git a/src/Avalonia.Base/PropertyStore/PriorityValue.cs b/src/Avalonia.Base/PropertyStore/PriorityValue.cs index 80496fc045..556f1a6269 100644 --- a/src/Avalonia.Base/PropertyStore/PriorityValue.cs +++ b/src/Avalonia.Base/PropertyStore/PriorityValue.cs @@ -197,8 +197,8 @@ namespace Avalonia.PropertyStore sink.ValueChanged(new AvaloniaPropertyChangedEventArgs( owner, (AvaloniaProperty)property, - oldValue.GetValueOrDefault(), - newValue.GetValueOrDefault(), + oldValue.Cast(), + newValue.Cast(), Priority)); } diff --git a/src/Avalonia.Controls.DataGrid/Collections/DataGridSortDescription.cs b/src/Avalonia.Controls.DataGrid/Collections/DataGridSortDescription.cs index 662ff91329..ca6020128c 100644 --- a/src/Avalonia.Controls.DataGrid/Collections/DataGridSortDescription.cs +++ b/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 _comparer; + + public IComparer SourceComparer => _innerComparer; + public override IComparer Comparer => _comparer; + public override ListSortDirection Direction => _direction; + public DataGridComparerSortDesctiption(IComparer comparer, ListSortDirection direction) + { + _innerComparer = comparer; + _direction = direction; + _comparer = Comparer.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 diff --git a/src/Avalonia.Controls.DataGrid/DataGrid.cs b/src/Avalonia.Controls.DataGrid/DataGrid.cs index 2604c7a082..a070a1b9d7 100644 --- a/src/Avalonia.Controls.DataGrid/DataGrid.cs +++ b/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); } diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumn.cs b/src/Avalonia.Controls.DataGrid/DataGridColumn.cs index 407d6ff058..9141fb2463 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridColumn.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridColumn.cs @@ -1009,6 +1009,14 @@ namespace Avalonia.Controls get; set; } + /// + /// Holds a Comparer to use for sorting, if not using the default. + /// + public System.Collections.IComparer CustomSortComparer + { + get; + set; + } /// /// 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() + .FirstOrDefault(s => s.SourceComparer == CustomSortComparer); + } + string propertyName = GetSortPropertyName(); return OwningGrid.DataConnection.SortDescriptions.FirstOrDefault(s => s.HasPropertyPath && s.PropertyPath == propertyName); diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs b/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs index 7f8d205949..6f957497cb 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs +++ b/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(); diff --git a/src/Avalonia.Controls/ApiCompatBaseline.txt b/src/Avalonia.Controls/ApiCompatBaseline.txt index 3a6810eed9..7199c15d21 100644 --- a/src/Avalonia.Controls/ApiCompatBaseline.txt +++ b/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 diff --git a/src/Avalonia.Controls/AutoCompleteBox.cs b/src/Avalonia.Controls/AutoCompleteBox.cs index aab6a41890..c656ba6f6c 100644 --- a/src/Avalonia.Controls/AutoCompleteBox.cs +++ b/src/Avalonia.Controls/AutoCompleteBox.cs @@ -31,7 +31,6 @@ namespace Avalonia.Controls /// /// event. /// - [PseudoClasses(":dropdownopen")] public class PopulatedEventArgs : EventArgs { /// @@ -253,6 +252,7 @@ namespace Avalonia.Controls /// drop-down that contains possible matches based on the input in the text /// box. /// + [PseudoClasses(":dropdownopen")] public class AutoCompleteBox : TemplatedControl { /// diff --git a/src/Avalonia.Controls/IMenuItem.cs b/src/Avalonia.Controls/IMenuItem.cs index 94d761f725..d92625218a 100644 --- a/src/Avalonia.Controls/IMenuItem.cs +++ b/src/Avalonia.Controls/IMenuItem.cs @@ -23,6 +23,12 @@ namespace Avalonia.Controls /// bool IsSubMenuOpen { get; set; } + /// + /// Gets or sets a value that indicates the submenu that this is + /// within should not close when this item is clicked. + /// + bool StaysOpenOnClick { get; set; } + /// /// Gets a value that indicates whether the is a top-level main menu item. /// diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs index 4dc8aec6f3..20d032f597 100644 --- a/src/Avalonia.Controls/ItemsControl.cs +++ b/src/Avalonia.Controls/ItemsControl.cs @@ -70,7 +70,7 @@ namespace Avalonia.Controls /// public ItemsControl() { - PseudoClasses.Add(":empty"); + UpdatePseudoClasses(0); SubscribeToItems(_items); } @@ -323,6 +323,16 @@ namespace Avalonia.Controls base.OnKeyDown(e); } + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == ItemCountProperty) + { + UpdatePseudoClasses(change.NewValue.GetValueOrDefault()); + } + } + /// /// Called when the property changes. /// @@ -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); } /// @@ -431,9 +437,6 @@ namespace Avalonia.Controls /// The items collection. 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, diff --git a/src/Avalonia.Controls/MenuItem.cs b/src/Avalonia.Controls/MenuItem.cs index 94099a970e..4c801c2e06 100644 --- a/src/Avalonia.Controls/MenuItem.cs +++ b/src/Avalonia.Controls/MenuItem.cs @@ -69,6 +69,12 @@ namespace Avalonia.Controls public static readonly StyledProperty IsSubMenuOpenProperty = AvaloniaProperty.Register(nameof(IsSubMenuOpen)); + /// + /// Defines the property. + /// + public static readonly StyledProperty StaysOpenOnClickProperty = + AvaloniaProperty.Register(nameof(StaysOpenOnClick)); + /// /// Defines the event. /// @@ -265,6 +271,16 @@ namespace Avalonia.Controls set { SetValue(IsSubMenuOpenProperty, value); } } + /// + /// Gets or sets a value that indicates the submenu that this is + /// within should not close when this item is clicked. + /// + public bool StaysOpenOnClick + { + get { return GetValue(StaysOpenOnClickProperty); } + set { SetValue(StaysOpenOnClickProperty, value); } + } + /// /// Gets or sets a value that indicates whether the has a submenu. /// diff --git a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs index e3e9e84d7e..984faa4d60 100644 --- a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs +++ b/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) diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 158f3ac886..69da667011 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/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, diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs index ef427b0cd9..a4c9392fa0 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs @@ -305,9 +305,11 @@ namespace Avalonia.Media.TextFormatting /// The current height. 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; diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_BatchUpdate.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_BatchUpdate.cs index 53ad87421e..01d5752ead 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_BatchUpdate.cs +++ b/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(); + + 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.Horizontal); + var raised = new List(); + + 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 BarProperty = AvaloniaProperty.Register(nameof(Bar)); + public static readonly StyledProperty BazProperty = + AvaloniaProperty.Register(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 : ObservableBase> diff --git a/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs index 684486cbae..bfece7871c 100644 --- a/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs +++ b/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() { 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() { 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() { }; + + 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() { }; + + 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() { 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() { 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() { diff --git a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs index 1eec1d84c1..64c0020f92 100644 --- a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs +++ b/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(