如何使用绑定和 DataTemplate -或 - MAUI 的 ContentControl 来呈现数据

问题描述 投票:0回答:2

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

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

ContentView
有一个 Content 属性,但它来自类型
View
ContentPresenter
有一个 Content 属性,但这也来自类型
View
WTF,为什么它只能呈现一个视图,而不被命名为 ViewPresenter ???有时毛伊岛很奇怪。

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

class PropertyViewModel {
   public string Name {get;set;}
   public object Value {get;set;}
}
<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>

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

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

编辑1:更详细的示例

controltemplate maui
2个回答
7
投票

我是一名 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 });
        }
    }
}

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>

用途:

<Frame WidthRequest="500" HeightRequest="500">
     <controls:TemplatedContentPresenter 
               Data="{Binding}" 
               DataTemplateSelector="{StaticResource CardTemplateSelector}"/>
</Frame>

更新: 当我写答案时,我想出了另一个带有简单转换器的解决方案: 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();
        }
    }

用途:

<Frame>
    <Frame.Resources>
        <converters:SingleObjectToArray x:Key="SingleObjectToArrayConverter"/>
    </Frame.Resources>
    <Grid BindableLayout.ItemsSource="{Binding Converter={StaticResource SingleObjectToArrayConverter}}"
          BindableLayout.ItemTemplateSelector="{StaticResource CardTemplateSelector}" /> 
</Frame>

0
投票

您可以使用免费的 DevExpress 套件中的 ContentPresenter 来重用 DataTemplates。

<ContentPage.Resources>
    <DataTemplate x:Key="employeeTemplate">
        <!-- ... -->
        <Label Text="{Binding FirstName}"/>
    </DataTemplate>
</ContentPage.Resources>

<dx:DXContentPresenter Content="{Binding Employee}" ContentTemplate="{StaticResource employeeTemplate}" />

请注意,DevExpress 仅提供移动组件(iOS、Android)

© www.soinside.com 2019 - 2024. All rights reserved.