|
Hello,
I have a problem with Binding and a ListView.
I have an order system with Orders and Customers.
I tried to make a sort of Datagrid of the order system with TextBoxes so I can edit all the values.
These bind perfectly.
However, I also want a ComboBox in it, with a drop down of all the customers I have.
The XAML code pasted below here has two parts. One with 3 textfields and this ComboBox I speak of, and 1 containing the DataGrid.
<Window x:Class="WpfLookup.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="320" Width="456" Loaded="Window_Loaded">
<Grid Height="Auto" Width="Auto" Margin="6">
<Grid.RowDefinitions>
<RowDefinition Height="115" />
<RowDefinition Height="28" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="90" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel Name="stackPanel1" Margin="0">
<Label Height="23" Name="label1" Width="Auto" HorizontalAlignment="Stretch" HorizontalContentAlignment="Right" Margin="3">Order ID</Label>
<Label Height="23" Name="label3" Width="Auto" HorizontalAlignment="Stretch" HorizontalContentAlignment="Right" Margin="3">Customer</Label>
<Label Height="23" Name="label2" Width="Auto" HorizontalAlignment="Stretch" HorizontalContentAlignment="Right" Margin="3">Order date</Label>
<Label Height="23" Name="label4" Width="Auto" HorizontalAlignment="Stretch" HorizontalContentAlignment="Right" Margin="3">Ship date</Label>
</StackPanel>
<Button Grid.Column="1" Grid.Row="1" Name="btnAdd" HorizontalAlignment="Right" Width="64" Height="22.745" VerticalAlignment="Bottom" Margin="3" Click="btnAdd_Click">Add</Button>
<Button Grid.Column="1" Grid.Row="1" Name="btnSave" Margin="0,0,75,3" Click="btnSave_Click" Height="22.745" VerticalAlignment="Bottom" HorizontalAlignment="Right" Width="64.163">Save</Button>
<StackPanel Grid.Column="1" Name="stackPanel2">
<TextBox
Text="{Binding Path=orderID, Mode=OneWay}"
IsReadOnly="True"
Height="23" Name="textBox1" Width="Auto" Margin="3" />
<ComboBox
SelectedValuePath="customerID"
SelectedValue="{Binding Path=customerID}"
Height="23" Name="cbCustomer" Width="Auto" Margin="3" SelectionChanged="cbCustomer_SelectionChanged">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="Images/salome.gif" Height="Auto" Width="Auto" MaxHeight="40" MaxWidth="40"/>
<TextBlock Name="tbLastName" VerticalAlignment="Center" Text="{Binding Path=lastName}" />
<TextBlock Name="tbComma" VerticalAlignment="Center" Text=", " />
<TextBlock Name="tbFirstName" VerticalAlignment="Center" Text="{Binding Path=firstName}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ComboBox>
<TextBox
Text="{Binding Path=orderDate}"
Height="23" Name="tbOrderDate" Width="Auto" Margin="3" TextChanged="tbOrderDate_TextChanged" />
<TextBox
Text="{Binding Path=shipDate}"
Height="23" Name="tbShipDate" Width="Auto" Margin="3" TextChanged="tbShipDate_TextChanged" />
<TextBox
Name="tbModified"
Text="{Binding Path=modified, Mode=OneWayToSource}"
Visibility="Hidden" />
</StackPanel>
<Button Grid.Row="1" Name="btnPrevious" HorizontalAlignment="Left" Width="18.18" Height="22.745" VerticalAlignment="Bottom" Margin="3" Click="btnPrevious_Click"><</Button>
<Button Grid.Row="1" HorizontalAlignment="Right" Name="btnNext" Width="18.18" Height="22.745" VerticalAlignment="Bottom" Margin="3" Click="btnNext_Click">></Button>
<ListView Name="testView"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding}"
Grid.Row="2" Grid.Column="2" Margin="3">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridViewColumn
Header="Order ID" DisplayMemberBinding="{Binding Path=orderID, Mode=OneWay}" Width="75"/>
<GridViewColumn Header="OrderDate" Width="75">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ComboBox
SelectedValuePath="customerID"
SelectedValue="{Binding Path=customerID}"
ItemsSource="{Binding dsCustomer.Customer}"
Name="cbCustomer2"
Height="23" Width="Auto" Margin="3" SelectionChanged="cbCustomer_SelectionChanged">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="Images/salome.gif" Height="Auto" Width="Auto" MaxHeight="40" MaxWidth="40"/>
<TextBlock Name="tbLastName" VerticalAlignment="Center" Text="{Binding Path=lastName}" />
<TextBlock Name="tbComma" VerticalAlignment="Center" Text=", " />
<TextBlock Name="tbFirstName" VerticalAlignment="Center" Text="{Binding Path=firstName}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ComboBox>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="OrderDate" Width="75">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox
Text="{Binding Path=orderDate}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Ship date" Width="75">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox
Text="{Binding Path=shipDate}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
If we focus on the first ComboBox:
<ComboBox
SelectedValuePath="customerID"
SelectedValue="{Binding Path=customerID}"
Height="23" Name="cbCustomer" Width="Auto" Margin="3" SelectionChanged="cbCustomer_SelectionChanged">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="Images/salome.gif" Height="Auto" Width="Auto" MaxHeight="40" MaxWidth="40"/>
<TextBlock Name="tbLastName" VerticalAlignment="Center" Text="{Binding Path=lastName}" />
<TextBlock Name="tbComma" VerticalAlignment="Center" Text=", " />
<TextBlock Name="tbFirstName" VerticalAlignment="Center" Text="{Binding Path=firstName}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ComboBox>
This one works perfectly. But only when I change the ItemsSource in C# code:
this.cbCustomer.ItemsSource = dsCustomer.Customer;
It won't work when I would do it in XAML code:
this.cbCustomer.ItemsSource = dsCustomer.Customer;
Am I doing something wron in the XAML code?
I'm quite new to WPF.
The reason I ask this, is that this ComboBox will return in the datagrid, I can't however name it, because multiple ComboBoxes will be generated, and I don't have the name to use in C# code.
I hope my problem is clear and someone knows what I am doing wrong, thanks in advance for the help.
|
|
|
|
|
From the XAML perspective, DataContext for the ComboBox is limited by the ListView's(it's Parent) DataContext. In other words, since ComboBox is the child of ListView it can access DataContext of the ListView and bind itself to that or anything relative to that. You need to set the ItemSource of the ComboBox relative to what is set for the ListView.
What is ListView's DataContext ? Is it collection of Customer's ?
|
|
|
|
|
I have not set the ListView's DataContext in XAML, but I did this:
this.view = (CollectionView) CollectionViewSource.GetDefaultView(dsOrder.Order);
So it is a collection of Orders.
What I want to display in the ComboBoxes however, are Customers linked (through a foreign key, customerID) in the database). I have the CustomerID of the current row in the listview, but it needs to look a customer up. I managed to do this earlier when I did not yet use a listview. I managed this by using the following code:
this.cbCustomer.ItemsSource = dsCustomer.Customer;
So the combobox gets a different ItemsSource then the rest of the User Interface.
My problem is now, since the TextBoxes get generated in the ListView (depending on the number of orders in the dataset), I am unable to call them by their name in C# code to set the ItemsSource. How do I change just the ComboBoxes, whom have no names, to have the Customer as datasource?
I hope this clarifies the problem.
Edit: To be clear, the other fields work in the Datagrid, Textboxes show the correct values (for this example dates).
modified on Thursday, February 19, 2009 10:33 AM
|
|
|
|
|
Check this MSDN[^]. Look for the section "Binding Related DataTables"
You can bind your ListView to the DataRelation between the two tables and can then access data from both the tables in the ListView.
|
|
|
|
|
That sounds great, but I have never heard of this DataRelation. I will search about this and test this first thing tomorrow (Netherlands) and let you know if it worked, thanks for the suggestion !
Edit: Didn't see the link you posted at first, but thanks for the reference as well!
|
|
|
|
|
Got the following exception.
"Cannot have a relationship between tables in different DataSets."
I could place everything in one DataSet, but it would be nice to know a solution when it's not possible to do so. I will try to build it like so that only one DataSet will be used for now, but is there a solution to my problem that works when multiple datasets are used?
modified on Friday, February 20, 2009 2:56 AM
|
|
|
|
|
Create a custom data object from the two datasets and bind the custom object instead of the datatable
|
|
|
|
|
Oke thanks,
Im now working at the earlier hint to put it all in one dataset, but I think I am doing something wrong
DataTable orderTable = dsOrder.Tables["Order"];
DataTable customerTable = dsOrder.Tables["Customer"];
dsOrder.Relations.Add("Order2Customer",
customerTable.Columns["customerID"],
orderTable.Columns["customerID"],
false);
this.view = (CollectionView)CollectionViewSource.GetDefaultView(orderTable);
this.DataContext = orderTable; (the added/changed code in C#)
<TextBox
Text="{Binding Path=orderID, Mode=OneWay}"
IsReadOnly="True"
Height="23" Name="textBox1" Width="Auto" Margin="3" />
<ComboBox
ItemsSource="{Binding Order2Customer}"
SelectedValuePath="customerID"
SelectedValue="{Binding Path=customerID}"
Height="23" Name="cbCustomer" Width="Auto" Margin="3" SelectionChanged="cbCustomer_SelectionChanged">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="Images/salome.gif" Height="Auto" Width="Auto" MaxHeight="40" MaxWidth="40"/>
<TextBlock Name="tbLastName" VerticalAlignment="Center" Text="{Binding Path=lastName}" />
<TextBlock Name="tbComma" VerticalAlignment="Center" Text=", " />
<TextBlock Name="tbFirstName" VerticalAlignment="Center" Text="{Binding Path=firstName}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ComboBox> (an example of my combobox and a textbox I use in XAML.)
The TextBox binds correctly, the comboBox does not. The relationship should work like this right? But it can't seem to bind to the customer fields...
|
|
|
|
|
Try this,
dsOrder.Relations.Add("Order2Customer",
orderTable.Columns["customerID"],
customerTable.Columns["customerID"],
false);
I interchanged the column references.
|
|
|
|
|
Yes I figured out that I should interchange them indeed, but still with no success.
The ComboBox remains empty.
|
|
|
|
|
Have you changed the ItemsSource of the ListView to bind to the DataRelation ?
|
|
|
|
|
First I tried to applied it to a combobox outside of the listview, so I havent altered the listview yet.
But I changed the ComboBox ItemsSource.
<ComboBox
ItemsSource="{Binding Order2Customer}"
SelectedValuePath="customerID"
SelectedValue="{Binding Path=customerID}"
This didn't work sadly.
Then I tried applying it to the ListView like you said:
<ListView Name="testView"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Order2Customer}"
Grid.Row="2" Grid.Column="2" Margin="3">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListView.ItemContainerStyle>
Now nothing in the ListView can be accessed anymore... :P So it's completely blank
I hope you see what I am doing wrong Thanks for the help so far ofcourse!
|
|
|
|
|
I am referring to the Microsoft example.
"this.view = (CollectionView)CollectionViewSource.GetDefaultView(orderTable)"
What is this for?? I think you do not need this part.
"this.DataContext = orderTable;"
This is setting the Orders table as DataSource for the Window. Am I right?
Maybe simplify the the view and see if data comes ?
Instead of a ComboBox use a TextBox and bind it to Order and Customer table fields and see if it works. Preferable use some other field also from the Customer Table (other than CustomerID).
<ListView Name="testView"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Order2Customer}"
Grid.Row="2" Grid.Column="2" Margin="3">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=OrderTableProp1}" />
<TextBlock Text="{Binding Path=CustomerTableProp2}"/>
<TextBlock Text="{Binding Path=CustomerTableProp3}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListView>
(Replaces the property names.)
|
|
|
|
|
It works when I just put stuff in like orderID, customerID, orderDate.
But one order can be bound to one customer, but I want to display all of the customers in the combobox, this was working previously outside of the listview, now it works nowhere anymore.
But given the customerID, I want to be able to select the correct customer. But I will do some more research because it's hard to describe. I could put one working source file without datagrid, and one with datagrid online, maybe that would be a little bit more clear? Because you will see what's the problem when you run it.
edit:
http://rapidshare.com/files/200322597/WpfLookupProblem.zip.html[^]
Edit:
No database ofcourse.. but still it can help I think?
modified on Friday, February 20, 2009 6:33 AM
|
|
|
|
|
I was thinking,and came to the conclusion that binding to the relationship would not suffice. I need to bind to the collection of customers, if I bind to the relationship, only one customer could be returned.
So I think im back where we started :P Find out how to set the ItemsSource of a generated combobox :P
|
|
|
|
|
I get a better picture of what you are stuck into. You have to take some more time to design this bugger
The working sample just had a single order displayed. If I understand correct, the problem is when you try to display all the orders together in a DataGrid form. Is that correct ?
You can create a CLR object of customer data. Create a resource in the Window resources,
<local:Customers x:Key="CustomerList"/>
where Customers is CLR object; a collection of Customer information.
Then bind it to the ComboBox inside the ListView, "{Binding Source={StaticResource CustomerList}"
Now, the combobox will have a different datasource than the ListView. But then you will have to handle the combobox selection change etc. in code behind.
I was trying to devise something more concrete.
|
|
|
|
|
The working sample just had a single order displayed. If I understand correct, the problem is when you try to display all the orders together in a DataGrid form. Is that correct ?
Not entirely, I could show them all and edit them all at once in the working example.
But it indeed displays only one.
I am able to display the the datagrid btw, I shall show you how it looks in a screenshot:
http://i42.tinypic.com/w03yfn.jpg[^]
I will try your Recource suggestion, I read about it but have not yet tried it.
|
|
|
|
|
Yes that is what I was trying to say.
Is it necessary to have the combobox at 2 places ?? You could have it on the top part and in the Grid just display.
|
|
|
|
|
I started with the top part, but I wanted to remove it at the end, so I only need it in the datagrid.
|
|
|
|
|
I have not worked much with these resources, how can I add the datatable of customers to a resource? I only see options to add files or strings in the default resource editor?
|
|
|
|
|
This might help you out MSDN[^]
|
|
|
|
|
Thanks, I think this is exactly what I need.
This is what I have so far:
xmlns:src="clr-namespace:WpfLookup"
<Window.Resources>
<src:OrderCustomerDataset x:Key="dsCustomer.Customer" />
</Window.Resources>
(added in the XAML file)
It gives no errors.
But what I really want to bind, is the table in the instance of my dataset, dsCustomer.Customer. I have a feeling though that x:Key is only the name for in the XAML referencing?
I added this
ItemsSource="{Binding Source={StaticResource dsCustomer.Customer}}"
as ItemsSource to the Customer. But it now only displays one entry with one photo and thats it, I feel we are getting closer though
I hope these questions don't bother you, you are a great help to me
|
|
|
|
|
Rolorob wrote: x:Key is only the name for in the XAML referencing
Yes, your right
Rolorob wrote: displays one entry
Is the ListView still bound to the DataRelation ? Is yes, put it back to where it was before
I suspect
IsSynchronizedWithCurrentItem="True" . Try removing the top part of the view which you would not be using and just run the bottom DataGrid part of your code.
If things still don't work, now since you are using a Grid sort of view, you can use a DataTemplate for the ListView instead of the CollectionView.
Rolorob wrote: questions don't bother you
Not at all
Rolorob wrote: you are a great help to me
Thanks
|
|
|
|
|
Nice :p
When I alter the top combobox so that it's just like the bottom.
So remove from C# code:
this.cbCustomer.ItemsSource = dsOrder.Customer;
And add
ItemsSource="{Binding Source={StaticResource Customer}, Path=.}"
It doesn't work anymore then.
When I look at this:
<Window.Resources>
<src:OrderCustomerDataset x:Key="Customer" />
</Window.Resources>
It doesn't really say to me that it is the right instance of the DataSet/DataTable to bind to, how should I refrace that so that it binds to dsOrder.Customer? I think that's the part that's missing
|
|
|
|
|
Well, you need to design the stuff as I mentioned earlier. Why dont you have a Wrapper Object containing Orders and Customers. You bind your main Window to this wrapper. Then, bind the Orders Grid View to DataContext.Orders and the customer combobox to DataContext.Customers (where DataContext is the datacontext of the main Window which you can reference it in the nested objects using the word "DataContext").
The path you are taking might also work. I do not know what OrderCustomerDataset is ?? If it is an object having a list of Customers then,
ItemsSource="{Binding Source={StaticResource Customer}, Path=ListOfCustomers}"
Remember, you will also need to handle the display of the appropriate customer in the combobox based on the CustomerId of the Order. On changing the customer the customerid in the orders table also has to be updated. You can use a ValueConverter(or MultiValueConverter) to drive the SelectedValue of ComboBox based on parameters and handle the combobox selection change.
Hope that helps(or makes matters worse??)
|
|
|
|