WPF DataGrid SelectedItem странное поведение - PullRequest
0 голосов
/ 16 июня 2020

Мы наблюдаем странное поведение свойства SelectedItem в нашем DataGrid. Некоторая справочная информация:

DataGrid отображает результат запроса к нашей базе данных. Есть кнопка, которая позволяет пользователю вручную обновить sh результаты в DataGrid. Существует механизм автоматического обновления sh, благодаря которому результаты будут автоматически обновляться sh каждые 30 секунд.

Мы видим, что свойство SelectedItem всегда становится индексом 0 ItemsSource для Datagrid когда происходит автоматическое обновление sh. Но мы хотим, чтобы текущая выбранная строка оставалась выбранной строкой после refre sh. Однако, если пользователь вручную нажимает refre sh, выбранная строка остается такой же после refre sh, что странно, потому что тот же код работает для refre sh logi c. И да, у нас есть код, который запоминает текущий выбранный элемент, который затем снова устанавливается после завершения sh refre.

Вот некоторые из соответствующих кодов:

<UserControl.Resources>         
    <CollectionViewSource Source="{Binding DataGridResults}" x:Key="ReferralItemsSource"/>
</UserControl.Resources>

<customControls:CustomDataGrid x:Name="GridControl"
                              ItemsSource="{Binding Source={StaticResource ReferralItemsSource}}"
                              SelectedItem="{Binding DataContext.SelectedReferral, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
                              IsReadOnly="False"
                              IsSynchronizedWithCurrentItem="True"
                              SelectionMode="Single">
private async void RefreshWorklist(bool invokedByAutoRefresh = false)
{
   try
   {
       if (Initialising || ShowSpinner || IsProcessing || ShowRefreshSpinner || IsCurrentWorklistDeleted || !_sessionData.IsActive()) return;

       IsProcessing = true;
       RefreshWorklistCommand.RaiseCanExecuteChanged();

       if (CurrentWorklistId != null)
       {
           var selectedReferralId = SelectedReferral.pk_Referral_ID;

           if (invokedByAutoRefresh)
           {
               // Refresh has been invoked by _timer, so show spinner on the results page only
               ShowRefreshSpinner = true;
           }
           else
           {
               // User has manually clicked refresh button so show app wide spinner
               ShowSpinner = true;
               if (_timer != null)
               {
                   SetupWorklistRefreshTimer(); // Setup _timer again so that it will refresh again at an appropriate time
               }
           }

           Referrals = await _referralRepository.GetReferralsFromWorklistAsync(CurrentWorklistId.Value, invokedByAutoRefresh);

           if (Filters.Count > 0)
           {
               var listOfReferralPks = ReferralFiltering.GetFilteredResults(Referrals, Filters.Where(f => f.HasBeenApplied).ToList());
               var filteredResults = Referrals.Where(r => listOfReferralPks.Contains(r.pk_Referral_ID)).ToList();
               DataGridResults = MapReferralLookupItemsToReferralLookupItemViewModels(filteredResults);
           }
           else
           {
               DataGridResults = MapReferralLookupItemsToReferralLookupItemViewModels(Referrals);
           }

           SelectedReferral = DataGridResults.FirstOrDefault(r => r.pk_Referral_ID == selectedReferralId);
       }
   }
   catch (Exception e)
   {
       _errorHandler.DisplayError(e);
   }
}

Как объяснялось ранее, RefreshWorklist() вызывается вручную refre sh через Command:

private void Execute_RefreshWorklist()
{
     RefreshWorklist();
}

Или автоматически с помощью Timer:

private void SetupWorklistRefreshTimer()
{
   _timer?.Dispose();
   var refreshInterval = _userSettingsRepository.GetIntegerSystemSetting("ReferralsWorklistRefreshInterval");
   if (refreshInterval <= 0) return; // If this is 0 or below then the refresh should be disabled

   if (refreshInterval < 10) // If it is less than 10 then set it to 10 to avoid too many MT calls
   {
       refreshInterval = 10;
   }

   var timeUntilFirstTick = refreshInterval * 1000;
   _timer = new Timer((s) => RefreshWorklist(true), null, timeUntilFirstTick, refreshInterval * 1000);
}


И, наконец, свойство привязки модели представления свойств SelectedItem:

public ReferralLookupItemViewModel SelectedReferral
{
   get { return _selectedReferral; }
   set
   {
       if (_selectedReferral != value)
       {
           _selectedReferral = value;
           OnPropertyChanged();
       }
   }
}

Кто-нибудь знает, почему происходит такое поведение? Это как-то связано с Timer? Я понимаю, что это непростой вопрос, поэтому, пожалуйста, обращайтесь за дополнительной информацией.

1 Ответ

1 голос
/ 16 июня 2020

Вам необходимо назначить свойства в Binding с UI в потоке UI .

Замените Timer на DispatcherTimer или используйте Dispatcher.Invoke или Dispatcher.BeginInvoke внутри существующего Timer обратного вызова при вызове RefreshWorklist.

Нажав Button вы уже находитесь в потоке пользовательского интерфейса, но Timer имеет собственный поток, отличный от потока пользовательского интерфейса. DispatcherTimer обратный вызов вызывается в потоке пользовательского интерфейса https://docs.microsoft.com/en-us/dotnet/api/system.windows.threading.dispatchertimer?view=netframework-4.0

...