如何在WPF中将List绑定到Listview userControl?

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

我正在尝试使用指定的 userControl UI 创建数据库中产品的 ListView。

这是我的清单。

 <ListView x:Name="ProductTable" Background="Transparent" ItemsSource="{Binding GetProducts}">
     <ListView.ItemTemplate>
         <DataTemplate>
             <v:ProductLineStyle/>

         </DataTemplate>
     </ListView.ItemTemplate>

     <ItemsControl></ItemsControl>


 </ListView>

我尝试通过使用 XAML 代码中的 ItemSource 属性或在代码隐藏中将其绑定到我在代码隐藏中的列表。

ProductTable.Items.Clear();
ProductTable.ItemsSource = GetProducts();

但我最后总是得到空列表项。

the number of items displayed in ListView is right but they should not be empty.

我检查了我想要绑定多次的列表,它不为空。

起初我尝试从ListView更改为ListBox,但它不起作用,我尝试使用userControl后面的代码作为产品类,但似乎userControl的构造函数不应该有参数,所以我使用了另一个上课,因为这样会更容易。

这就是我现在的用户控件中的代码

public ProductLineStyle()
{
    InitializeComponent();
    this.DataContext = this;
}

public string ProductID { get; set; }
public string ProductName { get; set; }
public int ProductPrice { get; set; }
public int ProductQTé { get; set; }

这是 XAML 脚本

<Rectangle HorizontalAlignment="Stretch" RadiusX="10" RadiusY="10" Fill="#2A3E50">

</Rectangle>
<DockPanel>
    <TextBlock x:Name="ID" Text="{Binding ProductID}" FontFamily="Inter Semi Bold" Margin="10" FontSize="20" Foreground="#01161E" VerticalAlignment="Center" Width="130" HorizontalAlignment="Left"/>
    <TextBlock x:Name="Product" Text="{Binding ProductName}" FontFamily="Inter Semi Bold" Margin="10" MaxWidth="400" FontSize="20" Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Left"/>
    <TextBlock x:Name="Qte" Text="{Binding ProductQTé}" FontFamily="Inter Semi Bold" Width="50" FontSize="20" Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Right" DockPanel.Dock="Right" Margin="0,0,10,0"/>
    <TextBlock x:Name="Price" Text="{Binding ProductPrice}" FontFamily="Inter Semi Bold" Margin="10,10,30,10" MaxWidth="400" FontSize="20" Foreground="White" VerticalAlignment="Center" DockPanel.Dock="Right" HorizontalAlignment="Right"/>

</DockPanel>

这是我正在使用的课程。

internal class Product
{
    public Product(string ID, string Name, int Price, int Qty) 
    {
        ProductID = ID;
        ProductName = Name;
        ProductPrice = Price;
        ProductQTé = Qty;
    }

    public string ProductID { get; set; }
    public string ProductName { get; set; }
    public int ProductPrice { get; set; }
    public int ProductQTé { get; set; }
}
c# wpf list listview data-binding
1个回答
0
投票

您必须将

ProductLineStyle
内的
DataTemplate
控件与
ItemsControl
的项目连接起来。您必须将
ProductLineStyle
绑定到
Product
项目。

但是为了能够做到这一点,在

ProductLineStyle
控件中定义的所有属性都必须定义为依赖属性。不允许您将绑定分配给非依赖属性。

接下来,因为

Product
类是绑定源,所以它必须实现
INotifyPropertyChanged
。如果不这样做,就会造成内存泄漏。

请勿对

ItemsControl.ItemsSource
进行操作。而是对源集合进行操作。除此之外,
ItesControl.Items.Clear
调用是多余的,只会增加性能成本。它强制集合的完整迭代,以便逐项删除。此操作使用项目索引。这意味着,集合越大,清除集合所需的时间就越长。如果您打算更换集合,则不必事先清除它。

在 WPF 中,您无法绑定到方法。以下代码不起作用:

<ListView x:Name="ProductTable" 
          ItemsSource="{Binding GetProducts}" />

您只能绑定到公共属性。

即使绑定有效,您也可以通过显式代码隐藏赋值来删除/覆盖它:

// Overrides the binding declared in XAML
ProductTable.ItemsSource = GetProducts();

使用其中之一。更喜欢数据绑定。

还有另一个重要的细节:永远不要在内部设置控件的 DataContext。特别是当该控件需要在其一个或多个属性上定义 Binding 时。
这将破坏所有外部数据绑定:

<Grid>
  <Grid.DataContext>
    <Product Id="Abc" />
  </Grid.DataContext>

  <!-- Binding is unexpectedly broken and won't resolve, 
       because the ProductStyle internally changes 
       the value of DataContext: 'DataContext = this;' 
  -->
  <ProductStyle ProductId="{Binding Id}" />
</Grid>

外部绑定通常针对当前

DataContext
。没有人预料到这个值会被控件默默地操纵。
关键是依赖属性引擎将自动从父级继承正确的
DataContext
值。
在您的场景中,该父项是
ListBoxItem
,继承的值是 Product 项。现在您可以轻松地将
ProductStyle
控件绑定到当前
DataContext
,即
Product
。绑定将按预期运行

要修复代码,您必须更改类设计和实现。

产品.cs

// This type must implement INotifyPropertyChanged.
// If this type had been a child of DependencyObject
// it would have to implement properties as dependency properties instead.
internal class Product : INotifyPropertyChanged
{
  // Use the common and recommend C# naming conventions
  // which states that fields and parameter names
  // must use camelCase. PascalCase is only for types and properties and events.
  public Product(string id) 
  {
    this.ProductId = id;
  }

  protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    => this.PropertyChanged?.Invoke(this, new PropertyChangedArgs(propertyName));

  public string Id { get; set; }
  public event PropertyChangedEventHandler PropertyChanged;
}

MainWindow.xaml.cs

partial class MainWindow : Window
{
  public ObservableCollection<Product> Products { get; }

  public MainWindow()
  {
    InitializeComponent();
   
    this.Products = new ObservableCollection<Product>
    {
      new Product("A112"),
      new Product("B543")
    };
  }
}

MainWindow.xaml

<Window>
  <ListBox ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=Products}">
    <ListBox.ItemTemplate>
      <ProductLineStyle ProductId="{Binding Id}" />
    </ListBox.ItemTemplate>
  </ListBox>
</Window>

ProductStyle.xaml.cs

class ProductLineStyle : UserControl
{
  public string ProductId
  {
    get => (string)GetValue(ProductIdProperty);
    set => SetValue(ProductIdProperty, value);
  }

  public static readonly DependencyProperty ProductIdProperty = DependencyProperty.Register(
    "ProductId",
    typeof(string),
    typeof(MainWindow),
    new PropertyMetadata(default));

  public ProductLineStyle()
  {
    InitializeComponent();
    
     // Don't do this. This will break your external data bindings 
     // (the data bindings defined on the ProductStyle element)!
     // The correct DataContext value will be automatically inherited from the parent.
     // In this scenario, the parent is the ListBoxItem and the value the Product type.

    //this.DataContext = this;
  }
}

产品样式.xaml

<UserControl>
 <TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=ProductId}" />
</UserControl>
© www.soinside.com 2019 - 2024. All rights reserved.