【问题标题】:How to bind a datatype to a control in WinUI 3?如何将数据类型绑定到 WinUI 3 中的控件?
【发布时间】:2022-10-09 17:23:46
【问题描述】:

我有一个数据类型(模型)我想通过使用数据绑定显示几个属性来在我的 UI 中显示数据。它适用于 GridViewListView,但是当我只想要一个单个模型绑定而不是集合?

要对集合执行此操作,请在 ListView 中执行以下操作:

<ListView x:Name="MyListView"
          ItemsSource="{x:Bind Shapes, Mode=OneWay}">
    <ListView.ItemTemplate>
        <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>
    </ListView.ItemTemplate>
</ListView>

在类型为ShapeObservableCollection 称为Shapes 的页面上:

public sealed partial class MyPage : Page
{
    // ...
    public ObservableCollection<Shape> Shapes { get; set; }
    // ...
}

使用以下型号Shape

public class Shape
{
    public string Name { get; set; }
    public string NumberOfSides { get; set; }
    public string Color { get; set; }
}

做这样的事情,但这不起作用:

<Grid>
    <StackPanel>
        <TextBlock Text="{x:Bind Name}"></TextBlock>
        <TextBlock Text="{x:Bind NumberOfSides}"></TextBlock>
        <TextBlock Text="{x:Bind Color}"></TextBlock>
    </StackPanel>
</Grid>

【问题讨论】:

    标签: c# xaml data-binding winui-3


    【解决方案1】:

    数据绑定实际上是对ListView 进行的,DataTemplate 只是声明了用于显示绑定模型的布局。

    要使用单个绑定项而不是集合来完成此操作,您需要使用仍具有模板属性的控件。这就是ContentControl 的用武之地(Microsoft's official documentation)。 ContentControl 有一个ContentTemplate 属性,它可以包含DataTemplate,就像ListViewGridView 一样!然后,您可以在 C# 代码中设置ContentControlContent 属性,或绑定到它(与绑定到ListViewGridViewItemsSource 属性相同,仅使用一个项目而不是集合)。

    简单的方法

    以下示例有效(请注意,DataTemplate 及其所有子项与它们在ListViewGridView 中的显示方式相同):

    <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。这需要三个步骤(这可能听起来很吓人,但对任何财产都一样):

    1. 实现接口INotifyPropertyChanged
    2. 添加PropertyChanged 事件。
    3. 修改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 值(OneTimeOneWayTwoWay)。

      如果您希望 ContentControl 内的绑定控件在您更改形状的属性时更新,例如在您这样做时更新 &lt;TextBlock Text="{x:Bind Name}"&gt;

      this.MyShape.Name = "A New Name";
      

      您可以使用相同的基本步骤在您的Shape 类本身上实现INotifyPropertyChanged。无论您使用的是ContentControlGridViewListView,还是任何其他数据绑定控件,这都是一样的。基本上,每个您希望能够更新图层的属性,并拥有数据绑定的 UI 更新,您需要这样做。无论您在此答案中使用了两种方式中的哪一种,都需要这样做。详情可以参考my answer here

    【讨论】:

      猜你喜欢
      • 2023-02-25
      • 2022-06-10
      • 2022-01-14
      • 2021-09-14
      • 1970-01-01
      • 2022-10-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多