【问题标题】:WPF DataGrid SelectedItem strange behaviourWPF DataGrid SelectedItem 奇怪的行为
【发布时间】:2020-10-06 04:32:33
【问题描述】:

我们在DataGrid 中看到围绕SelectedItem 属性的一些奇怪行为。 一些背景信息:

DataGrid 显示对我们数据库的查询结果。 有一个按钮允许用户手动刷新DataGrid 中的结果。有一个自动刷新机制,结果将每 30 秒自动刷新一次。

我们看到的是SelectedItem 属性将始终成为自动刷新发生时Datagrid 的ItemsSource 的索引0。但是我们希望当前选定的行在刷新后保持为选定的行。但是,如果用户手动单击刷新,则刷新后所选行保持不变,这很奇怪,因为刷新逻辑正在运行相同的代码。是的,我们有代码可以记住当前选择的项目,然后在刷新完成后再次设置。

以下是一些相关代码:

<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() 由通过 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 有关系吗?我很感激这不是一个简单的问题,所以请询问更多信息。

【问题讨论】:

  • 您是否尝试过:SelectedItem="{Binding DataContext.SelectedReferral, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Mode=TwoWay}"
  • @NawedNabiZada 是的,我试过了,似乎没有影响。

标签: c# wpf datagrid


【解决方案1】:

您需要在Binding 中使用UI 在UI 线程上 分配属性。

在调用RefreshWorklist 时将Timer 替换为DispatcherTimer 或在现有Timer 回调中使用Dispatcher.InvokeDispatcher.BeginInvoke

通过按下Button,您已经在UI 线程上,但是Timer 有自己的线程,不同于UI 线程。 DispatcherTimer 回调在 UI 线程上调用,而不是 https://docs.microsoft.com/en-us/dotnet/api/system.windows.threading.dispatchertimer?view=netframework-4.0

【讨论】:

    猜你喜欢
    • 2011-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-30
    • 2011-10-10
    • 2012-01-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多