在具有一些控件的窗体上绘制半透明覆盖图像

10

在窗体上绘制半透明覆盖图像,同时保留一些控件,使其所有子控件可见但不能被点击。它应该像我们透过半透明黑镜看到一些东西。

我尝试使用透明控件。这是子类化Panel控件并在该控件上绘制图像,但是所有控件都是完全可见的。

3个回答

30

这将需要另一个表单,您需要在现有表单之上显示它。使用其Opacity属性可以创建预期的效果。向项目中添加一个新类,并粘贴下面显示的代码。调用Close()方法以再次删除效果。

using System;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;

class Plexiglass : Form {
    public Plexiglass(Form tocover) {
        this.BackColor = Color.DarkGray;
        this.Opacity = 0.30;      // Tweak as desired
        this.FormBorderStyle = FormBorderStyle.None;
        this.ControlBox = false;
        this.ShowInTaskbar = false;
        this.StartPosition = FormStartPosition.Manual;
        this.AutoScaleMode = AutoScaleMode.None;
        this.Location = tocover.PointToScreen(Point.Empty);
        this.ClientSize = tocover.ClientSize;
        tocover.LocationChanged += Cover_LocationChanged;
        tocover.ClientSizeChanged += Cover_ClientSizeChanged;
        this.Show(tocover);
        tocover.Focus();
        // Disable Aero transitions, the plexiglass gets too visible
        if (Environment.OSVersion.Version.Major >= 6) {
            int value = 1;
            DwmSetWindowAttribute(tocover.Handle, DWMWA_TRANSITIONS_FORCEDISABLED, ref value, 4);
        }
    }
    private void Cover_LocationChanged(object sender, EventArgs e) {
        // Ensure the plexiglass follows the owner
        this.Location = this.Owner.PointToScreen(Point.Empty);
    }
    private void Cover_ClientSizeChanged(object sender, EventArgs e) {
        // Ensure the plexiglass keeps the owner covered
        this.ClientSize = this.Owner.ClientSize;
    }
    protected override void OnFormClosing(FormClosingEventArgs e) {
        // Restore owner
        this.Owner.LocationChanged -= Cover_LocationChanged;
        this.Owner.ClientSizeChanged -= Cover_ClientSizeChanged;
        if (!this.Owner.IsDisposed && Environment.OSVersion.Version.Major >= 6) {
            int value = 1;
            DwmSetWindowAttribute(this.Owner.Handle, DWMWA_TRANSITIONS_FORCEDISABLED, ref value, 4);
        }
        base.OnFormClosing(e);
    }
    protected override void OnActivated(EventArgs e) {
        // Always keep the owner activated instead
        this.BeginInvoke(new Action(() => this.Owner.Activate()));
    }
    private const int DWMWA_TRANSITIONS_FORCEDISABLED = 3;
    [DllImport("dwmapi.dll")]
    private static extern int DwmSetWindowAttribute(IntPtr hWnd, int attr, ref int value, int attrLen);
}

这个类应该怎么准确地调用? - Mike Baxter
1
看起来是 Utils.Plexiglass(this); ... ALT+F4 将关闭它。有没有办法在这个窗口上方显示黑色文本,例如“请等待”?(我只是将不透明度增加了三倍,并且这有助于我添加到此覆盖层的动态标签)。 - PeterX
嘿,PeterX,我想知道你是如何让标签显示在Plexiglass上的?有什么建议吗? :) - PhoenixDev
@HansPassant,这个答案非常有帮助,但它似乎也会使子控件(例如标签文本)半透明。有没有办法只让窗体半透明,而其子控件完全不透明?谢谢。 - Adam Milecki
1
当然,这就是 Opacity 的作用。这个类只有在覆盖现有窗口时才有用,“应该可见但无法点击”,正如 OP 所要求的那样,它不适用于显示任何 UI。 - Hans Passant

3
创建一个分层窗口,使其保持在主要窗口的顶部并与其位置同步。您可以使用32位RGBA图像来更改分层窗口的alpha值,以获得所需的效果。
这里有一篇不错的codeproject文章,向您展示如何做到这一点here

1
我认为更简单的方法是使用一个透明的标签控件,设置其不透明度并禁用其自动调整大小功能,并将标签调整到所需覆盖的表面大小。
然后,当您想要叠加标签时,将其发送到前面(通过编程)并使其可见。当您想要禁用叠加时,将其发送到后面并使其不可见。
我已经使用一个文本标签来叠加整个窗体。如果您不是设置Label控件的Text属性,而是设置半透明(PNG)图像,则认为它也可以正常工作。

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