用户控件和Tab键顺序问题

时间:2017-03-17 11:26:44

标签: c# winforms user-controls

我创建了一个包含多个用户控件的项目,以支持winforms的透明度,渐变和主题。 我正在寻找一种方法来创建textbox的替代品,因为winforms文本框没有使用其他winforms控件使用的常规OnPaintOnPaintBackground,而且确实如此,我'找到了我可以使用的东西right here on stackoverflow. Brian的评论给了我解决方案 - 在我自己的控制范围内包装一个透明的RichTextBox。

然而,这提出了一个新问题,我无法弄清楚如何解决 - TabIndex属性dosn按预期运行。

对于普通文本框,当您有多个文本框并且每个文本框都有不同的选项卡索引时,焦点将按照选项卡索引指定的顺序从一个文本框转到另一个文本框。就我而言,它没有。相反,它是不可预测的。 我已经尝试了多种不同布局和控件的表单,但我似乎无法找到任何可预测的行为模式来表明问题。

以下是相关控件的代码(如果重要,ZControl会继承UserControl):

/// <summary>
/// A stylable textbox. 
/// <Remarks>
/// The solution for writing a stylable textbox was inspired by this SO post and Brian's comment:
/// https://stackoverflow.com/a/4360341/3094533
/// </Remarks>
/// </summary>
[DefaultEvent("TextChanged")]
public partial class ZTextBox : ZControl
{
    #region ctor

    public ZTextBox()
    {
        TextBox = new TransparentRichTextBox();
        TextBox.BackColor = Color.Transparent;
        TextBox.BorderStyle = BorderStyle.None;
        TextBox.Multiline = false;
        TextBox.TextChanged += TextBox_TextChanged;
        TextBox.TabStop = true;
        TextBox.AcceptsTab = false;
        InitializeComponent();
        AdjustTextBoxRectangle();
        this.Controls.Add(TextBox);
        this.RoundedCorners.PropertyChanged += RoundedCorners_PropertyChanged;
    }

    #endregion ctor

    #region properties 

    private TransparentRichTextBox TextBox { get; }

    public override string Text
    {
        get
        {
            return TextBox.Text;
        }

        set
        {
            TextBox.Text = value;
        }
    }

    [DefaultValue(false)]
    public bool Multiline
    {
        get
        {
            return this.TextBox.Multiline;
        }
        set
        {
            this.TextBox.Multiline = value;
        }
    }

    public override Font Font
    {
        get
        {
            return base.Font;
        }

        set
        {
            if (base.Font != value)
            {
                base.Font = value;
                if (TextBox != null)
                {
                    TextBox.Font = value;
                }
            }
        }
    }

    public new int TabIndex
    {
        get
        {
            return this.TextBox.TabIndex;
        }
        set
        {
            this.TextBox.TabIndex = value;
        }
    }

    #region hidden properties

    [
        DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
        Browsable(false),
        EditorBrowsable(EditorBrowsableState.Never)
    ]
    public override Color ForeColor
    {
        get
        {
            return TextBox.ForeColor;
        }

        set
        {
            TextBox.ForeColor = value;
        }
    }

    [
        DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
        Browsable(false),
        EditorBrowsable(EditorBrowsableState.Never)
    ]
    public override ContentAlignment TextAlign
    {
        get
        {
            return base.TextAlign;
        }

        set
        {
            base.TextAlign = value;
        }
    }

    [
        DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
        Browsable(false),
        EditorBrowsable(EditorBrowsableState.Never)
    ]
    public override Point TextLocationOffset
    {
        get
        {
            return base.TextLocationOffset;
        }

        set
        {
            base.TextLocationOffset = value;
        }
    }

    #endregion hidden properties

    #endregion properties 

    #region methods

    protected override void OnGotFocus(EventArgs e)
    {
        base.OnGotFocus(e);
        TextBox.Focus();
    }

    protected override void DrawText(Graphics graphics, string text, ContentAlignment textAlign, Point locationOffset, Size stringSize)
    {
        // Do nothing - The transparent rich textbox is responsible for drawing the text...
    }

    protected override void OnResize(EventArgs e)
    {
        base.OnResize(e);
        AdjustTextBoxRectangle();
    }

    private void AdjustTextBoxRectangle()
    {
        var corners = this.RoundedCorners.Corners;
        var leftAdjustment = ((corners & RoundedEdges.TopLeft) == RoundedEdges.TopLeft || (corners & RoundedEdges.BottomLeft) == RoundedEdges.BottomLeft) ? this.RoundedCorners.ArcSize / 2 : 0;
        var rightAdjustment = ((corners & RoundedEdges.TopRight) == RoundedEdges.TopRight || (corners & RoundedEdges.BottomRight) == RoundedEdges.BottomRight) ? this.RoundedCorners.ArcSize / 2 : 0;

        TextBox.Top = 0;
        TextBox.Left = leftAdjustment;
        TextBox.Width = this.Width - leftAdjustment - rightAdjustment;
        TextBox.Height = this.Height;
    }

    #endregion methods

    #region event handlers

    private void RoundedCorners_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        AdjustTextBoxRectangle();
    }

    private void TextBox_TextChanged(object sender, EventArgs e)
    {
        OnTextChanged(e);
    }

    #endregion event handlers

    #region private classes 

    private class TransparentRichTextBox : RichTextBox
    {
        public TransparentRichTextBox()
        {
            this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
            this.SetStyle(ControlStyles.Opaque, true);
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
        }

        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams parms = base.CreateParams;
                parms.ExStyle |= 0x20;  // Turn on WS_EX_TRANSPARENT
                return parms;
            }
        }
    }

    #endregion private classes 
}

设计师代码,如果相关:

partial class ZTextBox
{
    /// <summary> 
    /// Required designer variable.
    /// </summary>
    private System.ComponentModel.IContainer components = null;

    /// <summary> 
    /// Clean up any resources being used.
    /// </summary>
    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

    #region Component Designer generated code

    /// <summary> 
    /// Required method for Designer support - do not modify 
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    {
        this.SuspendLayout();
        // 
        // ZTextBox
        // 
        this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
        this.Name = "ZTextBox";
        this.RoundedCorners.ArcSize = 50;
        this.RoundedCorners.Corners = Zohar.UserControls.RoundedEdges.None;
        this.Size = new System.Drawing.Size(100, 20);
        this.Style.DisabledStyle.BackColor = System.Drawing.Color.Empty;
        this.Style.DisabledStyle.BackgroundImage = null;
        this.Style.DisabledStyle.BorderColor = System.Drawing.Color.Empty;
        this.Style.DisabledStyle.ForeColor = System.Drawing.Color.Empty;
        this.Style.DisabledStyle.Gradient.Angle = 0F;
        this.Style.DisabledStyle.Gradient.BackColor = System.Drawing.Color.Empty;
        this.Style.DisabledStyle.Image = null;
        this.Style.DisabledStyle.Name = null;
        this.Style.EnabledStyle.BackColor = System.Drawing.Color.Empty;
        this.Style.EnabledStyle.BackgroundImage = null;
        this.Style.EnabledStyle.BorderColor = System.Drawing.Color.Empty;
        this.Style.EnabledStyle.ForeColor = System.Drawing.Color.Empty;
        this.Style.EnabledStyle.Gradient.Angle = 0F;
        this.Style.EnabledStyle.Gradient.BackColor = System.Drawing.Color.Empty;
        this.Style.EnabledStyle.Image = null;
        this.Style.EnabledStyle.Name = null;
        this.Style.HoverStyle.BackColor = System.Drawing.Color.Empty;
        this.Style.HoverStyle.BackgroundImage = null;
        this.Style.HoverStyle.BorderColor = System.Drawing.Color.Empty;
        this.Style.HoverStyle.ForeColor = System.Drawing.Color.Empty;
        this.Style.HoverStyle.Gradient.Angle = 0F;
        this.Style.HoverStyle.Gradient.BackColor = System.Drawing.Color.Empty;
        this.Style.HoverStyle.Image = null;
        this.Style.HoverStyle.Name = null;
        this.ResumeLayout(false);

    }

    #endregion
}

2 个答案:

答案 0 :(得分:3)

问题是由以下代码引起的:

public new int TabIndex
{
    get
    {
        return this.TextBox.TabIndex;
    }
    set
    {
        this.TextBox.TabIndex = value;
    }
}

您永远不应该为UserControl执行此操作(实际上对于任何控件)。 Control.TabIndex属性的文档说明:

  

获取或设置控件在容器中的Tab键顺序。

换句话说,控件TabIndex属性不是表单的全局属性,而是作用于控件容器(父级)。

效果是您的控件所在的表单设计器代码将调用阴影TabOrder setter,但选项卡导航处理将简单地调用基本Control属性,从而导致未确定的行为。

另请注意,设置内部TabIndex的{​​{1}}没有任何意义,因为它是容器(您的控件)中唯一的控件。而你真正需要的是在你的容器中设置你的控件的TextBox

话虽如此,只需删除上面的代码,一切都会按预期工作。

答案 1 :(得分:0)

这可能是因为您没有按顺序添加文本框,或者在添加文本框时删除了一些文本框,无论如何您可以选择控件属性的顺序=&gt; TabIndex