我正在尝试使用指定的 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();
但我最后总是得到空列表项。
我检查了我想要绑定多次的列表,它不为空。
起初我尝试从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; }
}
您必须将
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>