如何将 ObservableConcurrentDictionary 绑定到 WPF TreeView

How do I bind an ObservableConcurrentDictionary to WPF TreeView

我正在尝试将 Microsoft 示例 ObservableConcurrentDictionary.cs 对象绑定到 TreeView。我搜索了有关绑定字典的示例,但尽管有很多示例,但其中 none 似乎对我有用。每当我 运行 时,屏幕都会显示一个空的 treeview(只有白色轮廓)。我已将我的代码精简到最低限度以进行测试,我的实现如下:

<Window x:Name="AppWindow" x:Class="ControlCenter.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Control Center" Height="1000" Width="1200"
    WindowStartupLocation="CenterScreen">
<Grid x:Name="left_grid" Margin="362,199,551,237">
    <TreeView ItemsSource="{Binding _hostList}">
        <TreeView.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding _hostList.Values}"/>
            </DataTemplate>
        </TreeView.ItemTemplate>
    </TreeView>
</Grid>

public partial class MainWindow : Window
{

    public ObservableConcurrentDictionary<string, string> _hostList = new ObservableConcurrentDictionary<string, string>();
    public MainWindow()
    {
        InitializeComponent();
        _hostList.Add("TestHost1", "Host1");
        _hostList.Add("TestHost2", "Host2");
        _hostList.Add("TestHost3", "Host3");
    } 
}

using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Threading;
using System.Diagnostics;

namespace System.Collections.Concurrent
{
[DebuggerDisplay("Count={Count}")]
public class ObservableConcurrentDictionary<TKey, TValue> :
    ICollection<KeyValuePair<TKey, TValue>>, IDictionary<TKey, TValue>,
    INotifyCollectionChanged, INotifyPropertyChanged
{
    private readonly SynchronizationContext _context;
    private readonly ConcurrentDictionary<TKey, TValue> _dictionary;

    /// <summary>
    /// Initializes an instance of the ObservableConcurrentDictionary class.
    /// </summary>
    public ObservableConcurrentDictionary()
    {
        _context = AsyncOperationManager.SynchronizationContext;
        _dictionary = new ConcurrentDictionary<TKey, TValue>();
    }

    /// <summary>Event raised when the collection changes.</summary>
    public event NotifyCollectionChangedEventHandler CollectionChanged;
    /// <summary>Event raised when a property on the collection changes.</summary>
    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>
    /// Notifies observers of CollectionChanged or PropertyChanged of an update to the dictionary.
    /// </summary>
    private void NotifyObserversOfChange()
    {
        var collectionHandler = CollectionChanged;
        var propertyHandler = PropertyChanged;
        if (collectionHandler != null || propertyHandler != null)
        {
            _context.Post(s =>
            {
                if (collectionHandler != null)
                {
                    collectionHandler(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
                }
                if (propertyHandler != null)
                {
                    propertyHandler(this, new PropertyChangedEventArgs("Count"));
                    propertyHandler(this, new PropertyChangedEventArgs("Keys"));
                    propertyHandler(this, new PropertyChangedEventArgs("Values"));
                }
            }, null);
        }
    }

    /// <summary>Attempts to add an item to the dictionary, notifying observers of any changes.</summary>
    /// <param name="item">The item to be added.</param>
    /// <returns>Whether the add was successful.</returns>
    private bool TryAddWithNotification(KeyValuePair<TKey, TValue> item)
    {
        return TryAddWithNotification(item.Key, item.Value);
    }

    /// <summary>Attempts to add an item to the dictionary, notifying observers of any changes.</summary>
    /// <param name="key">The key of the item to be added.</param>
    /// <param name="value">The value of the item to be added.</param>
    /// <returns>Whether the add was successful.</returns>
    private bool TryAddWithNotification(TKey key, TValue value)
    {
        bool result = _dictionary.TryAdd(key, value);
        if (result) NotifyObserversOfChange();
        return result;
    }

    /// <summary>Attempts to remove an item from the dictionary, notifying observers of any changes.</summary>
    /// <param name="key">The key of the item to be removed.</param>
    /// <param name="value">The value of the item removed.</param>
    /// <returns>Whether the removal was successful.</returns>
    private bool TryRemoveWithNotification(TKey key, out TValue value)
    {
        bool result = _dictionary.TryRemove(key, out value);
        if (result) NotifyObserversOfChange();
        return result;
    }

    /// <summary>Attempts to add or update an item in the dictionary, notifying observers of any changes.</summary>
    /// <param name="key">The key of the item to be updated.</param>
    /// <param name="value">The new value to set for the item.</param>
    /// <returns>Whether the update was successful.</returns>
    private void UpdateWithNotification(TKey key, TValue value)
    {
        _dictionary[key] = value;
        NotifyObserversOfChange();
    }

    #region ICollection<KeyValuePair<TKey,TValue>> Members
    void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
    {
        TryAddWithNotification(item);
    }

    void ICollection<KeyValuePair<TKey, TValue>>.Clear()
    {
        ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).Clear();
        NotifyObserversOfChange();
    }

    bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
    {
        return ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).Contains(item);
    }

    void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
    {
        ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).CopyTo(array, arrayIndex);
    }

    int ICollection<KeyValuePair<TKey, TValue>>.Count
    {
        get { return ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).Count; }
    }

    bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly
    {
        get { return ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).IsReadOnly; }
    }

    bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
    {
        TValue temp;
        return TryRemoveWithNotification(item.Key, out temp);
    }
    #endregion

    #region IEnumerable<KeyValuePair<TKey,TValue>> Members
    IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
    {
        return ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).GetEnumerator();
    }
    #endregion

    #region IDictionary<TKey,TValue> Members
    public void Add(TKey key, TValue value)
    {
        TryAddWithNotification(key, value);
    }

    public bool ContainsKey(TKey key)
    {
        return _dictionary.ContainsKey(key);
    }

    public ICollection<TKey> Keys
    {
        get { return _dictionary.Keys; }
    }

    public bool Remove(TKey key)
    {
        TValue temp;
        return TryRemoveWithNotification(key, out temp);
    }

    public bool TryGetValue(TKey key, out TValue value)
    {
        return _dictionary.TryGetValue(key, out value);
    }

    public ICollection<TValue> Values
    {
        get { return _dictionary.Values; }
    }

    public TValue this[TKey key]
    {
        get { return _dictionary[key]; }
        set { UpdateWithNotification(key, value); }
    }
    #endregion
}

}

有一些问题。首先,因为您没有在绑定中指定来源,所以它会离开 DataContext,即 null

我认为您在想,既然您正在使用 MainWindow,那么您的绑定语句的源将是 MainWindow 对象,但这不是绑定的默认值。如果您想这样做,则必须在绑定中使用 RelativeSource,但通常您只需将集合作为视图模型的一部分或直接作为整个视图模型弹出到 DataContext 中.像这样:

public ObservableConcurrentDictionary<string, string> _hostList = new ObservableConcurrentDictionary<string, string>();
public MainWindow()
{
    InitializeComponent();
    _hostList.Add("TestHost1", "Host1");
    _hostList.Add("TestHost2", "Host2");
    _hostList.Add("TestHost3", "Host3");
    DataContext = _hostList;
} 

然后您只需让绑定脱离 DataContext,这样您就可以按如下方式更新代码:

<TreeView ItemsSource="{Binding}">
    <TreeView.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Values}"/>
        </DataTemplate>
    </TreeView.ItemTemplate>
</TreeView>

虽然我认为这不会完全解决您的问题,因为我认为您绑定到错误的属性。 ItemsSource 应该绑定到某种 IEnumerable 并且 Text 应该绑定到 string.

所以我不确定你想要展示什么,但我很确定你没有正确绑定。通常使用 TreeView 你将使用 HierarchicalDataTemplate 我认为这可能是你想要使用的。

我认为问题在于您需要绑定到 属性,而不是类型(字符串)。否则绑定将不起作用。具体

 public ObservableConcurrentDictionary<string, string>...

应该是

 public ObservableConcurrentDictionary<string, myobj>...

并确保显示的数据是正确的 属性,而不是字段。即

       public class myobj {
            public string mystg { get; set; }
        }

有设置

  listBox.ItemsSource = _hostList;

(你做了一棵树,我用列表视图测试了) 那么绑定应该是

{Binding Value.mystg}

我确实注意到,字典不支持排序,而且在我看来新添加的项目是随机出现的,所以排序可能是另一种动物。