Базовые линии привязки в пользовательских элементах управления Winforms - PullRequest
32 голосов
/ 18 сентября 2008

У меня есть пользовательский элемент управления с текстовым полем на нем, и я хотел бы представить базовую линию (текста в текстовом поле) вне пределов пользовательского элемента управления. Я знаю, что вы создаете конструктор (унаследованный от ControlDesigner) и переопределяете SnapLines, чтобы получить доступ к линиям привязки, но мне интересно, как получить текстовую базовую линию элемента управления, которую я предоставил своим пользовательским пользовательским элементом управления.

Ответы [ 5 ]

42 голосов
/ 03 июня 2009

В качестве обновления к ответу Мирала ... вот несколько «пропущенных шагов» для новичка, который ищет, как это сделать. :) Приведенный выше код на C # почти готов для вставки, за исключением изменения нескольких значений для ссылки на 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;
}

    }
  }
}
25 голосов
/ 31 декабря 2008

У меня просто была похожая потребность, и я решил ее так:

 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. Если на пути присутствуют другие элементы управления, влияющие на макет, тогда вычисление смещения становится немного более сложным.

7 голосов
/ 10 августа 2010

Спасибо всем за помощь. Это было трудно проглотить. Мысль о наличии частного подкласса в каждом пользовательском элементе управления была не очень приятной.

Я придумал этот базовый класс, чтобы помочь ..

[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;
        }
    }

    ...

}

Еще раз спасибо за публикацию этого.

6 голосов
/ 16 ноября 2009

VB.Net версия:
Примечание: вы должны изменить txtDescription на Textbox или другое имя внутреннего контроля, которое вы используете и 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
2 голосов
/ 18 сентября 2008

Вы на правильном пути. Вам нужно переопределить свойство 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 содержит текстовое поле. Код добавляет новую линию привязки, которая представляет базовую линию для текстового поля. Важно правильно рассчитать смещение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...