[英]How do I bind an ObservableConcurrentDictionary to WPF TreeView
I am trying to bind the Microsoft sample ObservableConcurrentDictionary.cs
object to a TreeView
.我正在尝试将 Microsoft 示例
ObservableConcurrentDictionary.cs
对象绑定到TreeView
。 I have searched for examples on binding a Dictionary but although there are many examples, none of them seem to work for me.我搜索了有关绑定字典的示例,但尽管有很多示例,但似乎没有一个对我有用。 Whenever I run it the screen shows up with an empty
treeview
(only white outline).每当我运行它时,屏幕都会显示一个空的
treeview
(只有白色轮廓)。 I have stripped down my code to the bare minimum to test and my implementation is as follows:我已经将我的代码精简到最低限度进行测试,我的实现如下:
<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
}
} }
There are a few problems.有几个问题。 First since you didn't specify a source in your binding it is going off of the
DataContext
which is null
.首先,因为您没有在绑定中指定源,所以它脱离了为
null
的DataContext
。
I think you are thinking that since you are working with MainWindow
that the source of your binding statements will be the MainWindow
object but that isn't the default for a binding.我认为您正在考虑,因为您正在使用
MainWindow
,所以绑定语句的源将是MainWindow
对象,但这不是绑定的默认值。 If you wanted to do that you would have to use a RelativeSource
in your bindings, but usually you would just pop the collection into the DataContext
as part of a view model or directly as the entire view model instead.如果您想这样做,您必须在绑定中使用
RelativeSource
,但通常您只需将集合作为视图模型的一部分或直接作为整个视图模型的一部分弹出到DataContext
中。 Something like this:像这样的东西:
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;
}
Then you will just have your bindings go off of the DataContext
so you would update your code as follows:然后,您只需将绑定从
DataContext
以便按如下方式更新您的代码:
<TreeView ItemsSource="{Binding}">
<TreeView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Values}"/>
</DataTemplate>
</TreeView.ItemTemplate>
</TreeView>
This won't completely resolve your problems I am thinking though since I think you are binding to the wrong properties.这不会完全解决我正在考虑的问题,因为我认为您绑定到了错误的属性。 ItemsSource should be binding to something that is an
IEnumerable
of some sort and Text
should be binding to a string
. ItemsSource 应该绑定到某种类型的
IEnumerable
并且Text
应该绑定到一个string
。
So I am not sure what you are ideally trying to show but am pretty sure you aren't binding correctly.所以我不确定你想要展示什么,但我很确定你没有正确绑定。 Usually with a
TreeView
you will be using a HierarchicalDataTemplate and I think this is likely what you are wanting to use.通常对于
TreeView
您将使用HierarchicalDataTemplate ,我认为这可能是您想要使用的。
I think the problem is you need to bind to a property, not a type (string).我认为问题是您需要绑定到一个属性,而不是一个类型(字符串)。 or the bindings won't work.
否则绑定将不起作用。 Specifically
具体
public ObservableConcurrentDictionary<string, string>...
should be应该是
public ObservableConcurrentDictionary<string, myobj>...
and be sure the data shown is a proper property, not a field.并确保显示的数据是正确的属性,而不是字段。 ie.
即。
public class myobj {
public string mystg { get; set; }
}
with setting带设置
listBox.ItemsSource = _hostList;
(well you did a tree, I tested with a list view) Then binding should be (好吧,你做了一棵树,我用列表视图进行了测试)然后绑定应该是
{Binding Value.mystg}
I do note, that the dictionary doesn't support sorting, and new items added appeared random in my view, so sorting might be another animal.我确实注意到,字典不支持排序,并且添加的新项目在我看来是随机的,因此排序可能是另一种动物。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.