diff --git a/Bin/net40/SunnyUI.Demo.exe b/Bin/net40/SunnyUI.Demo.exe
index 99b26243..565caebe 100644
Binary files a/Bin/net40/SunnyUI.Demo.exe and b/Bin/net40/SunnyUI.Demo.exe differ
diff --git a/Bin/net40/SunnyUI.dll b/Bin/net40/SunnyUI.dll
index f7663432..fa016b5b 100644
Binary files a/Bin/net40/SunnyUI.dll and b/Bin/net40/SunnyUI.dll differ
diff --git a/Bin/net5.0-windows/SunnyUI.dll b/Bin/net5.0-windows/SunnyUI.dll
index 852d8a5d..16495c0c 100644
Binary files a/Bin/net5.0-windows/SunnyUI.dll and b/Bin/net5.0-windows/SunnyUI.dll differ
diff --git a/Bin/net5.0-windows/ref/SunnyUI.dll b/Bin/net5.0-windows/ref/SunnyUI.dll
index 36870ca5..a4673b17 100644
Binary files a/Bin/net5.0-windows/ref/SunnyUI.dll and b/Bin/net5.0-windows/ref/SunnyUI.dll differ
diff --git a/Bin/netcoreapp3.1/SunnyUI.dll b/Bin/netcoreapp3.1/SunnyUI.dll
index be653714..78c1a89d 100644
Binary files a/Bin/netcoreapp3.1/SunnyUI.dll and b/Bin/netcoreapp3.1/SunnyUI.dll differ
diff --git a/SunnyUI/Units/UIStringCollection.cs b/SunnyUI/Units/UIStringCollection.cs
new file mode 100644
index 00000000..3cd4024d
--- /dev/null
+++ b/SunnyUI/Units/UIStringCollection.cs
@@ -0,0 +1,258 @@
+/******************************************************************************
+ * SunnyUI 开源控件库、工具类库、扩展类库、多页面开发框架。
+ * CopyRight (C) 2012-2021 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.
+ * 如果您使用此代码,请保留此说明。
+ ******************************************************************************
+ * 文件名称: UIStringCollection.cs
+ * 文件说明: 带集合个数改变事件的字符串集合类
+ * 当前版本: V3.0
+ * 创建日期: 2021-05-19
+ *
+ * 2021-05-19: V3.0.3 增加文件说明
+******************************************************************************/
+
+using System;
+using System.Collections;
+
+namespace Sunny.UI
+{
+ ///
+ /// Represents a collection of strings.
+ ///
+ [Serializable]
+ public class UIStringCollection : IList
+ {
+ private ArrayList data = new ArrayList();
+
+ public event EventHandler CountChange;
+
+ ///
+ /// Represents the entry at the specified index of the .
+ ///
+ public string this[int index]
+ {
+ get => (string)data[index];
+ set => data[index] = value;
+ }
+
+ ///
+ /// Gets the number of strings in the
+ /// .
+ ///
+ public int Count => data.Count;
+
+ bool IList.IsReadOnly => false;
+
+ bool IList.IsFixedSize => false;
+
+
+ ///
+ /// Adds a string with the specified value to the
+ /// .
+ ///
+ public int Add(string value)
+ {
+ int count = data.Add(value);
+ CountChange?.Invoke(this, new EventArgs());
+ return count;
+ }
+
+ ///
+ /// Copies the elements of a string array to the end of the .
+ ///
+ public void AddRange(string[] value)
+ {
+ if (value == null)
+ {
+ throw new ArgumentNullException("value");
+ }
+
+ data.AddRange(value);
+ CountChange?.Invoke(this, new EventArgs());
+ }
+
+ ///
+ /// Removes all the strings from the
+ /// .
+ ///
+ public void Clear()
+ {
+ data.Clear();
+ CountChange?.Invoke(this, new EventArgs());
+ }
+
+ ///
+ /// Gets a value indicating whether the
+ /// contains a string with the specified
+ /// value.
+ ///
+ public bool Contains(string value)
+ {
+ return data.Contains(value);
+ }
+
+ ///
+ /// Copies the values to a one-dimensional instance at the
+ /// specified index.
+ ///
+ public void CopyTo(string[] array, int index)
+ {
+ data.CopyTo(array, index);
+ }
+
+ ///
+ /// Returns an enumerator that can iterate through
+ /// the .
+ ///
+ public UIStringEnumerator GetEnumerator()
+ {
+ return new UIStringEnumerator(this);
+ }
+
+ ///
+ /// Returns the index of the first occurrence of a string in
+ /// the .
+ ///
+ public int IndexOf(string value)
+ {
+ return data.IndexOf(value);
+ }
+
+ ///
+ /// Inserts a string into the at the specified
+ /// index.
+ ///
+ public void Insert(int index, string value)
+ {
+ data.Insert(index, value);
+ CountChange?.Invoke(this, new EventArgs());
+ }
+
+ ///
+ /// Gets a value indicating whether the is read-only.
+ ///
+ public bool IsReadOnly => false;
+
+ ///
+ /// Gets a value indicating whether access to the
+ ///
+ /// is synchronized (thread-safe).
+ ///
+ public bool IsSynchronized => false;
+
+ ///
+ /// Removes a specific string from the
+ /// .
+ ///
+ public void Remove(string value)
+ {
+ data.Remove(value);
+ CountChange?.Invoke(this, new EventArgs());
+ }
+
+ ///
+ /// Removes the string at the specified index of the .
+ ///
+ public void RemoveAt(int index)
+ {
+ data.RemoveAt(index);
+ CountChange?.Invoke(this, new EventArgs());
+ }
+
+ ///
+ /// Gets an object that can be used to synchronize access to the .
+ ///
+ public object SyncRoot => data.SyncRoot;
+
+ object IList.this[int index]
+ {
+ get => this[index];
+ set => this[index] = (string)value;
+ }
+
+ int IList.Add(object value)
+ {
+ int count = Add((string)value);
+ CountChange?.Invoke(this, new EventArgs());
+ return count;
+ }
+
+ bool IList.Contains(object value)
+ {
+ return Contains((string)value);
+ }
+
+
+ int IList.IndexOf(object value)
+ {
+ return IndexOf((string)value);
+ }
+
+ void IList.Insert(int index, object value)
+ {
+ Insert(index, (string)value);
+ CountChange?.Invoke(this, new EventArgs());
+ }
+
+ void IList.Remove(object value)
+ {
+ Remove((string)value);
+ CountChange?.Invoke(this, new EventArgs());
+ }
+
+ void ICollection.CopyTo(Array array, int index)
+ {
+ data.CopyTo(array, index);
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return data.GetEnumerator();
+ }
+ }
+
+ ///
+ /// [To be supplied.]
+ ///
+ public class UIStringEnumerator
+ {
+ private readonly IEnumerator baseEnumerator;
+ private readonly IEnumerable temp;
+
+ internal UIStringEnumerator(UIStringCollection mappings)
+ {
+ this.temp = (IEnumerable)(mappings);
+ this.baseEnumerator = temp.GetEnumerator();
+ }
+
+ ///
+ /// [To be supplied.]
+ ///
+ public string Current => (string)(baseEnumerator.Current);
+
+ ///
+ /// [To be supplied.]
+ ///
+ public bool MoveNext()
+ {
+ return baseEnumerator.MoveNext();
+ }
+
+ ///
+ /// [To be supplied.]
+ ///
+ public void Reset()
+ {
+ baseEnumerator.Reset();
+ }
+
+ }
+}
\ No newline at end of file