Ответ 1
Я решил просто нарисовать родителя под моими дочерними элементами управления вручную. Вот хорошая статья.
У меня есть элемент управления (полученный из 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 правильно выравнивается в отладчике, но я до сих пор не получаю эффект прозрачности. У кого-нибудь есть идеи?
Я решил просто нарисовать родителя под моими дочерними элементами управления вручную. Вот хорошая статья.
Это простая вещь, которую я приготовил. Единственная проблема, которую я обнаружил, заключается в том, что она не обновляется при обновлении элементов управления пересечением.
Он работает, рисуя элемент управления, который позади/пересекается с текущим элементом управления в растровое изображение, затем рисует это растровое изображение для текущего элемента управления.
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();
}
}
Прозрачные элементы управления в DotNet реализованы благодаря наличию прозрачного элемента управления в прозрачном окне управления, а затем прозрачного элемента управления. Этот процесс не учитывает возможность перекрытия элементов управления. Поэтому вам нужно будет использовать какую-то работу, чтобы заставить ее работать.
В некоторых случаях у меня был успех с сложной вложенностью, но это в основном только полезно для быстрого и грязного разбиения растровых изображений, и это не решает никаких проблем с частично перекрывающимися элементами управления.
Я узнал, что приведенные ниже изменения делают вещи немного быстрее:
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
было бы еще быстрее, но я не успел поработать с ним.
Рисование братьев и сестер под управлением возможно, но это уродливо. Код ниже работает достаточно хорошо для меня, он расширяется по коду, указанному в ссылке в Ed S. ' ответить.
Возможные подводные камни:
DrawToBitmap
был введен с .net 2.0, поэтому не ожидайте, что он будет работать с чем-то старше этого. Но даже тогда что-то вроде этого может быть возможно, отправив WM_PRINT в элемент управления брата; AFAIK, что делает DrawToBitmap внутренне.WS_EX_TRANSPARENT
, поскольку в соответствии с msdn это окно стиль скрипки с порядком живописи. У меня нет элементов управления, которые используют этот стиль, поэтому я не могу сказать.Здесь код:
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);
}
}
Некоторые предложения (извинения за код 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
Это трюк, по крайней мере, для меня:
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));
}