如何使用绑定和 DataTemplate 呈现字符串、数字或视图模型?
我正在寻找 WPF ContentControl 的 MAUI 替代品。
ContentView
有一个 Content 属性,但它来自类型 View
。
ContentPresenter
有一个 Content 属性,但这也来自类型 View
。 如何通过为每种数据类型定义数据模板来呈现任何内容?
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,或者如果没有指定,它会查找资源来查找模板。
编辑1:更详细的示例
我是一名 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>
您可以使用免费的 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)