This is a migrated thread and some comments may be shown as answers.

DetailsPresenter with template selector not binding properly with MVVM Pattern

4 Answers 198 Views
GridView
This is a migrated thread and some comments may be shown as answers.
Jc
Top achievements
Rank 1
Jc asked on 13 Nov 2012, 07:31 AM
Hi guys,

I'm actually displaying my RadGridView row details in a DetailPresenter (working withRadControls for WPF Q3 2012).
Each rows can be of different types, therefore, I use a template selector to display details. Templates are hosting user controls connected to ViewModels, instantiating in there own codebehind in onrender methods:

Mainwindow.xaml:
   
<Grid VerticalAlignment="Top">
       <Grid.Resources>
           <DataTemplate x:Key="KidDataTemplate">
                   <View:KidView KidId="{Binding Id}" />
           </DataTemplate>
           <DataTemplate x:Key="AdultDataTemplate">
                   <View:AdultView AdultId="{Binding Id}" />
           </DataTemplate>
           <selectors:UserDataTemplateSelector x:Key="UserDataTemplateSelector" />
       </Grid.Resources>
       <StackPanel Orientation="Vertical">
           <telerik:RadGridView x:Name="grid1"
                                    HorizontalAlignment="Left"
                                    Margin="10,10,0,0"
                                    VerticalAlignment="Top"
                                    RowDetailsTemplateSelector="{StaticResource UserDataTemplateSelector}"
                                    RowDetailsVisibilityMode="Collapsed"
                                    AutoGenerateColumns="True"/>
           <Border Grid.Row="1"  BorderBrush="WhiteSmoke" BorderThickness="5">
               <telerik:DetailsPresenter DetailsProvider="{Binding RowDetailsProvider, ElementName=grid1}" />
           </Border>
       </StackPanel>
   </Grid>

AdultView.xaml.cs:

public partial class AdultView : UserControl
    {
 
        public int? AdultId
        {
            get { return (int?)GetValue(AdultIdProperty); }
            set { SetValue(AdultIdProperty, value); }
        }
        public static readonly DependencyProperty AdultIdProperty = DependencyProperty.Register("AdultId", typeof(int?), typeof(AdultView));
         
        public AdultView()
        {
            InitializeComponent();
        }
         
        protected override void OnRender(DrawingContext drawingContext)
        {
            base.OnRender(drawingContext);
            if (AdultId.HasValue)
            {
                this.DataContext = new RowDetailSample.ViewModel.AdultViewModel(AdultId.Value);
            }
        }
 
    }


This works quite well until I'm selecting a second line of the same type:
The user control placed in the selected template does not update it's binding handled by its ViewModel.

Do I implement things correctly(is onrender method the right place to instanciate the viewModel object)? Do I miss something?

Thanks for your help.
Kind regards,
JC

4 Answers, 1 is accepted

Sort by
0
Rossen Hristov
Telerik team
answered on 13 Nov 2012, 08:46 AM
Hello,

As described in the documentation, the DataContext of the element residing in the row details will be the respective data row item. i.e. the business object that has this Id property. Thanks to this fact, your AdultView.AdultId can be successfully bound to the Id property of the respective data row item (since this data row item is its DataContext).

Then you go on and totally replace the DataContext of the AdultView that was set by us to something completely different. I do not think that this is a valid approach. This will break everything.

I am not sure how you should implement this functionality, but have in mind that the DataContext of the control which is inside the row details template (AdultView) will be set to the respective data item by us and if you override this unexpected results will occur. So this AdultView can bind to anything that is part of the business object.

Maybe you should put all stuff that your AdultView needs to bind to on the business object class and since this business object will in fact be the DataContext of your AdultView it will be able to successfully bind to this stuff.

I hope this helps.

All the best,
Rossen Hristov
the Telerik team

Explore the entire Telerik portfolio by downloading Telerik DevCraft Ultimate.

0
Jc
Top achievements
Rank 1
answered on 13 Nov 2012, 09:22 AM

If I could put all stuff needed by the AdultView in the row business object, I would not need such a mechanism. This is just a sample app. My need is to display different object details inheriting from row business objects. Therefore, they don't have same fields to be displayed. 

What would be the good approach then? I also need a view model linked to the details because I've got some other mechanism to be implemented for each details.

If I don't use any DetailsPresenter and display the details inside the grid, it works perfectly, but my customer whants the details outside of the Grid :-)
0
Rossen Hristov
Telerik team
answered on 13 Nov 2012, 10:15 AM
Hello,

You can develop your own custom mechanism for this purpose. It is quite simple actually.

For example, you can place an empty ContentControl outside of RadGridView. It will be your details placeholder.

Each time selection changes in RadGridView, determine what DataTemplate you need to load for the currently selected row and load it dynamically with the DataTemplate.LoadContent() method. This will manufacture a FrameworkElement for you, i.e. AdultView, which you can set as the Content of your ContentControl placeholder. You will be free to data bind this FrameworkElement to anything that you want.

With this approach you will have total freedom to bind your view in any custom way that you need and you will not rely on our internal row details logic.

Regards,
Rossen Hristov
the Telerik team

Explore the entire Telerik portfolio by downloading Telerik DevCraft Ultimate.

0
Jc
Top achievements
Rank 1
answered on 13 Nov 2012, 12:48 PM
Hi again,

I followed your advice implementing my own detail presenter and it works well:
Here is the code, if it can help someone:
MainWindow.xaml:
<Grid VerticalAlignment="Top">
        <Grid.Resources>
            <DataTemplate x:Key="KidDataTemplate">
                    <View:KidView KidId="{Binding Id}" />
            </DataTemplate>
            <DataTemplate x:Key="AdultDataTemplate">
                    <View:AdultView AdultId="{Binding Id}" />
            </DataTemplate>
            <selectors:UserDataTemplateSelector x:Key="UserDataTemplateSelector" />
        </Grid.Resources>
        <StackPanel Orientation="Vertical">
            <telerik:RadGridView x:Name="grid1"
                                 ItemsSource="{Binding Users}"
                                 SelectedItem="{Binding SelectedUser}"
                                     HorizontalAlignment="Left"
                                     Margin="10,10,0,0"
                                     VerticalAlignment="Top"
                                     RowDetailsVisibilityMode="Collapsed"
                                     AutoGenerateColumns="True"/>
            <Border Grid.Row="1"  BorderBrush="WhiteSmoke" BorderThickness="5">
                <ContentControl x:Name="Presenter" Content="{Binding SelectedItem}" ContentTemplateSelector="{StaticResource UserDataTemplateSelector}"></ContentControl>
            </Border>
        </StackPanel>
    </Grid>

MainWindowViewModel.cs:
public class MainWindowViewModel : INotifyPropertyChanged
    {
        private List<User> _users;
        public List<User> Users
        {
            get
            {
                return Repository.Users;
            }
        }
 
        private User _selectedUser;
        public User SelectedUser
        {
            get
            {
                return _selectedUser;
            }
            set
            {
                _selectedUser = value;
                OnPropertyChanged("SelectedUser");
                OnPropertyChanged("SelectedItem");
            }
        }
 
        object _selectedItem;
        public object SelectedItem
        {
            get
            {
                if (SelectedUser != null)
                {
                    if (SelectedUser.Type == "KID")
                    {
                        _selectedItem = new KidViewModel(SelectedUser.Id);
                    }
                    else
                    {
                        _selectedItem = new AdultViewModel(SelectedUser.Id);
                    }
                }
                return _selectedItem;
            }
        }
 
        public MainWindowViewModel()
        {
        }
 
        public event PropertyChangedEventHandler PropertyChanged;
 
        protected void OnPropertyChanged(string name)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(name));
            }
        
    }

SearchItemDataTemplateSelector.cs:
public class UserDataTemplateSelector : DataTemplateSelector
    {
        public override DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container)
        {
            string resourceName = "AdultDataTemplate";
            if (item is KidViewModel)
            {
                resourceName = "KidDataTemplate";
            }
            else if (item is AdultViewModel)
            {
                resourceName = "AdultDataTemplate";
            }
            var element = container as FrameworkElement;
            return element.FindResource(resourceName) as DataTemplate;
        }
    }




Thanks a lot!
Regards,
JC
Tags
GridView
Asked by
Jc
Top achievements
Rank 1
Answers by
Rossen Hristov
Telerik team
Jc
Top achievements
Rank 1
Share this question
or