PropertyGrid控件-修改中心分割垂直线的位置

问题描述 投票:0回答:3

我在 WinForms 中有一个 PropertyGrid 控件 (http://msdn.microsoft.com/en-us/library/aa302326.aspx)。现在我想将中间的垂直线更向左移动(它总是居中,但我的键很短,而值是路径,很长。控件默认将线放在中间,即使用户可以移动它。考虑到用户友好性,我想以编程方式将线移到更左边。我现在已经多次搜索了 WinForms 设计器属性以及 PropertyGrid 控件的成员,但没有找到该选项(也没有任何与之相关的事件)。

是否通过私有来隐藏视线/修改?我只是监督了它吗? (在这种情况下,我真诚地感到抱歉)否则我该怎么办?

c# .net winforms propertygrid
3个回答
12
投票

是的,不幸的是,这需要一些基于反射的黑客才能实现。 这是一个示例扩展类:

PropertyGridExtensionHacks.cs

using System.Reflection;
using System.Windows.Forms;

namespace PropertyGridExtensionHacks
{
    public static class PropertyGridExtensions
    {
        /// <summary>
        /// Gets the (private) PropertyGridView instance.
        /// </summary>
        /// <param name="propertyGrid">The property grid.</param>
        /// <returns>The PropertyGridView instance.</returns>
        private static object GetPropertyGridView(PropertyGrid propertyGrid)
        { 
            //private PropertyGridView GetPropertyGridView();
            //PropertyGridView is an internal class...
            MethodInfo methodInfo = typeof(PropertyGrid).GetMethod("GetPropertyGridView", BindingFlags.NonPublic | BindingFlags.Instance);
            return methodInfo.Invoke(propertyGrid, new object[] {});
        }

        /// <summary>
        /// Gets the width of the left column.
        /// </summary>
        /// <param name="propertyGrid">The property grid.</param>
        /// <returns>
        /// The width of the left column.
        /// </returns>
        public static int GetInternalLabelWidth(this PropertyGrid propertyGrid)
        {
            //System.Windows.Forms.PropertyGridInternal.PropertyGridView
            object gridView = GetPropertyGridView(propertyGrid);

            //protected int InternalLabelWidth
            PropertyInfo propInfo = gridView.GetType().GetProperty("InternalLabelWidth", BindingFlags.NonPublic | BindingFlags.Instance);
            return (int)propInfo.GetValue(gridView);
        }

        /// <summary>
        /// Moves the splitter to the supplied horizontal position.
        /// </summary>
        /// <param name="propertyGrid">The property grid.</param>
        /// <param name="xpos">The horizontal position.</param>
        public static void MoveSplitterTo(this PropertyGrid propertyGrid, int xpos)
        {
            //System.Windows.Forms.PropertyGridInternal.PropertyGridView
            object gridView = GetPropertyGridView(propertyGrid);

            //private void MoveSplitterTo(int xpos);
            MethodInfo methodInfo = gridView.GetType().GetMethod("MoveSplitterTo", BindingFlags.NonPublic | BindingFlags.Instance);
            methodInfo.Invoke(gridView, new object[] { xpos });
        }
    }
}

要移动分割器位置,请使用 MoveSplitterTo 扩展方法。 使用 GetInternalLabelWidth 扩展方法获取分隔符的实际位置。请注意,我观察到,在分配 SelectedObject 并且 PropertyGrid 未显示之前,GetInternalLabelWidth 返回 (-1)。

使用示例:

using PropertyGridExtensionHacks;
//...

    private void buttonMoveSplitter_Click(object sender, EventArgs e)
    {
        int splitterPosition = this.propertyGrid1.GetInternalLabelWidth();
        this.propertyGrid1.MoveSplitterTo(splitterPosition + 10);
    }

3
投票

这是一种不依赖于直接使用私有方法或反射的方法。但它仍然使用未记录的接口。

在 .NET 4.0 中,

PropertyGrid.Controls
集合包含 4 个控件。
PropertyGrid.Controls.item(2)
是一个未记录的
PropertyGridView
(与使用反射的答案中的类型相同)。属性
PropertyGridView.LabelRatio
调整列的相对宽度。
LabelRatio
的范围看起来是 1.1 到 9。值越小,左列越宽。

我知道在最初显示控件之前设置

LabelRatio
是有效的。但是,我不确定一旦控件已经显示,您需要做什么才能使其生效。您可以通过 google MoveSplitterTo 查找 .NET 源代码并查看
PropertyGridView
的源代码以获取更多详细信息。涉及到的计算和运算看起来有些复杂,我没有详细分析。

LabelRatio 最初设置为 2(即将可用的 PropertyGrid 宽度分成两半)。将三分之三设置为 3,四分之一设置为 4。 代码需要导入 System.Reflection

Public Sub MoveVerticalSplitter(grid As PropertyGrid, Fraction As Integer)
    Try
        Dim info = grid.[GetType]().GetProperty("Controls")
        Dim collection = DirectCast(info.GetValue(grid, Nothing), Control.ControlCollection)

        For Each control As Object In collection
            Dim type = control.[GetType]()

            If "PropertyGridView" = type.Name Then
                control.LabelRatio = Fraction

                grid.HelpVisible = True
                Exit For
            End If
        Next

    Catch ex As Exception
        Trace.WriteLine(ex)
    End Try
End Sub

将 PropertyGrid 底部的描述窗格的大小更改为文本行

Public Sub ResizeDescriptionArea(grid As PropertyGrid, lines As Integer)
    Try
        Dim info = grid.[GetType]().GetProperty("Controls")
        Dim collection = DirectCast(info.GetValue(grid, Nothing), Control.ControlCollection)

        For Each control As Object In collection
            Dim type = control.[GetType]()

            If "DocComment" = type.Name Then
                Const Flags As BindingFlags = BindingFlags.Instance Or BindingFlags.NonPublic
                Dim field = type.BaseType.GetField("userSized", Flags)
                field.SetValue(control, True)

                info = type.GetProperty("Lines")
                info.SetValue(control, lines, Nothing)

                grid.HelpVisible = True
                Exit For
            End If
        Next

    Catch ex As Exception
        Trace.WriteLine(ex)
    End Try
End Sub

0
投票

该解决方案并不完全适合我,尽管其中部分是有效的。

  • MoveSplitterTo 很好
  • GetInternalLabelWidth 没有给出我所追求的东西。

这对我来说确实有用:

在我分配给 PropertyGrid 的类中,我创建了一个具有静态和实例重载的 GetMaxLabelWidth 方法。我可以让这个更通用,我会的。后来:

    public static int GetMaxLabelWidth(SignDefinition signDefinition, Control targetControl)
    {
        int maxLength = 0;
        foreach (PropertyInfo info in signDefinition.GetType().GetProperties())
        {
            PropertyHandling.GetPropertyDisplayName(signDefinition, info.Name, out string label);
            int length = TextRenderer.MeasureText(label, targetControl.Font).Width;
            if (length > maxLength) maxLength = length;
        }

        return maxLength;
    }

    public int GetMaxLabelWidth(Control targetControl)
    {
        return GetMaxLabelWidth(this, targetControl);
    }

我在 PropertyHandling 命名空间中有一个反射助手,用于获取 DisplayName 属性(如果已分配),否则获取属性名称:

    public static bool GetPropertyDisplayName(object findInObject, string propName, out string foundDisplayName)
    {
        bool result = false;
        foundDisplayName = string.Empty;
        bool displayNameFound = false;

        PropertyInfo pi = findInObject.GetType().GetProperty(propName);
        IEnumerable<CustomAttributeData> cadc = pi.CustomAttributes;
        foreach (CustomAttributeData cad in cadc)
        {
            if (cad.ToString().Contains("DisplayName"))
            {
                foundDisplayName = cad.ConstructorArguments[0].Value.ToString();
                result = true;
                displayNameFound = true;
            }
        }

        if (!displayNameFound)
        {
            foundDisplayName = propName;
        }

        return result;
    }

我返回一个布尔值,因为我可能想知道是否设置了显示名称属性并以不同的方式处理它。在这种情况下,我正在懒惰地使用它。

在表单中,当我将对象分配给 PropertyGrid 时,我调用 GetMaxLabelWidth,然后使用上面的“MoveSplitterTo”方法。在本例中,它位于 TreeView 的 AfterSelect 事件上,其中对象被分配给标签。

    private void signListTree_AfterSelect(object sender, TreeViewEventArgs e)
    {
        TreeNode selNode = ((TreeView)sender).SelectedNode;
        if (selNode.Tag != null)
        {
            signDetailsPropGrid.SelectedObject = selNode.Tag;                
            int labelWidth = ((SignDefinition)selNode.Tag).GetMaxLabelWidth(signDetailsPropGrid);
            signDetailsPropGrid.MoveSplitterTo(labelWidth + 30);
        }
        else
        {
            signDetailsPropGrid.SelectedObject = null;
        }
    }

添加 30 可补偿 PropertyGrid 左侧的栏。

为了处理正在调整大小的表单,我添加了Resize事件:

    private void Form1_Resize(object sender, EventArgs e)
    {
        TreeNode selNode = signListTree.SelectedNode;
        if (selNode.Tag != null)
        {
            int labelWidth = ((SignDefinition)selNode.Tag).GetMaxLabelWidth(signDetailsPropGrid);
            signDetailsPropGrid.MoveSplitterTo(labelWidth + 30);
        }
    }

这对我来说非常有效。

© www.soinside.com 2019 - 2024. All rights reserved.