如何使用绑定和 DataTemplate 或 ContentControl for MAUI 呈现数据

kux*_*kux 7 controltemplate maui

如何使用绑定和 DataTemplate 呈现字符串、数字或视图模型?

我正在寻找 WPF ContentControl 的 MAUI 替代品。

具有ContentViewContent 属性,但这是来自 type View。具有ContentPresenterContent 属性,但这也来自 type View<Ignorable>WTF,为什么它只能呈现一个视图,而不被命名为 ViewPresenter ???有时 MAUI 很奇怪。</Ignorable>

如何通过为每种数据类型定义数据模板来呈现任何内容?

class PropertyViewModel {
   public string Name {get;set;}
   public object Value {get;set;}
}
Run Code Online (Sandbox Code Playgroud)
<Page.Resources>
    <DataTemplate DataType="System.String">
        <Entry Text="{Binding}/>
    </DataTemplate>
    <DataTemplate DataType="System.Int32">
        <NumberPicker Value="{Binding}/>
    </DataTemplate>
    .. more templates, eg. DatePicker for System.DateOnly
</Page.Resources>

<DockLayout>
    <Label Text="{Binding Name}
    <TemplatedContenView Content={Binding Value}/> 
</DockPanel>
Run Code Online (Sandbox Code Playgroud)

TemplatedContenView 或 ContentControl(MAUI 中不存在)可以对不同类型的值使用不同的模板。在 WPF 中,ContentControl 使用 ContentTemplate、ContentTemplateSelector,或者如果未指定,则它会查找资源来查找模板。

<Ignorable>对于 MAUI,我经常有一种感觉,我必须不断地重新发明 WPF 中的标准东西。是的,我知道 MAUI 不是 WPF,但至少应该有类似的概念。从 WinForms 到 WPF 的转换要容易得多,而且差异也相当大。</Ignorable>

Edit1:更详细的示例

Art*_*nko 9

我是一名 WPF 开发人员,最近我开始了 MAUI 项目。看起来每次当你要编写你提到的这样一个简单的场景时,你都必须重新发明轮子:(。当你使用 WPF 来做时,你甚至不需要考虑这一点,它太容易实现了,但是当你使用MAUI时,你应该打破头脑去做这些小事。

我也遇到了同样的问题,但没有找到简单的解决方案。但我想到了创建一个控件,其中包含一些布局,其中附加了 BindableLayout 的属性

TemplatedContentPresenter.xaml.cs:

public partial class TemplatedContentPresenter : ContentView
{
    public TemplatedContentPresenter()
    {
        InitializeComponent();
    }

    public static readonly BindableProperty DataTemplateSelectorProperty = BindableProperty.Create(nameof(DataTemplateSelector), typeof(DataTemplateSelector), typeof(TemplatedContentPresenter), null, propertyChanged: DataTemplateSelectorChanged);
    public static readonly BindableProperty DataTemplateProperty = BindableProperty.Create(nameof(DataTemplate), typeof(DataTemplate), typeof(TemplatedContentPresenter), null, propertyChanged: DataTemplateChanged);
    public static readonly BindableProperty DataProperty = BindableProperty.Create(nameof(Data), typeof(object), typeof(TemplatedContentPresenter), null, propertyChanged: DataChanged);

    public DataTemplateSelector DataTemplateSelector
    {
        get =>(DataTemplateSelector)GetValue(DataTemplateSelectorProperty);
        set => SetValue(DataTemplateSelectorProperty, value);
    }

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

    public object Data
    {
        get => GetValue(DataProperty);
        set => SetValue(DataProperty, value);
    }

    private static void DataTemplateSelectorChanged(BindableObject bindable, object oldValue, object newValue)
    {
        if(bindable is TemplatedContentPresenter contentPresenter && newValue is DataTemplateSelector dataTemplateSelector)
        {
            BindableLayout.SetItemTemplateSelector(contentPresenter.HostGrid, dataTemplateSelector);
        }
    }
    private static void DataTemplateChanged(BindableObject bindable, object oldValue, object newValue)
    {
        if (bindable is TemplatedContentPresenter contentPresenter && newValue is DataTemplate dataTemplate)
        {
            BindableLayout.SetItemTemplate(contentPresenter.HostGrid, dataTemplate);
        }
    }

    private static void DataChanged(BindableObject bindable, object oldValue, object newValue)
    {
        if (bindable is TemplatedContentPresenter contentPresenter)
        {
            BindableLayout.SetItemsSource(contentPresenter.HostGrid, new object[] { newValue });
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

TemplatedContentPresenter.xaml:

<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MyApp.TemplatedContentPresenter">
    <Grid x:Name="HostGrid" x:FieldModifier="private" />
</ContentView>
Run Code Online (Sandbox Code Playgroud)

用法:

<Frame WidthRequest="500" HeightRequest="500">
     <controls:TemplatedContentPresenter 
               Data="{Binding}" 
               DataTemplateSelector="{StaticResource CardTemplateSelector}"/>
</Frame>
Run Code Online (Sandbox Code Playgroud)

UPD: 当我写答案时,我想出了另一个带有简单转换器的解决方案: SingleObjectToArray.xaml

    internal class SingleObjectToArray : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return new object[] { value };
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
Run Code Online (Sandbox Code Playgroud)

用法:

<Frame>
    <Frame.Resources>
        <converters:SingleObjectToArray x:Key="SingleObjectToArrayConverter"/>
    </Frame.Resources>
    <Grid BindableLayout.ItemsSource="{Binding Converter={StaticResource SingleObjectToArrayConverter}}"
          BindableLayout.ItemTemplateSelector="{StaticResource CardTemplateSelector}" /> 
</Frame>
Run Code Online (Sandbox Code Playgroud)