You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过底层Client模型高效访问ObservableCollection中的ClientViewModel?

问题描述

我有一个绑定到ListView的ObservableCollection<ClientViewModel>,其中每个ClientViewModel是对Client模型的ViewModel包装类,包含仅用于UI的属性。

当前遇到的问题是:当Client触发连接/断开事件时,若要更新或移除集合中对应的ClientViewModel,我必须通过LINQ遍历集合,逐一检查ClientViewModel中的Client是否与触发事件的Client匹配,再进行修改。

请问是否存在更高效的实现方式?比如使用可实现O(1)访问的ObservableDictionary?

附带代码片段:

public ObservableCollection<ClientViewModel> ClientViewModels { get; } = new();

public void OnClientConnected(object sender, Client e)
{
    ClientViewModels[...]?
}
public class ClientViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public Client Client { get; }

    private bool _online;
    public bool Online
    {
        get { return this._online; }
        set
        {
            this._online = value;
            this.NotifyPropertyChanged(nameof(Online));
        }
    }

    public ClientViewModel(Client client)
    {
        Client = client;
    }

    public void NotifyPropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}
高效实现方案

1. 维护字典映射表(最简便的O(1)方案)

保留ObservableCollection用于UI绑定,同时新增一个Dictionary<Client, ClientViewModel>做快速映射,这样可以直接通过触发事件的Client实例O(1)找到对应的ViewModel,无需遍历集合。

示例代码:

public ObservableCollection<ClientViewModel> ClientViewModels { get; } = new();
private Dictionary<Client, ClientViewModel> _clientVmMap = new();

public void OnClientConnected(object sender, Client e)
{
    if (!_clientVmMap.TryGetValue(e, out var vm))
    {
        vm = new ClientViewModel(e);
        ClientViewModels.Add(vm);
        _clientVmMap.Add(e, vm);
    }
    vm.Online = true;
}

public void OnClientDisconnected(object sender, Client e)
{
    if (_clientVmMap.TryGetValue(e, out var vm))
    {
        vm.Online = false;
        // 如需移除ViewModel
        // ClientViewModels.Remove(vm);
        // _clientVmMap.Remove(e);
    }
}

2. 自定义ObservableDictionary(直接绑定字典)

如果希望用字典类直接支持UI绑定,可以自定义实现INotifyCollectionChanged和INotifyPropertyChanged的ObservableDictionary,既保留字典的O(1)访问效率,又能像ObservableCollection一样通知UI更新。

简化版ObservableDictionary核心实现:

public class ObservableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, INotifyCollectionChanged, INotifyPropertyChanged
{
    public event NotifyCollectionChangedEventHandler CollectionChanged;
    public event PropertyChangedEventHandler PropertyChanged;

    public new void Add(TKey key, TValue value)
    {
        base.Add(key, value);
        CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value));
        OnPropertyChanged(nameof(Count));
        OnPropertyChanged(nameof(Keys));
        OnPropertyChanged(nameof(Values));
    }

    public new bool Remove(TKey key)
    {
        if (base.TryGetValue(key, out var value) && base.Remove(key))
        {
            CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, value));
            OnPropertyChanged(nameof(Count));
            OnPropertyChanged(nameof(Keys));
            OnPropertyChanged(nameof(Values));
            return true;
        }
        return false;
    }

    private void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

使用方式:

public ObservableDictionary<Client, ClientViewModel> ClientVmDictionary { get; } = new();

public void OnClientConnected(object sender, Client e)
{
    if (!ClientVmDictionary.TryGetValue(e, out var vm))
    {
        vm = new ClientViewModel(e);
        ClientVmDictionary.Add(e, vm);
    }
    vm.Online = true;
}

UI绑定示例(XAML):

<ListView ItemsSource="{Binding ClientVmDictionary.Values}">
    <!-- 自定义ItemTemplate -->
</ListView>

3. 让ViewModel自主监听事件(逻辑内聚优化)

修改ClientViewModel,让它内部直接监听Client的连接/断开事件,状态更新逻辑完全封装在ViewModel内部,外部无需手动匹配和更新。

修改后的ClientViewModel:

public class ClientViewModel : INotifyPropertyChanged, IDisposable
{
    public event PropertyChangedEventHandler PropertyChanged;

    public Client Client { get; }

    private bool _online;
    public bool Online
    {
        get { return _online; }
        set
        {
            _online = value;
            NotifyPropertyChanged(nameof(Online));
        }
    }

    public ClientViewModel(Client client)
    {
        Client = client;
        // 假设Client存在Connected/Disconnected事件
        client.Connected += OnClientConnected;
        client.Disconnected += OnClientDisconnected;
        // 初始化在线状态
        Online = client.IsOnline;
    }

    private void OnClientConnected(object sender, EventArgs e)
    {
        Online = true;
    }

    private void OnClientDisconnected(object sender, EventArgs e)
    {
        Online = false;
    }

    public void Dispose()
    {
        // 释放事件订阅,避免内存泄漏
        Client.Connected -= OnClientConnected;
        Client.Disconnected -= OnClientDisconnected;
    }

    public void NotifyPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

这种方式下,外部只需要负责将ClientViewModel添加到ObservableCollection,状态更新完全由ViewModel自动处理,进一步简化业务逻辑。


内容的提问来源于stack exchange,提问作者Meme Machine

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 17:36:24