I have a custom user control with a textbox on it and I'd like to expose the baseline (of the text in the textbox) snapline outside of the custom control. I know that you create a designer (inherited from ControlDesigner) and override SnapLines to get access to the snaplines, but I'm wondering how to get the text baseline of a control that I have exposed by my custom user control.
我有一个带有文本框的自定义用户控件,我想在自定义控件之外公开基线(文本框中的文本)。我知道您创建了一个设计器(继承自ControlDesigner)并覆盖了SnapLines以访问SnapLines,但是我想知道如何获得我的自定义用户控件公开的控件的文本基线。
5 个解决方案
#1
24
I just had a similar need, and I solved it like this:
我也有类似的需要,我就这样解决了:
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;
}
}
This way it's actually creating a temporary sub-designer for the subcontrol in order to find out where the "real" baseline snapline is.
这样,它实际上是为子控件创建一个临时子设计器,以便找出“真正的”基线线在哪里。
This seemed reasonably performant in testing, but if perf becomes a concern (and if the internal textbox doesn't move) then most of this code can be extracted to the Initialize method.
这在测试中似乎表现得很好,但是如果perf成为一个关注点(如果内部文本框不移动),那么可以将大部分代码提取到Initialize方法中。
This also assumes that the textbox is a direct child of the UserControl. If there are other layout-affecting controls in the way then the offset calculation becomes a bit more complicated.
这也假定文本框是UserControl的直接子控件。如果存在其他影响layout的控件,那么偏移计算将变得更加复杂。
#2
38
As an update to the Miral's answer.. here are a few of the "missing steps", for someone new that's looking how to do this. :) The C# code above is almost 'drop-in' ready, with the exception of changing a few of the values to reference the UserControl that will be modified.
作为Miral答案的更新。以下是一些“缺失的步骤”,对于那些正在寻找如何做到这一点的新人来说。:)上面的c#代码几乎已经准备好了,除了更改一些值以引用将要修改的UserControl之外。
Possible References Needed:
System.Design (@robyaw)
可能的引用需要:系统。设计(@robyaw)
Usings needed:
Usings需要:
using System.Windows.Forms.Design;
using System.Windows.Forms.Design.Behavior;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Collections;
On your UserControl you need the following Attribute:
在您的UserControl上,您需要以下属性:
[Designer(typeof(MyCustomDesigner))]
Then you need a "designer" class that will have the SnapLines override:
然后,您需要一个“设计器”类,它将具有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;
}
}
}
}
#3
6
Thanks to all those for the help. This was a tough one to swallow. The thought having a private sub-class in every UserControl wasn't very palatable.
谢谢大家的帮助。这是难以下咽的。在每个用户控件中拥有一个私有子类的想法并不令人满意。
I came up with this base class to help out..
我设计了这个基础课程来帮助你。
[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;
}
}
}
}
Next, derive your UserControl from this base:
接下来,从这个基础派生您的用户控件:
public partial class MyControl : UserControlBase
{
protected override Control SnapLineControl
{
get
{
return txtTextBox;
}
}
...
}
Thanks again for posting this.
再次感谢你发了这篇文章。
#4
6
VB.Net Version:
Note: you have to change the txtDescription
to the Textbox or another internal control name that you use. and ctlUserControl
to your usercontrol
name
VB。注意:必须将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
#5
2
You're on the right track. You will need to override the SnapLines property in your designr and do something like this:
你说得对。您需要重写设计中的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
In this example the usercontrol contains a textbox. The code adds a new snapline that represents the baseline for the textbox. The important thing is to calculate the offset correctly.
在本例中,usercontrol包含一个文本框。代码添加了一个新的线来表示文本框的基线。重要的是正确计算偏移量。
#1
24
I just had a similar need, and I solved it like this:
我也有类似的需要,我就这样解决了:
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;
}
}
This way it's actually creating a temporary sub-designer for the subcontrol in order to find out where the "real" baseline snapline is.
这样,它实际上是为子控件创建一个临时子设计器,以便找出“真正的”基线线在哪里。
This seemed reasonably performant in testing, but if perf becomes a concern (and if the internal textbox doesn't move) then most of this code can be extracted to the Initialize method.
这在测试中似乎表现得很好,但是如果perf成为一个关注点(如果内部文本框不移动),那么可以将大部分代码提取到Initialize方法中。
This also assumes that the textbox is a direct child of the UserControl. If there are other layout-affecting controls in the way then the offset calculation becomes a bit more complicated.
这也假定文本框是UserControl的直接子控件。如果存在其他影响layout的控件,那么偏移计算将变得更加复杂。
#2
38
As an update to the Miral's answer.. here are a few of the "missing steps", for someone new that's looking how to do this. :) The C# code above is almost 'drop-in' ready, with the exception of changing a few of the values to reference the UserControl that will be modified.
作为Miral答案的更新。以下是一些“缺失的步骤”,对于那些正在寻找如何做到这一点的新人来说。:)上面的c#代码几乎已经准备好了,除了更改一些值以引用将要修改的UserControl之外。
Possible References Needed:
System.Design (@robyaw)
可能的引用需要:系统。设计(@robyaw)
Usings needed:
Usings需要:
using System.Windows.Forms.Design;
using System.Windows.Forms.Design.Behavior;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Collections;
On your UserControl you need the following Attribute:
在您的UserControl上,您需要以下属性:
[Designer(typeof(MyCustomDesigner))]
Then you need a "designer" class that will have the SnapLines override:
然后,您需要一个“设计器”类,它将具有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;
}
}
}
}
#3
6
Thanks to all those for the help. This was a tough one to swallow. The thought having a private sub-class in every UserControl wasn't very palatable.
谢谢大家的帮助。这是难以下咽的。在每个用户控件中拥有一个私有子类的想法并不令人满意。
I came up with this base class to help out..
我设计了这个基础课程来帮助你。
[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;
}
}
}
}
Next, derive your UserControl from this base:
接下来,从这个基础派生您的用户控件:
public partial class MyControl : UserControlBase
{
protected override Control SnapLineControl
{
get
{
return txtTextBox;
}
}
...
}
Thanks again for posting this.
再次感谢你发了这篇文章。
#4
6
VB.Net Version:
Note: you have to change the txtDescription
to the Textbox or another internal control name that you use. and ctlUserControl
to your usercontrol
name
VB。注意:必须将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
#5
2
You're on the right track. You will need to override the SnapLines property in your designr and do something like this:
你说得对。您需要重写设计中的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
In this example the usercontrol contains a textbox. The code adds a new snapline that represents the baseline for the textbox. The important thing is to calculate the offset correctly.
在本例中,usercontrol包含一个文本框。代码添加了一个新的线来表示文本框的基线。重要的是正确计算偏移量。