Browse Source

Merge branch 'master' into fixes/macCaretPosition

pull/11021/head
Benedikt Stebner 4 years ago
committed by GitHub
parent
commit
a17aa5253f
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      .ncrunch/SafeAreaDemo.Android.v3.ncrunchproject
  2. 5
      .ncrunch/SafeAreaDemo.Desktop.v3.ncrunchproject
  3. 5
      .ncrunch/SafeAreaDemo.iOS.v3.ncrunchproject
  4. 5
      .ncrunch/SafeAreaDemo.v3.ncrunchproject
  5. 4
      samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs
  6. 2
      samples/ControlCatalog/Pages/ContextFlyoutPage.xaml
  7. 2
      samples/ControlCatalog/Pages/DataGridPage.xaml
  8. 4
      samples/ControlCatalog/Pages/DataGridPage.xaml.cs
  9. 4
      samples/ControlCatalog/Pages/DialogsPage.xaml
  10. 2
      samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml
  11. 27
      src/Avalonia.Controls.DataGrid/DataGrid.cs
  12. 2
      src/Avalonia.Controls.DataGrid/DataGridBoundColumn.cs
  13. 4
      src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs
  14. 4
      src/Avalonia.Controls.DataGrid/DataGridTemplateColumn.cs
  15. 22
      src/Avalonia.Controls.ItemsRepeater/Controls/ItemsRepeater.cs
  16. 36
      src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs
  17. 26
      src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs
  18. 3
      src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.cs
  19. 34
      src/Avalonia.Controls/Flyouts/MenuFlyout.cs
  20. 67
      src/Avalonia.Controls/ItemsControl.cs
  21. 2
      src/Avalonia.Controls/ItemsSourceView.cs
  22. 4
      src/Avalonia.Controls/RadioButton.cs
  23. 4
      src/Avalonia.Controls/Templates/FuncControlTemplate.cs
  24. 18
      src/Avalonia.Controls/Templates/IControlTemplate.cs
  25. 2
      src/Avalonia.Controls/Utils/ISelectionAdapter.cs
  26. 2
      src/Avalonia.Controls/Utils/SelectingItemsControlSelectionAdapter.cs
  27. 2
      src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml
  28. 8
      src/Avalonia.Themes.Fluent/Controls/TextBox.xaml
  29. 9
      src/Avalonia.Themes.Simple/Controls/TextBox.xaml
  30. 3
      src/Markup/Avalonia.Markup.Xaml/Templates/ControlTemplate.cs
  31. 2
      src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs
  32. 2
      src/Markup/Avalonia.Markup.Xaml/Templates/ItemsPanelTemplate.cs
  33. 2
      src/Markup/Avalonia.Markup.Xaml/Templates/Template.cs
  34. 5
      src/Markup/Avalonia.Markup.Xaml/Templates/TemplateContent.cs
  35. 2
      src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs
  36. 2
      src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs
  37. 8
      tests/Avalonia.Controls.ItemsRepeater.UnitTests/ItemsRepeaterTests.cs
  38. 10
      tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs
  39. 32
      tests/Avalonia.Controls.UnitTests/ListBoxTests.cs
  40. 2
      tests/Avalonia.Controls.UnitTests/MaskedTextBoxTests.cs
  41. 97
      tests/Avalonia.Controls.UnitTests/MenuItemTests.cs
  42. 2
      tests/Avalonia.Controls.UnitTests/TextBoxTests.cs
  43. 18
      tests/Avalonia.IntegrationTests.Appium/NativeMenuTests.cs
  44. 2
      tests/Avalonia.LeakTests/ControlTests.cs
  45. 2
      tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs
  46. 2
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs
  47. 6
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlTemplateTests.cs
  48. 2
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs

5
.ncrunch/SafeAreaDemo.Android.v3.ncrunchproject

@ -0,0 +1,5 @@
<ProjectConfiguration>
<Settings>
<IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
</Settings>
</ProjectConfiguration>

5
.ncrunch/SafeAreaDemo.Desktop.v3.ncrunchproject

@ -0,0 +1,5 @@
<ProjectConfiguration>
<Settings>
<IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
</Settings>
</ProjectConfiguration>

5
.ncrunch/SafeAreaDemo.iOS.v3.ncrunchproject

@ -0,0 +1,5 @@
<ProjectConfiguration>
<Settings>
<IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
</Settings>
</ProjectConfiguration>

5
.ncrunch/SafeAreaDemo.v3.ncrunchproject

@ -0,0 +1,5 @@
<ProjectConfiguration>
<Settings>
<IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
</Settings>
</ProjectConfiguration>

4
samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs

@ -114,7 +114,7 @@ namespace ControlCatalog.Pages
foreach (AutoCompleteBox box in GetAllAutoCompleteBox().Where(x => x.Name != "CustomAutocompleteBox")) foreach (AutoCompleteBox box in GetAllAutoCompleteBox().Where(x => x.Name != "CustomAutocompleteBox"))
{ {
box.Items = States; box.ItemsSource = States;
} }
var converter = new FuncMultiValueConverter<string, string>(parts => var converter = new FuncMultiValueConverter<string, string>(parts =>
@ -132,7 +132,7 @@ namespace ControlCatalog.Pages
asyncBox.AsyncPopulator = PopulateAsync; asyncBox.AsyncPopulator = PopulateAsync;
var customAutocompleteBox = this.Get<AutoCompleteBox>("CustomAutocompleteBox"); var customAutocompleteBox = this.Get<AutoCompleteBox>("CustomAutocompleteBox");
customAutocompleteBox.Items = Sentences.SelectMany(x => x); customAutocompleteBox.ItemsSource = Sentences.SelectMany(x => x);
customAutocompleteBox.TextFilter = LastWordContains; customAutocompleteBox.TextFilter = LastWordContains;
customAutocompleteBox.TextSelector = AppendWord; customAutocompleteBox.TextSelector = AppendWord;
} }

2
samples/ControlCatalog/Pages/ContextFlyoutPage.xaml

@ -67,7 +67,7 @@
</Style> </Style>
</Border.Styles> </Border.Styles>
<Border.ContextFlyout> <Border.ContextFlyout>
<MenuFlyout Items="{Binding MenuItems}" /> <MenuFlyout ItemsSource="{Binding MenuItems}" />
</Border.ContextFlyout> </Border.ContextFlyout>
<TextBlock Text="Dynamically Generated"/> <TextBlock Text="Dynamically Generated"/>
</Border> </Border>

2
samples/ControlCatalog/Pages/DataGridPage.xaml

@ -94,7 +94,7 @@
<Grid RowDefinitions="*,Auto"> <Grid RowDefinitions="*,Auto">
<!-- Example of columns inheriting the data type from the Items source --> <!-- Example of columns inheriting the data type from the Items source -->
<DataGrid Name="dataGridEdit" Margin="12" Grid.Row="0" <DataGrid Name="dataGridEdit" Margin="12" Grid.Row="0"
Items="{Binding DataGrid3Source}"> ItemsSource="{Binding DataGrid3Source}">
<DataGrid.Columns> <DataGrid.Columns>
<DataGridTextColumn Header="First Name" Binding="{Binding FirstName}" Width="2*" FontSize="{Binding #FontSizeSlider.Value, Mode=OneWay}" /> <DataGridTextColumn Header="First Name" Binding="{Binding FirstName}" Width="2*" FontSize="{Binding #FontSizeSlider.Value, Mode=OneWay}" />
<DataGridTextColumn Header="Last" Binding="{Binding LastName}" Width="2*" FontSize="{Binding #FontSizeSlider.Value, Mode=OneWay}" /> <DataGridTextColumn Header="Last" Binding="{Binding LastName}" Width="2*" FontSize="{Binding #FontSizeSlider.Value, Mode=OneWay}" />

4
samples/ControlCatalog/Pages/DataGridPage.xaml.cs

@ -36,7 +36,7 @@ namespace ControlCatalog.Pages
collectionView1.SortDescriptions.Add(dataGridSortDescription); collectionView1.SortDescriptions.Add(dataGridSortDescription);
} }
}; };
dg1.Items = collectionView1; dg1.ItemsSource = collectionView1;
var dg2 = this.Get<DataGrid>("dataGridGrouping"); var dg2 = this.Get<DataGrid>("dataGridGrouping");
dg2.IsReadOnly = true; dg2.IsReadOnly = true;
@ -44,7 +44,7 @@ namespace ControlCatalog.Pages
var collectionView2 = new DataGridCollectionView(Countries.All); var collectionView2 = new DataGridCollectionView(Countries.All);
collectionView2.GroupDescriptions.Add(new DataGridPathGroupDescription("Region")); collectionView2.GroupDescriptions.Add(new DataGridPathGroupDescription("Region"));
dg2.Items = collectionView2; dg2.ItemsSource = collectionView2;
var dg3 = this.Get<DataGrid>("dataGridEdit"); var dg3 = this.Get<DataGrid>("dataGridEdit");
dg3.IsReadOnly = false; dg3.IsReadOnly = false;

4
samples/ControlCatalog/Pages/DialogsPage.xaml

@ -45,7 +45,7 @@
</Expander> </Expander>
<AutoCompleteBox x:Name="CurrentFolderBox" Watermark="Write full path/uri or well known folder name"> <AutoCompleteBox x:Name="CurrentFolderBox" Watermark="Write full path/uri or well known folder name">
<AutoCompleteBox.Items> <AutoCompleteBox.ItemsSource>
<generic:List x:TypeArguments="storage:WellKnownFolder"> <generic:List x:TypeArguments="storage:WellKnownFolder">
<storage:WellKnownFolder>Desktop</storage:WellKnownFolder> <storage:WellKnownFolder>Desktop</storage:WellKnownFolder>
<storage:WellKnownFolder>Documents</storage:WellKnownFolder> <storage:WellKnownFolder>Documents</storage:WellKnownFolder>
@ -54,7 +54,7 @@
<storage:WellKnownFolder>Videos</storage:WellKnownFolder> <storage:WellKnownFolder>Videos</storage:WellKnownFolder>
<storage:WellKnownFolder>Music</storage:WellKnownFolder> <storage:WellKnownFolder>Music</storage:WellKnownFolder>
</generic:List> </generic:List>
</AutoCompleteBox.Items> </AutoCompleteBox.ItemsSource>
</AutoCompleteBox> </AutoCompleteBox>
<TextBlock x:Name="PickerLastResultsVisible" <TextBlock x:Name="PickerLastResultsVisible"

2
samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml

@ -66,7 +66,7 @@
<ScrollViewer Name="scroller" <ScrollViewer Name="scroller"
HorizontalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto"> VerticalScrollBarVisibility="Auto">
<ItemsRepeater Name="repeater" Background="Transparent" Items="{Binding Items}" <ItemsRepeater Name="repeater" Background="Transparent" ItemsSource="{Binding Items}"
ItemTemplate="{StaticResource elementFactory}"/> ItemTemplate="{StaticResource elementFactory}"/>
</ScrollViewer> </ScrollViewer>
</Border> </Border>

27
src/Avalonia.Controls.DataGrid/DataGrid.cs

@ -152,8 +152,6 @@ namespace Avalonia.Controls
private double _verticalOffset; private double _verticalOffset;
private byte _verticalScrollChangesIgnored; private byte _verticalScrollChangesIgnored;
private IEnumerable _items;
public event EventHandler<ScrollEventArgs> HorizontalScroll; public event EventHandler<ScrollEventArgs> HorizontalScroll;
public event EventHandler<ScrollEventArgs> VerticalScroll; public event EventHandler<ScrollEventArgs> VerticalScroll;
@ -652,21 +650,18 @@ namespace Avalonia.Controls
} }
/// <summary> /// <summary>
/// Identifies the ItemsSource dependency property. /// Identifies the ItemsSource property.
/// </summary> /// </summary>
public static readonly DirectProperty<DataGrid, IEnumerable> ItemsProperty = public static readonly StyledProperty<IEnumerable> ItemsSourceProperty =
AvaloniaProperty.RegisterDirect<DataGrid, IEnumerable>( AvaloniaProperty.Register<DataGrid, IEnumerable>(nameof(ItemsSource));
nameof(Items),
o => o.Items,
(o, v) => o.Items = v);
/// <summary> /// <summary>
/// Gets or sets a collection that is used to generate the content of the control. /// Gets or sets a collection that is used to generate the content of the control.
/// </summary> /// </summary>
public IEnumerable Items public IEnumerable ItemsSource
{ {
get { return _items; } get => GetValue(ItemsSourceProperty);
set { SetAndRaise(ItemsProperty, ref _items, value); } set => SetValue(ItemsSourceProperty, value);
} }
public static readonly StyledProperty<bool> AreRowDetailsFrozenProperty = public static readonly StyledProperty<bool> AreRowDetailsFrozenProperty =
@ -713,7 +708,7 @@ namespace Avalonia.Controls
HorizontalScrollBarVisibilityProperty, HorizontalScrollBarVisibilityProperty,
VerticalScrollBarVisibilityProperty); VerticalScrollBarVisibilityProperty);
ItemsProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnItemsPropertyChanged(e)); ItemsSourceProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnItemsSourcePropertyChanged(e));
CanUserResizeColumnsProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnCanUserResizeColumnsChanged(e)); CanUserResizeColumnsProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnCanUserResizeColumnsChanged(e));
ColumnWidthProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnColumnWidthChanged(e)); ColumnWidthProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnColumnWidthChanged(e));
FrozenColumnCountProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnFrozenColumnCountChanged(e)); FrozenColumnCountProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnFrozenColumnCountChanged(e));
@ -816,10 +811,10 @@ namespace Avalonia.Controls
} }
/// <summary> /// <summary>
/// ItemsProperty property changed handler. /// ItemsSourceProperty property changed handler.
/// </summary> /// </summary>
/// <param name="e">The event arguments.</param> /// <param name="e">The event arguments.</param>
private void OnItemsPropertyChanged(AvaloniaPropertyChangedEventArgs e) private void OnItemsSourcePropertyChanged(AvaloniaPropertyChangedEventArgs e)
{ {
if (!_areHandlersSuspended) if (!_areHandlersSuspended)
{ {
@ -830,7 +825,7 @@ namespace Avalonia.Controls
if (LoadingOrUnloadingRow) if (LoadingOrUnloadingRow)
{ {
SetValueNoCallback(ItemsProperty, oldValue); SetValueNoCallback(ItemsSourceProperty, oldValue);
throw DataGridError.DataGrid.CannotChangeItemsWhenLoadingRows(); throw DataGridError.DataGrid.CannotChangeItemsWhenLoadingRows();
} }
@ -1855,7 +1850,7 @@ namespace Avalonia.Controls
{ {
get get
{ {
if (CurrentSlot == -1 || Items == null || RowGroupHeadersTable.Contains(CurrentSlot)) if (CurrentSlot == -1 || ItemsSource == null || RowGroupHeadersTable.Contains(CurrentSlot))
{ {
return null; return null;
} }

2
src/Avalonia.Controls.DataGrid/DataGridBoundColumn.cs

@ -25,7 +25,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
//TODO Binding //TODO Binding
[AssignBinding] [AssignBinding]
[InheritDataTypeFromItems(nameof(DataGrid.Items), AncestorType = typeof(DataGrid))] [InheritDataTypeFromItems(nameof(DataGrid.ItemsSource), AncestorType = typeof(DataGrid))]
public virtual IBinding Binding public virtual IBinding Binding
{ {
get get

4
src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs

@ -122,9 +122,9 @@ namespace Avalonia.Controls
// We need to use the raw ItemsSource as opposed to DataSource because DataSource // We need to use the raw ItemsSource as opposed to DataSource because DataSource
// may be the ItemsSource wrapped in a collection view, in which case we wouldn't // may be the ItemsSource wrapped in a collection view, in which case we wouldn't
// be able to take T to be the type if we're given IEnumerable<T> // be able to take T to be the type if we're given IEnumerable<T>
if (_dataType == null && _owner.Items != null) if (_dataType == null && _owner.ItemsSource != null)
{ {
_dataType = _owner.Items.GetItemType(); _dataType = _owner.ItemsSource.GetItemType();
} }
return _dataType; return _dataType;
} }

4
src/Avalonia.Controls.DataGrid/DataGridTemplateColumn.cs

@ -24,7 +24,7 @@ namespace Avalonia.Controls
(o, v) => o.CellTemplate = v); (o, v) => o.CellTemplate = v);
[Content] [Content]
[InheritDataTypeFromItems(nameof(DataGrid.Items), AncestorType = typeof(DataGrid))] [InheritDataTypeFromItems(nameof(DataGrid.ItemsSource), AncestorType = typeof(DataGrid))]
public IDataTemplate CellTemplate public IDataTemplate CellTemplate
{ {
get { return _cellTemplate; } get { return _cellTemplate; }
@ -51,7 +51,7 @@ namespace Avalonia.Controls
/// <remarks> /// <remarks>
/// If this property is <see langword="null"/> the column is read-only. /// If this property is <see langword="null"/> the column is read-only.
/// </remarks> /// </remarks>
[InheritDataTypeFromItems(nameof(DataGrid.Items), AncestorType = typeof(DataGrid))] [InheritDataTypeFromItems(nameof(DataGrid.ItemsSource), AncestorType = typeof(DataGrid))]
public IDataTemplate CellEditingTemplate public IDataTemplate CellEditingTemplate
{ {
get => _cellEditingCellTemplate; get => _cellEditingCellTemplate;

22
src/Avalonia.Controls.ItemsRepeater/Controls/ItemsRepeater.cs

@ -36,13 +36,13 @@ namespace Avalonia.Controls
ItemsControl.ItemTemplateProperty.AddOwner<ItemsRepeater>(); ItemsControl.ItemTemplateProperty.AddOwner<ItemsRepeater>();
/// <summary> /// <summary>
/// Defines the <see cref="Items"/> property. /// Defines the <see cref="ItemsSource"/> property.
/// </summary> /// </summary>
public static readonly DirectProperty<ItemsRepeater, IEnumerable?> ItemsProperty = public static readonly DirectProperty<ItemsRepeater, IEnumerable?> ItemsSourceProperty =
AvaloniaProperty.RegisterDirect<ItemsRepeater, IEnumerable?>( AvaloniaProperty.RegisterDirect<ItemsRepeater, IEnumerable?>(
nameof(Items), nameof(ItemsSource),
o => o.Items, o => o.ItemsSource,
(o, v) => o.Items = v); (o, v) => o.ItemsSource = v);
/// <summary> /// <summary>
/// Defines the <see cref="Layout"/> property. /// Defines the <see cref="Layout"/> property.
@ -65,7 +65,7 @@ namespace Avalonia.Controls
private readonly ViewManager _viewManager; private readonly ViewManager _viewManager;
private readonly ViewportManager _viewportManager; private readonly ViewportManager _viewportManager;
private readonly TargetWeakEventSubscriber<ItemsRepeater, EventArgs> _layoutWeakSubscriber; private readonly TargetWeakEventSubscriber<ItemsRepeater, EventArgs> _layoutWeakSubscriber;
private IEnumerable? _items; private IEnumerable? _itemsSource;
private RepeaterLayoutContext? _layoutContext; private RepeaterLayoutContext? _layoutContext;
private EventHandler<ChildIndexChangedEventArgs>? _childIndexChanged; private EventHandler<ChildIndexChangedEventArgs>? _childIndexChanged;
private bool _isLayoutInProgress; private bool _isLayoutInProgress;
@ -116,16 +116,16 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Gets or sets an object source used to generate the content of the ItemsRepeater. /// Gets or sets an object source used to generate the content of the ItemsRepeater.
/// </summary> /// </summary>
public IEnumerable? Items public IEnumerable? ItemsSource
{ {
get => _items; get => _itemsSource;
set => SetAndRaise(ItemsProperty, ref _items, value); set => SetAndRaise(ItemsSourceProperty, ref _itemsSource, value);
} }
/// <summary> /// <summary>
/// Gets or sets the template used to display each item. /// Gets or sets the template used to display each item.
/// </summary> /// </summary>
[InheritDataTypeFromItems(nameof(Items))] [InheritDataTypeFromItems(nameof(ItemsSource))]
public IDataTemplate? ItemTemplate public IDataTemplate? ItemTemplate
{ {
get => GetValue(ItemTemplateProperty); get => GetValue(ItemTemplateProperty);
@ -415,7 +415,7 @@ namespace Avalonia.Controls
/// <inheritdoc /> /// <inheritdoc />
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
if (change.Property == ItemsProperty) if (change.Property == ItemsSourceProperty)
{ {
var (oldEnumerable, newEnumerable) = change.GetOldAndNewValue<IEnumerable?>(); var (oldEnumerable, newEnumerable) = change.GetOldAndNewValue<IEnumerable?>();

36
src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs

@ -143,12 +143,12 @@ namespace Avalonia.Controls
nameof(TextSelector)); nameof(TextSelector));
/// <summary> /// <summary>
/// Identifies the <see cref="Items" /> property. /// Identifies the <see cref="ItemsSource" /> property.
/// </summary> /// </summary>
/// <value>The identifier for the <see cref="Items" /> property.</value> /// <value>The identifier for the <see cref="ItemsSource" /> property.</value>
public static readonly StyledProperty<IEnumerable?> ItemsProperty = public static readonly StyledProperty<IEnumerable?> ItemsSourceProperty =
AvaloniaProperty.Register<AutoCompleteBox, IEnumerable?>( AvaloniaProperty.Register<AutoCompleteBox, IEnumerable?>(
nameof(Items)); nameof(ItemsSource));
/// <summary> /// <summary>
/// Identifies the <see cref="AsyncPopulator" /> property. /// Identifies the <see cref="AsyncPopulator" /> property.
@ -311,10 +311,10 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Gets the text that is used to filter items in the /// Gets the text that is used to filter items in the
/// <see cref="Items" /> item collection. /// <see cref="ItemsSource" /> item collection.
/// </summary> /// </summary>
/// <value>The text that is used to filter items in the /// <value>The text that is used to filter items in the
/// <see cref="Items" /> item collection.</value> /// <see cref="ItemsSource" /> item collection.</value>
/// <remarks> /// <remarks>
/// The SearchText value is typically the same as the /// The SearchText value is typically the same as the
/// Text property, but is set after the TextChanged event occurs /// Text property, but is set after the TextChanged event occurs
@ -339,7 +339,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Gets or sets how the text in the text box is used to filter items /// Gets or sets how the text in the text box is used to filter items
/// specified by the <see cref="Items" /> /// specified by the <see cref="ItemsSource" />
/// property for display in the drop-down. /// property for display in the drop-down.
/// </summary> /// </summary>
/// <value>One of the <see cref="AutoCompleteFilterMode" /> /// <value>One of the <see cref="AutoCompleteFilterMode" />
@ -366,11 +366,11 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Gets or sets the custom method that uses user-entered text to filter /// Gets or sets the custom method that uses user-entered text to filter
/// the items specified by the <see cref="Items" /> /// the items specified by the <see cref="ItemsSource" />
/// property for display in the drop-down. /// property for display in the drop-down.
/// </summary> /// </summary>
/// <value>The custom method that uses the user-entered text to filter /// <value>The custom method that uses the user-entered text to filter
/// the items specified by the <see cref="Items" /> /// the items specified by the <see cref="ItemsSource" />
/// property. The default is null.</value> /// property. The default is null.</value>
/// <remarks> /// <remarks>
/// The filter mode is automatically set to Custom if you set the /// The filter mode is automatically set to Custom if you set the
@ -384,11 +384,11 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Gets or sets the custom method that uses the user-entered text to /// Gets or sets the custom method that uses the user-entered text to
/// filter items specified by the <see cref="Items" /> /// filter items specified by the <see cref="ItemsSource" />
/// property in a text-based way for display in the drop-down. /// property in a text-based way for display in the drop-down.
/// </summary> /// </summary>
/// <value>The custom method that uses the user-entered text to filter /// <value>The custom method that uses the user-entered text to filter
/// items specified by the <see cref="Items" /> /// items specified by the <see cref="ItemsSource" />
/// property in a text-based way for display in the drop-down.</value> /// property in a text-based way for display in the drop-down.</value>
/// <remarks> /// <remarks>
/// The search mode is automatically set to Custom if you set the /// The search mode is automatically set to Custom if you set the
@ -402,11 +402,11 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Gets or sets the custom method that combines the user-entered /// Gets or sets the custom method that combines the user-entered
/// text and one of the items specified by the <see cref="Items" />. /// text and one of the items specified by the <see cref="ItemsSource" />.
/// </summary> /// </summary>
/// <value> /// <value>
/// The custom method that combines the user-entered /// The custom method that combines the user-entered
/// text and one of the items specified by the <see cref="Items" />. /// text and one of the items specified by the <see cref="ItemsSource" />.
/// </value> /// </value>
public AutoCompleteSelector<object>? ItemSelector public AutoCompleteSelector<object>? ItemSelector
{ {
@ -417,11 +417,11 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Gets or sets the custom method that combines the user-entered /// Gets or sets the custom method that combines the user-entered
/// text and one of the items specified by the /// text and one of the items specified by the
/// <see cref="Items" /> in a text-based way. /// <see cref="ItemsSource" /> in a text-based way.
/// </summary> /// </summary>
/// <value> /// <value>
/// The custom method that combines the user-entered /// The custom method that combines the user-entered
/// text and one of the items specified by the <see cref="Items" /> /// text and one of the items specified by the <see cref="ItemsSource" />
/// in a text-based way. /// in a text-based way.
/// </value> /// </value>
public AutoCompleteSelector<string?>? TextSelector public AutoCompleteSelector<string?>? TextSelector
@ -442,10 +442,10 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
/// <value>The collection that is used to generate the items of the /// <value>The collection that is used to generate the items of the
/// drop-down portion of the <see cref="AutoCompleteBox" /> control.</value> /// drop-down portion of the <see cref="AutoCompleteBox" /> control.</value>
public IEnumerable? Items public IEnumerable? ItemsSource
{ {
get => GetValue(ItemsProperty); get => GetValue(ItemsSourceProperty);
set => SetValue(ItemsProperty, value); set => SetValue(ItemsSourceProperty, value);
} }
} }
} }

26
src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs

@ -419,9 +419,9 @@ namespace Avalonia.Controls
/// ItemsSourceProperty property changed handler. /// ItemsSourceProperty property changed handler.
/// </summary> /// </summary>
/// <param name="e">Event arguments.</param> /// <param name="e">Event arguments.</param>
private void OnItemsPropertyChanged(AvaloniaPropertyChangedEventArgs e) private void OnItemsSourcePropertyChanged(AvaloniaPropertyChangedEventArgs e)
{ {
OnItemsChanged((IEnumerable?)e.NewValue); OnItemsSourceChanged((IEnumerable?)e.NewValue);
} }
private void OnItemTemplatePropertyChanged(AvaloniaPropertyChangedEventArgs e) private void OnItemTemplatePropertyChanged(AvaloniaPropertyChangedEventArgs e)
@ -461,7 +461,7 @@ namespace Avalonia.Controls
SearchTextProperty.Changed.AddClassHandler<AutoCompleteBox>((x,e) => x.OnSearchTextPropertyChanged(e)); SearchTextProperty.Changed.AddClassHandler<AutoCompleteBox>((x,e) => x.OnSearchTextPropertyChanged(e));
FilterModeProperty.Changed.AddClassHandler<AutoCompleteBox>((x,e) => x.OnFilterModePropertyChanged(e)); FilterModeProperty.Changed.AddClassHandler<AutoCompleteBox>((x,e) => x.OnFilterModePropertyChanged(e));
ItemFilterProperty.Changed.AddClassHandler<AutoCompleteBox>((x,e) => x.OnItemFilterPropertyChanged(e)); ItemFilterProperty.Changed.AddClassHandler<AutoCompleteBox>((x,e) => x.OnItemFilterPropertyChanged(e));
ItemsProperty.Changed.AddClassHandler<AutoCompleteBox>((x,e) => x.OnItemsPropertyChanged(e)); ItemsSourceProperty.Changed.AddClassHandler<AutoCompleteBox>((x,e) => x.OnItemsSourcePropertyChanged(e));
ItemTemplateProperty.Changed.AddClassHandler<AutoCompleteBox>((x,e) => x.OnItemTemplatePropertyChanged(e)); ItemTemplateProperty.Changed.AddClassHandler<AutoCompleteBox>((x,e) => x.OnItemTemplatePropertyChanged(e));
IsEnabledProperty.Changed.AddClassHandler<AutoCompleteBox>((x,e) => x.OnControlIsEnabledChanged(e)); IsEnabledProperty.Changed.AddClassHandler<AutoCompleteBox>((x,e) => x.OnControlIsEnabledChanged(e));
} }
@ -559,7 +559,7 @@ namespace Avalonia.Controls
_adapter.Commit -= OnAdapterSelectionComplete; _adapter.Commit -= OnAdapterSelectionComplete;
_adapter.Cancel -= OnAdapterSelectionCanceled; _adapter.Cancel -= OnAdapterSelectionCanceled;
_adapter.Cancel -= OnAdapterSelectionComplete; _adapter.Cancel -= OnAdapterSelectionComplete;
_adapter.Items = null; _adapter.ItemsSource = null;
} }
_adapter = value; _adapter = value;
@ -570,7 +570,7 @@ namespace Avalonia.Controls
_adapter.Commit += OnAdapterSelectionComplete; _adapter.Commit += OnAdapterSelectionComplete;
_adapter.Cancel += OnAdapterSelectionCanceled; _adapter.Cancel += OnAdapterSelectionCanceled;
_adapter.Cancel += OnAdapterSelectionComplete; _adapter.Cancel += OnAdapterSelectionComplete;
_adapter.Items = _view; _adapter.ItemsSource = _view;
} }
} }
} }
@ -1128,7 +1128,7 @@ namespace Avalonia.Controls
{ {
if (!cancellationToken.IsCancellationRequested) if (!cancellationToken.IsCancellationRequested)
{ {
SetCurrentValue(ItemsProperty, resultList); SetCurrentValue(ItemsSourceProperty, resultList);
PopulateComplete(); PopulateComplete();
} }
}); });
@ -1475,7 +1475,7 @@ namespace Avalonia.Controls
/// adapter's ItemsSource to the view if appropriate. /// adapter's ItemsSource to the view if appropriate.
/// </summary> /// </summary>
/// <param name="newValue">The new enumerable reference.</param> /// <param name="newValue">The new enumerable reference.</param>
private void OnItemsChanged(IEnumerable? newValue) private void OnItemsSourceChanged(IEnumerable? newValue)
{ {
// Remove handler for oldValue.CollectionChanged (if present) // Remove handler for oldValue.CollectionChanged (if present)
_collectionChangeSubscription?.Dispose(); _collectionChangeSubscription?.Dispose();
@ -1492,9 +1492,9 @@ namespace Avalonia.Controls
// Clear and set the view on the selection adapter // Clear and set the view on the selection adapter
ClearView(); ClearView();
if (SelectionAdapter != null && SelectionAdapter.Items != _view) if (SelectionAdapter != null && SelectionAdapter.ItemsSource != _view)
{ {
SelectionAdapter.Items = _view; SelectionAdapter.ItemsSource = _view;
} }
if (IsDropDownOpen) if (IsDropDownOpen)
{ {
@ -1545,9 +1545,9 @@ namespace Avalonia.Controls
{ {
// Significant changes to the underlying data. // Significant changes to the underlying data.
ClearView(); ClearView();
if (Items != null) if (ItemsSource != null)
{ {
_items = new List<object>(Items.Cast<object>()); _items = new List<object>(ItemsSource.Cast<object>());
} }
} }
@ -1582,9 +1582,9 @@ namespace Avalonia.Controls
PopulatedEventArgs populated = new PopulatedEventArgs(new ReadOnlyCollection<object>(_view!)); PopulatedEventArgs populated = new PopulatedEventArgs(new ReadOnlyCollection<object>(_view!));
OnPopulated(populated); OnPopulated(populated);
if (SelectionAdapter != null && SelectionAdapter.Items != _view) if (SelectionAdapter != null && SelectionAdapter.ItemsSource != _view)
{ {
SelectionAdapter.Items = _view; SelectionAdapter.ItemsSource = _view;
} }
bool isDropDownOpen = _userCalledPopulate && (_view!.Count > 0); bool isDropDownOpen = _userCalledPopulate && (_view!.Count > 0);

3
src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.cs

@ -875,10 +875,11 @@ namespace Avalonia.Controls
{ {
if (_textBox != null) if (_textBox != null)
{ {
SetCurrentValue(TextProperty, String.Empty);
if (string.IsNullOrEmpty(Watermark) && !UseFloatingWatermark) if (string.IsNullOrEmpty(Watermark) && !UseFloatingWatermark)
{ {
DateTimeFormatInfo dtfi = DateTimeHelper.GetCurrentDateFormat(); DateTimeFormatInfo dtfi = DateTimeHelper.GetCurrentDateFormat();
SetCurrentValue(TextProperty, string.Empty);
_defaultText = string.Empty; _defaultText = string.Empty;
var watermarkFormat = "<{0}>"; var watermarkFormat = "<{0}>";
string watermarkText; string watermarkText;

34
src/Avalonia.Controls/Flyouts/MenuFlyout.cs

@ -1,6 +1,5 @@
using System.Collections; using System.Collections;
using System.ComponentModel; using System.ComponentModel;
using Avalonia.Collections;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
using Avalonia.Metadata; using Avalonia.Metadata;
@ -12,17 +11,15 @@ namespace Avalonia.Controls
{ {
public MenuFlyout() public MenuFlyout()
{ {
_items = new AvaloniaList<object>(); Items = new ItemCollection();
} }
/// <summary> /// <summary>
/// Defines the <see cref="Items"/> property /// Defines the <see cref="ItemsSource"/> property
/// </summary> /// </summary>
public static readonly DirectProperty<MenuFlyout, IEnumerable?> ItemsProperty = public static readonly StyledProperty<IEnumerable?> ItemsSourceProperty =
AvaloniaProperty.RegisterDirect<MenuFlyout, IEnumerable?>( AvaloniaProperty.Register<MenuFlyout, IEnumerable?>(
nameof(Items), nameof(ItemsSource));
x => x.Items,
(x, v) => x.Items = v);
/// <summary> /// <summary>
/// Defines the <see cref="ItemTemplate"/> property /// Defines the <see cref="ItemTemplate"/> property
@ -45,14 +42,16 @@ namespace Avalonia.Controls
public Classes FlyoutPresenterClasses => _classes ??= new Classes(); public Classes FlyoutPresenterClasses => _classes ??= new Classes();
[Content]
public ItemCollection Items { get; }
/// <summary> /// <summary>
/// Gets or sets the items of the MenuFlyout /// Gets or sets the items of the MenuFlyout
/// </summary> /// </summary>
[Content] public IEnumerable? ItemsSource
public IEnumerable? Items
{ {
get => _items; get => GetValue(ItemsSourceProperty);
set => SetAndRaise(ItemsProperty, ref _items, value); set => SetValue(ItemsSourceProperty, value);
} }
/// <summary> /// <summary>
@ -83,14 +82,13 @@ namespace Avalonia.Controls
} }
private Classes? _classes; private Classes? _classes;
private IEnumerable? _items;
private IDataTemplate? _itemTemplate; private IDataTemplate? _itemTemplate;
protected override Control CreatePresenter() protected override Control CreatePresenter()
{ {
return new MenuFlyoutPresenter return new MenuFlyoutPresenter
{ {
[!ItemsControl.ItemsSourceProperty] = this[!ItemsProperty], ItemsSource = Items,
[!ItemsControl.ItemTemplateProperty] = this[!ItemTemplateProperty], [!ItemsControl.ItemTemplateProperty] = this[!ItemTemplateProperty],
[!ItemsControl.ItemContainerThemeProperty] = this[!ItemContainerThemeProperty], [!ItemsControl.ItemContainerThemeProperty] = this[!ItemContainerThemeProperty],
}; };
@ -113,5 +111,13 @@ namespace Avalonia.Controls
base.OnOpening(args); base.OnOpening(args);
} }
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (change.Property == ItemsSourceProperty)
Items.SetItemsSource(change.GetNewValue<IEnumerable?>());
}
} }
} }

67
src/Avalonia.Controls/ItemsControl.cs

@ -378,48 +378,48 @@ namespace Avalonia.Controls
if (container is HeaderedContentControl hcc) if (container is HeaderedContentControl hcc)
{ {
hcc.Content = item; SetIfUnset(hcc, HeaderedContentControl.ContentProperty, item);
if (item is IHeadered headered) if (item is IHeadered headered)
hcc.Header = headered.Header; SetIfUnset(hcc, HeaderedContentControl.HeaderProperty, headered.Header);
else if (item is not Visual) else if (item is not Visual)
hcc.Header = item; SetIfUnset(hcc, HeaderedContentControl.HeaderProperty, item);
if (itemTemplate is not null) if (itemTemplate is not null)
hcc.HeaderTemplate = itemTemplate; SetIfUnset(hcc, HeaderedContentControl.HeaderTemplateProperty, itemTemplate);
} }
else if (container is ContentControl cc) else if (container is ContentControl cc)
{ {
cc.Content = item; SetIfUnset(cc, ContentControl.ContentProperty, item);
if (itemTemplate is not null) if (itemTemplate is not null)
cc.ContentTemplate = itemTemplate; SetIfUnset(cc, ContentControl.ContentTemplateProperty, itemTemplate);
} }
else if (container is ContentPresenter p) else if (container is ContentPresenter p)
{ {
p.Content = item; SetIfUnset(p, ContentPresenter.ContentProperty, item);
if (itemTemplate is not null) if (itemTemplate is not null)
p.ContentTemplate = itemTemplate; SetIfUnset(p, ContentPresenter.ContentTemplateProperty, itemTemplate);
} }
else if (container is ItemsControl ic) else if (container is ItemsControl ic)
{ {
if (itemTemplate is not null) if (itemTemplate is not null)
ic.ItemTemplate = itemTemplate; SetIfUnset(ic, ItemTemplateProperty, itemTemplate);
if (ItemContainerTheme is { } ict && !ict.IsSet(ItemContainerThemeProperty)) if (ItemContainerTheme is { } ict)
ic.ItemContainerTheme = ict; SetIfUnset(ic, ItemContainerThemeProperty, ict);
} }
// These conditions are separate because HeaderedItemsControl and // These conditions are separate because HeaderedItemsControl and
// HeaderedSelectingItemsControl also need to run the ItemsControl preparation. // HeaderedSelectingItemsControl also need to run the ItemsControl preparation.
if (container is HeaderedItemsControl hic) if (container is HeaderedItemsControl hic)
{ {
hic.Header = item; SetIfUnset(hic, HeaderedItemsControl.HeaderProperty, item);
hic.HeaderTemplate = itemTemplate; SetIfUnset(hic, HeaderedItemsControl.HeaderTemplateProperty, itemTemplate);
hic.PrepareItemContainer(this); hic.PrepareItemContainer(this);
} }
else if (container is HeaderedSelectingItemsControl hsic) else if (container is HeaderedSelectingItemsControl hsic)
{ {
hsic.Header = item; SetIfUnset(hsic, HeaderedSelectingItemsControl.HeaderProperty, item);
hsic.HeaderTemplate = itemTemplate; SetIfUnset(hsic, HeaderedSelectingItemsControl.HeaderTemplateProperty, itemTemplate);
hsic.PrepareItemContainer(this); hsic.PrepareItemContainer(this);
} }
} }
@ -458,30 +458,35 @@ namespace Avalonia.Controls
{ {
if (container is HeaderedContentControl hcc) if (container is HeaderedContentControl hcc)
{ {
if (hcc.Content is Control) hcc.ClearValue(HeaderedContentControl.ContentProperty);
hcc.Content = null; hcc.ClearValue(HeaderedContentControl.HeaderProperty);
if (hcc.Header is Control) hcc.ClearValue(HeaderedContentControl.HeaderTemplateProperty);
hcc.Header = null;
} }
else if (container is ContentControl cc) else if (container is ContentControl cc)
{ {
if (cc.Content is Control) cc.ClearValue(ContentControl.ContentProperty);
cc.Content = null; cc.ClearValue(ContentControl.ContentTemplateProperty);
} }
else if (container is ContentPresenter p) else if (container is ContentPresenter p)
{ {
if (p.Content is Control) p.ClearValue(ContentPresenter.ContentProperty);
p.Content = null; p.ClearValue(ContentPresenter.ContentTemplateProperty);
} }
else if (container is HeaderedItemsControl hic) else if (container is ItemsControl ic)
{
ic.ClearValue(ItemTemplateProperty);
ic.ClearValue(ItemContainerThemeProperty);
}
if (container is HeaderedItemsControl hic)
{ {
if (hic.Header is Control) hic.ClearValue(HeaderedItemsControl.HeaderProperty);
hic.Header = null; hic.ClearValue(HeaderedItemsControl.HeaderTemplateProperty);
} }
else if (container is HeaderedSelectingItemsControl hsic) else if (container is HeaderedSelectingItemsControl hsic)
{ {
if (hsic.Header is Control) hsic.ClearValue(HeaderedSelectingItemsControl.HeaderProperty);
hsic.Header = null; hsic.ClearValue(HeaderedSelectingItemsControl.HeaderTemplateProperty);
} }
// Feels like we should be clearing the HeaderedItemsControl.Items binding here, but looking at // Feels like we should be clearing the HeaderedItemsControl.Items binding here, but looking at
@ -707,6 +712,12 @@ namespace Avalonia.Controls
LogicalChildren.AddRange(toAdd); LogicalChildren.AddRange(toAdd);
} }
private void SetIfUnset<T>(AvaloniaObject target, StyledProperty<T> property, T value)
{
if (!target.IsSet(property))
target.SetCurrentValue(property, value);
}
private void RemoveControlItemsFromLogicalChildren(IEnumerable? items) private void RemoveControlItemsFromLogicalChildren(IEnumerable? items)
{ {
if (items is null) if (items is null)

2
src/Avalonia.Controls/ItemsSourceView.cs

@ -262,7 +262,7 @@ namespace Avalonia.Controls
_source = source switch _source = source switch
{ {
ItemsSourceView => throw new ArgumentException("Cannot wrap an existing ItemsSourceView.", nameof(source)), ItemsSourceView isv => isv.Source,
IList list => list, IList list => list,
INotifyCollectionChanged => throw new ArgumentException( INotifyCollectionChanged => throw new ArgumentException(
"Collection implements INotifyCollectionChanged but not IList.", "Collection implements INotifyCollectionChanged but not IList.",

4
src/Avalonia.Controls/RadioButton.cs

@ -86,7 +86,7 @@ namespace Avalonia.Controls
continue; continue;
} }
if (current != radioButton && current.IsChecked.GetValueOrDefault()) if (current != radioButton && current.IsChecked.GetValueOrDefault())
current.IsChecked = false; current.SetCurrentValue(IsCheckedProperty, false);
i++; i++;
} }
if (group.Count == 0) if (group.Count == 0)
@ -193,7 +193,7 @@ namespace Avalonia.Controls
foreach (var sibling in siblings) foreach (var sibling in siblings)
{ {
if (sibling.IsChecked.GetValueOrDefault()) if (sibling.IsChecked.GetValueOrDefault())
sibling.IsChecked = false; sibling.SetCurrentValue(IsCheckedProperty, false);
} }
} }
} }

4
src/Avalonia.Controls/Templates/FuncControlTemplate.cs

@ -18,10 +18,10 @@ namespace Avalonia.Controls.Templates
{ {
} }
public new ControlTemplateResult Build(TemplatedControl param) public new TemplateResult<Control> Build(TemplatedControl param)
{ {
var (control, scope) = BuildWithNameScope(param); var (control, scope) = BuildWithNameScope(param);
return new ControlTemplateResult(control, scope); return new(control, scope);
} }
} }
} }

18
src/Avalonia.Controls/Templates/IControlTemplate.cs

@ -5,23 +5,7 @@ namespace Avalonia.Controls.Templates
/// <summary> /// <summary>
/// Interface representing a template used to build a <see cref="TemplatedControl"/>. /// Interface representing a template used to build a <see cref="TemplatedControl"/>.
/// </summary> /// </summary>
public interface IControlTemplate : ITemplate<TemplatedControl, ControlTemplateResult?> public interface IControlTemplate : ITemplate<TemplatedControl, TemplateResult<Control>?>
{ {
} }
public class ControlTemplateResult : TemplateResult<Control>
{
public Control Control { get; }
public ControlTemplateResult(Control control, INameScope nameScope) : base(control, nameScope)
{
Control = control;
}
public new void Deconstruct(out Control control, out INameScope scope)
{
control = Control;
scope = NameScope;
}
}
} }

2
src/Avalonia.Controls/Utils/ISelectionAdapter.cs

@ -36,7 +36,7 @@ namespace Avalonia.Controls.Utils
/// </summary> /// </summary>
/// <value>The collection that is used to generate content for the /// <value>The collection that is used to generate content for the
/// selection adapter.</value> /// selection adapter.</value>
IEnumerable? Items { get; set; } IEnumerable? ItemsSource { get; set; }
/// <summary> /// <summary>
/// Occurs when a selected item is not cancelled and is committed as the /// Occurs when a selected item is not cancelled and is committed as the

2
src/Avalonia.Controls/Utils/SelectingItemsControlSelectionAdapter.cs

@ -140,7 +140,7 @@ namespace Avalonia.Controls.Utils
/// </summary> /// </summary>
/// <value>The collection used to generate content for the selection /// <value>The collection used to generate content for the selection
/// adapter.</value> /// adapter.</value>
public IEnumerable? Items public IEnumerable? ItemsSource
{ {
get get
{ {

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

@ -51,7 +51,7 @@
<DataGrid <DataGrid
x:Name="DataGrid" x:Name="DataGrid"
Items="{Binding PropertiesView}" ItemsSource="{Binding PropertiesView}"
Grid.Row="2" Grid.Row="2"
BorderThickness="0" BorderThickness="0"
RowBackground="Transparent" RowBackground="Transparent"

8
src/Avalonia.Themes.Fluent/Controls/TextBox.xaml

@ -1,4 +1,4 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui" <ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Design.PreviewWith> <Design.PreviewWith>
<Border Padding="20"> <Border Padding="20">
@ -26,12 +26,6 @@
<MenuItem x:Name="TextBoxContextFlyoutPasteItem" Header="Paste" Command="{Binding $parent[TextBox].Paste}" IsEnabled="{Binding $parent[TextBox].CanPaste}" InputGesture="{x:Static TextBox.PasteGesture}"/> <MenuItem x:Name="TextBoxContextFlyoutPasteItem" Header="Paste" Command="{Binding $parent[TextBox].Paste}" IsEnabled="{Binding $parent[TextBox].CanPaste}" InputGesture="{x:Static TextBox.PasteGesture}"/>
</MenuFlyout> </MenuFlyout>
<ContextMenu x:Key="DefaultTextBoxContextMenu" x:Name="TextBoxContextMenu">
<MenuItem x:Name="TextBoxContextMenuCutItem" Header="Cut" Command="{Binding $parent[TextBox].Cut}" IsEnabled="{Binding $parent[TextBox].CanCut}" InputGesture="{x:Static TextBox.CutGesture}" />
<MenuItem x:Name="TextBoxContextMenuCopyItem" Header="Copy" Command="{Binding $parent[TextBox].Copy}" IsEnabled="{Binding $parent[TextBox].CanCopy}" InputGesture="{x:Static TextBox.CopyGesture}"/>
<MenuItem x:Name="TextBoxContextMenuPasteItem" Header="Paste" Command="{Binding $parent[TextBox].Paste}" IsEnabled="{Binding $parent[TextBox].CanPaste}" InputGesture="{x:Static TextBox.PasteGesture}"/>
</ContextMenu>
<ControlTheme x:Key="FluentTextBoxButton" TargetType="Button"> <ControlTheme x:Key="FluentTextBoxButton" TargetType="Button">
<Setter Property="Background" Value="{DynamicResource TextControlButtonBackground}" /> <Setter Property="Background" Value="{DynamicResource TextControlButtonBackground}" />
<Setter Property="BorderBrush" Value="{DynamicResource TextControlButtonBorderBrush}" /> <Setter Property="BorderBrush" Value="{DynamicResource TextControlButtonBorderBrush}" />

9
src/Avalonia.Themes.Simple/Controls/TextBox.xaml

@ -13,15 +13,6 @@
IsEnabled="{Binding $parent[TextBox].CanPaste}" InputGesture="{x:Static TextBox.PasteGesture}" /> IsEnabled="{Binding $parent[TextBox].CanPaste}" InputGesture="{x:Static TextBox.PasteGesture}" />
</MenuFlyout> </MenuFlyout>
<ContextMenu x:Key="SimpleTextBoxContextMenu" x:Name="TextBoxContextMenu">
<MenuItem x:Name="TextBoxContextMenuCutItem" Header="Cut" Command="{Binding $parent[TextBox].Cut}"
IsEnabled="{Binding $parent[TextBox].CanCut}" InputGesture="{x:Static TextBox.CutGesture}" />
<MenuItem x:Name="TextBoxContextMenuCopyItem" Header="Copy" Command="{Binding $parent[TextBox].Copy}"
IsEnabled="{Binding $parent[TextBox].CanCopy}" InputGesture="{x:Static TextBox.CopyGesture}" />
<MenuItem x:Name="TextBoxContextMenuPasteItem" Header="Paste" Command="{Binding $parent[TextBox].Paste}"
IsEnabled="{Binding $parent[TextBox].CanPaste}" InputGesture="{x:Static TextBox.PasteGesture}" />
</ContextMenu>
<ControlTheme x:Key="SimpleTextBoxClearButtonTheme" <ControlTheme x:Key="SimpleTextBoxClearButtonTheme"
BasedOn="{StaticResource {x:Type Button}}" BasedOn="{StaticResource {x:Type Button}}"
TargetType="Button"> TargetType="Button">

3
src/Markup/Avalonia.Markup.Xaml/Templates/ControlTemplate.cs

@ -1,4 +1,5 @@
using System; using System;
using Avalonia.Controls;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
using Avalonia.Metadata; using Avalonia.Metadata;
@ -13,6 +14,6 @@ namespace Avalonia.Markup.Xaml.Templates
public Type? TargetType { get; set; } public Type? TargetType { get; set; }
public ControlTemplateResult? Build(TemplatedControl control) => TemplateContent.Load(Content); public TemplateResult<Control>? Build(TemplatedControl control) => TemplateContent.Load(Content);
} }
} }

2
src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs

@ -30,7 +30,7 @@ namespace Avalonia.Markup.Xaml.Templates
public Control? Build(object? data, Control? existing) public Control? Build(object? data, Control? existing)
{ {
return existing ?? TemplateContent.Load(Content)?.Control; return existing ?? TemplateContent.Load(Content)?.Result;
} }
} }
} }

2
src/Markup/Avalonia.Markup.Xaml/Templates/ItemsPanelTemplate.cs

@ -10,7 +10,7 @@ namespace Avalonia.Markup.Xaml.Templates
[TemplateContent] [TemplateContent]
public object? Content { get; set; } public object? Content { get; set; }
public Panel? Build() => (Panel?)TemplateContent.Load(Content)?.Control; public Panel? Build() => (Panel?)TemplateContent.Load(Content)?.Result;
object? ITemplate.Build() => Build(); object? ITemplate.Build() => Build();
} }

2
src/Markup/Avalonia.Markup.Xaml/Templates/Template.cs

@ -10,7 +10,7 @@ namespace Avalonia.Markup.Xaml.Templates
[TemplateContent] [TemplateContent]
public object? Content { get; set; } public object? Content { get; set; }
public Control? Build() => TemplateContent.Load(Content)?.Control; public Control? Build() => TemplateContent.Load(Content)?.Result;
object? ITemplate.Build() => Build(); object? ITemplate.Build() => Build();
} }

5
src/Markup/Avalonia.Markup.Xaml/Templates/TemplateContent.cs

@ -1,15 +1,16 @@
using System; using System;
using Avalonia.Controls;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
namespace Avalonia.Markup.Xaml.Templates namespace Avalonia.Markup.Xaml.Templates
{ {
public static class TemplateContent public static class TemplateContent
{ {
public static ControlTemplateResult? Load(object? templateContent) public static TemplateResult<Control>? Load(object? templateContent)
{ {
if (templateContent is Func<IServiceProvider?, object?> direct) if (templateContent is Func<IServiceProvider?, object?> direct)
{ {
return (ControlTemplateResult?)direct(null); return (TemplateResult<Control>?)direct(null);
} }
if (templateContent is null) if (templateContent is null)

2
src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs

@ -54,7 +54,7 @@ namespace Avalonia.Markup.Xaml.Templates
public Control? Build(object? data) public Control? Build(object? data)
{ {
var visualTreeForItem = TemplateContent.Load(Content)?.Control; var visualTreeForItem = TemplateContent.Load(Content)?.Result;
if (visualTreeForItem != null) if (visualTreeForItem != null)
{ {
visualTreeForItem.DataContext = data; visualTreeForItem.DataContext = data;

2
src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs

@ -35,7 +35,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime
scope.Complete(); scope.Complete();
if(typeof(T) == typeof(Control)) if(typeof(T) == typeof(Control))
return new ControlTemplateResult((Control)obj, scope); return new TemplateResult<Control>((Control)obj, scope);
return new TemplateResult<T>((T)obj, scope); return new TemplateResult<T>((T)obj, scope);
}; };

8
tests/Avalonia.Controls.ItemsRepeater.UnitTests/ItemsRepeaterTests.cs

@ -9,16 +9,16 @@ namespace Avalonia.Controls.UnitTests
public void Can_Reassign_Items() public void Can_Reassign_Items()
{ {
var target = new ItemsRepeater(); var target = new ItemsRepeater();
target.Items = new ObservableCollection<string>(); target.ItemsSource = new ObservableCollection<string>();
target.Items = new ObservableCollection<string>(); target.ItemsSource = new ObservableCollection<string>();
} }
[Fact] [Fact]
public void Can_Reassign_Items_To_Null() public void Can_Reassign_Items_To_Null()
{ {
var target = new ItemsRepeater(); var target = new ItemsRepeater();
target.Items = new ObservableCollection<string>(); target.ItemsSource = new ObservableCollection<string>();
target.Items = null; target.ItemsSource = null;
} }
} }
} }

10
tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs

@ -89,7 +89,7 @@ namespace Avalonia.Controls.UnitTests
bool closeEvent = false; bool closeEvent = false;
control.DropDownOpened += (s, e) => openEvent = true; control.DropDownOpened += (s, e) => openEvent = true;
control.DropDownClosed += (s, e) => closeEvent = true; control.DropDownClosed += (s, e) => closeEvent = true;
control.Items = CreateSimpleStringArray(); control.ItemsSource = CreateSimpleStringArray();
textbox.Text = "a"; textbox.Text = "a";
Dispatcher.UIThread.RunJobs(); Dispatcher.UIThread.RunJobs();
@ -258,7 +258,7 @@ namespace Avalonia.Controls.UnitTests
control.FilterMode = AutoCompleteFilterMode.None; control.FilterMode = AutoCompleteFilterMode.None;
control.Populating += (s, e) => control.Populating += (s, e) =>
{ {
control.Items = new string[] { custom }; control.ItemsSource = new string[] { custom };
Assert.Equal(search, e.Parameter); Assert.Equal(search, e.Parameter);
}; };
control.Populated += (s, e) => control.Populated += (s, e) =>
@ -380,7 +380,7 @@ namespace Avalonia.Controls.UnitTests
{ {
RunTest((control, textbox) => RunTest((control, textbox) =>
{ {
object selectedItem = control.Items.Cast<object>().First(); object selectedItem = control.ItemsSource.Cast<object>().First();
string input = "42"; string input = "42";
control.TextSelector = (text, item) => text + item; control.TextSelector = (text, item) => text + item;
@ -397,7 +397,7 @@ namespace Avalonia.Controls.UnitTests
{ {
RunTest((control, textbox) => RunTest((control, textbox) =>
{ {
object selectedItem = control.Items.Cast<object>().First(); object selectedItem = control.ItemsSource.Cast<object>().First();
string input = "42"; string input = "42";
control.ItemSelector = (text, item) => text + item; control.ItemSelector = (text, item) => text + item;
@ -1053,7 +1053,7 @@ namespace Avalonia.Controls.UnitTests
using (UnitTestApplication.Start(Services)) using (UnitTestApplication.Start(Services))
{ {
AutoCompleteBox control = CreateControl(); AutoCompleteBox control = CreateControl();
control.Items = CreateSimpleStringArray(); control.ItemsSource = CreateSimpleStringArray();
TextBox textBox = GetTextBox(control); TextBox textBox = GetTextBox(control);
var window = new Window {Content = control}; var window = new Window {Content = control};
window.ApplyStyling(); window.ApplyStyling();

32
tests/Avalonia.Controls.UnitTests/ListBoxTests.cs

@ -554,6 +554,36 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(new[] { "Bar" }, target.Selection.SelectedItems); Assert.Equal(new[] { "Bar" }, target.Selection.SelectedItems);
} }
[Fact]
public void Content_Can_Be_Bound_In_ItemContainerTheme()
{
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
{
var items = new[] { new ItemViewModel("Foo"), new ItemViewModel("Bar") };
var theme = new ControlTheme(typeof(ListBoxItem))
{
Setters =
{
new Setter(ListBoxItem.ContentProperty, new Binding("Caption")),
}
};
var target = new ListBox
{
Template = ListBoxTemplate(),
ItemsSource = items,
ItemContainerTheme = theme,
};
Prepare(target);
var containers = target.GetRealizedContainers().Cast<ListBoxItem>().ToList();
Assert.Equal(2, containers.Count);
Assert.Equal("Foo", containers[0].Content);
Assert.Equal("Bar", containers[1].Content);
}
}
private static FuncControlTemplate ListBoxTemplate() private static FuncControlTemplate ListBoxTemplate()
{ {
return new FuncControlTemplate<ListBox>((parent, scope) => return new FuncControlTemplate<ListBox>((parent, scope) =>
@ -918,6 +948,8 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(1, raised); Assert.Equal(1, raised);
} }
private record ItemViewModel(string Caption);
private class ResettingCollection : List<string>, INotifyCollectionChanged private class ResettingCollection : List<string>, INotifyCollectionChanged
{ {
public ResettingCollection(int itemCount) public ResettingCollection(int itemCount)

2
tests/Avalonia.Controls.UnitTests/MaskedTextBoxTests.cs

@ -72,7 +72,7 @@ namespace Avalonia.Controls.UnitTests
Text = "1234", Text = "1234",
ContextFlyout = new MenuFlyout ContextFlyout = new MenuFlyout
{ {
Items = new List<MenuItem> Items =
{ {
new MenuItem { Header = "Item 1" }, new MenuItem { Header = "Item 1" },
new MenuItem {Header = "Item 2" }, new MenuItem {Header = "Item 2" },

97
tests/Avalonia.Controls.UnitTests/MenuItemTests.cs

@ -1,16 +1,16 @@
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Linq;
using System.Windows.Input; using System.Windows.Input;
using Avalonia.Collections;
using Avalonia.Controls.Presenters; using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
using Avalonia.Controls.Utils;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Styling;
using Avalonia.UnitTests; using Avalonia.UnitTests;
using Avalonia.VisualTree;
using Moq; using Moq;
using Xunit; using Xunit;
@ -36,7 +36,6 @@ namespace Avalonia.Controls.UnitTests
Assert.False(target.Focusable); Assert.False(target.Focusable);
} }
[Fact] [Fact]
public void MenuItem_Is_Disabled_When_Command_Is_Enabled_But_IsEnabled_Is_False() public void MenuItem_Is_Disabled_When_Command_Is_Enabled_But_IsEnabled_Is_False()
{ {
@ -232,7 +231,7 @@ namespace Avalonia.Controls.UnitTests
return true; return true;
}); });
var target = new MenuItem(); var target = new MenuItem();
var flyout = new MenuFlyout { Items = new AvaloniaList<MenuItem> { target } }; var flyout = new MenuFlyout { Items = { target } };
var button = new Button { Flyout = flyout }; var button = new Button { Flyout = flyout };
var window = new Window { Content = button }; var window = new Window { Content = button };
window.ApplyStyling(); window.ApplyStyling();
@ -393,6 +392,87 @@ namespace Avalonia.Controls.UnitTests
} }
} }
[Fact]
public void Header_And_ItemsSource_Can_Be_Bound_In_Style()
{
using var app = Application();
var items = new[]
{
new MenuViewModel("Foo")
{
Children = new[]
{
new MenuViewModel("FooChild"),
},
},
new MenuViewModel("Bar"),
};
var target = new Menu
{
ItemsSource = items,
Styles =
{
new Style(x => x.OfType<MenuItem>())
{
Setters =
{
new Setter(MenuItem.HeaderProperty, new Binding("Header")),
new Setter(MenuItem.ItemsSourceProperty, new Binding("Children")),
}
}
}
};
var root = new TestRoot(true, target);
root.LayoutManager.ExecuteInitialLayoutPass();
var children = target.GetRealizedContainers().Cast<MenuItem>().ToList();
Assert.Equal(2, children.Count);
Assert.Equal("Foo", children[0].Header);
Assert.Equal("Bar", children[1].Header);
Assert.Same(items[0].Children, children[0].ItemsSource);
}
[Fact]
public void Header_And_ItemsSource_Can_Be_Bound_In_ItemContainerTheme()
{
using var app = Application();
var items = new[]
{
new MenuViewModel("Foo")
{
Children = new[]
{
new MenuViewModel("FooChild"),
},
},
new MenuViewModel("Bar"),
};
var target = new Menu
{
ItemsSource = items,
ItemContainerTheme = new ControlTheme(typeof(MenuItem))
{
Setters =
{
new Setter(MenuItem.HeaderProperty, new Binding("Header")),
new Setter(MenuItem.ItemsSourceProperty, new Binding("Children")),
}
}
};
var root = new TestRoot(true, target);
root.LayoutManager.ExecuteInitialLayoutPass();
var children = target.GetRealizedContainers().Cast<MenuItem>().ToList();
Assert.Equal(2, children.Count);
Assert.Equal("Foo", children[0].Header);
Assert.Equal("Bar", children[1].Header);
Assert.Same(items[0].Children, children[0].ItemsSource);
}
private IDisposable Application() private IDisposable Application()
{ {
var screen = new PixelRect(new PixelPoint(), new PixelSize(100, 100)); var screen = new PixelRect(new PixelPoint(), new PixelSize(100, 100));
@ -447,6 +527,9 @@ namespace Avalonia.Controls.UnitTests
public void RaiseCanExecuteChanged() => _canExecuteChanged?.Invoke(this, EventArgs.Empty); public void RaiseCanExecuteChanged() => _canExecuteChanged?.Invoke(this, EventArgs.Empty);
} }
private record MenuViewModel(string Header); private record MenuViewModel(string Header)
{
public IList<MenuViewModel> Children { get; set;}
}
} }
} }

2
tests/Avalonia.Controls.UnitTests/TextBoxTests.cs

@ -93,7 +93,7 @@ namespace Avalonia.Controls.UnitTests
Text = "1234", Text = "1234",
ContextFlyout = new MenuFlyout ContextFlyout = new MenuFlyout
{ {
Items = new List<MenuItem> Items =
{ {
new MenuItem { Header = "Item 1" }, new MenuItem { Header = "Item 1" },
new MenuItem {Header = "Item 2" }, new MenuItem {Header = "Item 2" },

18
tests/Avalonia.IntegrationTests.Appium/NativeMenuTests.cs

@ -18,7 +18,7 @@ namespace Avalonia.IntegrationTests.Appium
} }
[PlatformFact(TestPlatforms.MacOS)] [PlatformFact(TestPlatforms.MacOS)]
public void View_Menu_Select_Button_Tab() public void MacOS_View_Menu_Select_Button_Tab()
{ {
var tabs = _session.FindElementByAccessibilityId("MainTabs"); var tabs = _session.FindElementByAccessibilityId("MainTabs");
var buttonTab = tabs.FindElementByName("Button"); var buttonTab = tabs.FindElementByName("Button");
@ -33,5 +33,21 @@ namespace Avalonia.IntegrationTests.Appium
Assert.True(buttonTab.Selected); Assert.True(buttonTab.Selected);
} }
[PlatformFact(TestPlatforms.Windows)]
public void Win32_View_Menu_Select_Button_Tab()
{
var tabs = _session.FindElementByAccessibilityId("MainTabs");
var buttonTab = tabs.FindElementByName("Button");
var viewMenu = _session.FindElementByXPath("//MenuItem[@Name='View']");
Assert.False(buttonTab.Selected);
viewMenu.Click();
var buttonMenu = viewMenu.FindElementByName("Button");
buttonMenu.Click();
Assert.True(buttonTab.Selected);
}
} }
} }

2
tests/Avalonia.LeakTests/ControlTests.cs

@ -51,7 +51,7 @@ namespace Avalonia.LeakTests
{ {
Content = new DataGrid Content = new DataGrid
{ {
Items = _observableCollection ItemsSource = _observableCollection
} }
}; };

2
tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs

@ -1978,7 +1978,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
public bool Match(object data) => FancyDataType?.IsInstanceOfType(data) ?? true; public bool Match(object data) => FancyDataType?.IsInstanceOfType(data) ?? true;
public Control Build(object data) => TemplateContent.Load(Content)?.Control; public Control Build(object data) => TemplateContent.Load(Content)?.Result;
} }
public class CustomDataTemplateInherit : CustomDataTemplate { } public class CustomDataTemplateInherit : CustomDataTemplate { }

2
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs

@ -605,7 +605,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
var control = new ContentControl(); var control = new ContentControl();
var result = (ContentPresenter)template.Build(control).Control; var result = (ContentPresenter)template.Build(control).Result;
Assert.NotNull(result); Assert.NotNull(result);
} }

6
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlTemplateTests.cs

@ -258,7 +258,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
"; ";
var template = AvaloniaRuntimeXamlLoader.Parse<ControlTemplate>(xaml); var template = AvaloniaRuntimeXamlLoader.Parse<ControlTemplate>(xaml);
var parent = (ContentControl)template.Build(new ContentControl()).Control; var parent = (ContentControl)template.Build(new ContentControl()).Result;
Assert.Equal("parent", parent.Name); Assert.Equal("parent", parent.Name);
@ -283,7 +283,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
Assert.Equal(typeof(ContentControl), template.TargetType); Assert.Equal(typeof(ContentControl), template.TargetType);
Assert.IsType(typeof(ContentPresenter), template.Build(new ContentControl()).Control); Assert.IsType(typeof(ContentPresenter), template.Build(new ContentControl()).Result);
} }
[Fact] [Fact]
@ -299,7 +299,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
"; ";
var template = AvaloniaRuntimeXamlLoader.Parse<ControlTemplate>(xaml); var template = AvaloniaRuntimeXamlLoader.Parse<ControlTemplate>(xaml);
var panel = (Panel)template.Build(new ContentControl()).Control; var panel = (Panel)template.Build(new ContentControl()).Result;
Assert.Equal(2, panel.Children.Count); Assert.Equal(2, panel.Children.Count);

2
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs

@ -424,7 +424,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
}; };
var list = window.FindControl<ItemsRepeater>("list"); var list = window.FindControl<ItemsRepeater>("list");
list.Items = collection; list.ItemsSource = collection;
window.Show(); window.Show();

Loading…
Cancel
Save