自定义Winforms控件中的基准对齐线
我有一个带有文本框的自定义用户控件,我想在自定义控件之外显示基线(文本框中的文本)对齐线。我知道我们创建了一个设计器(从ControlDesigner继承),并且重写了SnapLines以访问细线,但是我想知道如何获取自定义用户控件公开的控件的文本基线。
解决方案
我们走在正确的轨道上。我们将需要在设计器中覆盖SnapLines属性,并执行以下操作:
Public Overrides ReadOnly Property SnapLines() As System.Collections.IList Get Dim snapLinesList As ArrayList = TryCast(MyBase.SnapLines, ArrayList) Dim offset As Integer Dim ctrl As MyControl = TryCast(Me.Control, MyControl) If ctrl IsNot Nothing AndAlso ctrl.TextBox1 IsNot Nothing Then offset = ctrl.TextBox1.Bottom - 5 End If snapLinesList.Add(New SnapLine(SnapLineType.Baseline, offset, SnapLinePriority.Medium)) Return snapLinesList End Get End Property
在此示例中,用户控件包含一个文本框。该代码添加了一个新的捕捉线,该捕捉线表示文本框的基线。重要的是要正确计算偏移量。
我只是有一个类似的需求,我这样解决了它:
public override IList SnapLines { get { IList snapLines = base.SnapLines; MyControl control = Control as MyControl; if (control == null) { return snapLines; } IDesigner designer = TypeDescriptor.CreateDesigner( control.textBoxValue, typeof(IDesigner)); if (designer == null) { return snapLines; } designer.Initialize(control.textBoxValue); using (designer) { ControlDesigner boxDesigner = designer as ControlDesigner; if (boxDesigner == null) { return snapLines; } foreach (SnapLine line in boxDesigner.SnapLines) { if (line.SnapLineType == SnapLineType.Baseline) { snapLines.Add(new SnapLine(SnapLineType.Baseline, line.Offset + control.textBoxValue.Top, line.Filter, line.Priority)); break; } } } return snapLines; } }
这样,它实际上为子控件创建了一个临时子设计器,以找出"实际"基线对齐线在哪里。
这似乎在测试中表现合理,但是如果担心性能问题(并且内部文本框不移动),则可以将大部分代码提取到Initialize方法中。
这也假定文本框是UserControl的直接子级。如果还有其他影响布局的控件,则偏移量计算将变得更加复杂。
作为Miral答案的更新..这里是一些"缺失的步骤",对于正在寻找如何做到这一点的新手。 :)上面的Ccode几乎可以"直接插入",除了更改一些值以引用将要修改的UserControl之外。
可能需要的参考:
System.Design(@robyaw)
需要的用途:
using System.Windows.Forms.Design; using System.Windows.Forms.Design.Behavior; using System.ComponentModel; using System.ComponentModel.Design; using System.Collections;
在UserControl上,我们需要以下属性:
[Designer(typeof(MyCustomDesigner))]
然后,我们需要一个"设计器"类,该类将具有SnapLines覆盖:
private class MyCustomerDesigner : ControlDesigner { public override IList SnapLines { get { /* Code from above */ IList snapLines = base.SnapLines; // *** This will need to be modified to match your user control MyControl control = Control as MyControl; if (control == null) { return snapLines; } // *** This will need to be modified to match the item in your user control // This is the control in your UC that you want SnapLines for the entire UC IDesigner designer = TypeDescriptor.CreateDesigner( control.textBoxValue, typeof(IDesigner)); if (designer == null) { return snapLines; } // *** This will need to be modified to match the item in your user control designer.Initialize(control.textBoxValue); using (designer) { ControlDesigner boxDesigner = designer as ControlDesigner; if (boxDesigner == null) { return snapLines; } foreach (SnapLine line in boxDesigner.SnapLines) { if (line.SnapLineType == SnapLineType.Baseline) { // *** This will need to be modified to match the item in your user control snapLines.Add(new SnapLine(SnapLineType.Baseline, line.Offset + control.textBoxValue.Top, line.Filter, line.Priority)); break; } } } return snapLines; } } } }
VB.Net版本:
注意:我们必须将txtDescription更改为文本框或者使用的其他内部控件名称。和ctlUserControl
到usercontrol
名称
<Designer(GetType(ctlUserControl.MyCustomDesigner))> _ Partial Public Class ctlUserControl '... 'Your Usercontrol class specific code '... Class MyCustomDesigner Inherits ControlDesigner Public Overloads Overrides ReadOnly Property SnapLines() As IList Get ' Code from above Dim lines As IList = MyBase.SnapLines ' *** This will need to be modified to match your user control Dim control__1 As ctlUserControl = TryCast(Me.Control, ctlUserControl) If control__1 Is Nothing Then Return lines ' *** This will need to be modified to match the item in your user control ' This is the control in your UC that you want SnapLines for the entire UC Dim designer As IDesigner = TypeDescriptor.CreateDesigner(control__1.txtDescription, GetType(IDesigner)) If designer Is Nothing Then Return lines End If ' *** This will need to be modified to match the item in your user control designer.Initialize(control__1.txtDescription) Using designer Dim boxDesigner As ControlDesigner = TryCast(designer, ControlDesigner) If boxDesigner Is Nothing Then Return lines End If For Each line As SnapLine In boxDesigner.SnapLines If line.SnapLineType = SnapLineType.Baseline Then ' *** This will need to be modified to match the item in your user control lines.Add(New SnapLine(SnapLineType.Baseline, line.Offset + control__1.txtDescription.Top, line.Filter, line.Priority)) Exit For End If Next End Using Return lines End Get End Property End Class End Class
感谢所有这些人的帮助。这是一项艰巨的任务。在每个UserControl中都有一个私有子类的想法不是很可口。
我想出了这个基础课程来提供帮助。
[Designer(typeof(UserControlSnapLineDesigner))] public class UserControlBase : UserControl { protected virtual Control SnapLineControl { get { return null; } } private class UserControlSnapLineDesigner : ControlDesigner { public override IList SnapLines { get { IList snapLines = base.SnapLines; Control targetControl = (this.Control as UserControlBase).SnapLineControl; if (targetControl == null) return snapLines; using (ControlDesigner controlDesigner = TypeDescriptor.CreateDesigner(targetControl, typeof(IDesigner)) as ControlDesigner) { if (controlDesigner == null) return snapLines; controlDesigner.Initialize(targetControl); foreach (SnapLine line in controlDesigner.SnapLines) { if (line.SnapLineType == SnapLineType.Baseline) { snapLines.Add(new SnapLine(SnapLineType.Baseline, line.Offset + targetControl.Top, line.Filter, line.Priority)); break; } } } return snapLines; } } } }
接下来,从此基础派生UserControl:
public partial class MyControl : UserControlBase { protected override Control SnapLineControl { get { return txtTextBox; } } ... }
再次感谢我们发布此信息。