We use cookies to give you the best experience on our website. If you continue to browse, then you agree to our privacy policy and cookie policy. Image for the cookie policy date

sfDataGrid the MVVM way

Hi,
I am very new to WPF and MVVM.
I am trying to bind an event RowValidated the MVVM way but I couldn't find any reference to it. This is what I did:

<Syncfusion:SfDataGrid ItemsSource="{Binding MembersList, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" AutoGenerateColumns="False" RowValidated="{Binding EditMemberCommand}">
            <Syncfusion:SfDataGrid.Columns>
                <Syncfusion:GridTextColumn MappingName="FirstName" HeaderText="First Name" AllowSorting="True" AllowEditing="True"/>
                <Syncfusion:GridTextColumn MappingName="LastName" HeaderText="Last Name" AllowSorting="True"/>
            </Syncfusion:SfDataGrid.Columns>
        </Syncfusion:SfDataGrid>

But when I run the application, I get the following error:

Unable to cast object of type 'System.Reflection.RuntimeEventInfo' to type 'System.Reflection.MethodInfo'

Here is my RelayCommand

public class RelayCommand : ICommand
    {
        private Action<object> execute;
        private Func<object, bool> canExecute;
        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
        public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
        {
            this.execute = execute;
            this.canExecute = canExecute;
        }
        public bool CanExecute(object parameter)
        {
            return this.canExecute == null || this.canExecute(parameter);
        }
        public void Execute(object parameter)
        {
            this.execute(parameter);
        }
    }

Thanks

21 Replies

JG Jai Ganesh S Syncfusion Team January 12, 2017 05:58 AM UTC

Hi Andy, 
You can achieve your requirement for binding the event in MVVM by using the below code, 
<i:Interaction.Triggers> 
                <i:EventTrigger EventName="RowValidated"> 
                    <i:InvokeCommandAction Command="{Binding Path=RowValidated}" CommandParameter="{Binding}"/> 
                </i:EventTrigger> 
 </i:Interaction.Triggers> 
 
private ICommand _rowValidated; 
        public ICommand RowValidated 
        { 
            get 
            { 
                return _rowValidated ?? (_rowValidated = new RelayCommand(CanExecute)); 
            } 
        } 
        private bool _canExecute; 
        public void CanExecute(object obj) 
        { 
             
        } 
 
Regards, 
Jai Ganesh S 
 



AJ Andy Johnson January 12, 2017 03:22 PM UTC

Hi Jai Ganesh,
I tried your code and it gets into my Save and Can Save methods of my ICommand but the problem is: it gets me all the objects in my list. I want to get an object which was being modified so that I can write it to the database.
Can you please modify your code to show me, how can I get the changed object?
Thanks


AJ Andy Johnson January 12, 2017 03:35 PM UTC

I am also trying the event in Context menu, it doesn't seem to be triggering as well

<Syncfusion:SfDataGrid.RecordContextMenu>
                <ContextMenu Style="{x:Null}">
                    <MenuItem Header="Edit">
                        <i:Interaction.Triggers>
                            <i:EventTrigger EventName="Click">
                                <i:InvokeCommandAction Command="{Binding Path=EditMemberCommand}" CommandParameter="{Binding}"/>
                            </i:EventTrigger>
                        </i:Interaction.Triggers>
                    </MenuItem>
                </ContextMenu>
            </Syncfusion:SfDataGrid.RecordContextMenu>

I also tried the following and that is also not working.
<Syncfusion:SfDataGrid.RecordContextMenu>
                <ContextMenu Style="{x:Null}">
                    <MenuItem Header="Edit" Command="{Binding EditMemberCommand}" CommandParameter="{Binding}"/>
                </ContextMenu>
            </Syncfusion:SfDataGrid.RecordContextMenu>


SR Sivakumar R Syncfusion Team January 12, 2017 04:06 PM UTC

Hi Andy, 
 
Please find the details below, 
 
How to pass object being edited to command from RowValidated 
You can get the currently editing object from SfDataGrid.CurrentItem. You can pass this as CommandParameter using the below code. 
<Syncfusion:SfDataGrid x:Name="datagrid"  
                        AllowEditing="True"  
                        SelectionUnit="Cell" 
                        AutoGenerateColumns="False" 
                        AllowFiltering="True" 
                        ShowGroupDropArea="True" 
                        ItemsSource="{Binding GDCSource}" 
                        AllowSorting="True"> 
    <i:Interaction.Triggers> 
        <i:EventTrigger EventName="RowValidated"> 
            <i:InvokeCommandAction Command="{Binding Path=RowValidated}" CommandParameter="{Binding ElementName=datagrid, Path=CurrentItem}"/> 
        </i:EventTrigger> 
    </i:Interaction.Triggers> 
</Syncfusion:SfDataGrid> 
 
How to bind command in ViewModel to SfDataGrid.RecordContextMenu’s MenuItems command 
SfDataGrid sets GridRecordContextMenuInfo as DataContext to RecordContextMenu. You can bind command in ViewModel as in the below code snippet, 
 
<Syncfusion:SfDataGrid ItemsSource="{Binding GDCSource}" 
                        AllowSorting="True"> 
    <Syncfusion:SfDataGrid.RecordContextMenu> 
        <ContextMenu Style="{x:Null}"> 
            <MenuItem Command="{Binding DataGrid.DataContext.CMenuCommand}" Header="Header"/> 
        </ContextMenu> 
    </Syncfusion:SfDataGrid.RecordContextMenu> 
</Syncfusion:SfDataGrid> 
 
Sample: 
 
Reference, 
 
Thanks, 
Sivakumar 



AJ Andy Johnson January 12, 2017 04:47 PM UTC

Sivakumar,
The command on RowValidated is working fine now. Thanks for your input on it.
For now, I am trying to use the same command for the contextMenu but it is not working.

One thing I don't understand is: Why you have set the command to be DataGrid.DataContext.CMenuCommand

because CMenuCommand  is available at Window DataContext and not the DataGrid DataContext level.


Here is my XAML

 <Syncfusion:SfDataGrid x:Name="datagrid"
                            AllowEditing="True"
                            SelectionUnit="Cell"                       
                            AllowFiltering="True"
                           AutoGenerateColumns="True"
                            ShowGroupDropArea="True"
                            ItemsSource="{Binding GDCSource}"
                            AllowSorting="True">
        <Syncfusion:SfDataGrid.RecordContextMenu>
            <ContextMenu Style="{x:Null}">
                <MenuItem Command="{Binding DataGrid.DataContext.CMenuCommand}" Header="Header"/>
            </ContextMenu>
        </Syncfusion:SfDataGrid.RecordContextMenu>
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="RowValidated">
                <i:InvokeCommandAction Command="{Binding Path=RowValidated}" CommandParameter="{Binding ElementName=datagrid, Path=CurrentItem}"/>
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </Syncfusion:SfDataGrid>




AJ Andy Johnson January 12, 2017 04:49 PM UTC

Sorry,
By mistake, I copied and pasted your XAML code:

Here is mine:
<Syncfusion:SfDataGrid ItemsSource="{Binding MembersList, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" AutoGenerateColumns="False" x:Name="MembersGrid">

            <i:Interaction.Triggers>
                <i:EventTrigger EventName="RowValidated">
                    <i:InvokeCommandAction Command="{Binding Path=EditMemberCommand}" CommandParameter="{Binding ElementName=FamilyMembersGrid, Path=CurrentItem}"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>


            <Syncfusion:SfDataGrid.Columns>
                <Syncfusion:GridTextColumn MappingName="FirstName" HeaderText="First Name" AllowSorting="True" AllowEditing="True" SetCellBoundValue="True" CellTemplate="{StaticResource celltemplate}"/>
                <Syncfusion:GridTextColumn MappingName="LastName" HeaderText="Last Name" AllowSorting="True"/>               
                <Syncfusion:GridDateTimeColumn MappingName="DateOfBirth" HeaderText="Birth Date" AllowSorting="True"/>
                <Syncfusion:GridTextColumn MappingName="City" HeaderText="City" AllowSorting="True"/>
            </Syncfusion:SfDataGrid.Columns>
            <Syncfusion:SfDataGrid.RecordContextMenu>
                <ContextMenu Style="{x:Null}">
                    <MenuItem Header="Edit" Command="{Binding DataGrid.DataContext.EditMemberCommand}">
                       
                    </MenuItem>
                </ContextMenu>
            </Syncfusion:SfDataGrid.RecordContextMenu>
        </Syncfusion:SfDataGrid>


SR Sivakumar R Syncfusion Team January 13, 2017 12:33 PM UTC

Hi Andy, 
 
As mentioned in my previous update, SfDataGrid sets GridRecordContextMenuInfo as DataContext to RecordContextMenu. So, to bind the Command in ViewModel to MenuItem, I have accessed SfDataGrid.DataContext. If you set DataContext to Window, then the same DataContext passed to all the elements in VisualTree until you explicitly set DataContext for particular element. So, we can access ViewModel (which is DataContext of Window) from SfDataGrid.DataContext also. 
 
Are you seeing different behavior when running the sample provided in last update? 
 
Thanks, 
Sivakumar 



AJ Andy Johnson January 13, 2017 02:39 PM UTC

Sivakumar,
Thanks for your response.
Looks like Command is working now. New problem is Command Parameter doesn't pass the selected object data. I tried CurrentItem, SelectedItem, SelectedIndex but no luck.
In Save and CanSave method, object passed is null.

Same Command is working fine for RowValidated and passes the CurrentItem object fine as per our previous discussion.

Sorry to give you trouble but since I started MVVM way, I don't want to do anything non-mvvm way otherwise, Click event is working already on context menu item. I tested it yesterday.

Here is my XAML

<MenuItem Header="Edit" Command="{Binding DataGrid.DataContext.EditMemberCommand}" CommandParameter="{Binding ElementName=FamilyMembersGrid, Path=CurrentItem}">
                       
 </MenuItem>

Thanks


AJ Andy Johnson January 14, 2017 01:10 AM UTC

Sivakumar,
I am also facing one more issue here. I have created my custom style in a resource dictionary as seen below (file ="/Styles/sfDataGridStyles.xaml"):

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:syncfusion="clr-namespace:Syncfusion.UI.Xaml.Grid;assembly=Syncfusion.SfGrid.WPF">
    <Style x:Key="SfDataGridStyle1" TargetType="{x:Type syncfusion:SfDataGrid}">
     .
     .
     .
    </Style>
</ResourceDictionary>

Here is my App.Caml

<ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/Styles/sfDataGridStyles.xaml" />
            </ResourceDictionary.MergedDictionaries>

            My other styles to follow
</ResourceDictionary>

Now when I use this style in my user control, it gets displayed in my IDE in design view but when I run the application it doesn't display..
Here is where I am using my style:

<syncfusion:SfDataGrid ItemsSource="{Binding MembersList, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" AutoGenerateColumns="False" x:Name="FamilyMembersGrid" HorizontalAlignment="Center" Margin="0,20,0,10" SelectionUnit="Cell" AllowFiltering="True" ShowGroupDropArea="True" Style="{DynamicResource SfDataGridStyle1}">

What is that I am doing wrong here?

Thanks
Andy



SR Sivakumar R Syncfusion Team January 16, 2017 08:36 AM UTC

Hi Andy, 
 
Please find the answers for your queries, 
 
How to pass CommandParameter to ContextMenu Command 
SfDataGrid sets GridRecordContextMenuInfo as DataContext to RecordContextMenu. You can pass GridRecordContextMenuInfo as command parameter using below code, so you can access all the properties of GridRecordContextMenuInfo in ViewModel. 
<Syncfusion:SfDataGrid x:Name="datagrid" > 
    <Syncfusion:SfDataGrid.RecordContextMenu> 
        <ContextMenu Style="{x:Null}"> 
                <MenuItem Command="{Binding DataGrid.DataContext.CMenuCommand}" CommandParameter="{Binding}" Header="Header"/> 
        </ContextMenu> 
    </Syncfusion:SfDataGrid.RecordContextMenu> 
</Syncfusion:SfDataGrid> 
 
If you want to pass only CurrentItem of SfDataGrid as parameter then your requirement can be achieved using below code snippet, 
<Syncfusion:SfDataGrid x:Name="datagrid" > 
    <Syncfusion:SfDataGrid.RecordContextMenu> 
        <ContextMenu Style="{x:Null}"> 
                <MenuItem Command="{Binding DataGrid.DataContext.CMenuCommand}" CommandParameter="{Binding Path=DataGrid.CurrentItem}" Header="Header"/> 
        </ContextMenu> 
    </Syncfusion:SfDataGrid.RecordContextMenu> 
</Syncfusion:SfDataGrid> 
 
We have modified the sample based on this requirement.  
 
Custom style for SfDataGrid not working 
We have tried to replicate the problem. But its working fine as expected. Please refer the below working sample for both use cases, 
 
Sample: 
 
Thanks, 
Sivakumar 



AJ Andy Johnson January 16, 2017 11:04 PM UTC

Sivakumar,
Thanks for your response.
I managed to get the CurrentItem object upon clicking the context menu item but for some reason, I can't get the style to work unless I add it to as UserControl's resources.
I tried your code to move the resources at the app.xaml level and it is working.. That is not a big problem. Only I will have to do is, I will have to add it at the UserControl level instead at global level.

I just have one more question on Grid. Currently I have only four data in my table to display in a grid (It will grow over time though) and Grid is displayed using the RowDefinition to occupy  rest of the space on a screen

<RowDefinition Height="Auto"></RowDefinition> -----> for heading
            <RowDefinition Height="*"></RowDefinition>  -----> for a grid
            <RowDefinition Height="Auto"></RowDefinition> ---> for a button


Now the problem is, even though , I have a only four rows, grid occupies the complete space. How can I instruct Grid to occupy the space that are of only records height and then expand to * and provide stroller later when the data grows?

Thanks


FP Farjana Parveen Ayubb Syncfusion Team January 17, 2017 06:39 AM UTC

Hi Andy,  
 
Thank you for your response. 
 
We have analyzed your query, you can achieve your requirement by setting Height for RowDefinition as Auto which is used for SfDataGrid. And you have to set the MaxHeight for SfDataGrid for Virtualization. 
 
Please find the code example and sample in the following location, 
 
Code Example (RowDefinition) 
 
<Grid.RowDefinitions> 
    <RowDefinition Height="Auto"></RowDefinition> 
    <RowDefinition Height="Auto"></RowDefinition> 
    <RowDefinition Height="Auto"></RowDefinition> 
</Grid.RowDefinitions> 
 
Code Example (MaxHeight)  
 
void sfdatagrid_Loaded(object sender, RoutedEventArgs e) 
{ 
    this.sfdatagrid.MaxHeight = this.Height; 
} 
 
 
Sample Location: Height 
 
Regards, 
Farjana Parveen A 





AJ Andy Johnson January 30, 2017 07:23 PM UTC

Hi,
I have run into one more issue of data not populating into sfDataGrid. I have a similar grid where data is being populated just fine.

Here is my View model property which contains OnservableCollection<Institution>
I conformed that it has few rows of data in it..

ViewModel Class property declaration
 public ObservableCollection<Institution> InstitutionList { get; set; }

In my VieModel Constructor,

 InstitutionList = new ObservableCollection<Institution>(institutionOperations.GetAllInstitutions());

Here is my very simple DataGrid xaml for my user control
 <UserControl.DataContext>
        <viewModels:AllInstitutionsViewModel />
    </UserControl.DataContext>

<Syncfusion:SfDataGrid Grid.Row="1" ItemsSource="{Binding Path=InstitutionList, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" AutoGenerateColumns="False" x:Name="InvestorsGrid" HorizontalAlignment="Center" Margin="10,20,10,10" SelectionUnit="Row"  AllowFiltering="True" ShowGroupDropArea="True" Style="{DynamicResource SfDataGridStyle1}">
            <Syncfusion:SfDataGrid.Columns>
                <Syncfusion:GridTextColumn MappingName="Name" HeaderText="Name" AllowSorting="True" HorizontalHeaderContentAlignment="Stretch"/>
            </Syncfusion:SfDataGrid.Columns>
        </Syncfusion:SfDataGrid>

When I run this, the grid is completely empty. It even doesn't have the column called "Name"

Can someone please point me what I am doing wrong here.

As I said, I have similar model which works perfectly fine.




AJ Andy Johnson replied to Andy Johnson January 30, 2017 10:29 PM UTC

Hi,
I have run into one more issue of data not populating into sfDataGrid. I have a similar grid where data is being populated just fine.

Here is my View model property which contains OnservableCollection<Institution>
I conformed that it has few rows of data in it..

ViewModel Class property declaration
 public ObservableCollection<Institution> InstitutionList { get; set; }

In my VieModel Constructor,

 InstitutionList = new ObservableCollection<Institution>(institutionOperations.GetAllInstitutions());

Here is my very simple DataGrid xaml for my user control
 <UserControl.DataContext>
        <viewModels:AllInstitutionsViewModel />
    </UserControl.DataContext>

<Syncfusion:SfDataGrid Grid.Row="1" ItemsSource="{Binding Path=InstitutionList, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" AutoGenerateColumns="False" x:Name="InvestorsGrid" HorizontalAlignment="Center" Margin="10,20,10,10" SelectionUnit="Row"  AllowFiltering="True" ShowGroupDropArea="True" Style="{DynamicResource SfDataGridStyle1}">
            <Syncfusion:SfDataGrid.Columns>
                <Syncfusion:GridTextColumn MappingName="Name" HeaderText="Name" AllowSorting="True" HorizontalHeaderContentAlignment="Stretch"/>
            </Syncfusion:SfDataGrid.Columns>
        </Syncfusion:SfDataGrid>

When I run this, the grid is completely empty. It even doesn't have the column called "Name"

Can someone please point me what I am doing wrong here.

As I said, I have similar model which works perfectly fine.



Well,
It was working but with one issue. If I have exact one record in table, datagrid was not showing any columns and that one row. As soon as I added one more record to a table, it started showing both the rows.


JG Jai Ganesh S Syncfusion Team January 31, 2017 01:05 PM UTC

Hi Andy, 
We have prepared a sample to load at one item initially with one column and then add more number of rows but we are unable to reproduce the issue. 
Could you please modify the above sample to replicate the issue along with replication procedure or please share the entire xaml and c# code. This would be more helpful for us to analyze further. 
Regards, 
Jai Ganesh S  



AJ Andy Johnson January 31, 2017 10:39 PM UTC

Jai Ganesh,
I went through your sample and I couldn't reproduce the thing that I was running into. The problem is, my setup is pretty complex. I am using a separate API project for Database and also using entity framework and annotation for model validation for SQLite database. It is hard to put everything into one project and reproduce the same error. But as I mentioned, as soon as I added the second row into the database, it started showing both rows and after that, more rows is not a problem anymore.

I still will try to put everything in one project and see if I  can reproduce the same behavior. I still consistently can reproduce it in my development project.

I also wanted to ask, if I can add the Edit link to the Datagrid? because, currently  with SelectionUnit of either Cell or Any, you have to first select the cell and then hyperlink inside the cell is clickable. So it is a two step process to click on link in the cell. I want to make it so that it is clickable without selecting the cell first.

Thanks


JG Jai Ganesh S Syncfusion Team February 1, 2017 09:13 AM UTC

Hi Andy, 

By default in SfDataGrid the GridHyperLink column fired the link while clinking the cell at first time. 

Sample: 

http://www.syncfusion.com/downloads/support/directtrac/general/ze/Gridhyperlinkcolumn-519990559.zip 

Could you please check this in your side and if still the this occurs then please modify the sample replicate the issue. This would be more helpful fos us to provide the better solution. 

Regards, 

Jai Ganesh S 




AJ Andy Johnson February 1, 2017 04:12 PM UTC

Jai Ganesh,
How do I bind an event to GridHyperlinkColumn? I tried Binding and BindingParameter in the XAML tag but it is not available. In your provided sample, Binding is not implemented. I want to bind the click event to a RelayCommand I have in my ViewModel. Also I don't want the MappingName. Instead, I just want to display "Edit" with hyperlink and then on click, it should fire my ViewModel Binding.
I also looked at the online documentation for the GridHyperlinkColumn but it doesn't implement the MVVM way.

Thanks


JG Jai Ganesh S Syncfusion Team February 2, 2017 05:00 AM UTC

Hi Andy, 
When you click the GridHyperLinkColumn, the CurrentCellRequesNavigate event will be fired. We have implemented this in MVVM by binding through viewmodel using evet trigger like below, 
<Syncfusion:SfDataGrid AutoGenerateColumns="False"  HorizontalAlignment="Left" Name="datagrid"  AllowEditing="True" ItemsSource="{Binding model}"    VerticalAlignment="Top" > 
            <i:Interaction.Triggers> 
                <i:EventTrigger EventName="CurrentCellRequestNavigate"> 
                    <i:InvokeCommandAction Command="{Binding Path=CurrentCellRequestNavigateCommand}" CommandParameter="{Binding}"/> 
                </i:EventTrigger> 
            </i:Interaction.Triggers> 
</Syncfusion:SfDataGrid> 
 
private RelayCommand _editCommand; 
        public ICommand CurrentCellRequestNavigateCommand 
        { 
            get 
            { 
                if (_editCommand == null) 
                { 
                    _editCommand = new RelayCommand(param => this.LoadUser(param)); 
                } 
                return _editCommand; 
            } 
        } 
 
        public void LoadUser(object ob) 
        { 
            //Write your code here 
        } 
 
Sample: http://www.syncfusion.com/downloads/support/directtrac/general/ze/Gridhyperlinkcolumn1967037921.zip 
Regards, 
Jai Ganesh S 



AJ Andy Johnson February 2, 2017 05:59 PM UTC

Thanks Jai Ganesh,
I really appreciate your help. I now know how can I use regular event in MVVM.




JG Jai Ganesh S Syncfusion Team February 3, 2017 05:19 AM UTC

Hi Andy, 
Thank you for the update. 
Please let us know if you need further assistance on this. 
Regards, 
Jai Ganesh S 


SIGN IN To post a reply.
Loader.
Up arrow icon