如何在WPF中将自定义对象绑定到ListBox

时间:2013-03-05 16:15:03

标签: c# wpf binding listbox custom-object

我是WPF的新手,所以如果我的问题很愚蠢,请接受我的道歉。

我正在创建一个食物订购系统,它由2个主要部分组成,即“食物菜单”和“订单列表”。当用户从食物菜单中选择一个项目时,它将被添加到代表订单列表的列表框控件中。

我为“OrderLine”创建了一些自定义对象:OrderOrderLineItem以及Collection类OrderLineCollection。它们看起来如下:

public class Order
{
    public string ID { get; set; }
    public DateTime DateTime { get; set; }
    public double TotalAmt { get; set; }
    public OrderLineCollection LineItems { get; set; }
}

public class OrderLine
{
    public Item Item { get; set; }
    public double UnitPrice { get; set; }
    public int Quantity { get; set; }
    public double SubTotal { get { return unitPrice * quantity; } }
}

[Serializable]
public class OrderLineCollection : CollectionBase
{
    public OrderLine this[int index]
    {
        get { return (OrderLine)List[index]; }
        set { List[index] = value; }
    }
}

public class Item
{
    public string ID { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public double UnitPrice { get; set; }
    public byte[] Image { get; set; }
}

我的ListBox控件有DataTemplate,因此每个项目都会显示更多详细信息。 XAML如下:

<Page x:Class="FoodOrdering.OrderList"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      Title="Order List">
    <ListBox Name="lbxOrder" HorizontalContentAlignment="Stretch">            
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Grid Margin="5">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*"/>
                        <ColumnDefinition Width="30"/>
                        <ColumnDefinition Width="80"/>
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition/>
                        <RowDefinition Height="40"/>
                    </Grid.RowDefinitions>
                    <TextBlock Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" Text="{Binding item.name}"/>
                    <TextBlock Grid.Row="0" Grid.Column="1" Text="x "/>
                    <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding quantity}" Margin="10,0,0,0"/>
                    <TextBlock Grid.Row="0" Grid.Column="2" Text="{Binding subTotal, Converter={StaticResource priceConverter}}" HorizontalAlignment="Right"/>
                    <Button Grid.Row="1" Grid.Column="2" Margin="0,5,0,0" Click="btnDelete_Click">Delete</Button>
                </Grid>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Page>

所以当添加项目时,ListBox将如下图所示: https://dl.dropbox.com/u/6322828/orderList.png

在代码隐藏中,我创建了一个静态变量currentOrder来存储当前的顺序,它将被其他类/方法使用。

每次更改其值时,都会调用以下愚蠢的方法LoadCurrentOrder()来刷新ListBox视图。

public partial class OrderList : Page
{
    public static Order currentOrder = new Order();
    public OrderList()
    {
        InitializeComponent();
        LoadCurrentOrder();
    }

    public void LoadCurrentOrder()
    {
        lbxOrder.Items.Clear();
        foreach (OrderLine ol in currentOrder.LineItems)
            lbxOrder.Items.Add(ol);
    }
}

我的问题是如何以优雅的方式绑定数据(例如使用Resources ItemsSource等)而不是使用上述方法,以便ListBox每次都会自动更新变量的值是否改变了?

我试过了

lbxOrder.ItemsSource = currentOrder; 

但它不起作用,因为currentOrder不是System.Collections.IEnumerable对象。

2 个答案:

答案 0 :(得分:4)

1 - 不要创建自己的集合类型,.Net框架基类库已经包含了您需要的所有内容。

删除OrderLineCollection并使用ObservableCollection<OrderLine>

2 - 尝试坚持MVVM约定。这意味着您不应该在代码中操作UI元素。

public OrderList()
{
    InitializeComponent();
    LoadCurrentOrder();
    DataContext = this;
}

然后在XAML中:

<ListBox ItemsSource="{Binding LineItems}" HorizontalContentAlignment="Stretch">

3 - 除非您需要在XAML中引用它们,否则不要在XAML中命名UI元素。每当您发现自己想要在过程代码中操作UI元素时,这将帮助您重新考虑您的方法。

4 - 习惯C#命名约定。属性名称应为“正确套装”(I.E LineItems而非lineItems

答案 1 :(得分:0)

您所要做的就是将lineItems属性变为ObservableCollection<yourType>。然后,当更改此集合(添加,删除项目)时,列表框将自动刷新。

如果您的问题是订单本身发生了变化,您需要刷新列表框。然后,您需要做的就是实现INotifyPropertyChanged,当订单发生变化时,触发属性已更改的通知,UI将通过绑定进行刷新。

以更优雅的方式绑定。您可以绑定到currentOrder.lineItems

lbxOrder.ItemsSource = currentOrder.lineItems;//this should be an observable collection if you intend to have items change