diff --git a/Bin/SunnyUI.dll b/Bin/SunnyUI.dll
index b286be08..3e1c8f33 100644
Binary files a/Bin/SunnyUI.dll and b/Bin/SunnyUI.dll differ
diff --git a/Bin/SunnyUI.pdb b/Bin/SunnyUI.pdb
index 1cd76dcb..1a3548e4 100644
Binary files a/Bin/SunnyUI.pdb and b/Bin/SunnyUI.pdb differ
diff --git a/SunnyUI.Demo/Bin/SunnyUI.dll b/SunnyUI.Demo/Bin/SunnyUI.dll
index b286be08..3e1c8f33 100644
Binary files a/SunnyUI.Demo/Bin/SunnyUI.dll and b/SunnyUI.Demo/Bin/SunnyUI.dll differ
diff --git a/SunnyUI/Controls/UITreeView.cs b/SunnyUI/Controls/UITreeView.cs
index b4fe84f2..22645d1f 100644
--- a/SunnyUI/Controls/UITreeView.cs
+++ b/SunnyUI/Controls/UITreeView.cs
@@ -497,7 +497,6 @@ namespace Sunny.UI
//
view.BackColor = Color.White;
view.BorderStyle = BorderStyle.None;
- view.CheckBoxes = true;
view.DrawMode = TreeViewDrawMode.OwnerDrawAll;
view.ForeColor = Color.FromArgb(48, 48, 48);
view.FullRowSelect = true;
diff --git a/SunnyUI/SunnyUI.csproj b/SunnyUI/SunnyUI.csproj
index c44b02f6..42b8556f 100644
--- a/SunnyUI/SunnyUI.csproj
+++ b/SunnyUI/SunnyUI.csproj
@@ -403,6 +403,7 @@
Component
+
diff --git a/SunnyUI/Units/UConcurrentDoubleKeyDictionary.cs b/SunnyUI/Units/UConcurrentDoubleKeyDictionary.cs
index cac88d47..397dabfc 100644
--- a/SunnyUI/Units/UConcurrentDoubleKeyDictionary.cs
+++ b/SunnyUI/Units/UConcurrentDoubleKeyDictionary.cs
@@ -11,7 +11,7 @@
* If you use this code, please keep this note.
* 如果您使用此代码,请保留此说明。
******************************************************************************
- * 文件名称: ULogFile.cs
+ * 文件名称: UConcurrentDoubleKeyDictionary.cs
* 文件说明: 双主键线程安全字典,适用场景主键和键值都唯一,可双向查找
* 当前版本: V2.2
* 创建日期: 2020-08-17
diff --git a/SunnyUI/Units/UConcurrentGroupDictionary.cs b/SunnyUI/Units/UConcurrentGroupDictionary.cs
new file mode 100644
index 00000000..be653479
--- /dev/null
+++ b/SunnyUI/Units/UConcurrentGroupDictionary.cs
@@ -0,0 +1,169 @@
+/******************************************************************************
+ * SunnyUI 开源控件库、工具类库、扩展类库、多页面开发框架。
+ * CopyRight (C) 2012-2020 ShenYongHua(沈永华).
+ * QQ群:56829229 QQ:17612584 EMail:SunnyUI@qq.com
+ *
+ * Blog: https://www.cnblogs.com/yhuse
+ * Gitee: https://gitee.com/yhuse/SunnyUI
+ * GitHub: https://github.com/yhuse/SunnyUI
+ *
+ * SunnyUI.dll can be used for free under the GPL-3.0 license.
+ * If you use this code, please keep this note.
+ * 如果您使用此代码,请保留此说明。
+ ******************************************************************************
+ * 文件名称: UConcurrentGroupDictionary.cs
+ * 文件说明: 分组线程安全字典
+ * 当前版本: V2.2
+ * 创建日期: 2020-08-18
+ *
+ * 2020-08-18: V2.2.7 增加文件说明
+******************************************************************************/
+
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Sunny.UI
+{
+ public class ConcurrentGroupDictionary
+ {
+ private readonly ConcurrentDictionary> GroupDictionary
+ = new ConcurrentDictionary>();
+ private readonly ConcurrentDictionary ObjectDictionary
+ = new ConcurrentDictionary();
+
+ public ConcurrentDictionary this[TGroup group]
+ {
+ get => ContainsGroup(group) ? GroupDictionary[group] : new ConcurrentDictionary();
+ set
+ {
+ if (!ContainsGroup(group)) TryAdd(group);
+ GroupDictionary[group] = value;
+ }
+ }
+
+ public bool ContainsGroup(TGroup group)
+ {
+ return GroupDictionary.ContainsKey(group);
+ }
+
+ public bool ContainsKey(TKey key)
+ {
+ return ObjectDictionary.ContainsKey(key);
+ }
+
+ public bool ContainsKey(TGroup group, TKey key)
+ {
+ return ContainsGroup(group) && GroupDictionary[group].ContainsKey(key);
+ }
+
+ public void TryAdd(TGroup group)
+ {
+ if (!GroupDictionary.ContainsKey(group))
+ GroupDictionary.TryAdd(group, new ConcurrentDictionary());
+ }
+
+ public void TryAdd(TGroup group, TKey key, TValue value)
+ {
+ TryAdd(group);
+
+ if (GroupDictionary[group].ContainsKey(key))
+ GroupDictionary[group][key] = value;
+ else
+ GroupDictionary[group].TryAdd(key, value);
+
+ if (ObjectDictionary.ContainsKey(key))
+ ObjectDictionary[key] = value;
+ else
+ ObjectDictionary.TryAdd(key, value);
+ }
+
+ public void TryRemove(TGroup group, TKey key, TValue value)
+ {
+ if (GroupDictionary.ContainsKey(group))
+ {
+ if (GroupDictionary[group].ContainsKey(key)) GroupDictionary[group].TryRemove(key, out _);
+ if (GroupDictionary[group].Count == 0) GroupDictionary.TryRemove(group, out _);
+ }
+
+ if (ObjectDictionary.ContainsKey(key))
+ {
+ ObjectDictionary.TryRemove(key, out _);
+ }
+ }
+
+ public void ClearAll()
+ {
+ List groups = GroupDictionary.Keys.ToList();
+ foreach (var group in groups)
+ {
+ ClearGroup(group);
+ }
+
+ GroupDictionary.Clear();
+ ObjectDictionary.Clear();
+ }
+
+ public void ClearGroup(TGroup group)
+ {
+ if (GroupDictionary.ContainsKey(group))
+ {
+ List keys = GroupDictionary[group].Keys.ToList();
+ foreach (var key in keys)
+ {
+ if (ObjectDictionary.ContainsKey(key))
+ ObjectDictionary.TryRemove(key, out _);
+ }
+
+ GroupDictionary[group].Clear();
+ }
+ }
+
+ public List GetValues(TGroup group)
+ {
+ if (!this.GroupDictionary.ContainsKey(group))
+ {
+ return new List();
+ }
+
+ return GroupDictionary[group].Values.ToList();
+ }
+
+ public List Groups()
+ {
+ return GroupDictionary.Keys.ToList();
+ }
+
+ public List AllValues()
+ {
+ return ObjectDictionary.Values.ToList();
+ }
+
+ public List AllKeys()
+ {
+ return ObjectDictionary.Keys.ToList();
+ }
+
+ public List GroupKeys(TGroup group)
+ {
+ return ContainsGroup(group) ? GroupDictionary[group].Keys.ToList() : new List();
+ }
+
+ public List GroupValues(TGroup group)
+ {
+ return ContainsGroup(group) ? GroupDictionary[group].Values.ToList() : new List();
+ }
+
+ public TValue GetValue(TKey key)
+ {
+ return ObjectDictionary.ContainsKey(key) ? ObjectDictionary[key] : default;
+ }
+
+ public int Count() => ObjectDictionary.Count;
+
+ public int Count(TGroup group)
+ {
+ return ContainsGroup(group) ? GroupDictionary[group].Count : 0;
+ }
+ }
+}