I have the Silverlight grid hierarchy load on demand using MVVM and RIA services example working. I updated it to work with Silverlight and .Net Frameworks 4.0 with a few small changes (none to the xaml). Assuming that you had the product table in in the DataContext how would you add the ProductName and ProductID to a ComboBox column in the details row and display the correct product name for the details ProductID field? I added the ComboBox column to the xaml and the Product column shows up but it did not bind to the product data. I retrieved the product data at startup time and it is available but I can't seem to bind to it.
Thanks
Rich
10 Answers, 1 is accepted
I tried the following but the binding to Products does not happen. Any thoughts on this?
Thanks
Rich
Added to DataContext class
public partial class Product
{
Boolean productsLoaded = false;
public IEnumerable<Product> Products
{
get
{
if (!productsLoaded)
{
MyDataContext.DomainContext.Load(MyDataContext.DomainContext.GetProductsQuery());
productsLoaded = true;
}
return MyDataContext.DomainContext.Products;
}
}
Added to MainPage.xaml
<DataTemplate>
<telerik:RadGridView IsReadOnly="False" ShowGroupPanel="False" AutoGenerateColumns="False"
SelectionChanged="RadGridViewDetail_SelectionChanged"
KeyDown="RadGridViewDetail_KeyDown"
ItemsSource="{Binding Details}">
<telerik:RadGridView.Columns>
<telerik:GridViewDataColumn Header="Unit Price" DataMemberBinding="{Binding UnitPrice}" />
<telerik:GridViewDataColumn Header="Quantity" DataMemberBinding="{Binding Quantity}" />
<telerik:GridViewDataColumn Header="Discount" DataMemberBinding="{Binding Discount}" />
<telerik:GridViewComboBoxColumn
Header="Product Name"
SelectedValueMemberPath="ProductID"
DisplayMemberPath="ProductName"
ItemsSource="{Binding Products}"
DataMemberBinding="{Binding ProductID}"/>
</telerik:RadGridView.Columns>
</telerik:RadGridView>
</DataTemplate>
Firstly, please accept my apologies for the late response.
The issue you encountered can be resolved by setting ItemsSourceBinding instead of ItemsSource. Thus the in the definition of your GridViewComboBoxColumn would be:
<
telerik:GridViewComboBoxColumn
Header
=
"Product Name"
SelectedValueMemberPath
=
"ProductID"
DisplayMemberPath
=
"ProductName"
ItemsSourceBinding
=
"{Binding Products}"
DataMemberBinding
=
"{Binding ProductID}"
/>
I hope that helps.
Best wishes,
Maya
the Telerik team
I tried it and got the same result. Could you please try and modify your original example and see if you can get it to work. I hit the wall on this. ItemsSourceBinding 'AllProducts' does not bind to MyDataContext.AllProducts. Attached is my code for mainpage.xaml, mainpage.xaml.cs and mainpage.xaml. See my minimal changes in bold Red.
Thanks
Rich
MainPage.xaml.cs
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
DataContext = new MyDataContext();
}
private void Submit_Click(object sender, RoutedEventArgs e)
{
// result1 and result2 both contain all products...Just checking
var result1 = ((MyDataContext)DataContext).AllProducts;
var result2 = MyDataContext.DomainContext.Products;
if (MyDataContext.DomainContext.HasChanges)
{
MyDataContext.DomainContext.SubmitChanges();
}
}
}
MyDataContext.cs
public class MyDataContext
{
static NWindContext _domainContext;
public static NWindContext DomainContext
{
get
{
if (_domainContext == null)
{
_domainContext = new NWindContext();
_domainContext.Load(_domainContext.GetProductsQuery());
}
return _domainContext;
}
}
Boolean customersLoaded = false;
public IEnumerable<Customer> Customers
{
get
{
if (!customersLoaded)
{
DomainContext.Load(DomainContext.GetCustomersQuery());
customersLoaded = true;
}
return DomainContext.Customers;
}
}
public IEnumerable<Product> AllProducts
{
get
{
return MyDataContext.DomainContext.Products;
}
}
}
public partial class Customer
{
Boolean ordersLoaded = false;
public IEnumerable<Order> CustomerOrders
{
get
{
if (!ordersLoaded)
{
MyDataContext.DomainContext.Load<Order>(MyDataContext.DomainContext.GetCustomerOrdersQuery(CustomerID));
ordersLoaded = true;
}
return MyDataContext.DomainContext.Orders;
}
}
}
public partial class Order
{
Boolean detailsLoaded = false;
public IEnumerable<Order_Detail> Details
{
get
{
if (!detailsLoaded)
{
MyDataContext.DomainContext.Load<Order_Detail>(MyDataContext.DomainContext.GetOrderDetailsQuery(OrderID));
detailsLoaded = true;
}
return MyDataContext.DomainContext.Order_Details;
}
}
}
MainPage.xaml
<UserControl x:Class="TelerikMVVC2.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModel="clr-namespace:TelerikMVVC2.Web"
xmlns:telerik="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.GridView"
mc:Ignorable="d"
d:DesignHeight="400" d:DesignWidth="600">
<Grid x:Name="LayoutRoot" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height ="30" />
<RowDefinition Height ="*" />
<RowDefinition Height ="Auto" />
</Grid.RowDefinitions>
<Button Grid.Row="0" Content="Submit" Click="Submit_Click"/>
<telerik:RadGridView Grid.Row="1" Name="RadGridView1"
IsReadOnly="false"
ShowGroupPanel="False"
CanUserFreezeColumns="False"
IsFilteringAllowed="False"
AutoGenerateColumns="False"
ItemsSource="{Binding PagedSource, ElementName=radDataPager1}">
<telerik:RadGridView.ChildTableDefinitions>
<telerik:GridViewTableDefinition />
</telerik:RadGridView.ChildTableDefinitions>
<telerik:RadGridView.Columns>
<telerik:GridViewDataColumn Header="Customer ID" DataMemberBinding="{Binding CustomerID}" />
<telerik:GridViewDataColumn Header="Company Name" DataMemberBinding="{Binding CompanyName}" />
<telerik:GridViewDataColumn Header="Contact Name" DataMemberBinding="{Binding ContactName}" />
<telerik:GridViewDataColumn Header="City" DataMemberBinding="{Binding City}" />
<telerik:GridViewDataColumn Header="Country" DataMemberBinding="{Binding Country}" />
</telerik:RadGridView.Columns>
<telerik:RadGridView.HierarchyChildTemplate>
<DataTemplate>
<telerik:RadGridView IsReadOnly="False" ShowGroupPanel="true" AutoGenerateColumns="False" ItemsSource="{Binding CustomerOrders}">
<telerik:RadGridView.ChildTableDefinitions>
<telerik:GridViewTableDefinition />
</telerik:RadGridView.ChildTableDefinitions>
<telerik:RadGridView.Columns>
<telerik:GridViewDataColumn Header="Order ID" DataMemberBinding="{Binding OrderID}" />
<telerik:GridViewDataColumn Header="Order Date" DataMemberBinding="{Binding OrderDate}" />
</telerik:RadGridView.Columns>
<telerik:RadGridView.HierarchyChildTemplate>
<DataTemplate>
<telerik:RadGridView IsReadOnly="False" ShowGroupPanel="False" AutoGenerateColumns="False"
ItemsSource="{Binding Details}">
<telerik:RadGridView.Columns>
<telerik:GridViewDataColumn Header="Unit Price" DataMemberBinding="{Binding UnitPrice}" />
<telerik:GridViewDataColumn Header="Quantity" DataMemberBinding="{Binding Quantity}" />
<telerik:GridViewDataColumn Header="Discount" DataMemberBinding="{Binding Discount}" />
<telerik:GridViewComboBoxColumn
Header="Product Name"
SelectedValueMemberPath="ProductID"
DisplayMemberPath="ProductName"
ItemsSourceBinding="{Binding AllProducts}"
DataMemberBinding="{Binding ProductID}"/>
</telerik:RadGridView.Columns>
</telerik:RadGridView>
</DataTemplate>
</telerik:RadGridView.HierarchyChildTemplate>
</telerik:RadGridView>
</DataTemplate>
</telerik:RadGridView.HierarchyChildTemplate>
</telerik:RadGridView>
<telerik:RadDataPager Grid.Row="2"
x:Name="radDataPager1"
PageSize="20"
DisplayMode="All"
Source="{Binding Customers}"
IsTotalItemCountFixed="True"/>
</Grid>
</UserControl>
How do you test xaml binding in immed window?
For your consideration:
Many enterprise databases are in third normal form and therefore the frequent need to handle Foreign Key relationships. It would be very helpful to me and I expect many other developers to have a Hiarchtical RadGridView example using the Northwind database that contains Customer, Order and Order_Details tables containing a combobox column in the Order_Details row with the ProductName from the Products table. Since this example exists without the Product table I'm hoping it would not take to long to update the example including the Product table.
http://blogs.telerik.com/Libraries/Vladimir_Enchev/SilverlightGridHierarchyUsingMVVMAndServices.sflb?download=true
Additional nice to have
Insert and delete keys functioning at all row levels
Upgrade the example to Silverlight 4.0 and .NET Frameworks 4.0
Thanks
Rich
Thank you for your feedback! We would definitely consider updating the blog post so that it meets more requirements.
As for the issue with the binding of the ComboBoxColumn, the problem comes from the fact that the ItemsSource of the last grid is set to "Details", but there is no property "Products" in the Order_Details class. What you can do in this case is to define the ItemsSource of the ComboBoxColumn during the DataLoaded event of the grid and set it to the property Products of the Product class. For instance:
private void OrderDetailsGrid_DataLoading(object sender, Telerik.Windows.Controls.GridView.GridViewDataLoadingEventArgs e)
{
RadGridView orderDetailsGrid = sender as RadGridView;
GridViewComboBoxColumn comboColumn = new GridViewComboBoxColumn();
comboColumn.Header = "Product Name";
comboColumn.SelectedValueMemberPath = "ProductID";
comboColumn.DisplayMemberPath = "ProductName";
comboColumn.DataMemberBinding = new Binding("ProductID");
comboColumn.ItemsSource = MyDataContext.DomainContext.Products;
MyDataContext.DomainContext.Load(MyDataContext.DomainContext.GetProductsQuery());
orderDetailsGrid.Columns.Add(comboColumn);
}
I am sending you the sample project so that you can use it as a reference.
Maya
the Telerik team
When I expanded the first customer and first order the Product name field in each order detail row was blank. When I clicked on the product name field the product name showed up. All other detail rows when expanded were correct. Since you explained how it works I eliminated the load event and put the combobox back in the xaml and used a converter to load the Product info. That took care of the above mentioned blank product name field.
One thing I noticed is that when I try to filter on the combo box I get a list of Product ID's instead of Product Names. Can I change that behavior?
I also tried to use commands as shown in the on-line Commands example. I could not get it to work. I did try
MyDataContext
.DomainContext.SubmitChanges(); and that worked fine. I think I copied everything correctly from the example and don't think I missed anything. Is there something else I need to do or is it a RIA thing???
Thanks
Rich
Indeed the values shown in the filter of the GridViewComboBoxColumn are the ProductID-s and for the time being this is the default behavior. However, in order to change it, you may use one of the two possible workarounds.
The first one is explained in details in this blog post.
The second one is to make the filter show the ProductName-s during the DistinctValuesLoading event, but to make it operate with the ProductID-s so that it can do its job - filtering.
private void OrderDetailsGrid_DistinctValuesLoading(object sender, Telerik.Windows.Controls.GridView.GridViewDistinctValuesLoadingEventArgs e)
{
e.ItemsSource = MyDataContext.DomainContext.Products.Select(p => p.ProductName);
}
private void OrderDetailsGrid_Filtering(object sender, Telerik.Windows.Controls.GridView.GridViewFilteringEventArgs e)
{
foreach (Telerik.Windows.Data.FilterDescriptor fd in e.Added)
{
var product = MyDataContext.DomainContext.Products.Where(p => p.ProductName == fd.Value).FirstOrDefault();
if(product != null)
{
fd.Value = product.ProductID;
}
}
}
However, in this scenario the lower part of the Filter with the Operator (Is Equal To, Is Less Than, Or, etc.) will not work. We would introduce a new property ShowFieldFilters that will enable you to collapse that part of the filter. This will be available in our latest internal build this Friday.
Adding a Filter specified for the GridViewComboBoxColumn is in our to-do list and we are working on its development.
Best wishes,
Maya
the Telerik team
Filtering GridViewComboBoxColumn in RadGridView for WPF. Does the code also work for Silverlight? Are the internal builds available and if so how do I get access.
Thanks
Rich
The second proposed solution is for Silverlight and it is developed and tested on the sample project I have sent to you previously. The workaround exposed in the blog post is also applicable for Silverlight.
You can download our latest internal builds from: Your Account ->Free Trials -> RadControls for Silverlight -> Latest Internal Builds (Trial).
Maya
the Telerik team