Подтвердить что ты не робот

Как создать прозрачный элемент управления, который работает, когда поверх других элементов управления?

У меня есть элемент управления (полученный из System.Windows.Forms.Control), который должен быть прозрачным в некоторых областях. Я реализовал это с помощью SetStyle():

public TransparentControl()
{
    SetStyle(ControlStyles.SupportsTransparentBackColor, true);
    this.BackColor = Color.Transparent.
}

Теперь это работает, если между формой и прозрачным элементом управления нет элементов управления. Однако, если есть еще один элемент управления под прозрачным элементом управления (который здесь используется), он не работает. Промежуточный контроль - это не рисование, но приведенная ниже форма показывает. Я могу получить эффект, который мне нужен, переопределив CreateParams и установив flasg WS_EX_TRANSPARENT следующим образом:

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

Проблема здесь в том, что она действительно замедляет картину элемента управления. Элемент управления уже дважды буферизирован, поэтому ничего не делать. Снижение производительности настолько плохо, что это неприемлемо. Кто-нибудь еще столкнулся с этой проблемой? Все ресурсы, которые я могу найти, предлагают использовать метод # 1, но опять же, это не работает в моем случае.

EDIT: Я должен отметить, что у меня есть обходное решение. Детский (прозрачный) элемент управления мог просто нарисовать себя на родительском объекте Graphics, но он действительно нечеткий, и мне совсем не нравится это решение (хотя это может быть все, что у меня есть).

EDIT2: Итак, следуя советам, которые я получил о том, как прозрачность работает в .NET, я реализовал интерфейс IContainer в своем пользовательском элементе управления, который содержит прозрачные элементы управления. Я создал класс, который реализует ISite, я добавляю свои дочерние элементы управления в коллекцию компонентов UserControl, свойство Container правильно выравнивается в отладчике, но я до сих пор не получаю эффект прозрачности. У кого-нибудь есть идеи?

4b9b3361

Ответ 1

Я решил просто нарисовать родителя под моими дочерними элементами управления вручную. Вот хорошая статья.

Ответ 2

Это простая вещь, которую я приготовил. Единственная проблема, которую я обнаружил, заключается в том, что она не обновляется при обновлении элементов управления пересечением.

Он работает, рисуя элемент управления, который позади/пересекается с текущим элементом управления в растровое изображение, затем рисует это растровое изображение для текущего элемента управления.

protected override void OnPaint(PaintEventArgs e)
{
    if (Parent != null)
    {
        Bitmap behind = new Bitmap(Parent.Width, Parent.Height);
        foreach (Control c in Parent.Controls)
            if (c.Bounds.IntersectsWith(this.Bounds) & c != this)
                c.DrawToBitmap(behind, c.Bounds);
        e.Graphics.DrawImage(behind, -Left, -Top);
        behind.Dispose();
    }
}

Ответ 3

Прозрачные элементы управления в DotNet реализованы благодаря наличию прозрачного элемента управления в прозрачном окне управления, а затем прозрачного элемента управления. Этот процесс не учитывает возможность перекрытия элементов управления. Поэтому вам нужно будет использовать какую-то работу, чтобы заставить ее работать.

В некоторых случаях у меня был успех с сложной вложенностью, но это в основном только полезно для быстрого и грязного разбиения растровых изображений, и это не решает никаких проблем с частично перекрывающимися элементами управления.

Ответ 4

Я узнал, что приведенные ниже изменения делают вещи немного быстрее:

if((this.BackColor == Color.Transparent) && (Parent != null)) {
    Bitmap behind = new Bitmap(Parent.Width, Parent.Height);
    foreach(Control c in Parent.Controls) {
        if(c != this && c.Bounds.IntersectsWith(this.Bounds)) {
            c.DrawToBitmap(behind, c.Bounds);
        }
    }
    e.Graphics.DrawImage(behind, -Left, -Top);
    behind.Dispose();
}

Я также думаю, что использование this.Width/this.Height вместо Parent.Width/Parent.Height было бы еще быстрее, но я не успел поработать с ним.

Ответ 5

Рисование братьев и сестер под управлением возможно, но это уродливо. Код ниже работает достаточно хорошо для меня, он расширяется по коду, указанному в ссылке в Ed S. ' ответить.

Возможные подводные камни:

  • DrawToBitmap был введен с .net 2.0, поэтому не ожидайте, что он будет работать с чем-то старше этого. Но даже тогда что-то вроде этого может быть возможно, отправив WM_PRINT в элемент управления брата; AFAIK, что делает DrawToBitmap внутренне.
  • Это может также иметь проблемы, если у вас есть элементы управления под вашим контролем, которые используют WS_EX_TRANSPARENT, поскольку в соответствии с msdn это окно стиль скрипки с порядком живописи. У меня нет элементов управления, которые используют этот стиль, поэтому я не могу сказать.
  • Я запускаю XP SP3 с VS2010, поэтому этот подход может иметь дополнительные проблемы в Vista или W7.

Здесь код:

if (Parent != null)
{
    float
        tx = -Left,
        ty = -Top;

    // make adjustments to tx and ty here if your control
    // has a non-client area, borders or similar

    e.Graphics.TranslateTransform(tx, ty);

    using (PaintEventArgs pea = new PaintEventArgs(e.Graphics,e.ClipRectangle))
    {
        InvokePaintBackground(Parent, pea);
        InvokePaint(Parent, pea);
    }

    e.Graphics.TranslateTransform(-tx, -ty);

    // loop through children of parent which are under ourselves
    int start = Parent.Controls.GetChildIndex(this);
    Rectangle rect = new Rectangle(Left, Top, Width, Height);
    for (int i = Parent.Controls.Count - 1; i > start; i--)
    {
        Control c = Parent.Controls[i];

        // skip ...
        // ... invisible controls
        // ... or controls that have zero width/height (Autosize Labels without content!)
        // ... or controls that don't intersect with ourselves
        if (!c.Visible || c.Width == 0 || c.Height == 0 || !rect.IntersectsWith(new Rectangle(c.Left, c.Top, c.Width, c.Height)))
            continue;

        using (Bitmap b = new Bitmap(c.Width, c.Height, e.Graphics))
        {
            c.DrawToBitmap(b, new Rectangle(0, 0, c.Width, c.Height));

            tx = c.Left - Left;
            ty = c.Top - Top;

            // make adjustments to tx and ty here if your control
            // has a non-client area, borders or similar

            e.Graphics.TranslateTransform(tx, ty);
            e.Graphics.DrawImageUnscaled(b, new Point(0, 0));
            e.Graphics.TranslateTransform(-tx, -ty);
        }
}

Ответ 6

Некоторые предложения (извинения за код VB).

Старайтесь не рисовать фон:

Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
    If m.Msg = &H14 Then
        Return
    End If
    MyBase.WndProc(m)
End Sub

Protected Overrides Sub OnPaintBackground(ByVal pevent As System.Windows.Forms.PaintEventArgs)
    Return
End Sub

Не вызывайте базовый метод рисования элементов управления:

Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
    'MyBase.OnPaint(e) - comment out - do not call
End Sub

Ответ 7

Это трюк, по крайней мере, для меня:

protected override void OnPaintBackground(PaintEventArgs e)
{
    //base.OnPaintBackground(e);
    this.CreateGraphics().DrawRectangle(new Pen(Color.Transparent, 1), new Rectangle(0, 0, this.Size.Width, this.Size.Height));
}