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

RadGridView Row fails to find ancestor on some cases

5 Answers 399 Views
GridView
This is a migrated thread and some comments may be shown as answers.
BENN
Top achievements
Rank 1
BENN asked on 09 Dec 2015, 12:45 PM

This is related to the grid's virtualization. The following code demonstrates the problem

View Models and models:

public class MainViewModel : ViewModelBase
{
    private ObservableCollection<Person> _persons = new ObservableCollection<Person>();
    private DelegateCommand<Person> _deletePersonCommand;
 
    public MainViewModel()
    {
        for (int i = 0; i < 30; i++)
        {
            _persons.Add(new Person()
            {
                FirstName = "Hello" + i,
                LastName = "World" + i,
            });
        }
    }
 
    public ObservableCollection<Person> Persons
    {
        get
        {
            return _persons;
        }
    }
 
    public DelegateCommand<Person> DeletePersonCommand
    {
        get
        {
            if (_deletePersonCommand == null)
            {
                _deletePersonCommand = new DelegateCommand<Person>(deletePerson);
            }
            return _deletePersonCommand;
        }
    }
 
    private void deletePerson(Person person)
    {
        _persons.Remove(person);
    }
}
 
public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

 

 I have excluded the definition of the Delegate Command. It doesn't matter to this problem, since it will also happen with CallMethodAction, for example...

Here is the XAML:

<Window x:Class="GridViewRowRelativeSourceBug.MainWindow"
        Title="MainWindow" Height="250" Width="525">
    <Grid>
        <telerik:RadGridView Name="radGridView1" ShowGroupPanel="False" ShowGroupFooters="False" AutoGenerateColumns="False" ItemsSource="{Binding Persons}">
            <telerik:RadGridView.Columns>
                <telerik:GridViewDataColumn IsReadOnly="True" Header="First Name">
                    <telerik:GridViewDataColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding FirstName}" />
                        </DataTemplate>
                    </telerik:GridViewDataColumn.CellTemplate>
                </telerik:GridViewDataColumn>
                 
                <telerik:GridViewDataColumn IsReadOnly="True" Header="Last Name">
                    <telerik:GridViewDataColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding LastName}" />
                        </DataTemplate>
                    </telerik:GridViewDataColumn.CellTemplate>
                </telerik:GridViewDataColumn>
 
                <telerik:GridViewDataColumn IsReadOnly="True" Header="Delete" Width="80">
                    <telerik:GridViewDataColumn.CellTemplate>
                        <DataTemplate>
                            <Button Content="Delete Me" Command="{Binding RelativeSource={RelativeSource AncestorType=telerik:RadGridView}, Path=DataContext.DeletePersonCommand}" CommandParameter="{Binding}" />
                        </DataTemplate>
                    </telerik:GridViewDataColumn.CellTemplate>
                </telerik:GridViewDataColumn>
            </telerik:RadGridView.Columns>
        </telerik:RadGridView>
    </Grid>
</Window>

 

Please note that instead of having a command for each item, the mainViewModel that contains the items, gets the command and the item to remove as a command parameter.

 

If you run this example, then there are 2 ways to delete the rows, one will work and the other will fail after deleting about 10 rows

 

Way #1, deleting from the start

first delete the items from the start, meaning, click on the "Delete Me" button of the first row, now after the first row was deleted, then click the "Delete Me" of the new first row.

You should be able to delete all the rows in that grid.

 

 

Way #2, deleting from the end:

Scroll to the end of the grid, and click on the "Delete Me" of the last row. Once it was removed, repeat this procedure for about 10 more times (the number of times is related to the number if rows that fits into the view, and I assume that container recycling is one of the causes to this problem).

You should see that at some point, the "Delete Me" will not do anything, and that the deletePerson function is never called.
In the Output window of Visual Studio you will see:

System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='Telerik.Windows.Controls.RadGridView', AncestorLevel='1''. BindingExpression:Path=DataContext.DeletePersonCommand; DataItem=null; target element is 'Button' (Name=''); target property is 'Command' (type 'ICommand')

 

 

I must say that this bug is pretty old, and its weird that no one has reported it yet.

 

 

5 Answers, 1 is accepted

Sort by
0
Stefan Nenchev
Telerik team
answered on 14 Dec 2015, 04:17 PM
Hello Benn, 

Indeed, this appears to be related to RadGridView's virtualization and we managed to replicate it. We'll further investigate the precise reason and whether this can be resolved without intervening with the control's functionality. Meanwhile. I'd suggest adding the main view model as a static resource to the view and binding the command through it in order to avoid the issue.

Regards,
Stefan Nenchev
Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
0
BENN
Top achievements
Rank 1
answered on 15 Dec 2015, 09:23 AM

Hi, there is lots of situations where the VM can't be a static one.

I have implemented a solution specifically for commands using a solution I have found on the net.

 

public class CommandReference : Freezable, ICommand
    {
        public event EventHandler CanExecuteChanged;
        public event EventHandler CommandExecuted;
        public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(CommandReference), new PropertyMetadata(new PropertyChangedCallback(OnCommandChanged)));
         
        public ICommand Command
        {
            get { return (ICommand)GetValue(CommandProperty); }
            set { SetValue(CommandProperty, value); }
        }
 
        #region ICommand Members
        public bool CanExecute(object parameter)
        {
            return (Command != null) ? Command.CanExecute(parameter) : false;
        }
 
        public void Execute(object parameter)
        {
            Command.Execute(parameter);
          
            if (CommandExecuted != null)
                CommandExecuted(this, null);
        }
 
 
        private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            CommandReference commandReference = d as CommandReference;
            if (commandReference != null)
            {
                ICommand oldCommand = e.OldValue as ICommand;
                if (oldCommand != null) oldCommand.CanExecuteChanged -= commandReference.CanExecuteChanged;
 
                ICommand newCommand = e.NewValue as ICommand;
                if (newCommand != null) newCommand.CanExecuteChanged += commandReference.CanExecuteChanged;
            }
        }
        #endregion
 
        #region Freezable
        protected override Freezable CreateInstanceCore()
        {
            return new CommandReference();
        }
        #endregion
    }

 

 And then in the XAML:

 

<UserControl.Resources>
     <commands:CommandReference Command="{Binding DeletePersonCommand}" x:Key="deletePersonCommand"/>
</UserControl.Resources>

 

 

And then reference the static command from the row (So instead of having a static resource VM, we have a static resource command), and the command parameter can be passed just like before.

Ugly, but it works

 

 

 

0
Accepted
Petya
Telerik team
answered on 15 Dec 2015, 05:16 PM
Hello Benn,

You certainly have a point about the view models being static and when further discussing this within the team we agreed there are other cases where such binding would be the desired approach. As at this point we couldn't engage with a specific time-frame for a fix, I'm glad to hear you managed to find a workaround that works for you. You can also subscribe to this item on the public portal to receive updates about this issue.

Regards,
Petya
Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
0
Jacek
Top achievements
Rank 1
answered on 21 Mar 2017, 02:12 PM
I have the same problem as BENN. Is it fixed in 2017.1.222.45? If not when will you fix it.
0
Stefan Nenchev
Telerik team
answered on 24 Mar 2017, 07:50 AM
Hi Jacek,

The behavior has not been investigated at our end, yet. I have increased the priority of the bug report as per your report so we can have a look at it as soon as possible. Unfortunately, I cannot provide a specific time-frame as currently, we are working on other issues with higher priority. I suggest you follow the item in order to receive notifications when there is some progress.

In the meantime, you can use the workaround of using static ViewModels or try the approach that Benn suggested. 

Regards,
Stefan Nenchev
Telerik by Progress
Want to extend the target reach of your WPF applications, leveraging iOS, Android, and UWP? Try UI for Xamarin, a suite of polished and feature-rich components for the Xamarin framework, which you to write beautiful native mobile apps using a single shared C# codebase.
Tags
GridView
Asked by
BENN
Top achievements
Rank 1
Answers by
Stefan Nenchev
Telerik team
BENN
Top achievements
Rank 1
Petya
Telerik team
Jacek
Top achievements
Rank 1
Share this question
or