数据绑定实际上是对ListView 进行的,DataTemplate 只是声明了用于显示绑定模型的布局。
要使用单个绑定项而不是集合来完成此操作,您需要使用仍具有模板属性的控件。这就是ContentControl 的用武之地(Microsoft's official documentation)。 ContentControl 有一个ContentTemplate 属性,它可以包含DataTemplate,就像ListView 或GridView 一样!然后,您可以在 C# 代码中设置ContentControl 的Content 属性,或绑定到它(与绑定到ListView 或GridView 的ItemsSource 属性相同,仅使用一个项目而不是集合)。
简单的方法
以下示例有效(请注意,DataTemplate 及其所有子项与它们在ListView 或GridView 中的显示方式相同):
<ContentControl x:Name="MyContentControl">
<ContentControl.ContentTemplate>
<DataTemplate x:DataType="models:Shape">
<StackPanel>
<TextBlock Text="{x:Bind Name}"></TextBlock>
<TextBlock Text="{x:Bind NumberOfSides}"></TextBlock>
<TextBlock Text="{x:Bind Color}"></TextBlock>
</StackPanel>
</DataTemplate>
<ContentControl.ContentTemplate>
</ContentControl>
然后在您的 C# 代码中:
public sealed partial class MyPage : Page
{
// ...
public void SetShape(Shape shape)
{
this.MyContentControl.Content = shape;
}
// ...
}
完整的数据绑定方式
您还可以使用数据绑定来绑定到 shape 属性,但这需要更多的工作。首先将绑定添加到ContentControl,如下所示:
<ContentControl x:Name="MyContentControl"
Content="{x:Bind MyShape}">
<ContentControl.ContentTemplate>
<!-- Contents all the same as before -->
<ContentControl.ContentTemplate>
</ContentControl>
并添加MyShape 属性以绑定到MyPage:
public sealed partial class MyPage : Page
{
// ...
public Shape MyShape { get; set; }
// ...
}
照原样,这是行不通的。最初设置时它可能会起作用,但是如果您更改MyShape,绑定的 UI 将不会更新。
请注意,如果您使用的是ObservableCollection(例如在ListView 示例中),您可以在调用Add() 或Remove() 的ObservableCollection 函数时更新UI,但是不是当您更改 ObservableCollection 引用本身时.原因是ObservableCollection 实现了INotifyPropertyChanged,当您更改集合中的项目集时,它告诉绑定更新。以下将不是自动工作:
public sealed partial class MyPage : Page
{
// ...
public Shape MyShape { get; set; }
// ...
public void UpdateShape(Shape newShape)
{
this.MyShape = newShape;
}
}
为了让它工作,你需要在MyPage 上实现INotifyPropertyChanged。这需要三个步骤(这可能听起来很吓人,但对任何财产都一样):
- 实现接口
INotifyPropertyChanged。
- 添加
PropertyChanged 事件。
- 修改
MyShape 设置器以引发PropertyChanged 事件。
实现接口INotifyPropertyChanged。
public sealed partial class MyPage : Page, INotifyPropertyChanged
{
// ...
}
添加PropertyChanged 事件。
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raise the PropertChanged event for the given property name.
/// </summary>
/// <param name="name">Name of the property changed.</param>
public void RaisePropertyChanged(string name)
{
// Ensure a handler is listening for the event.
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
修改 MyShape 设置器以引发 PropertyChanged 事件。
private Shape myShape;
public Shape MyShape
{
get => this.myShape;
set
{
this.myShape = value;
this.RaisePropertyChanged("MyShape");
}
}
您的最终 C# 代码将如下所示:
public sealed partial class MyPage : Page, INotifyPropertyChanged
{
// ...
private Shape myShape;
public Shape MyShape
{
get => this.myShape;
set
{
this.myShape = value;
this.RaisePropertyChanged("MyShape");
}
}
// ...
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raise the PropertChanged event for the given property name.
/// </summary>
/// <param name="name">Name of the property changed.</param>
public void RaisePropertyChanged(string name)
{
// Ensure a handler is listening for the event.
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
// ...
public void UpdateShape(Shape newShape)
{
this.MyShape = newShape;
}
}
现在您的ContentControl 将按预期使用不同的BindingMode 值(OneTime、OneWay 和TwoWay)。
如果您希望 ContentControl 内的绑定控件在您更改形状的属性时更新,例如在您这样做时更新 <TextBlock Text="{x:Bind Name}">:
this.MyShape.Name = "A New Name";
您可以使用相同的基本步骤在您的Shape 类本身上实现INotifyPropertyChanged。无论您使用的是ContentControl、GridView、ListView,还是任何其他数据绑定控件,这都是一样的。基本上,每个您希望能够更新图层的属性,并拥有数据绑定的 UI 更新,您需要这样做。无论您在此答案中使用了两种方式中的哪一种,都需要这样做。详情可以参考my answer here。