using CommonFroms.Properties; using CommonLang; using CommonLang.Xml; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using System.Windows.Forms; namespace CommonFroms.G2D.DataGrid { public class G2DPropertyGrid : System.Windows.Forms.PropertyGrid { private int descriptionAreaLineCount = 2; private int descriptionAreaLineCountMin = 2; private Control docComment = null; private Control propertyGridView = null; private ToolStrip tools; private Type docCommentType = null; private PropertyInfo linesProperty; private bool sizeChangeIsFromUser = true; private bool inited = false; private Type last_field_decleard_type = null; private ToolStripButton btn_copy; private ToolStripButton btn_paste; private ToolStripDropDownButton btn_paste_plus; private ToolStripButton btn_cut; private ToolStripButton btn_delete; private ToolStripLabel lbl_copying; /// /// Initializes a new instance of the CustomPropertyGrid class. /// public G2DPropertyGrid() { this.ImeMode = ImeMode.On; this.MinDescriptionAreaLineCount = 5; foreach (Control control in this.Controls) { Type controlType = control.GetType(); if (controlType.Name == "DocComment") { this.docCommentType = controlType; this.docComment = control; this.linesProperty = this.docCommentType.GetProperty("Lines"); FieldInfo userSizedField = this.docCommentType.BaseType.GetField( "userSized", BindingFlags.Instance | BindingFlags.NonPublic); userSizedField.SetValue(this.docComment, true); this.docComment.SizeChanged += this.HandleDocCommentSizeChanged; } else if (controlType.Name == "PropertyGridView") { this.propertyGridView = control; } else if (controlType.Name == "ToolStrip") { this.tools = control as ToolStrip; } } this.SelectedGridItemChanged += G2DPropertyGrid_SelectedGridItemChanged; if (tools != null) { InitTools(); } } public void SetSelectedObject(object value, params IG2DPropertyAdapter[] adds) { base.SelectedObject = new G2DPropertyDescriptor(value, adds); } public object GetSelectedValue() { if (base.SelectedObject is G2DPropertyDescriptor) { return (base.SelectedObject as G2DPropertyDescriptor).EditData; } return base.SelectedObject; } private void G2DPropertyGrid_SelectedGridItemChanged(object sender, SelectedGridItemChangedEventArgs e) { this.OnRefreshHistoryValiable(SelectedGridItem); } protected override void OnPropertyValueChanged(PropertyValueChangedEventArgs e) { base.OnPropertyValueChanged(e); this.Refresh(); } protected override void OnInvalidated(InvalidateEventArgs e) { base.OnInvalidated(e); if (!inited) { inited = true; this.DescriptionAreaLineCount = this.MinDescriptionAreaLineCount; } } //------------------------------------------------------------------------------------------------------ #region _key_events_ protected override bool ProcessCmdKey(ref Message msg, Keys kd) { if (Keyboard.IsCtrlDown) { Keys keyData = kd ^ Keys.Control; switch (keyData) { case Keys.C: if (ProcessKeyDown_CtrlC != null) { ProcessKeyDown_CtrlC.Invoke(this, new KeyEventArgs(keyData)); } DoCopy(); return true; case Keys.V: if (ProcessKeyDown_CtrlV != null) { ProcessKeyDown_CtrlV.Invoke(this, new KeyEventArgs(keyData)); } DoPaste(); return true; case Keys.X: if (ProcessKeyDown_CtrlX != null) { ProcessKeyDown_CtrlX.Invoke(this, new KeyEventArgs(keyData)); } DoCut(); return true; } } switch (kd) { case Keys.Delete: if (ProcessKeyDown_Delete != null) { ProcessKeyDown_Delete.Invoke(this, new KeyEventArgs(kd)); } DoDelete(); return true; } return base.ProcessCmdKey(ref msg, kd); } public delegate void KeyDownProcessHandler(object sender, KeyEventArgs e); /// /// 键盘Ctrl+C触发 /// public event KeyDownProcessHandler ProcessKeyDown_CtrlC; /// /// 键盘Ctrl+V触发 /// public event KeyDownProcessHandler ProcessKeyDown_CtrlV; /// /// 键盘Ctrl+X触发 /// public event KeyDownProcessHandler ProcessKeyDown_CtrlX; /// /// 键盘Del触发 /// public event KeyDownProcessHandler ProcessKeyDown_Delete; #endregion //------------------------------------------------------------------------------------------------------ #region _tools_ private void InitTools() { tools.CanOverflow = false; tools.LayoutStyle = ToolStripLayoutStyle.Flow; // try // { // FieldInfo btnViewPropertyPagesField = base.GetType().GetField("btnViewPropertyPages", BindingFlags.Instance); // ToolStripButton btnViewPropertyPages = btnViewPropertyPagesField.GetValue(this) as ToolStripButton; // btnViewPropertyPages.Visible = false; // } // catch (Exception err) { } tools.Items.Add(new ToolStripSeparator()); tools.Items.Add(btn_copy = new ToolStripButton("复制", Resources.icons_copy)); tools.Items.Add(btn_paste = new ToolStripButton("粘贴", Resources.icons_paste)); tools.Items.Add(btn_cut = new ToolStripButton("剪切", Resources.icons_cut)); tools.Items.Add(btn_delete = new ToolStripButton("删除", Resources.icons_delete)); tools.Items.Add(new ToolStripSeparator()); tools.Items.Add(btn_paste_plus = new ToolStripDropDownButton("", Resources.icons_paste_plus)); tools.Items.Add(lbl_copying = new ToolStripLabel("")); btn_paste_plus.ToolTipText = "粘贴自"; btn_paste_plus.ForeColor = System.Drawing.Color.Blue; btn_paste_plus.DisplayStyle = ToolStripItemDisplayStyle.ImageAndText; btn_paste_plus.DropDownItemClicked += Btn_paste_plus_DropDownItemClicked; btn_paste_plus.DropDownOpening += Btn_paste_plus_DropDownOpening; btn_paste_plus.AutoToolTip = true; lbl_copying.ForeColor = System.Drawing.Color.Blue; lbl_copying.Overflow = ToolStripItemOverflow.Never; lbl_copying.AutoToolTip = true; btn_delete.Visible = false; btn_cut.DisplayStyle = ToolStripItemDisplayStyle.Image; btn_copy.DisplayStyle = ToolStripItemDisplayStyle.Image; btn_paste.DisplayStyle = ToolStripItemDisplayStyle.Image; btn_delete.DisplayStyle = ToolStripItemDisplayStyle.Image; btn_cut.Click += Btn_cut_Click; ; btn_copy.Click += Btn_copy_Click; btn_paste.Click += Btn_paste_Click; btn_delete.Click += Btn_delete_Click; } private void RefreshToolTips(List list) { if (list != null && list.Count > 0) { CopyHistory.Entry current = list[0]; this.btn_paste_plus.Text = list.Count.ToString(); this.btn_paste_plus.ToolTipText = "粘贴自: " + current.lbl_text; this.lbl_copying.Text = current.lst_text ; this.lbl_copying.ToolTipText = current.lbl_text ; this.btn_paste_plus.Visible = true; this.lbl_copying.Visible = true; } else { this.btn_paste_plus.Visible = false; this.lbl_copying.Visible = false; } } /// /// 刷新粘贴可用项目 /// /// private void OnRefreshHistoryValiable(GridItem item) { if (item != null && item.PropertyDescriptor is IG2DPropertyDescriptor) { Type decleard_type = (item.PropertyDescriptor as IG2DPropertyDescriptor).DecleardFieldType; if (!decleard_type.Equals(this.last_field_decleard_type)) { this.last_field_decleard_type = decleard_type; this.btn_paste_plus.DropDownItems.Clear(); List list = CopyHistory.GetHistoryList(decleard_type); if (list != null && list.Count > 0) { foreach (var h in list) { var add = new ToolStripButton(h.lst_text); add.AutoToolTip = true; add.ToolTipText = h.lbl_text; add.DisplayStyle = ToolStripItemDisplayStyle.Text; add.ForeColor = (add.Enabled) ? System.Drawing.Color.Blue : System.Drawing.Color.Gray; add.Tag = h; btn_paste_plus.DropDownItems.Add(add); } RefreshToolTips(list); return; } else { RefreshToolTips(null); } } } else { this.last_field_decleard_type = null; RefreshToolTips(null); } } private void OnRefreshHistoryAdded(GridItem item, bool new_item, List list) { if (list != null && list.Count > 0) { if (new_item) { CopyHistory.Entry current = list[0]; var add = new ToolStripButton(current.lst_text); add.AutoToolTip = true; add.ToolTipText = current.lbl_text; add.DisplayStyle = ToolStripItemDisplayStyle.Text; add.ForeColor = (add.Enabled) ? System.Drawing.Color.Blue : System.Drawing.Color.Gray; add.Tag = current; btn_paste_plus.DropDownItems.Insert(0, add); } RefreshToolTips(list); } else { RefreshToolTips(null); } } private void Btn_delete_Click(object sender, EventArgs e) { DoDelete(); } private void Btn_paste_Click(object sender, EventArgs e) { DoPaste(); } private void Btn_copy_Click(object sender, EventArgs e) { DoCopy(); } private void Btn_cut_Click(object sender, EventArgs e) { DoCut(); } private void Btn_paste_plus_DropDownItemClicked(object sender, ToolStripItemClickedEventArgs e) { var history = e.ClickedItem.Tag as CopyHistory.Entry; if (history != null) { DoPastePlus(history); } } private void Btn_paste_plus_DropDownOpening(object sender, EventArgs e) { } #endregion //------------------------------------------------------------------------------------------------------ #region _copying_and_pasting_ public static int CopyHistoryLimit { get { return CopyHistory.CopyHistoryLimit; } set { CopyHistory.CopyHistoryLimit = value; } } public static bool TryConvertTo(object src, Type targetType) { object target; return TryConvertTo(src, targetType, out target); } public static bool TryConvertTo(object src, Type targetType, out object target) { if (targetType.IsInstanceOfType(src)) { target = src; return true; } if (targetType.IsPrimitive && src.GetType().IsPrimitive) { try { target = Convert.ChangeType(src, targetType); if (targetType.IsInstanceOfType(target)) { return true; } } catch (Exception ) { } } target = null; return false; } private static class CopyHistory { private static HashMap> copying_history = new HashMap>(); private static List copying_history_primitive = new List(); private static int max_history_count = 22; public static int CopyHistoryLimit { get { return max_history_count; } set { if (value >= 20) { max_history_count = value; } } } public static List GetHistoryList(Type decleard_type) { if (decleard_type.IsPrimitive && decleard_type.IsValueType && (!decleard_type.Equals(typeof(bool)))) { return copying_history_primitive; } List ret = copying_history.Get(decleard_type); return ret; } public static bool Add(object owner, GridItem item, out List out_list) { if (item != null && item.PropertyDescriptor is IG2DPropertyDescriptor) { IG2DPropertyDescriptor g2dpp = item.PropertyDescriptor as IG2DPropertyDescriptor; if (item.Value != null) { var x = XmlUtil.ToString(XmlUtil.ObjectToXml(item.Value, "data", true)); List list = GetHistoryList(g2dpp.DecleardFieldType); if (list != null) { foreach (var old in list) { if (old.data.Equals(item.Value) || old.EqualsXmlText(x)) { old.Renew(owner); list.Sort(); out_list = list; return false; } } } else { list = new List(); copying_history.Put(g2dpp.DecleardFieldType, list); } var copying_value = new Entry(owner, x, item); list.Add(copying_value); list.Sort(); if (list.Count > max_history_count) { list.RemoveRange(max_history_count, list.Count - max_history_count); } out_list = list; return true; } } out_list = null; return false; } public static Entry CurrentAvaliable(Type decleard_type) { List list = GetHistoryList(decleard_type); if (list != null && list.Count > 0) { return list[0]; } return null; } public static int GetHistoryListCount(Type decleard_type) { List ret = GetHistoryList(decleard_type); if (ret != null) { return ret.Count; } return 0; } //---------------------------------------------------------------------------------- public class Entry : IComparable { public readonly object data; private readonly string cvt_text; private readonly string xml_text; private readonly string lbl_suffix; public DateTime time { get; private set; } public string lbl_text { get; private set; } public string lst_text { get; private set; } internal Entry(object owner, string x, GridItem item) { this.time = DateTime.Now; this.data = item.Value; this.xml_text = x; this.cvt_text = item.Value.ToString(); Type expect_type = item.Value.GetType(); try { if (item.PropertyDescriptor.Converter != null) { cvt_text = item.PropertyDescriptor.Converter.ConvertToString(item.Value); } if (item.PropertyDescriptor is IG2DPropertyDescriptor) { IG2DPropertyDescriptor g2dpp = item.PropertyDescriptor as IG2DPropertyDescriptor; expect_type = g2dpp.DecleardFieldType; } } catch (Exception ) { this.lst_text = item.Value.ToString(); } this.lst_text = string.Format("[{0}] {1}", expect_type.Name, cvt_text); this.lbl_suffix = string.Format("{0} = {1}", item.Label, lst_text); this.lbl_text = string.Format("{0}:{1}", owner, lbl_suffix); Clipboard.SetText(cvt_text); } public void Renew(object owner) { this.time = DateTime.Now; this.lbl_text = string.Format("{0}:{1}", owner, lbl_suffix); Clipboard.SetText(cvt_text); } public bool CanConvertTo(Type decleard_type) { if (TryConvertTo(data, decleard_type)) { return true; } return false; } public int CompareTo(Entry other) { return -this.time.CompareTo(other.time); } public object CloneData(Type decleard_type) { object value = XmlUtil.XmlToObject(data.GetType(), XmlUtil.FromString(xml_text), true); object target; if (TryConvertTo(value, decleard_type, out target)) { return target; } return value; } public bool EqualsXmlText(string x) { return this.xml_text.Equals(x); } } } //---------------------------------------------------------------------------------- private void DoCopy() { GridItem item = SelectedGridItem; if (item != null && item.PropertyDescriptor != null && item.Value != null) { List list; bool new_item = CopyHistory.Add(SelectedObject, item, out list); this.OnRefreshHistoryAdded(item, new_item, list); } } private void DoPaste() { GridItem item = SelectedGridItem; if (item != null && item.PropertyDescriptor is IG2DPropertyDescriptor) { IG2DPropertyDescriptor g2dpp = item.PropertyDescriptor as IG2DPropertyDescriptor; var current = CopyHistory.CurrentAvaliable(g2dpp.DecleardFieldType); if (current != null) { DoPastePlus(current); } } } private void DoPastePlus(CopyHistory.Entry copying) { GridItem item = SelectedGridItem; if (item != null && (copying != null) && (item.PropertyDescriptor != null) && (!item.PropertyDescriptor.IsReadOnly)) { if (item.PropertyDescriptor is IG2DPropertyDescriptor) { IG2DPropertyDescriptor g2dpp = item.PropertyDescriptor as IG2DPropertyDescriptor; try { var target = copying.CloneData(g2dpp.DecleardFieldType); if (target != null) { item.PropertyDescriptor.SetValue(g2dpp.ComponentData, target); this.Refresh(); } } catch (Exception err) { MessageBox.Show(err.Message); } } } } private void DoDelete() { GridItem item = SelectedGridItem; if (item != null && (item.PropertyDescriptor != null) && (!item.PropertyDescriptor.IsReadOnly)) { if (item.PropertyDescriptor is IG2DPropertyDescriptor) { IG2DPropertyDescriptor g2dpp = item.PropertyDescriptor as IG2DPropertyDescriptor; if (!g2dpp.NotNull) { try { item.PropertyDescriptor.SetValue(g2dpp.ComponentData, null); this.Refresh(); } catch (Exception err) { MessageBox.Show(err.Message); } } else if (g2dpp.FieldValue != null) { if (g2dpp.DecleardFieldType.IsArray) { var array = (Array)g2dpp.FieldValue; Array.Clear(array, 0, array.Length); } else if (g2dpp.DecleardFieldType.GetInterface(typeof(IDictionary).Name) != null) { var map = (IDictionary)g2dpp.FieldValue; map.Clear(); } else if (g2dpp.DecleardFieldType.GetInterface(typeof(IList).Name) != null) { var list = (IList)g2dpp.FieldValue; list.Clear(); } try { item.PropertyDescriptor.SetValue(g2dpp.ComponentData, g2dpp.FieldValue); this.Refresh(); } catch (Exception err) { MessageBox.Show(err.Message); } } else { MessageBox.Show("字段不可删除!"); } } } } private void DoCut() { DoCopy(); DoDelete(); } #endregion //------------------------------------------------------------------------------------------------------ #region _description_ /// /// Occurs when the description area size is changed by the user. /// public event EventHandler UserChangedDescriptionAreaSize; /// /// 设置最小默认注释行数量 /// public int MinDescriptionAreaLineCount { get { return this.descriptionAreaLineCountMin; } set { if (value <= 0) { throw new ArgumentException("The value cannot be less than zero."); } this.descriptionAreaLineCountMin = value; } } /// /// Gets or sets the description area line count. /// /// The description area line count. /// If value is less than zero. /// If not of the all objects required to set the field were found. public int DescriptionAreaLineCount { get { return this.descriptionAreaLineCount; } set { if (!inited) { return; } if (value < MinDescriptionAreaLineCount) { throw new ArgumentException("The value cannot be less than " + MinDescriptionAreaLineCount + "."); } if (this.docCommentType == null || this.docComment == null || this.propertyGridView == null || this.linesProperty == null) { throw new TypeLoadException("Not all of the objects required to set the field were found."); } try { int oldDocCommentHeight = this.docComment.Height; int oldValue = this.DescriptionAreaLineCount; this.linesProperty.SetValue(this.docComment, value, null); int difference = this.docComment.Height - oldDocCommentHeight; if (this.docComment.Top - difference > this.propertyGridView.Top) { this.sizeChangeIsFromUser = false; this.propertyGridView.Height -= difference; this.docComment.Top -= difference; this.descriptionAreaLineCount = value; this.sizeChangeIsFromUser = true; } else { this.linesProperty.SetValue(this.docComment, oldValue, null); } } catch (TargetInvocationException) { } this.Refresh(); } } /// /// Gets or sets the height of the description area. /// /// The height of the description area. public int DescriptionAreaHeight { get { return this.docComment.Height; } set { if (!inited) { return; } int difference = value - this.docComment.Height; if (this.docComment.Top - difference > this.propertyGridView.Top) { this.docComment.Height = value; this.docComment.Top -= difference; this.propertyGridView.Height -= difference; this.Refresh(); } } } /// /// Raises the UserChangedDescriptionAreaSize event. /// /// The System.EventArgs instance containing the event data. protected void OnUserChangedDescriptionAreaSize(EventArgs e) { EventHandler handler = this.UserChangedDescriptionAreaSize; if (handler != null) { handler(this, e); } } /// /// Handles this.docComment.SizeChanged. /// /// The sender. /// The System.EventArgs instance containing the event data. private void HandleDocCommentSizeChanged(object sender, EventArgs e) { if (this.sizeChangeIsFromUser) { try { this.descriptionAreaLineCount = (int)this.linesProperty.GetValue(this.docComment, null); this.OnUserChangedDescriptionAreaSize(EventArgs.Empty); } catch (TargetInvocationException) { } } } #endregion //------------------------------------------------------------------------------------------------------ } }