Как запустить дорогостоящий процесс на SelectionChanged, но не при быстрой прокрутке. (Обработчик события задержки)? - PullRequest
1 голос
/ 05 мая 2020

Мне приходится запускать то, что может быть довольно медленной задачей каждый раз, когда запускается событие SelectionChanged объекта DataGrid.

У меня проблема в том, что мне нужно поддерживать отзывчивость приложения, и если пользователь очень быстро прокручивает с помощью клавиш со стрелками, я не хочу выполнять задачу для каждого элемента. Только предмет, на котором они останавливаются. (Надеюсь, это имеет смысл!)

Я установил очень простой c пример для демонстрации, который отображает список слов в DataGrid, а затем, когда вы прокручиваете их, он добавляет их в ListView .

Это то, что я пробовал до сих пор:

CancellationTokenSource cts;
private bool loading;
private async void dgData_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (loading)
    {
        cts.Cancel();
        return;
    }

    cts = new CancellationTokenSource();

    loading = true;
    var x = dgData.SelectedItem.ToString();

    await Task.Run(async () =>
    {
        Thread.Sleep(1000); //Wait a second to see if scrolling quickly...
        await ExpensiveProcess(x);
    });

    loading = false;
}

private async Task ExpensiveProcess(string text)
{
    if (cts.IsCancellationRequested)
    {
        loading = false;
        return;
    }

    await Task.Factory.StartNew(() =>
    {
        //Expensive process will be done here...
    });

    Application.Current.Dispatcher.Invoke(() =>
    {
        lvwItems.Items.Add(text);
    });
    loading = false;
}

Кажется, это работает в том факте, что если быстро стрелка вниз, он пропускает элементы, но когда я останавливаюсь на одном и хочу его запустить не получается?

Где я ошибаюсь? Это даже лучший подход? Мы будем благодарны за любые советы и будем рады предоставить дополнительную информацию. Заранее спасибо.

ОБНОВЛЕНИЕ:

Я нашел видео на YouTube, в котором предлагалось сделать это, и оно работает так, как я ожидал, поэтому сейчас я собираюсь это, но пока оставляем вопрос открытым для обратной связи.

Создайте таймер, который будет запускать дорогостоящий процесс и установить интервал на что-то низкое, но не слишком медленное, чтобы нажималась клавиша.

var myTimer = new DispatcherTimer();
myTimer.Interval = TimeSpan.FromSeconds(1);
myTimer.Tick += MyTimer_Tick

По событию тика таймера запускает длительный процесс.

private void MyTimer_Tick(object sender, EventArgs e)
{
    var x = dgData.SelectedItem.ToString();
    Task.Run(async () => 
    {
        Thread.Sleep(1000); //Needs to be removed
        await ExpensiveProcess(x);
    });
}

Затем в обычном событии SelectionChanged просто останавливайте и запускайте таймер. Также не забудьте остановить таймер в конце длительного процесса.

Ответы [ 2 ]

1 голос
/ 07 мая 2020

Вы можете запустить таймер в обработчике событий SelectionChanged, а затем проверить, выбран ли элемент по-прежнему, когда таймер истечет.

Если это так, вы вызываете длительный метод с CancellationToken, который вы отменяете, если происходит другой выбор.

Следующий пример кода должен дать вам идею:

private CancellationTokenSource _cts = null;
...
dataGrid.SelectionChanged += async(ss, ee) =>
{
    //cancel any previous long running operation
    if (_cts != null)
    {
        _cts.Cancel();
        _cts.Dispose();
    }
    _cts = new CancellationTokenSource();
    //store a local copy the unique id or something of the currently selected item
    var id = (dataGrid.SelectedItem as TestItem).Id;
    //wait a second and a half before doing anything...
    await Task.Delay(1500);
    //if no other item has been selected since {id} was selected, call the long running operation
    if (_cts != null && id == (dataGrid.SelectedItem as TestItem).Id)
    {
        try
        {
            await LongRunningOperation(id, _cts.Token);
        }
        finally
        {
            _cts.Cancel();
            _cts.Dispose();
            _cts = null;
        }
    }
};
0 голосов
/ 05 мая 2020

Вместо использования события, если вы используете привязку данных к свойству SelectedItem, вы можете легко добиться этого с помощью свойства Delay. Delay будет ждать n миллисекунд перед обработкой изменения.

<DataGrid ...
    SelectedItem="{Binding SelectedItem, Delay=1000}">
    ...
</DataGrid>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...