【问题标题】:Problems with user controls and Tab order用户控件和 Tab 顺序的问题
【发布时间】:2017-03-17 11:26:43
【问题描述】:

我创建了一个包含多个用户控件的项目,以支持 winform 的透明度、渐变和主题。 我正在寻找一种方法来创建textbox 的替代品,因为winforms 文本框没有使用其他winforms 控件使用的常规OnPaintOnPaintBackground,果然,我找到了可以使用@ 的东西987654321@ Brian 的评论给了我解决方案 - 在我自己的控件中包装一个透明的 RichTextBox。

但是,这带来了一个我不知道如何解决的新问题 - TabIndex 属性没有按预期运行。

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

这是相关控件的代码(如果重要,父级 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
}

【问题讨论】:

  • 您是否将控件放置在表格布局面板中?
  • 不,我已经将它们放在表单中一次,其中没有其他控件 - tabindex 似乎正在向后工作。我已经将它们放在另一个容器中(也继承了 ZControl),该选项卡似乎根本不起作用。我尽量避免使用表格布局面板。
  • 不幸的是,如果您想完成这项工作,您将不得不使用表格布局面板。 Caz 一旦我遇到与您相同的问题,我就以这种方式解决了问题。与表格布局面板一样,您也可以实现响应性
  • 是TabIndex属性,很微妙。基本问题是设置它的值不会强制设计者重写 InitializeComponent() 方法。如果您更改多个属性,而不是仅更改 TabIndex,则有效。您还需要摆脱 OnGetFocus() 覆盖,这可能是让您修改 TabIndex 的那个。 UserControl 已经这样做了。
  • @hans 谢谢,我会在我的电脑前试一试。

标签: c# winforms user-controls


【解决方案1】:

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

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

你不应该为UserControl(实际上是为任何控件)这样做。 Control.TabIndex 属性的文档说明:

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

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

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

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

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

【讨论】:

  • 嗯,这很奇怪。我添加这个的唯一原因是首先尝试处理这种奇怪的行为。但是,在测试项目中,您的答案似乎是正确的,而在实时项目中,标签仍然存在问题……但是,这似乎是朝着正确方向迈出的重要一步,并且可能是对此特定问题的正确答案问题,因此您将在可能的情况下获得赏金。 (从现在起 22 小时)
【解决方案2】:

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

【讨论】:

  • 感谢您的回答,但不幸的是这是错误的。我尝试了多种方案,包括在一个空表单上添加 3 个文本框。这篇文章的重点是 TabIndex 属性不能按预期工作......
猜你喜欢
  • 1970-01-01
  • 2020-05-20
  • 1970-01-01
  • 1970-01-01
  • 2011-09-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-04
相关资源
最近更新 更多