【问题标题】:Baseline snaplines in custom Winforms controls自定义 Winforms 控件中的基线对齐线
【发布时间】:2010-09-10 17:57:47
【问题描述】:

我有一个带有文本框的自定义用户控件,我想在自定义控件之外公开基线(文本框中的文本)对齐线。我知道您创建了一个设计器(从 ControlDesigner 继承)并覆盖 SnapLines 以访问对齐线,但我想知道如何获取自定义用户控件公开的控件的文本基线。

【问题讨论】:

    标签: .net winforms user-controls design-time windows-forms-designer


    【解决方案1】:

    作为对 Miral 答案的更新..这里有一些“缺失的步骤”,适合正在寻找如何做到这一点的新人。 :) 上面的 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;
    }
    
        }
      }
    }
    

    【讨论】:

    • 一个很大的投票(希望我能给出更多),因为您懒得提,您需要将 ControlDesigner 类放在您的 UserControl 中,并使用指定 ControlDesigner 类的 Designer 属性装饰您的 UserControl 类。一旦你看到这一点,一切都是有道理的,但不是之前。干杯。
    • Stuart - 我遇到了同样的问题,这就是我添加到这个问题的原因。我很高兴额外的信息能够帮助你!
    • 我正在尝试这个,但 VS2012 无法识别 ControlDesigner,我使用的是 winforms,而 .net 4.5 我以前从未这样做过,所以假设我做错了什么但不知道是什么?
    • 我将我的泛型设置为类似于Robert H.,但它不需要您的UserControl 从特殊类github.com/ImaginaryDevelopment/TypeProvisor/blob/master/… 继承
    【解决方案2】:

    我只是有类似的需求,我是这样解决的:

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

    这样它实际上是为子控件创建一个临时子设计器,以便找出“真正的”基线对齐线在哪里。

    这在测试中似乎表现不错,但如果 perf 成为问题(并且如果内部文本框没有移动),那么大部分代码都可以提取到 Initialize 方法中。

    这也假定文本框是 UserControl 的直接子级。如果中间还有其他影响布局的控件,那么偏移量的计算就变得有点复杂了。

    【讨论】:

    • 太棒了。那做了我们需要它做的事情。谢谢!
    • 我们有几个控件需要它来工作,所以我们最终将其分解为一个通用的控件设计器,我们已经实现了一个“ISnapable”接口,我们显式地实现并公开了这些控件。这样逻辑仍然被封装在设计器中,但被泛化了。
    【解决方案3】:

    感谢所有人的帮助。这是一个难以下咽的东西。在每个 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;
            }
        }
    
        ...
    
    }
    

    再次感谢您发布此内容。

    【讨论】:

    • 这是一个漂亮而通用的解决方案。谢谢。
    【解决方案4】:

    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
    

    【讨论】:

      【解决方案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
      

      在本例中,用户控件包含一个文本框。该代码添加了一个新的对齐线,表示文本框的基线。重要的是正确计算偏移量。

      【讨论】:

        猜你喜欢
        • 2018-01-16
        • 1970-01-01
        • 1970-01-01
        • 2021-08-08
        • 2014-11-26
        • 2011-09-07
        • 1970-01-01
        • 2012-08-30
        • 2017-02-19
        相关资源
        最近更新 更多