You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET MAUI自定义控件绑定属性设置CollectionView ItemTemplate失败

.NET MAUI自定义控件中通过XAML设置CollectionView ItemTemplate无效的解决方法

我开发了一个内嵌CollectionView的.NET MAUI自定义控件,想要通过控件的绑定属性让使用者在XAML中配置CollectionView的ItemTemplate,但目前通过XAML设置的内容完全无效。已经验证所有绑定都正常,绑定属性本身能工作,但ItemTemplate就是无法通过XAML生效。后台代码直接设置ItemTemplate是正常的,但我希望使用者能通过XAML进行个性化配置。


原自定义控件XAML

<?xml version="1.0" encoding="utf-8" ?>
<StackLayout x:Name="DataGridControl" 
             xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="PensionApp.CustomUIControls.DataGrid"
             xmlns:local="clr-namespace:PensionApp.CustomUIControls"
             HeightRequest="500"
             WidthRequest="500"
             Orientation="Vertical">
    <CollectionView 
             x:Name="Datagrid_collection_view"
             ItemsSource="{Binding CollectionToDisplay ,Source={x:Reference DataGridControl}}">

        <CollectionView.ItemTemplate>
            <DataTemplate >
                <ContentView Content="{Binding CustomContentTemplate, Source={x:Reference DataGridControl}}" />
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
</StackLayout>

原自定义控件后台代码

namespace PensionApp.CustomUIControls 
{ 
using System.Collections.ObjectModel;
using System.Reflection;
using PensionApp;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Collections;
using System.ComponentModel;
using Microsoft.Maui.Controls;
    public partial class DataGrid : StackLayout
    {
        public DataGrid()
        {
            InitializeComponent();
        }
        public static readonly BindableProperty CollectionToDisplayProperty = BindableProperty.Create(
            propertyName: nameof(CollectionToDisplay),
            returnType: typeof(ObservableCollection<Object>),
            defaultValue: null,
            declaringType: typeof(DataGrid),
            defaultBindingMode: BindingMode.TwoWay,
            propertyChanged: OnCollectionToDisplayChanged
            );
        public ObservableCollection<Object> CollectionToDisplay
        {
            get => (ObservableCollection<Object>)GetValue(CollectionToDisplayProperty);
            set
            {
                SetValue(CollectionToDisplayProperty, (ObservableCollection<Object>)value);
            }
        }
        private static void OnCollectionToDisplayChanged(BindableObject bindable, object oldValue, object newValue)
        {
            var DataGridControlInstance = (DataGrid)bindable;
            DataGridControlInstance.Datagrid_collection_view.ItemsSource = (ObservableCollection<object>)newValue;
        }
        
        public static readonly BindableProperty CustomContentTemplateProperty = BindableProperty.Create(
        propertyName: nameof(CustomContentTemplate),
            returnType: typeof(View),
            defaultValue: null,
            declaringType: typeof(DataGrid),
            defaultBindingMode: BindingMode.OneWay,
            propertyChanged: OnCustomContentTemplateChanged
            );
        public View CustomContentTemplate
        {
            get => (View)GetValue(CustomContentTemplateProperty);
            set
            {
                SetValue(CustomContentTemplateProperty, (View)value);
            }
        }
        private static void OnCustomContentTemplateChanged(BindableObject bindable, object oldValue, object newValue)
        {  
        }
    }
}

原MainPage XAML

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="PensionApp.MainPage"
             xmlns:controls="clr-namespace:PensionApp.CustomUIControls"
             x:Name="MainPageInstance">
    <ScrollView>
        <VerticalStackLayout
            Spacing="25"
            Padding="30,0"
            VerticalOptions="Center">
            <controls:DataGrid  CollectionToDisplay="{Binding  CollectionToPass,Source={x:Reference MainPageInstance}}">
                <controls:DataGrid.CustomContentTemplate>

                    <Grid RowDefinitions="Auto" ColumnDefinitions="Auto,Auto" HeightRequest="20">
                        <Entry Text="achnou" Grid.Row="0" Grid.Column="0"></Entry>
                        <Entry Text="male" Grid.Row="0" Grid.Column="1"></Entry>
                    </Grid>
                </controls:DataGrid.CustomContentTemplate>
            </controls:DataGrid>
        </VerticalStackLayout>
    </ScrollView>
</ContentPage>

原MainPage后台代码

using System.Collections.ObjectModel;
using System.ComponentModel;

namespace PensionApp;

public partial class MainPage : ContentPage
{
    int count = 0;
    public ObservableCollection<Object> CollectionToPass { get; set;}
    public MainPage()
    {
        CollectionToPass = new ObservableCollection<Object>();
        CollectionToPass.Add(new Somedataexemple("XXXX", "XXXXX"));
        CollectionToPass.Add(new Somedataexemple("YYYYY", "YYYYY"));
        CollectionToPass.Add(new Somedataexemple("ZZZZZ", "ZZZZ"));
        InitializeComponent();
    }
}
public class Somedataexemple : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    public Somedataexemple(String first, string last)
    {
        First_Name = first;
        Last_Name = last;
    }
    private string first_Name;
    private string last_Name;
    public string First_Name
    {
        get => first_Name;
        set{first_Name = value; RaisePropertyChanged(nameof(First_Name));}
    }
    public string Last_Name 
    {
        get => last_Name;
        set{last_Name = value; RaisePropertyChanged(nameof(Last_Name));}
    }
    public void RaisePropertyChanged(string propertyName)
    {PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));}
}

问题根源

核心问题在于你定义的CustomContentTemplate属性类型是View,但CollectionView的ItemTemplate需要的是DataTemplate。直接绑定View会导致所有列表项共用同一个View实例,而且DataTemplate内部的绑定上下文是列表项的数据对象,不是自定义控件本身,所以之前的绑定逻辑无法正确工作。

修复步骤

1. 修改自定义控件的BindableProperty类型

把CustomContentTemplateProperty的类型从View改为DataTemplate,并在属性变化时直接赋值给CollectionView的ItemTemplate:

public static readonly BindableProperty CustomContentTemplateProperty = BindableProperty.Create(
    propertyName: nameof(CustomContentTemplate),
    returnType: typeof(DataTemplate),
    defaultValue: null,
    declaringType: typeof(DataGrid),
    defaultBindingMode: BindingMode.OneWay,
    propertyChanged: OnCustomContentTemplateChanged
);

public DataTemplate CustomContentTemplate
{
    get => (DataTemplate)GetValue(CustomContentTemplateProperty);
    set => SetValue(CustomContentTemplateProperty, value);
}

private static void OnCustomContentTemplateChanged(BindableObject bindable, object oldValue, object newValue)
{
    var dataGrid = (DataGrid)bindable;
    dataGrid.Datagrid_collection_view.ItemTemplate = (DataTemplate)newValue;
}

2. 简化自定义控件的XAML

移除固定的ItemTemplate嵌套,让外部通过属性传入:

<?xml version="1.0" encoding="utf-8" ?>
<StackLayout x:Name="DataGridControl" 
             xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="PensionApp.CustomUIControls.DataGrid"
             xmlns:local="clr-namespace:PensionApp.CustomUIControls"
             HeightRequest="500"
             WidthRequest="500"
             Orientation="Vertical">
    <CollectionView 
             x:Name="Datagrid_collection_view"
             ItemsSource="{Binding CollectionToDisplay, Source={x:Reference DataGridControl}}">
    </CollectionView>
</StackLayout>

3. 修改MainPage的XAML使用方式

现在可以直接传入DataTemplate,并且能正确绑定到列表项的数据:

<controls:DataGrid CollectionToDisplay="{Binding CollectionToPass, Source={x:Reference MainPageInstance}}">
    <controls:DataGrid.CustomContentTemplate>
        <DataTemplate>
            <Grid RowDefinitions="Auto" ColumnDefinitions="Auto,Auto" HeightRequest="20">
                <Entry Text="{Binding First_Name}" Grid.Row="0" Grid.Column="0"/>
                <Entry Text="{Binding Last_Name}" Grid.Row="0" Grid.Column="1"/>
            </Grid>
        </DataTemplate>
    </controls:DataGrid.CustomContentTemplate>
</controls:DataGrid>

4. 优化CollectionToDisplay的绑定

原代码中手动赋值ItemsSource的逻辑多余,因为XAML已经绑定了ItemsSource,可移除相关代码简化:

// 移除属性变化事件
// private static void OnCollectionToDisplayChanged(BindableObject bindable, object oldValue, object newValue)
// {
//     var DataGridControlInstance = (DataGrid)bindable;
//     DataGridControlInstance.Datagrid_collection_view.ItemsSource = (ObservableCollection<object>)newValue;
// }

// 创建BindableProperty时去掉propertyChanged参数
public static readonly BindableProperty CollectionToDisplayProperty = BindableProperty.Create(
    propertyName: nameof(CollectionToDisplay),
    returnType: typeof(ObservableCollection<Object>),
    defaultValue: null,
    declaringType: typeof(DataGrid),
    defaultBindingMode: BindingMode.TwoWay
);

内容的提问来源于stack exchange,提问作者W Mernissi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 04:20:33