.NET Windows Forms 透明控件

15

我想在一个Windows Forms应用程序中模拟一种“Web 2.0” Lightbox样式的用户界面技术。也就是说,通过“暗淡”窗口客户区域中的所有其他内容来吸引某些前景控件的注意力。

显而易见的解决方案是创建一个简单的部分透明矩形控件,可以停靠在窗口的客户区域并置于Z-Order的最前端。它需要像脏玻璃一样工作,可以看到其他控件(因此可以继续绘制自己)。这是可能的吗?

我已经四处搜索并尝试了一些方法,但迄今为止没有成功。 如果不可能,还有什么其他方法可以做到这一点?

参见:http://www.useit.com/alertbox/application-design.html(在Lightbox部分查看屏幕截图以说明我的意思。)

6个回答

15

你能在.NET/C#中做到这个吗?

当然可以,但需要一些努力。我建议采用以下方法:创建一个顶级窗体,没有边框或标题栏区域,并确保通过将TransparencyKey和BackColor设置为相同的值来不绘制客户端区域背景。现在你有了一个什么也不显示的窗口...

public class DarkenArea : Form
{
    public DarkenArea()
    {
        FormBorderStyle = FormBorderStyle.None;
        SizeGripStyle = SizeGripStyle.Hide;
        StartPosition = FormStartPosition.Manual;
        MaximizeBox = false;
        MinimizeBox = false;
        ShowInTaskbar = false;
        BackColor = Color.Magenta;
        TransparencyKey = Color.Magenta;
        Opacity = 0.5f;
    }
}

创建并放置一个DarkenArea窗口覆盖在你的表单客户区上。然后,您需要能够显示窗口而不使其成为焦点,因此您需要以以下方式平台调用来显示窗口而不激活它...

public void ShowWithoutActivate()
{
    // Show the window without activating it (i.e. do not take focus)
    PlatformInvoke.ShowWindow(this.Handle, (short)SW_SHOWNOACTIVATE);
}

你需要确保它能够绘制出某些东西,但要排除控件中你想保留高亮的区域。因此,重写OnPaint处理程序并使用黑色/蓝色或其他颜色进行绘制,但要排除你想要保持明亮的区域...

protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);
    // Do your painting here be exclude the area you want to be brighter
}

最后,您需要重写 WndProc 以防止鼠标与窗口交互,如果用户尝试像在变暗的区域单击一样做出一些疯狂的操作。类似于这样...

protected override void WndProc(ref Message m)
{
    if (m.Msg == (int)WM_NCHITTEST)
        m.Result = (IntPtr)HTTRANSPARENT;
    else
        base.WndProc(ref m);
}

这应该足以产生所需的效果。当您准备撤销效果时,只需处理 DarkenArea 实例并继续即可。


8
这是一个非常酷的想法 - 我可能会使用它,所以谢谢。无论如何,我的解决方案非常简单......在当前窗体上打开一个新的50%不透明度窗体,然后为该窗体自定义绘制一个背景图像,矩形匹配您想突出显示的控件的边界,并填充透明键颜色。

在我的简单示例中,我称此表单为'LBform',其主要内容如下:

public Rectangle ControlBounds { get; set; }
private void LBform_Load(object sender, EventArgs e)
{
    Bitmap background = new Bitmap(this.Width, this.Height);
    Graphics g = Graphics.FromImage(background);
    g.FillRectangle(Brushes.Fuchsia, this.ControlBounds);

    g.Flush();

    this.BackgroundImage = background;
    this.Invalidate();
}

Color.Fuchia是这个半透明窗体的TransparencyKey,因此您将能够透过绘制的矩形并与主窗体中其边界内的任何内容进行交互。

在我编写的实验项目中,我使用了动态添加到窗体上的UserControl,但您也可以轻松地使用已经存在于窗体上的控件。在主窗体(您要遮挡的窗体)中,我将相关代码放入了一个按钮单击事件中:

private void button1_Click(object sender, EventArgs e)
{
    // setup user control:
    UserControl1 uc1 = new UserControl1();
    uc1.Left = (this.Width - uc1.Width) / 2;
    uc1.Top = (this.Height - uc1.Height) / 2;
    this.Controls.Add(uc1);
    uc1.BringToFront();

    // load the lightbox form:
    LBform lbform = new LBform();
    lbform.SetBounds(this.Left + 8, this.Top + 30, this.ClientRectangle.Width, this.ClientRectangle.Height);
    lbform.ControlBounds = uc1.Bounds;

    lbform.Owner = this;
    lbform.Show();
}

如果您愿意,您可以按照自己的方式进行一些基本的操作,但只需添加用户控件,然后将灯箱表单设置在主表单上,并设置边界属性以在正确位置呈现完全透明度。拖动表单、关闭灯箱表单和用户控件等操作在我的快速示例中未得到处理。噢,还别忘了处理Graphics实例的释放-我也把这个遗漏了(太晚了,我真的很累)。

这是我在20分钟内完成的演示


我的解决方案与之类似,不同的是我的“LightBox”表单(半透明的表单,以模态方式显示在主表单上方)在其构造函数中传递了一个UserControl,然后它自己以模态方式显示 - 所以你得到了两个模态窗口的堆栈。这符合我的要求。 - Fake Jim

1

另一个不需要使用新表单的解决方案:

  • 制作容器(表单/面板/其他)的图像,
  • 更改其不透明度,
  • 在一个新面板的背景中显示它。
  • 用该面板填充您的容器。

现在来看代码...

假设我有一个名为Frame的用户控件,我想要应用我的灯箱效果:

public partial class Frame : UserControl
{
    private Panel shadow = new Panel();
    private static float LIGHTBOX_OPACITY = 0.3f;

    public Frame()
    {
        InitializeComponent(); 
        shadow.Dock = DockStyle.Fill;
    }

    public void ShowLightbox()
    {
        Bitmap bmp = new Bitmap(this.Width, this.Height);
        this.pnlContainer.DrawToBitmap(bmp, new Rectangle(0, 0, this.Width, this.Height));
        shadow.BackgroundImage = SetImgOpacity(bmp, LIGHTBOX_OPACITY );
        this.Controls.Add(shadow);
        shadow.BringToFront();
    }

    // http://www.geekpedia.com/code110_Set-Image-Opacity-Using-Csharp.html
    private Image SetImgOpacity(Image imgPic, float imgOpac)
    {
        Bitmap bmpPic = new Bitmap(imgPic.Width, imgPic.Height);
        Graphics gfxPic = Graphics.FromImage(bmpPic);
        ColorMatrix cmxPic = new ColorMatrix();
        cmxPic.Matrix33 = imgOpac;
        ImageAttributes iaPic = new ImageAttributes();
        iaPic.SetColorMatrix(cmxPic, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
        gfxPic.DrawImage(imgPic, new Rectangle(0, 0, bmpPic.Width, bmpPic.Height), 0, 0, imgPic.Width, imgPic.Height, GraphicsUnit.Pixel, iaPic);
        gfxPic.Dispose();
        return bmpPic;
    }
}

使用此技术的优点包括:

  • 无需处理所有鼠标事件
  • 无需管理多个表单以与灯箱元素通信
  • 不需要覆盖WndProc
  • 你会很酷,因为你是唯一一个不使用表单来实现这种效果的人。

缺点主要是这种技术速度较慢,因为您必须处理整个图像来使用ColorMatrix纠正每个像素点。


1
表单本身具有完美的Opacity属性,但我认为大多数单独的控件并没有。您需要自己绘制它。

1

大的调光矩形很好。

另一种方法是:

自己编写基本的表单类,创建一个名为MyLightboxAwareForm的类,该类将侦听来自LightboxManagerLightboxEvent。然后让所有表单都继承自MyLightboxAwareForm

在调用任何LightboxShow方法时,LightboxManager会向所有MyLightboxAwareForm实例广播LightboxShown事件,并使它们变暗。

这样做的优点是,正常的Win32表单功能将继续工作,例如当您单击其模态对话框之一或管理鼠标悬停/按下事件时,任务栏会闪烁表单等等。

如果您想要矩形调光样式,只需将逻辑放入MyLightboxAwareForm即可。


0
每个窗体都有“Opacity”属性。将其设置为50%(或从代码中设置为0.5),则会半透明。在您想要聚焦的窗体之前删除边框并将其最大化显示。您可以更改窗体的BackColor,甚至为不同的效果设置背景图像。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接