属性网格控件 - 修改中央分割垂直线的位置
本文关键字:分割 垂直线 位置 网格 控件 修改 属性 | 更新日期: 2023-09-27 17:56:26
我在WinForms (http://msdn.microsoft.com/en-us/library/aa302326.aspx)中有一个PropertyGrid控件。现在我想将中间垂直线向左移动(它总是居中,但我的键很短,而值是 Paths,很长。默认情况下,该控件将线条放在中间,即使用户可以移动它。关于用户友好性,我想以编程方式将行向左移动。我现在已经多次搜索了WinForms设计器属性以及PropertyGrid控件的成员,但没有找到该选项(也没有任何与之相关的事件)。
它是否因私密而隐藏在视线/修改之外?我只是监督它吗?(在这种情况下,我真诚地道歉)或者我该怎么做?
是的,不幸的是,这需要一些基于反射的黑客才能实现。下面是一个示例扩展类:
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);
}
这是一个不依赖于直接使用私有方法或反射的方法。不过,它仍然使用未记录的接口。
在 .NET 4.0 中,PropertyGrid.Controls
集合包含 4 个控件。 PropertyGrid.Controls.item(2)
是一个未记录PropertyGridView
(与使用反射的答案中的类型相同)。属性PropertyGridView.LabelRatio
调整列的相对宽度。LabelRatio
的范围看起来像是 1.1 到 9。值越小,左列越宽。
我知道在您最初显示控件之前LabelRatio
设置有效。但是,我不确定一旦控件已经显示,您需要做什么才能使其生效。你可以谷歌MoveSplitterTo来查找.NET源代码,并查看源代码以获取PropertyGridView
以获取更多详细信息。涉及它的计算和操作似乎有些复杂,我没有详细分析它们。
LabelRatio 最初设置为 2(即将可用的 PropertyGrid 宽度分成两半)。将其设置为 3 表示三分之一,4 表示四分之一。代码需要导入系统反射
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
该解决方案对我并不完全有效,尽管其中的一部分是。
- 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"方法。 在本例中,它位于树视图的 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 将补偿属性网格左侧的条形图。
为了处理正在调整大小的窗体,我添加了 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);
}
}
这对我来说效果非常好。