【问题标题】:Prevent ListView.SelectedItem from being set to null when ChangeTrackEnabled is set设置 ChangeTrackEnabled 时防止 ListView.SelectedItem 设置为 null
【发布时间】:2018-04-17 05:20:32
【问题描述】:

给定以下视图模型:

public class AttachmentsViewModel : ReactiveObject, ISupportsActivation
 {
     private ReactiveList<Attachment> _attachments;
     public ReactiveList<Attachment> Attachments
     {
         get => _attachments;
         set => this.RaiseAndSetIfChanged( ref _attachments, value );
     }

     private IReactiveDerivedList<AttachmentViewModel> _attachmentViewModels;
     public IReactiveDerivedList<AttachmentViewModel> AttachmentViewModels
     {
         get => _attachmentViewModels;
         set => this.RaiseAndSetIfChanged(ref _attachmentViewModels, value );
     }

     private AttachmentViewModel _selected;
     public AttachmentViewModel Selected
     {
         get => _selected;
         set => this.RaiseAndSetIfChanged( ref _selected, value );
     }
}

Attachments 和 AttachmentViewModels 使用以下代码设置:

var items = DataManager.GetAttachmentsList();
Attachments = new ReactiveList<Attachment>( items ) { ChangeTrackingEnabled = true };
AttachmentViewModels = Attachments.CreateDerivedCollection( x => new AttachmentViewModel(x) );

AttachmentViewModels 绑定到ListView:

this.OneWayBind( ViewModel, vm => vm.AttachmentViewModels, v => v.List.ItemsSource );
this.Bind( ViewModel, vm => vm.Selected, v => v.List.SelectedItem );

但是,如果我通过 AttachmentViewModel 更新其中一个附件,那么因为 Attachments.ChangeTrackingEnabled 设置为 true,AttachmentViewModels.CollectionChanged 被触发。出于某种原因,这又将List.SelectedItem 设置为null。

有没有办法避免这种行为?另一个 SO post 暗示这可能是因为我没有在 AttachmentViewModel 中实现适当的相等运算符。我尝试遵循此建议,但似乎没有帮助。

另外,我必须将 ListView.IsSynchronizedWithCurrentItem 设置为 true - 否则 ListView 将不会保持原来的选择。但是,这并不能阻止SelectedItem 被设置为null,它只是意味着它被设置了两次。一次到null,然后又回到原来的选择。这没关系,只是它会在SelectedItem 更改时导致 UI 出现视觉故障。

【问题讨论】:

标签: xaml reactiveui


【解决方案1】:

当ReactiveList 中的项目的属性发生更改并且ChangeTrackingEnabled 为true - 触发CollectionChanged 事件。对于关联的IReactiveDerivedList,这意味着重新创建整个集合。由于生成了新的AttachmentViewModel(对于相同的Attachment),SelectedItem 不再指向列表中的项目。它的默认行为是变成null。

通过实现缓存,可以重新设置相同的AttachmentViewModel - 尽管发生了集合更改事件。

基于这个ReactiveUI issue,这是我实现的:

public class AttachmentsViewModel : ReactiveObject
{
    private readonly Dictionary<Attachment, AttachmentViewModel> _cache = new Dictionary<Attachment, AttachmentViewModel>();

    private ReactiveList<Attachment> _attachments;
    public ReactiveList<Attachment> Attachments
    {
        get => _attachments;
        set => this.RaiseAndSetIfChanged( ref _attachments, value );
    }

    private IReactiveDerivedList<AttachmentViewModel> _attachmentViewModels;
    public IReactiveDerivedList<AttachmentViewModel> AttachmentViewModels
    {
        get => _attachmentViewModels;
        set => this.RaiseAndSetIfChanged(ref _attachmentViewModels, value );
    }

    private AttachmentViewModel _selected;
    public AttachmentViewModel Selected
    {
        get => _selected;
        set => this.RaiseAndSetIfChanged( ref _selected, value );
    }

    public void LoadAttachments()
    {
        _cache.Clear();

        var items = DataManager.GetAttachmentsList();
        Attachments = new ReactiveList<Attachment>( items ) { ChangeTrackingEnabled = true };
        AttachmentViewModels = Attachments.CreateDerivedCollection( x => {
            AttachmentViewModel viewModel;

            if ( _cache.ContainsKey(x) ) {
                viewModel = _cache[x];
            } else {
                viewModel = new AttachmentViewModel( x );

                _cache.Add( x, viewModel );
            }

            return viewModel;
        });
    }
}

请注意,Attachment 对象有自己的相等运算符(基于 ID 属性)。用户可以恢复他们的更改,所以我不得不添加_cache.Clear() 来补偿。

【讨论】:

  • 这让我想知道,这是否可以通过在AttachmentViewModel 中实现适当的相等运算符来解决?然后SelectedItem 将指向列表中的一个项目。如果相等是基于底层的Attachment.ID,那么也许就不需要缓存了?
猜你喜欢
  • 2019-05-25
  • 2017-01-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多