The solution wasn't completely working for me, although parts of it were.
- MoveSplitterTo was fine
- GetInternalLabelWidth wasn't giving back what I was chasing.
This did work for me though:
In the class I was assigning to the PropertyGrid, I created a GetMaxLabelWidth method with a static and instance overload. I could make this more generic, and I will. Later:
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);
}
I have a reflection helper in a PropertyHandling namespace for getting the DisplayName attribute if one is assigned, or the property name otherwise:
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;
}
I return a bool as I may want to know whether a display name attribute is set or not and handle it differently. In this case, I'm using it lazily.
In the form, at the point I'm assigning the object to the PropertyGrid, I call GetMaxLabelWidth, then use the "MoveSplitterTo" method above. In this case it's on the AfterSelect event of a TreeView, where the object is assigned to the Tag.
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;
}
}
Adding 30 compensates for the bar on the left of the PropertyGrid.
In order to deal with the form being resized, I add the Resize event:
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);
}
}
This is working really well for me.