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

.NET MAUI中如何用C#访问DataTemplate对象实现条件渲染

解决方案步骤

1. 将Foo从结构体改为类并实现属性变更通知

结构体是值类型,绑定后属性变更无法正确触发UI更新,必须改用引用类型并实现INotifyPropertyChanged:

using System.ComponentModel;
using System.Runtime.CompilerServices;

public class Foo : INotifyPropertyChanged
{
    private ContentType _contentType;
    public ContentType ContentType
    {
        get => _contentType;
        set
        {
            if (_contentType != value)
            {
                _contentType = value;
                OnPropertyChanged();
            }
        }
    }

    private string _text;
    public string Text
    {
        get => _text;
        set
        {
            if (_text != value)
            {
                _text = value;
                OnPropertyChanged();
            }
        }
    }

    private string _imageUrl;
    public string ImageUrl
    {
        get => _imageUrl;
        set
        {
            if (_imageUrl != value)
            {
                _imageUrl = value;
                OnPropertyChanged();
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

public enum ContentType
{
    Text,
    Image
}

2. 正确实现FooItemView的绑定属性

之前绑定属性为null的问题是因为未遵循MAUI自定义绑定属性的规范,以下是正确实现:

public class FooItemView : ContentView
{
    public static readonly BindableProperty FooItemProperty =
        BindableProperty.Create(
            nameof(FooItem), 
            typeof(Foo), 
            typeof(FooItemView), 
            null, 
            propertyChanged: OnFooItemChanged);

    public Foo FooItem
    {
        get => (Foo)GetValue(FooItemProperty);
        set => SetValue(FooItemProperty, value);
    }

    private readonly Label _textLabel;
    private readonly Image _contentImage;

    public FooItemView()
    {
        _textLabel = new Label();
        _contentImage = new Image();

        var grid = new Grid();
        grid.Children.Add(_textLabel);
        grid.Children.Add(_contentImage);

        Content = grid;
    }

    private static void OnFooItemChanged(BindableObject bindable, object oldValue, object newValue)
    {
        var view = (FooItemView)bindable;
        var fooItem = (Foo)newValue;

        if (fooItem == null)
        {
            view._textLabel.IsVisible = false;
            view._contentImage.IsVisible = false;
            return;
        }

        // 绑定控件属性到FooItem,确保属性变更时UI自动更新
        view._textLabel.SetBinding(Label.TextProperty, new Binding(nameof(Foo.Text), source: fooItem));
        view._contentImage.SetBinding(Image.SourceProperty, new Binding(nameof(Foo.ImageUrl), source: fooItem));

        // 初始化显示状态
        UpdateVisibility(view, fooItem);

        // 监听ContentType变更,实时切换控件可见性
        fooItem.PropertyChanged += (s, e) =>
        {
            if (e.PropertyName == nameof(Foo.ContentType))
            {
                UpdateVisibility(view, fooItem);
            }
        };
    }

    private static void UpdateVisibility(FooItemView view, Foo fooItem)
    {
        view._textLabel.IsVisible = fooItem.ContentType == ContentType.Text;
        view._contentImage.IsVisible = fooItem.ContentType == ContentType.Image;
    }
}

3. ViewModel中处理异步数据与集合更新

使用ObservableCollection代替数组,集合增减时自动通知UI;ViewModel同样需要实现属性变更通知:

public class MainViewModel : INotifyPropertyChanged
{
    private ObservableCollection<Foo> _fooItems;
    public ObservableCollection<Foo> FooItems
    {
        get => _fooItems;
        set
        {
            if (_fooItems != value)
            {
                _fooItems = value;
                OnPropertyChanged();
            }
        }
    }

    public MainViewModel()
    {
        FooItems = new ObservableCollection<Foo>();
        _ = LoadFooItemsAsync();
    }

    private async Task LoadFooItemsAsync()
    {
        // 模拟HTTP请求加载数据
        await Task.Delay(1000);

        FooItems.Add(new Foo { ContentType = ContentType.Text, Text = "文本内容1" });
        FooItems.Add(new Foo { ContentType = ContentType.Image, ImageUrl = "https://example.com/image1.jpg" });

        // 模拟后续ContentType变更
        await Task.Delay(2000);
        FooItems[0].ContentType = ContentType.Image;
        FooItems[0].ImageUrl = "https://example.com/image2.jpg";
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

4. 主页面配置CollectionView

用C#代码创建CollectionView并绑定到ViewModel:

public class MainPage : ContentPage
{
    public MainPage()
    {
        var viewModel = new MainViewModel();
        BindingContext = viewModel;

        var collectionView = new CollectionView
        {
            ItemsSource = viewModel.FooItems,
            ItemTemplate = new DataTemplate(() => new FooItemView())
        };

        Content = new ScrollView { Content = collectionView };
    }
}

关键注意事项

  • 结构体转类:值类型的绑定属性变更无法触发UI更新,必须使用引用类型并实现INotifyPropertyChanged
  • 用ObservableCollection:数组无法通知UI集合的增减变化,ObservableCollection会自动处理这类通知
  • 绑定属性规范:自定义View的BindableProperty必须正确声明,通过propertyChanged回调处理UI初始化与属性变更监听
  • 实时监听变更:在Foo的PropertyChanged事件中监听ContentType字段,确保切换时立即更新UI

内容的提问来源于stack exchange,提问作者Mancy's grandson

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 13:50:23