Click here to Skip to main content
15,917,862 members
Home / Discussions / C#
   

C#

 
GeneralFree Lightweight C# SDE Pin
Tristan Rhodes7-Dec-04 8:30
Tristan Rhodes7-Dec-04 8:30 
GeneralRe: Free Lightweight C# SDE Pin
Stanciu Vlad7-Dec-04 9:48
Stanciu Vlad7-Dec-04 9:48 
GeneralRe: Free Lightweight C# SDE Pin
Heath Stewart7-Dec-04 14:21
protectorHeath Stewart7-Dec-04 14:21 
GeneralRe: Free Lightweight C# SDE Pin
leppie8-Dec-04 2:42
leppie8-Dec-04 2:42 
GeneralRe: Free Lightweight C# SDE Pin
Nick Parker7-Dec-04 16:00
protectorNick Parker7-Dec-04 16:00 
GeneralRe: Free Lightweight C# SDE Pin
leppie8-Dec-04 2:40
leppie8-Dec-04 2:40 
GeneralMaking parts of a panel transparent Pin
G.Ringbom7-Dec-04 8:28
G.Ringbom7-Dec-04 8:28 
GeneralRe: Making parts of a panel transparent Pin
Heath Stewart7-Dec-04 14:16
protectorHeath Stewart7-Dec-04 14:16 
Using multiple panels contained within another panel has another problem: unnecessary overhead. Each is a control with its own window handle, window procedure, and more - all consuming memory and ticking away at the number of possible window handles allocated by the OS.

There are a couple ways you could do this. The easiest way is - for Windows 2000 and newer - to use layered Windows. This is what the Opacity property of a Form uses. In this case, however, you'll need to code something yourself, which I'll get to in a moment.

The other way is supported on any Windows platforms (perhaps other platforms, too) that supports the .NET Framework. You exclude a region of your control from painting. This is known as clipping. Anything below it is sent a WM_PAINT message to redraw that portion of itself. This can be expensive (in terms of resources), which is why layered Windows are nice (but only supported on Windows 2000 and newer).

The first way requires that you P/Invoke SetLayeredWindowAttributes and - upon creation of your control (override OnHandleCreated, and be sure to call base.OnHandleCreated) - you pass it the Handle property, the color of your transparent color, and LWA_COLORKEY (0x1) to tell the function to use your color, not an alpa value (which is what the Form.Opacity property actually uses). Then you assign an image as your BackgroundImage for your Panel that contains the color you want to mask out.

See the example below, where Example.jpg is just an image with white that I mask out:
using System;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;
 
class Example : Form
{
  Example()
  {
    SetStyle(ControlStyles.AllPaintingInWmPaint
      | ControlStyles.ResizeRedraw, true);
 
    using (Image bg = Image.FromStream(
      GetType().Assembly.GetManifestResourceStream("Example.jpg")))
    {
      BackgroundImage = (Image)bg.Clone();
      Size = bg.Size;
    }
 
    FormBorderStyle = FormBorderStyle.None;
  }
 
  void SetTransparentColor(Color c)
  {
    int key = ColorTranslator.ToWin32(c);
    SetLayeredWindowAttributes(Handle, key, 0, LWA_COLORKEY);
  }
 
  protected override System.Windows.Forms.CreateParams CreateParams
  {
    get
    {
      System.Windows.Forms.CreateParams cp = base.CreateParams;
      cp.ExStyle = WS_EX_LAYERED;
      return cp;
    }
  }
 
  protected override void OnHandleCreated(EventArgs e)
  {
    base.OnHandleCreated(e);
    SetTransparentColor(Color.White);
  }
 
  protected override void OnClick(EventArgs e)
  {
    base.OnClick(e);
    Close();
  }
 
  const int LWA_COLORKEY = 0x1;
  const int WS_EX_LAYERED = 0x00080000;
 
  [DllImport("user32.dll")]
  static extern bool SetLayeredWindowAttributes(IntPtr hwnd,
    [MarshalAs(UnmanagedType.U4)] int crKey, byte bAlpha,
    [MarshalAs(UnmanagedType.U4)] int dwFlags);
 
  static void Main()
  {
    Application.Run(new Example());
  }
}
The other way you override OnPaint and set the Graphics.Clip to a Region. You can easily construct this Region from a GraphicsPath (see the .NET Framework SDK documentation for detials) that you can add shapes to - rectangles, polygons, circles, etc. This is the region that is not painted.

Note that you shouldn't calculate this region in OnPaint - it's very expensive. You really only need to calculate it again if your control is resized (and your shape resizes along with it) or if your shape changes. Below I posted an example I posted a while back that discusses some other topics, but you'll see your question answered in there, too:
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
 
using AS = System.Windows.Forms.AnchorStyles;
 
class Test : Form
{
  static void Main()
  {
    Application.Run(new Test());
  }
 
  Test()
  {
    Text = "Triangle Example";
 
    Triangle t = new Triangle();
    Controls.Add(t);
    t.Dock = DockStyle.Fill;
 
    BackColor = Color.White;
  }
}
 
class Triangle : Control
{
  public Triangle()
  {
    SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint
      | ControlStyles.DoubleBuffer | ControlStyles.ResizeRedraw, true);
 
    UpdatePath();
  }
 
  protected override void OnPaint(PaintEventArgs e)
  {
    base.OnPaint(e);
 
    if (!init)
    {
      UpdateBrush();
      init = true;
    }
 
    Graphics g = e.Graphics;
    g.Clip = region;
    g.CompositingQuality = CompositingQuality.AssumeLinear;
    g.SmoothingMode = SmoothingMode.AntiAlias;
 
    g.FillPath(brush, path);
  }
 
  protected override void OnResize(EventArgs e)
  {
    base.OnResize(e);
    UpdatePath();
  }
 
  bool drag;
  protected override void OnMouseDown(MouseEventArgs e)
  {
    drag = true;
    SetCenter(e.X, e.Y);
 
    base.OnMouseDown(e);
  }
 
  protected override void OnMouseMove(MouseEventArgs e)
  {
    if (drag)
      SetCenter(e.X, e.Y);
 
    base.OnMouseMove(e);
  }
 
  protected override void OnMouseUp(MouseEventArgs e)
  {
    drag = false;
    base.OnMouseUp(e);
  }
 
  protected override void Dispose(bool disposing)
  {
    if (disposing)
    {
      if (path != null) path.Dispose();
      if (region != null) region.Dispose();
      if (brush != null) brush.Dispose();
    }
  }
 
  void SetCenter(int x, int y)
  {
    if (brush != null)
    {
      brush.CenterPoint = new PointF(x, y);
      Invalidate();
    }
  }
 
  GraphicsPath path;
  Region region;
  void UpdatePath()
  {
    if (Width == 0 || Bottom == 0) return;
 
    PointF p1 = new PointF(Width / 2, Top);
    PointF p2 = new PointF(0, Bottom);
    PointF p3 = new PointF(Width, Bottom);
 
    if (path != null)
    {
      path.Dispose();
      path = null;
    }
 
    if (region != null)
    {
      region.Dispose();
      region = null;
    }
 
    path = new GraphicsPath();
    path.AddPolygon(new PointF[] {p1, p2, p3});
    region = new Region(path);
 
    if (init) UpdateBrush();
  }
 
  PathGradientBrush brush;
  bool init = false;
  void UpdateBrush()
  {
    PointF point = PointF.Empty;
    if (brush != null)
    {
      point = brush.CenterPoint;
      brush.Dispose();
    }
 
    brush = new PathGradientBrush(path);
    brush.CenterColor = Color.Gray;
    if (point != PointF.Empty)
      brush.CenterPoint = point;
    brush.SurroundColors = new Color[] {c1, c2, c3};
  }
 
  Color c1 = Color.Red, c2 = Color.Green, c3 = Color.Blue;
  public Color C1
  {
    get { return c1; }
    set { c1 = value; UpdateBrush(); }
  }
 
  public Color C2
  {
    get { return c2; }
    set { c2 = value; UpdateBrush(); }
  }
 
  public Color C3
  {
    get { return c3; }
    set { c3 = value; UpdateBrush(); }
  }
}
This draws a triangular control (instead of rectangular) and sets the clipping region so that mouse messages outside of the rectangle are translated to the control behind the triangular control. Both layered windows and clipping regions have this affect. The control is seemingly not there (in the masked region).

If you want a really cool design-time method for assigning such regions, see the RegionMaster Controls[^] on WindowsForms.net.

This posting is provided "AS IS" with no warranties, and confers no rights.

Software Design Engineer
Developer Division Sustained Engineering
Microsoft

[My Articles] [My Blog]
GeneralRe: Making parts of a panel transparent Pin
G.Ringbom8-Dec-04 1:45
G.Ringbom8-Dec-04 1:45 
GeneralRe: Making parts of a panel transparent Pin
Heath Stewart8-Dec-04 6:22
protectorHeath Stewart8-Dec-04 6:22 
GeneralRe: Making parts of a panel transparent Pin
G.Ringbom8-Dec-04 6:38
G.Ringbom8-Dec-04 6:38 
GeneralConsole Application problem...! Pin
QzRz7-Dec-04 8:09
QzRz7-Dec-04 8:09 
GeneralRe: Console Application problem...! Pin
Daniel Turini7-Dec-04 8:14
Daniel Turini7-Dec-04 8:14 
GeneralRe: Console Application problem...! Pin
QzRz7-Dec-04 8:16
QzRz7-Dec-04 8:16 
GeneralI can't get my codebehind to work Pin
bigtone787-Dec-04 6:44
bigtone787-Dec-04 6:44 
GeneralRe: I can't get my codebehind to work Pin
Dave Kreskowiak7-Dec-04 7:19
mveDave Kreskowiak7-Dec-04 7:19 
GeneralRe: I can't get my codebehind to work Pin
bigtone787-Dec-04 7:30
bigtone787-Dec-04 7:30 
GeneralRe: I can't get my codebehind to work Pin
Colin Angus Mackay7-Dec-04 12:01
Colin Angus Mackay7-Dec-04 12:01 
Generalicon not repainted on minimize of an overlaying window Pin
stefan houtz7-Dec-04 5:41
stefan houtz7-Dec-04 5:41 
GeneralRe: icon not repainted on minimize of an overlaying window Pin
Dave Kreskowiak7-Dec-04 7:14
mveDave Kreskowiak7-Dec-04 7:14 
GeneralRe: icon not repainted on minimize of an overlaying window Pin
stefan houtz7-Dec-04 23:12
stefan houtz7-Dec-04 23:12 
Generalsatellite software Pin
SESCO LIBYA7-Dec-04 4:19
SESCO LIBYA7-Dec-04 4:19 
GeneralRe: satellite software Pin
Daniel Turini7-Dec-04 4:33
Daniel Turini7-Dec-04 4:33 
GeneralRe: satellite software Pin
Dave Kreskowiak7-Dec-04 5:34
mveDave Kreskowiak7-Dec-04 5:34 
GeneralRe: satellite software Pin
SESCO Libya18-Dec-04 5:47
sussSESCO Libya18-Dec-04 5:47 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.