代码之家  ›  专栏  ›  技术社区  ›  Fake Jim

.NET Windows窗体透明控件

  •  15
  • Fake Jim  · 技术社区  · 17 年前

    我想在一个 Windows Forms 申请。也就是说,通过“调暗”窗口的工作区中的所有其他内容来引起对某些前景控件的注意。

    显而易见的解决方案是创建一个控件,该控件只是一个部分透明的矩形,可以停靠到窗口的工作区,并将其带到z顺序的前面。它需要表现得像一块脏兮兮的玻璃,其他控件仍然可以通过它看到(因此继续绘制它们自己)。这可能吗?

    我有一个很好的狩猎回合,并尝试了一些技术,但迄今为止一直没有成功。 如果不可能的话,还有什么别的办法呢?

    见: http://www.useit.com/alertbox/application-design.html (在lightbox部分下面是一个截图来说明我的意思。)

    6 回复  |  直到 15 年前
        1
  •  14
  •   Phil Wright    17 年前

    你能在.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;
        }
    }
    

    在窗体的工作区上创建并定位此深色区域窗口。然后,您需要能够显示窗口而不占用焦点,因此您将需要以以下方式显示平台调用,而不使其变为活动的…

    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);
    }
    

    这应该足以达到预期的效果。当你准备好逆转效果的时候,你会处理暗黑女星实例并继续。

        2
  •  8
  •   ZeroBugBounce    17 年前

    这是一个非常酷的主意-我可能会用它,所以谢谢。不管怎样,我的解决方案很简单…在当前窗体上打开一个新的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();
    }
    

    fuchia是这个半不透明表单的透明键,因此您可以看到绘制的矩形,并与主表单边界内的任何内容交互。

    在我尝试这个的实验项目中,我使用了一个动态添加到表单中的用户控件,但是您可以同样轻松地使用表单上已经存在的控件。在主窗体(您正在隐藏的窗体)中,我将相关代码放入一个按钮中单击:

    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();
    }
    

    如果你喜欢的话,你可以用自己的方式来做一些基本的事情,但是它只是添加了usercontrol,然后在主窗体上设置lightbox表单,并设置bounds属性来在正确的地方呈现完全透明。在我的快速示例中,诸如窗体拖动和关闭lightbox窗体和usercontrol之类的操作不会被处理。哦,别忘了处理图形实例-我也忘了(太晚了,我真的很累了)。

    Here's my very did-it-in-20-minutes demo

        3
  •  1
  •   chakrit Dutchie432    17 年前

    大的调光矩形很好。

    另一种方法是:

    滚你自己的基础表单类说 MyLightboxAwareForm 会听 LightboxEvent 从A LightboxManager . 那就让你所有的表格都继承自 Mylightboxawareform公司 .

    打电话给 Show 任何方法 Lightbox , the Lightbox管理器 会广播一个 LightboxShown 事件到所有 Mylightboxawareform公司 实例并使其自身变暗。

    这有一个优点,即正常的win32窗体功能将继续工作,例如当您单击窗体的一个模式对话框时,该窗体的任务栏闪烁,或者mouseover/mousedown事件的管理仍将正常工作等。

    如果你想要矩形调暗的样式,你可以把逻辑放在mylightboxawareform中

        4
  •  1
  •   Peter Mortensen Pieter Jan Bonestroo    16 年前

    表单本身具有属性 Opacity 这将是完美的,但我不认为大多数单独的控制。你得自己画。

        5
  •  1
  •   Gad    15 年前

    另一个不涉及使用新表单的解决方案:

    • 给你的容器拍张照片(表格/面板/随便什么),
    • 改变它的不透明度,
    • 在新面板的背景中显示。
    • 把你的容器装满那个面板。

    现在密码…

    假设我有一个名为frame的用户控件,我想对它应用lightbox效果:

    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;
        }
    }
    

    使用这种技术的优点是:

    • 你不必处理所有的鼠标事件
    • 您不必管理多个表单来与lightbox元素通信
    • 不重写WndProc
    • 你会很酷,因为你将是唯一一个不使用表单来达到这种效果的人。

    缺点主要是这项技术要慢得多,因为必须处理整个图像才能使用ColorMatrix校正每个像素点。

        6
  •  0
  •   Miroslav Zadravec    17 年前

    每个窗体都有“不透明度”属性。将其设置为50%(或代码中的0.5),这样将是半透明的。移除边框,并在要聚焦的窗体之前将其最大化。您可以更改窗体的背景色,甚至可以为不同的效果设置背景图像。