Friday, September 21, 2012

ComboBox inside DataGrid, I need an SL4 and DataContext solution

ComboBox inside DataGrid, I need an SL4 and DataContext solution

Hi there:

I need a solution for a ComboBox inside a DataGrid that works with SL4 and uses DataContext instead of StaticResource to reference the ViewModel.

Let me resume the story that led me to what I now need.

Sometime ago I posted a thread asking for a complete solution to having a ComboBox inside a DataGrid that took it's contents from the Entity Data Model, using the MVVM approach and RIA Services.

I found several articles showing similar solutions, but they took the data from objects built in memory, I wanted the inner combo to take it's data from an entity also, as the outer datagrid was doing.

Something like this:

 I recieved solutions that failed to make the combo show the actual Customer's Country name (using the actual sample). They worked like this:

As you can see, the combo shows the data but it's initial content is not set.

Finally, I recieved a solution that worked well, this solution uses Trigger Behaviors, and StaticResource to reference the ViewModel, the solution for my example is:

<UserControl.Resources>     <ViewModels:CustomersViewModel x:Key="CustomerViewModel" />  </UserControl.Resources>  <Grid x:Name="LayoutRoot"        Background="White">     <sdk:DataGrid AutoGenerateColumns="False"                    Height="200"                    HorizontalAlignment="Left"                    ItemsSource="{Binding Customers, Mode=TwoWay, Source={StaticResource CustomerViewModel}}"                    SelectedItem="{Binding SelectedCustomer, Mode=TwoWay, Source={StaticResource CustomerViewModel}}"                    x:Name="customerDataGrid"                    RowDetailsVisibilityMode="VisibleWhenSelected"                    VerticalAlignment="Top"                    Width="548">        <sdk:DataGrid.Columns>           <sdk:DataGridTextColumn x:Name="customerNameColumn"                                   Binding="{Binding CustomerName}"                                   Header="Name"                                   Width="SizeToCells" />           <sdk:DataGridTextColumn x:Name="customerContactColumn"                                   Binding="{Binding CustomerContact}"                                   Header="Contact"                                   Width="SizeToCells" />           <sdk:DataGridTemplateColumn Header="Country">              <sdk:DataGridTemplateColumn.CellTemplate>                 <DataTemplate>                    <Grid x:Name="ComboGrid">                       <ComboBox ItemsSource="{Binding Path=Countries, Source={StaticResource CustomerViewModel}}"                                   SelectedValue="{Binding ElementName=ComboGrid,                                                           Path=DataContext.CustomerCountryID,                                                            Mode=TwoWay}"                                   SelectedValuePath="CountryID"                                   DisplayMemberPath="CountryName"                                   >                          <i:Interaction.Triggers>                             <i:EventTrigger>                                <Behaviors:FindElementParent />                             </i:EventTrigger>                          </i:Interaction.Triggers>                       </ComboBox>                    </Grid>                 </DataTemplate>              </sdk:DataGridTemplateColumn.CellTemplate>           </sdk:DataGridTemplateColumn>           <sdk:DataGridTextColumn x:Name="customerPhoneColumn"                                   Binding="{Binding CustomerPhone}"                                   Header="Phone"                                   Width="SizeToCells" />        </sdk:DataGrid.Columns>     </sdk:DataGrid>  </Grid>

Where the FindElementParent class is:

   public class FindElementParent : TriggerAction<DependencyObject> {          protected override void Invoke(object parameter) {           DependencyObject parent = this.AssociatedObject as DependencyObject;           FrameworkElement fe = this.AssociatedObject as FrameworkElement;           while (parent != null) {              if (parent.GetType() == typeof(DataGrid)) {                 DataGrid context = parent as DataGrid;                 fe.DataContext = context.DataContext;                 break;              }              parent = VisualTreeHelper.GetParent(parent) as DependencyObject;           }        }     }

The problem is that this solution uses StaticResource to reference the ViewModel and it makes the solution unstable, sometimes it works and other it doesn't, I think the problem is that StaticResources are not meant for DataModels nor ViewModels.

I also got a solution that used DataContext but worked only in Silverlight 5, it used the AncestorType property in RelativeSource, it only works in SL5

<sdk:DataGridTemplateColumn Header="Contact Title">     <sdk:DataGridTemplateColumn.CellTemplate>        <DataTemplate>           <ComboBox DisplayMemberPath="TitleDescription"                          SelectedValuePath="TitleID"                          ItemsSource="{Binding                                    Path=DataContext.Titles,                                   RelativeSource={RelativeSource                                       AncestorType=UserControl}}"                          SelectedItem="{Binding Path=Title,                                                   Mode=TwoWay}">           </ComboBox>        </DataTemplate>     </sdk:DataGridTemplateColumn.CellTemplate>  </sdk:DataGridTemplateColumn>

The problem is that SL5 has problems with the DataForm control, sometimes the DataForm throws very weird exceptions in SL5. The DataForm is very important to me cause it saves a lot of time.

That's why I need a solution that works with SL4 and uses DataContext for the ViewModel.

Sorry for the long explanation but I wanted to save you time when sending your suggestions.

This is the sample project I'm using so you can make your modifications. It's an SL4 solution and uses the StaticResource approach. Remember that I want to use DataContext instead of StaticResource.

Solution is called ValidationSample.

https://skydrive.live.com/#cid=228822BB183339A9&id=228822BB183339A9%21234

I included a BackUp of the data I'm using in the sample application, it's called MiniNorthind, you can use the Restore database command in SQL Expresss.

(I'm studying Silverlight from A to Z, regarding only LOB solutions)

Answers & Comments...

Answer: 1

Rafael_Soteldo

 I recieved solutions that failed to make the combo show the actual Customer's Country name (using the actual sample). They worked like this:

As you can see, the combo shows the data but it's initial content is not set.

Hi, I downloaded the ValidationSample.rar and decompression it, everything is ok but the database backup file. I can't restore it by SQLServer 2008 R2. But I think you can refer to this post I wrote a few days ago.

Although it not meet your requirement very well, but I think you can got your direction with it. You can specify the ComboBox initial content (ComboBox.SelectedIndex) in ComboBox_Loaded event.

Hope this will help you.

Best Regards,



Answer: 2

I'm using SQL Server management Studio for SQL Express 2008.

You shouldn't have any problem.

The post you did can't solve my problem since the ComboBox is inside a DataGrid, each line of the DataGrid is a selected item for the inner combo.

The combo initial content is one for each line in the DataGrid.





No comments:

Post a Comment

Send us your comment related to the topic mentioned on the blog