Browse Source

Support multiple `InheritDataTypeFromItems`.

To allow binding both `ItemsControl.Items` and `ItemsSource` we need to support multiple `InheritDataTypeFromItems` attributes.
pull/10590/head
Steven Kirk 4 years ago
parent
commit
57c997bed7
  1. 2
      src/Avalonia.Base/Metadata/InheritDataTypeFromItemsAttribute.cs
  2. 2
      src/Avalonia.Controls/ItemsControl.cs
  3. 1
      src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
  4. 43
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs

2
src/Avalonia.Base/Metadata/InheritDataTypeFromItemsAttribute.cs

@ -9,7 +9,7 @@ namespace Avalonia.Metadata;
/// A typical usage example is a ListBox control, where <see cref="InheritDataTypeFromItemsAttribute"/> is defined on the ItemTemplate property, /// A typical usage example is a ListBox control, where <see cref="InheritDataTypeFromItemsAttribute"/> is defined on the ItemTemplate property,
/// allowing the template to inherit the data type from the Items collection binding. /// allowing the template to inherit the data type from the Items collection binding.
/// </remarks> /// </remarks>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)] [AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = true)]
public sealed class InheritDataTypeFromItemsAttribute : Attribute public sealed class InheritDataTypeFromItemsAttribute : Attribute
{ {
/// <summary> /// <summary>

2
src/Avalonia.Controls/ItemsControl.cs

@ -95,6 +95,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
[AssignBinding] [AssignBinding]
[InheritDataTypeFromItems(nameof(ItemsSource))] [InheritDataTypeFromItems(nameof(ItemsSource))]
[InheritDataTypeFromItems(nameof(Items))]
public IBinding? DisplayMemberBinding public IBinding? DisplayMemberBinding
{ {
get => GetValue(DisplayMemberBindingProperty); get => GetValue(DisplayMemberBindingProperty);
@ -218,6 +219,7 @@ namespace Avalonia.Controls
/// Gets or sets the data template used to display the items in the control. /// Gets or sets the data template used to display the items in the control.
/// </summary> /// </summary>
[InheritDataTypeFromItems(nameof(ItemsSource))] [InheritDataTypeFromItems(nameof(ItemsSource))]
[InheritDataTypeFromItems(nameof(Items))]
public IDataTemplate? ItemTemplate public IDataTemplate? ItemTemplate
{ {
get => GetValue(ItemTemplateProperty); get => GetValue(ItemTemplateProperty);

1
src/Avalonia.Controls/Primitives/SelectingItemsControl.cs

@ -235,6 +235,7 @@ namespace Avalonia.Controls.Primitives
/// </summary> /// </summary>
[AssignBinding] [AssignBinding]
[InheritDataTypeFromItems(nameof(ItemsSource))] [InheritDataTypeFromItems(nameof(ItemsSource))]
[InheritDataTypeFromItems(nameof(Items))]
public IBinding? SelectedValueBinding public IBinding? SelectedValueBinding
{ {
get => GetValue(SelectedValueBindingProperty); get => GetValue(SelectedValueBindingProperty);

43
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs

@ -73,27 +73,32 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
// Infer data type from collection binding on a control that displays items. // Infer data type from collection binding on a control that displays items.
var property = context.ParentNodes().OfType<XamlPropertyAssignmentNode>().FirstOrDefault(); var property = context.ParentNodes().OfType<XamlPropertyAssignmentNode>().FirstOrDefault();
var attributeType = context.GetAvaloniaTypes().InheritDataTypeFromItemsAttribute; var attributeType = context.GetAvaloniaTypes().InheritDataTypeFromItemsAttribute;
var attribute = property?.Property?.GetClrProperty().CustomAttributes var attributes = property?.Property?.GetClrProperty().CustomAttributes
.FirstOrDefault(a => a.Type == attributeType); .Where(a => a.Type == attributeType).ToList();
if (attribute is not null) if (attributes?.Count > 0)
{ {
var propertyName = (string)attribute.Parameters.First(); foreach (var attribute in attributes)
XamlAstConstructableObjectNode parentObject;
if (attribute.Properties.TryGetValue("AncestorType", out var type)
&& type is IXamlType xamlType)
{
parentObject = context.ParentNodes().OfType<XamlAstConstructableObjectNode>()
.FirstOrDefault(n => n.Type.GetClrType().FullName == xamlType.FullName);
}
else
{ {
parentObject = context.ParentNodes().OfType<XamlAstConstructableObjectNode>().FirstOrDefault(); var propertyName = (string)attribute.Parameters.First();
} XamlAstConstructableObjectNode parentObject;
if (attribute.Properties.TryGetValue("AncestorType", out var type)
if (parentObject != null) && type is IXamlType xamlType)
{ {
inferredDataContextTypeNode = InferDataContextOfPresentedItem(context, on, parentObject, propertyName); parentObject = context.ParentNodes().OfType<XamlAstConstructableObjectNode>()
.FirstOrDefault(n => n.Type.GetClrType().FullName == xamlType.FullName);
}
else
{
parentObject = context.ParentNodes().OfType<XamlAstConstructableObjectNode>().FirstOrDefault();
}
if (parentObject != null)
{
inferredDataContextTypeNode = InferDataContextOfPresentedItem(context, on, parentObject, propertyName);
if (inferredDataContextTypeNode != null)
break;
}
} }
} }

Loading…
Cancel
Save