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

SelectCell not working

I have a datagrid that's bound to an observable collection of "Percentiles" (basically a double value).  I have an "Add" button that adds a new percentile to the collection and I would like that to result in the newly added cell being selected so the user can immediately type a value without needing to select the cell themselves.

I've wired up the collectionchanged event of the collection and this is being fired correctly.  This then calls the SelectCell method on the datagrid.  However, this always seems to select the cell in the first row, not the added row.  NewItems[0] (which is what I'm using to identify the row) is returning the correct underlying element from the collection so I believe the problem is something to do with the way I'm calling the SelectCell method.

Any idea what I'm doing wrong?

Here is my xaml:-

<Window x:Class="TestGridSelection.MainWindow"

        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:local="clr-namespace:TestGridSelection"

        xmlns:sf="http://schemas.syncfusion.com/wpf"

        mc:Ignorable="d"

        Title="MainWindow" Height="450" Width="800">

    <Grid>

        <Grid.RowDefinitions>

            <RowDefinition Height="*"/>

            <RowDefinition Height="Auto"/>

        </Grid.RowDefinitions>

        <sf:SfDataGrid Name="PercentilesGrid"

                                   HorizontalAlignment="Stretch" VerticalAlignment="Stretch"

                                   ItemsSource="{Binding LevelPercentiles}"

                                   AutoGenerateColumns="True" AutoGenerateColumnsMode="SmartReset"

                                   GridValidationMode="InEdit"

                                   AllowResizingColumns="True" ColumnSizer="Auto"

                                   ShowRowHeader="True" SelectionMode="Multiple"

                                   AllowEditing="True" EditTrigger="OnTap"

                                   AllowDeleting="True" SelectionUnit="Cell"

                                   Grid.Row="0" Margin="2,2,2,2">

            <sf:SfDataGrid.Columns>

                <sf:GridTemplateColumn sf:FocusManagerHelper.WantsKeyInput="True">

                    <sf:GridTemplateColumn.CellTemplate>

                        <DataTemplate>

                            <Button Content="x"

                                            sf:FocusManagerHelper.FocusedElement="True"

                                            sf:VisualContainer.WantsMouseInput="True"

                                            ClickMode="Press"

                                            Click="DeleteLevelPercentile"

                                            Width="Auto"

                                            ToolTip="Delete Level Percentile"

                                            Tag="{Binding}" />

                        </DataTemplate>

                    </sf:GridTemplateColumn.CellTemplate>

                </sf:GridTemplateColumn>

            </sf:SfDataGrid.Columns>

        </sf:SfDataGrid>

        <Button Click="AddNewLevelPercentile" Grid.Row="1">Add</Button>

    </Grid>

</Window>



And the code behind:-

using System;

using System.Collections.Generic;

using System.Collections.ObjectModel;

using System.Collections.Specialized;

using System.ComponentModel;

using System.ComponentModel.DataAnnotations;

using System.Linq;

using System.Runtime.CompilerServices;

using System.Text;

using System.Threading.Tasks;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Data;

using System.Windows.Documents;

using System.Windows.Input;

using System.Windows.Media;

using System.Windows.Media.Imaging;

using System.Windows.Navigation;

using System.Windows.Shapes;


namespace TestGridSelection

{

    /// <summary>

    /// Interaction logic for MainWindow.xaml

    /// </summary>

    public partial class MainWindow : Window

    {

        public ViewModel ViewModel;

        public MainWindow()

        {

            InitializeComponent();

            ViewModel = new ViewModel() { LevelPercentiles = new LevelPercentileConfigurations() };

            DataContext = ViewModel;


            PercentilesGrid.Loaded += PercentilesGrid_Loaded;

        }



        public void AddNewLevelPercentile(object sender, RoutedEventArgs e)

        {

            (this.DataContext as ViewModel).LevelPercentiles.Add(new LevelPercentileConfiguration() { Percentile = 0 });

        }

        private void DeleteLevelPercentile(object sender, EventArgs e)

        {

            (this.DataContext as ViewModel).LevelPercentiles.Remove((LevelPercentileConfiguration)(sender as Button).Tag);

        }


        private void PercentilesGrid_Loaded(object sender, System.Windows.RoutedEventArgs e)

        {

            this.PercentilesGrid.View.Records.CollectionChanged += PercentilesGrid_CollectionChanged;

        }


        private void PercentilesGrid_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)

        {

            if (e.Action == NotifyCollectionChangedAction.Add)

            {

                PercentilesGrid.SelectCell(e.NewItems[0], PercentilesGrid.Columns[1]);

            }

        }

    }


    public class ViewModel

    {

        public LevelPercentileConfigurations LevelPercentiles { get; set; }

    }


    public class LevelPercentileConfigurations : ObservableCollection<LevelPercentileConfiguration>

    { }


    public class LevelPercentileConfiguration : INotifyPropertyChanged

    {

        private double mPercentile;

        [Range(0, 100, ErrorMessage = "Invalid Percentile Value")]

        public double Percentile { get { return mPercentile; } set { mPercentile = value; NotifyPropertyChanged(nameof(Percentile)); } }


        #region INotifyPropertyChanged

        public event PropertyChangedEventHandler PropertyChanged;

        protected void NotifyPropertyChanged([CallerMemberName] String propertyName = "")

        {

            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

        }

        #endregion

    }

}



5 Replies 1 reply marked as answer

VS Vijayarasan Sivanandham Syncfusion Team February 17, 2023 02:07 PM UTC

Hi Declan Hillier,

The SfDataGrid.View.Records.CollectionChanged event is triggered when a new row is added to the SfDataGrid. If the SelectCell method is called within this event, it can cause issues with the background thread updating the contents of the SfDataGrid while the selection is not updated.

However, you can resolve the reported problem by using the Dispatcher and MoveCurrentCell method. Refer to the below code snippet,

private void PercentilesGrid_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)

{

    if (e.Action == NotifyCollectionChangedAction.Add)

    {

        PercentilesGrid.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new Action(() =>

        {

            //Select the Specific Cell. If Selection is Multiple or Extended

            //the selection only moved to the NewRow while calling the SelectCell method

            PercentilesGrid.SelectCell(e.NewItems[0], PercentilesGrid.Columns[1]);

 

 

            //We need to move the current cell to edit the currently added row in SfDataGrid 

            //By using the MoveCurrentCell helper method

 

            //Here get the RowIndex of added record by using the ResolveToRowIndex helper method

            var rowIndex = PercentilesGrid.ResolveToRowIndex(e.NewItems[0]);

 

            //Here get the column index of which need to enter edit mode

            var columnIndex = PercentilesGrid.Columns.IndexOf(PercentilesGrid.Columns[1]);

 

            //Returns start column index of the VisibleColumn.

            //For ex: RowHeader displayed case return the start index of column that data displayed in SfDataGrid

            var startColumnINdex = PercentilesGrid.ResolveToStartColumnIndex();

 

            //Here pass the column and rowindex which is need enter edit while pressing the key

            var rowColumnIndex = new RowColumnIndex(rowIndex, (startColumnINdex + columnIndex));

 

            //Move the current cell to specific RowColumnIndex

            PercentilesGrid.MoveCurrentCell(rowColumnIndex, false);

 

            if (PercentilesGrid.SelectionController != null && PercentilesGrid.SelectionController.CurrentCellManager != null

             && PercentilesGrid.SelectionController.CurrentCellManager.CurrentCell != null && PercentilesGrid.SelectionController.CurrentCellManager.CurrentCell.Element != null)

               //Wile moving the current cell programmatically focus not set in GridCell. So, we need to set the focus to GridCell.

                PercentilesGrid.SelectionController.CurrentCellManager.CurrentCell.Element.Focus();

        }));

 

    }

 


For more information related to the helper method, refer to the below user guide documentation link,

UG Link: https://help.syncfusion.com/wpf/datagrid/helpers

https://help.syncfusion.com/wpf/datagrid/selection#process-current-cell

API Link: MoveCurrentCell

Find the sample in the attachment.

Regards,
Vijayarasan S

If this post is helpful, please consider Accepting it as the solution so that other members can locate it more quickly.


Attachment: Sample_59eeb055.zip


DH Declan Hillier replied to Vijayarasan Sivanandham February 17, 2023 02:53 PM UTC

That worked perfectly.


Just checking I've understood what you're doing here. You're using the Grid's dispatcher thread to get around any conflicts on the main thread. In that dispatcher thread you are explicitly calculating, moving to and setting focus to the desired cell.

Is that basically what's happening?


I am still getting a slight problem.  This is selecting the cell correctly.  When you click the first time, it sets the background of the cell to grey - presumably this is to do with the selected state of the cell?  A second click adds a new cell, greys it out and clears the previous cell back to a white background.  Subsequent clicks add a new, grey cell, but do not clear the grey from the previous cell.  So I end up with a grid where the first cell is white and all others are grey.

Clicking in any of the cells seems to toggle back and forward between grey and white.

Personally, I'd rather it got rid of the grey and always set the background to white, regardless of its state.  Is that possible?



VS Vijayarasan Sivanandham Syncfusion Team February 20, 2023 02:01 PM UTC

Declan Hillier,

Find the responses to your queries below.

Queries

Responses


Just checking I've understood what you're doing here. You're using the Grid's dispatcher thread to get around any conflicts on the main thread. In that dispatcher thread you are explicitly calculating, moving to and setting focus to the desired cell.

Is that basically what's happening?

Our aim is to perform this process (making the newly added cell selected by using the SelectCell method) last. For that, we have done this inside the dispatcher. The CollectionChanged event gets the notification once the bounded collection data is changed before updating the UI. However, as per your requirement, we need to perform the above-mentioned process after the UI is updated. For that, we have used the Dispatcher. Basically, the code block that is present in the dispatcher works after all other codes have been processed.

 

I'd rather it got rid of the grey and always set the background to white, regardless of its state.  Is that possible?


In the previous update, we shared the suggestion of using the MoveCurrentCell method with these parameters to maintain the selection and move the current cell. Refer to the code snippet below.

//Move the current cell to specific RowColumnIndex with selection

PercentilesGrid.MoveCurrentCell(rowColumnIndex, false);


If you don't want to maintain the selection, then remove the Boolean parameter, as shown in the code snippet below.

 

//Move the current cell to specific RowColumnIndex without selection

PercentilesGrid.MoveCurrentCell(rowColumnIndex);


Find the modified sample in the attachment.


Regards,

Vijayarasan S


If this post is helpful, please consider Accepting it as the solution so that other members can locate it more quickly.


Attachment: Modified_Sample_e6321bc.zip

Marked as answer

DH Declan Hillier replied to Vijayarasan Sivanandham February 23, 2023 08:39 AM UTC

That is perfect and does exactly what I want.  (Looking at the parameter name it was kind of obvious in hidsight)



VS Vijayarasan Sivanandham Syncfusion Team February 24, 2023 06:29 AM UTC

Declan Hillier,


We are glad to know that the reported problem has been resolved at your end. Please let us know if you have any further queries on this. We are happy to help you😊.


Loader.
Up arrow icon