3

What I have below is a dll that when ran and supplied with pos, pic, and transparency. Will display the picture on the screen, the picture will be the top most object no matter what. You will also be able to click through the picture. The problem I am having is, When using a circular picture it has white around the edges of the circle to make it square. I want to get rid of those white edges of the picture so i am just left with a circle that is top most and I can click through. Everything in the code below works except when I have f.TransparencyKey = BackColor; I can no longer click through the picture, but it does make it a circle. Now how would I make it so i can click through the picture while getting it to be a circle instead of a square?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace pic
{
public class Class1
{
    [DllImport("user32.dll", SetLastError = true)]
    private static extern UInt32 GetWindowLong(IntPtr hWnd, int nIndex);
    [DllImport("user32.dll")]
    static extern int SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
    [DllImport("user32.dll")]
    static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags);
    public const int GWL_EXSTYLE = -20;
    public const int WS_EX_LAYERED = 0x80000;
    public const int WS_EX_TRANSPARENT = 0x20;
    public const int LWA_ALPHA = 0x2;
    public const int LWA_COLORKEY = 0x1;


    public void t(int LocalX, int LocalY, string PicLocal, byte transparency)
    {
        Bitmap bitmap;
        Form f = new Form();
        f.BackColor = Color.White;
        f.FormBorderStyle = FormBorderStyle.None;
        f.Bounds = Screen.PrimaryScreen.Bounds;
        f.TopMost = true;
        bitmap = new Bitmap(PicLocal);
        f.Size = new Size(bitmap.Size.Width, bitmap.Size.Height);
        f.StartPosition = FormStartPosition.Manual;
        f.SetDesktopLocation(LocalX, LocalY);
        Application.EnableVisualStyles();
        SetWindowLong(f.Handle, GWL_EXSTYLE,
        (IntPtr)(GetWindowLong(f.Handle, GWL_EXSTYLE) | WS_EX_LAYERED | WS_EX_TRANSPARENT));
        // set transparency to 50% (128)
        SetLayeredWindowAttributes(f.Handle, 0, transparency, LWA_ALPHA);
        f.BackgroundImage = Bitmap.FromFile(PicLocal);
        //f.AllowTransparency = true;
        //Color BackColor = Color.White;
        // Make the background color of form display transparently. 
        //f.TransparencyKey = BackColor;
        Application.Run(f);

    }

}
}
hurnhu
  • 888
  • 2
  • 11
  • 30
  • 1
    When you add flags, you don't want an *exclusive* XOR (`^`), you just want a regular OR (`|`). So change the code to: `GetWindowLong(f.Handle, GWL_EXSTYLE) | WS_EX_LAYERED | WS_EX_TRANSPARENT)` – Cody Gray - on strike Jul 18 '14 at 06:00
  • @CodyGray i have changed it to that and nothing has chaged – hurnhu Jul 18 '14 at 19:03

3 Answers3

3

Try this

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

namespace WindowsFormsApplication4
{
static class Program
{
    [DllImport("Gdi32.dll", EntryPoint = "CreateRoundRectRgn")]
    private static extern IntPtr CreateRoundRectRgn
    (
        int nLeftRect, // x-coordinate of upper-left corner
        int nTopRect, // y-coordinate of upper-left corner
        int nRightRect, // x-coordinate of lower-right corner
        int nBottomRect, // y-coordinate of lower-right corner
        int nWidthEllipse, // height of ellipse
        int nHeightEllipse // width of ellipse
     );

    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);

        string PicLocal = @"C:\Projects\Screenshot_1.bmp";

        Form f = new Form() {  FormBorderStyle = FormBorderStyle.None, StartPosition = FormStartPosition.CenterScreen};
        f.BackgroundImage = new Bitmap(PicLocal);
        f.Size = new Size(f.BackgroundImage.Size.Width, f.BackgroundImage.Size.Height);
        f.Region = System.Drawing.Region.FromHrgn(CreateRoundRectRgn(0, 0, 200, 200, 2000, 2000));
        f.Click += (s, e) => { System.Windows.Forms.MessageBox.Show("Clicked"); };

        Application.Run(f);

    }
}

}

I found an even simpler solution here Make a form's background transparent

You don't have to use the Region Property and CreateRoundRectRgn ,just make sure you have a round transparent image

 f.BackColor = Color.Magenta;
 f.TransparencyKey = Color.Magenta;
Community
  • 1
  • 1
George Vovos
  • 7,563
  • 2
  • 22
  • 45
  • I think `CreateRoundRectRgn` and `f.Region = System.Drawing.Region.FromHrgn(CreateRoundRectRgn(0, 0, 200, 200, 2000, 2000));` is not needed when the image itself is circular transparent. – Matin Lotfaliee Jul 24 '14 at 21:04
  • Matin this does not work for me.Have toy tried that? In any case ,i found i simpler solution.I edited my answer – George Vovos Jul 25 '14 at 06:57
  • Yes, if the image have PNG, gif or BMP format with transparency (alpha channel), it will work. – Matin Lotfaliee Jul 25 '14 at 07:01
  • this does get rid of the alpha channel, but does not let me click thought the form to what is behind it. – hurnhu Jul 27 '14 at 23:11
3
    SetWindowLong(f.Handle, GWL_EXSTYLE,
    (IntPtr)(GetWindowLong(f.Handle, GWL_EXSTYLE) | WS_EX_LAYERED | WS_EX_TRANSPARENT));

This is where the problem started. It is a very troublesome statement that has far too many side-effects. Core issue is that you use the Handle property, that forces Winforms to create the native window. Too early, and with the wrong style flags. WS_EX_LAYERED is already a style flag that Winforms itself uses, it is required to implement the Form.Opacity and the Form.TransparencyKey properties.

This code will misbehave completely on Windows XP and any later Windows version that has Aero turned off. Creating a layered window in that case requires far more work, Windows has to create a video overlay in the video adapter. Which provides the alpha and color-keying effects, the video adapter has a simple mixer that combines the pixels from the frame buffer with the pixels in the overlay. Creating that overlay cannot be done by setting the style flags with SetWindowLong(), it has to be done by CreateWindowEx().

In other words, you'd have to override the CreateParams property. It is virtual for exactly this reason, overriding the window style flags before they are used to create the window. Setting the TransparencyKey property afterwards will not have the desired effect, the window was created too early.

The solution is a very simple one, just get rid of this code completely. Both the SetWindowLong() and the SetLayeredWindowAttributes() pinvokes. Since Winforms already implements what you are trying to do. Fix:

    Color BackColor = Color.White;
    f.TransparencyKey = BackColor;
    f.Opacity = transparency/255f;

And consider dividing transparency by 100f so it is true percentage. Renaming it to "opacity" doesn't hurt either.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • doing this make it so i can not click through to what ever is behind the picture though. – hurnhu Jul 27 '14 at 22:53
  • You'll have to create the right kind of picture, its background needs to be transparent as well so it doesn't screw up the TransparencyKey. Most any half-decent painting program supports that. Use Paint.NET for example. Or set the form's TransparencyKey to match the picture's background, that's fine too. Color-keying is a simple concept, you see it everyday with the weatherman in front of the weathermap. Just make the colors match. – Hans Passant Jul 27 '14 at 22:55
  • i have gotten rid of SetLayeredWindowAttributes and SetWindowLong like you said to. like picture shows up while being opaque but if i click on it the click does not travel through it, it just sets the opaque picture as the focus. – hurnhu Jul 27 '14 at 23:10
  • What do you click on? Don't click on the visible part, it of course is expected to get the click since you can't see what's behind it. – Hans Passant Jul 27 '14 at 23:12
  • yes i was clicking on the visible part, but what i am trying to achieve is when you click on that visible part the click goes through it to what ever is behind it. which is possible, the code i provided does just that, has an opaque image that no matter where you lick on the image the click goes through. but sadly the image is always square, and i can not have round images – hurnhu Jul 27 '14 at 23:17
0

I ended up using a picturebox to display the pic, this allows me to have a picture i can click through while having transparency's on the picture its self.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace pic
{
public class Class1
{
    [DllImport("user32.dll", SetLastError = true)]
    private static extern UInt32 GetWindowLong(IntPtr hWnd, int nIndex);
    [DllImport("user32.dll")]
    static extern int SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
    [DllImport("user32.dll")]
    static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags);
    public const int GWL_EXSTYLE = -20;
    public const int WS_EX_LAYERED = 0x80000;
    public const int WS_EX_TRANSPARENT = 0x20;
    public const int LWA_ALPHA = 0x2;
    public const int LWA_COLORKEY = 0x1;


    public void t(int LocalX, int LocalY, string PicLocal, byte transparency)
    {
        Bitmap bitmap;
        Form f = new Form();
        f.BackColor = Color.White;
        f.FormBorderStyle = FormBorderStyle.None;
        f.Bounds = Screen.PrimaryScreen.Bounds;
        f.TopMost = true;
        bitmap = new Bitmap(PicLocal);
        f.Size = new Size(600, 600);
        f.StartPosition = FormStartPosition.Manual;
        f.SetDesktopLocation(LocalX, LocalY);
        Application.EnableVisualStyles();



        PictureBox PictureBox1 = new PictureBox();

        PictureBox1.Location = new System.Drawing.Point(70, 120);
        PictureBox1.Image = bitmap;

       PictureBox1.Size = new System.Drawing.Size(140, 140);

        PictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;

        f.Controls.Add(PictureBox1);

        f.AllowTransparency = true;
        SetWindowLong(f.Handle, GWL_EXSTYLE,
        (IntPtr)(GetWindowLong(f.Handle, GWL_EXSTYLE) | WS_EX_LAYERED | WS_EX_TRANSPARENT));
        // set transparency to 50% (128)
        SetLayeredWindowAttributes(f.Handle, 0, transparency, LWA_ALPHA);

        Color BackColor = Color.White;
        f.TransparencyKey = BackColor;
        f.Opacity = transparency / 255f;

        Application.Run(f);

    }

}
}
hurnhu
  • 888
  • 2
  • 11
  • 30