У меня есть пользовательский элемент управления с текстовым полем на нем, и я хотел бы открыть привязку базовой линии (текста в текстовом поле) за пределами настраиваемого элемента управления. Я знаю, что вы создаете конструктор (унаследованный от ControlDesigner) и переопределяете SnapLines, чтобы получить доступ к snaplines, но мне интересно, как получить текстовую базовую линию элемента управления, который я открыл пользовательским пользовательским элементом управления.
Базовые линии в пользовательских элементах управления Winforms
Ответ 1
У меня была аналогичная потребность, и я решил это следующим образом:
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. Если в пути есть другие элементы управления компоновкой, то расчет смещения становится немного сложнее.
Ответ 2
Как обновление для ответа на Miral.. вот несколько из "пропущенных шагов", для кого-то нового, который смотрит, как это сделать.:) Код С#, приведенный выше, почти готов к заходу, за исключением изменения нескольких значений для ссылки на 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))]
Затем вам понадобится класс "designer", который переопределит 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
Версия 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
Ответ 4
Спасибо всем за помощь. Это было трудно усвоить. Мысль, имеющая частный подкласс в каждом 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;
}
}
...
}
Еще раз спасибо за сообщение об этом.
Ответ 5
Ты на правильном пути. Вам необходимо переопределить свойство 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
В этом примере usercontrol содержит текстовое поле. Код добавляет новую привязку, которая представляет базовую линию для текстового поля. Важно правильно рассчитать смещение.