【问题标题】:Refresh display of transparent user control刷新透明用户控件的显示
【发布时间】:2015-12-05 15:02:13
【问题描述】:

我编写了一个支持透明背景(除其他外)的用户控件。
但是,我发现了一个问题,当背景是透明的,并且您更改了用户控件的文本时,以前的文本仍然显示在屏幕上,在新文本下方,无法阅读。
我已经在谷歌上搜索了半天,发现各种在我的情况下不起作用的建议,其中大多数涉及将父控件绘制到位图上并在我的控件表面上绘制该位图。
但是,在我的情况下,父控件也是透明的,所以我尝试像建议的here 一样提升到表单的级别,但我得到了一个 InvalidArgumentException, 我试过像建议的here 那样使父控件无效,但也没有运气。

我的代码基本上是这样的(截断到最低限度):

protected override CreateParams CreateParams
{
    get
    {
        CreateParams cp = base.CreateParams;
        cp.ExStyle |= 0x20;
        return cp;
    }
}

protected override void OnPaintBackground(PaintEventArgs e)
{
    if(this.BackColor != Color.Transparent)
    {
        base.OnPaintBackground(e);
    }
}

【问题讨论】:

  • Winforms /sigh... 您可以尝试使所有控件无效(form.Invalidate()),但这会导致大量闪烁。然后您将尝试减少闪烁(双缓冲、位图等)。然后你切换到 wpf。
  • @Sinatr 我很想转wpf,不幸的是我的老板似乎认为学习曲线太长,项目的截止日期太短......还有其他建议吗?
  • 您没有重新绘制背景,因此没有过度绘制旧文本的像素。所以这完全是意料之中的。 UserControl 对透明度的支持很好,只需将其 BackColor 设置为透明即可。删除您发布的两种方法。在透明背景上绘制文本通常是不明智的,抗锯齿像素会出现错误的颜色,使文本看起来非常难看。最好用“blobby”来形容。您必须使用 TextRenderingHint.SingleBitPerPixelGridFit 来避免这种情况。
  • 为什么首先需要透明度?也许您可以将它们组合成一个控件并完全控制Paint,而不是在某些东西上使用控件?
  • @HansPassant:我已经知道这是保留旧文本的原因(抱歉在我的问题中忘记提及它)。我试图删除 CreateParams 和 OnPaintBackground 的覆盖,但它在显示中给了我很差的结果。无论如何,由于 Sinatr 的第一条评论和this answer,我想我已经找到了解决方案。很快就会发布答案

标签: c# winforms user-controls transparency


【解决方案1】:

我一次又一次地了解到,简单地向其他人解释问题通常可以帮助您解决问题。

所以,在结合了我所有搜索的一些答案后, 主要是this one关于使父的指定矩形无效, 和this one 关于获取表单上控件的位置,
我想出了这个:

// Make the Text property browsable again, call Refresh when changed.
[Browsable(true), 
 DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public override string Text 
{ 
    get { return _Text; }
    set
    { 
        if(_Text != value)
        {
            this.Refresh();
            _Text = value;
        }
    }
}

// Override Refresh to invalidate the relevant part of the parent form 
public override void Refresh()
{
    Form form = this.FindForm();
    // only for transparent controls that has text and no background image
    if (this.BackColor == Color.Transparent &&
        !string.IsNullOrEmpty(this.Text) &&
        (this.Gradient.BackColor2==Color.Transparent || !this.Gradient.IsGradient) && 
        this.BackgroundImage == null && 
        form != null)
    {
        Point locationOnForm = form.PointToClient(
                                   this.Parent.PointToScreen(this.Location)
                               );
        // Invalidate the rectangle of the form that's behind the current control
        form.Invalidate(new Rectangle(locationOnForm, this.Size));
    }
    base.Invalidate();
}

我仍然需要处理当前控件和表单之间的父控件,但现在这对我来说已经足够了。

【讨论】:

    【解决方案2】:

    我认为我们在这一点上已经接近了。

    首先,您不能像那样抑制 OnPaintBackground 并期望其余的 winform 按预期运行,这意味着根本没有任何东西被绘制到该区域,所以我们留下了 GDI+ 或 windows 或者可能是其他一些构造来“填充空白”。表单上不透明的控件可能想要调用您的 UserControl 来获取它的背景,所以我们必须绘制一些东西......

    所以我们总是调用base.OnPaintBackground,但是如果背景是透明的,那么我们需要使要重绘的控件的整个表面无效。

        public Glass()
        {
            InitializeComponent();
            this.SetStyle(ControlStyles.Opaque, true);
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
        }
    
        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams cp = base.CreateParams;
                cp.ExStyle |= 0x20;
                return cp;
            }
        }
    
        private bool rePaintState = false;
        protected override void OnPaintBackground(PaintEventArgs e)
        {
            base.OnPaintBackground(e);
            if (this.BackColor == Color.Transparent)
            {
                // we want to invalidate and force it to re-paint but just once
                if (rePaintState)
                {
                    rePaintState = false;
                }
                else
                {
                    rePaintState = true;
                    this.Invalidate();
                }
            }
        }
    

    这仍然不完整,但保存了我在 UserControl 上方和下方的控件具有在计时器上刷新值的文本框时遇到的大部分闪烁。

    我知道它的帮助不大,但是 WPF 对这些类型的 UX 问题非常有用 ^-^

    【讨论】:

    • 感谢您的回答,但这并没有太大帮助。我的控件还具有其他一些属性,例如渐变背景色和圆角,这不适用于它们。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-07
    • 2016-02-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多