telerik / UI-For-UWP

This repo contains the source code for Telerik UI for Universal Windows Platform (UWP), which includes 20+ UI controls for developers building UWP applications.
http://www.telerik.com/uwp/
Other
1.16k stars 234 forks source link

Can't select a cell in the DataGrid reliably #450

Open mhmd-azeez opened 4 years ago

mhmd-azeez commented 4 years ago

Description

When I add a row to the datagrid, I want to focus on the first editable cell of the row. This is the code I use for that:

var item = new Item
{
    Barcode = barcodeTextBox.Text,
    Name = $"Item ({barcodeTextBox.Text})",
    Price = _random.Next(1000, 25000),
    Quantity = _random.Next(1, 200),
};

_items.Insert(0, item);

// Sort of a work around for this bug
await Task.Delay(100);

dataGrid.BeginEdit(item);
dataGrid.SelectCell(new Telerik.UI.Xaml.Controls.Grid.DataGridCellInfo(item, quantityColumn));

I have two issues:

  1. If I don't put a task delay before dataGrid.BeginEdit, then it tries to edit the second row in the column. If the grid contains no items, then it will throw an InvalidOperationException "Sequence contains no elements". Here is the full stack trace:
   at System.Linq.Enumerable.First[TSource](IEnumerable`1 source)
   at Telerik.UI.Xaml.Controls.Grid.RadDataGrid.BeginEdit(Object item, ActionTrigger trigger, Object parameter)
   at Telerik.UI.Xaml.Controls.Grid.RadDataGrid.BeginEdit(Object item)
   at RadDataGrid_444.MainPage.barcodeTextBox_KeyUp(Object sender, KeyRoutedEventArgs e)

Note: If I use _items.Add(item); without Task.Delay, it will alwasy throw the exception.

  1. Even after I put I put Task.Delay, It still won't select the desired column, the user still have to use tab to go to the first editable column.

Steps to Reproduce

  1. Add a new item to the datagrid
  2. Try to edit the row and then select a cell using this code snippet:
dataGrid.BeginEdit(item);
dataGrid.SelectCell(new Telerik.UI.Xaml.Controls.Grid.DataGridCellInfo(item, quantityColumn));

Expected Behavior

The added row begins editing and the quantity column is focused.

Actual Behavior

Throws an exception if no Task.Delay is used and doesn't focus on the quantity column.

Basic Information

Screenshots

https://youtu.be/a_3j64aVf5A

Reproduction Link

DataGridBeginEditRepro.zip

Zofware commented 4 years ago

You are not alone. I wish I knew the "proper" way to insert then immediately edit a new item.

I found that simply waiting 100 milliseconds after the insert wasn't always enough. I ended up putting the wait in a loop that attempts to select the new item after the wait, then confirms that the new item is selected. If not, the loop continues up to some reasonable maximum (5 times?). After the new item is successfully selected, then I call ScrollItemIntoView and use the scrollCompletedAction to call BeginEdit.

I admit this workaround is completely crazy. There's got to be a better way.

private async Task EditNewItem(TViewModel newItem)
{
    // Problem: The Telerik RadDataGrid takes a while to fully update after adding a new row.
    // Unfortunately, I don't know how to wait until the grid is ready before starting an edit operation.
    // If I start the edit too soon then all kinds of things get out of sync with the grid.
    // Part of the grid state gets updated when the Xaml "measure" operation occurs.
    // How to know when "measurement" is done?

    try
    {
        int nAttempts = 0;
        do
        {
            await Task.Delay(100);
            DataGrid.SelectItem(newItem);
        } while (
            nAttempts++ < 5 &&
            (
                DataGrid.SelectedItem == null ||
                (DataGrid.SelectedItem as TViewModel)?.PrimaryKey != newItem.PrimaryKey
            ));

        await Task.Delay(100);

        DataGrid.ScrollItemIntoView(newItem, () =>
        {
            DataGrid.BeginEdit(newItem);
        });
    }
    catch (Exception ex)
    {
        Diagnostics.LogException(ex);
    }
}
APopatanasov commented 4 years ago

Hi @encrypt0r , With the current implementation of the control the desired by you scenario of selecting/editing the row that has just been added could only be achieved by using the approach you are using or with the one suggested by @Zofware. The main reason for that is the fact the DataGrid is executing all CollectionChanged events asynchronously. By the the time the BeginEdit method is invoked the control could still be rebuilding and because of that a small delay is needed in order to make sure the control is completely loaded. Currently, I cannot suggest you any better approach.

About your second issue with the focus of the NumericBox. Basically, the control is focused, however the TextBox inside it is not. In order to focus the TextBox when the row is created and is edited I can suggest you the following approach.

First you can create a custom NumericBox. Once it gets the focus you can propagate the focus to the TextBox - that should only be done initially in order to prevent some unexpected behavior. In order to use that custom NumericBox a custom DataGridNumericalColumn should be created as well. Inside it the custom NumericBox editor could be se set as an editor type for the column and focusing should be working as desired. I have modified the sample project in order to demonstrate the described above approach - please, check it: RadDataGrid_444.zip

I will leave the issue open for now. I believe that the DataGrid control could a little bit be improved at least with some API that could tell when the DataGrid is completely loaded and when it will be possible to perform different operations like BeginEdit or selection.

Hope this helps.