Click here to Skip to main content
15,896,557 members
Please Sign up or sign in to vote.
5.00/5 (2 votes)
See more:
Hi there,

Sorry about my English first. I am trying to write a program to show lyrics on the screen above all the window.I found a third party application doing the same thing and its effect is very nice. Here is the URL to see it http://www.easyremember.net/downloads/ThirdParty1.jpg[^]

I used my own customized label to drawing the lyrics and almost get the same effect, only the difference is there always the wired outline showing. See what my application drew http://www.easyremember.net/downloads/MyApp1.jpg[^]

The way I make the label transparent is below code:
C#
this.TransparencyKey = this.MyLabelWord.BackColor;

Please notice the little difference about the outline color around the words when the desktop background is not white.Especially the words "That you". I tried to set the difference back color of my label, the use this.TransparencyKey = this.MyLabelWord.BackColor; to make it transparent.No matter what backcolor I choose, when it works in transparent mode, there is always a outline around the letters since the desktop always has difference color. But the third party application always draws the perfect strings no matter you move it to where.

But if my application is not in transparent mode, it is very close to the third party application except the color difference. There is no outline. This is screen shot of the third party application show lyrics in white back ground.
http://www.easyremember.net/downloads/ThirdParty0.jpg[^]

This is screen shot of the my application show lyrics in white back ground.
http://www.easyremember.net/downloads/MyApp0.jpg[^]

My question is how can I get rid of the outline in transparent mode. How can I get the same effect as that application?

The way I draw the label with two color is just wrote a OnPaint() using GDI+ drawstring method. It works similar to bellow codes, the original one is too long to post ( There are more codes dealing with multiline, highlight text, highlight color etc...)

C#
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
        {
            Graphics g1 = e.Graphics;

            g1.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;

            Rectangle rect = new Rectangle(0, 0, this.Font.Height, this.Font.Height);
            LinearGradientBrush myBrush = new LinearGradientBrush(rect,Color.White,Color.Blue,LinearGradientMode.Vertical);
            g1.TextRenderingHint = TextRenderingHint.AntiAlias;

            try 
            {
               g1.DrawString(string1, this.Font, myBrush, 0, 0); 

            }
            catch
            {

            }
            finally
            {
                // Dispose of brush resources after use
                //it's important to call the Dispose() method after using a Pen or Brush. 
                myBrush.Dispose();
            }
        }


I found very similar example using c++ . You can download and run it, it's very small application, but exactly what I need.http://www.codeproject.com/KB/GDI-plus/DesktopLyrics.aspx The problem for me now is I am not familiar with C++. If somebody can translater it to c#, that will be perfect solution for me. Thanks a lot in advance!
Posted
Updated 17-Mar-11 5:55am
v6

I do not recommend using transparency mode. Not using it could solve all your problems. You're rendering you graphics on Control anyway (on a Form, as I can see) anyway. This is very good.

Instead, you should simple use transparent colors. All GDI+ objects, including the gradient brush you use can use colors with alpha channel which actually represent transparency. See: http://msdn.microsoft.com/en-us/library/at1k42eh.aspx[^], http://msdn.microsoft.com/en-us/library/ms142561.aspx[^], and so on.

—SA
 
Share this answer
 
Comments
Sergey Alexandrovich Kryukov 16-Mar-11 1:45am    
OP commented:

Hi SA,

Thank you for your quick reply.
I have checked the 2 links, but still didn't get it. I did use LinearGradientBrush to drawstring on the label. But the label is in the WinForm, how can I make the winForm transparent? I need show string on the desktop directly.

Thanks
Sergey Alexandrovich Kryukov 16-Mar-11 1:56am    
I think you're using wrong design. There is no physical "transparency" in the video card, right? Transparency simply means that you have two (or more) layers of graphics; usually pictured as painted on plates of absolutely transparent glass. Let's say, there are two. Why doing second (top) layer transparent? To show something underneath. In fact, you simply will get a colors of two layers blended together. You should not use transparent windows which is inherently problematic (and you should not render anything on a form, you should draw on custom control, and make a control as a child of the form or another control, and you don't need your transparent control). Use the control, and in its Paint (or OnPaint) draw your 2 layers: background picture first, you semi-transparent layer with text second. If you only use alpha channel on your text layers, if will be blended automatically to show effect of transparency.

Is that clear now?

--SA
Hanson Cheng 16-Mar-11 11:33am    
Thank you for your explanation. "Use the control, and in its Paint (or OnPaint) draw your 2 layers: background picture first, you semi-transparent layer with text second." Actually I tried this before, the problem I met is to draw the background picture I need capture the image underneath first. I tried using "opacity=0" first then capture the image covered by the container form, but this way made flashing when I move the text around the screen. Is there any other way I can capture the image underneath?
Sergey Alexandrovich Kryukov 16-Mar-11 16:51pm    
OK, I has to post whole bit Answer to explain that. You could find this way yourself after some thinking. If I addressed wrong problem, please forgive me. In this case, clarify and ask another follow-up question.
--SA
Hanson Cheng 16-Mar-11 22:56pm    
I found this article "Desktop Lyrics"excactly doing the same thing. http://www.codeproject.com/KB/GDI-plus/DesktopLyrics.aspx But it is written by c++. I noticed ths steps:
1. Create a compatible bitmap.
2. Draw on the bitmap.
3. Draw bitmap on the device content.
4. Invoke the UpdateLayeredWindows().
Then I found UpdateLayeredWindows maybe the API I need. Then I googled and found this article http://www.vcskicks.com/splash-screen.php. So I start to study both the article.
I believe this is the direction I should go.
Answering a follow-up Question:

Actually I tried this before, the problem I met is to draw the background picture I need capture the image underneath first.
It is not quite clear what do you mean by "capture". Do you mean screen capture? Why do you need it if this your program anyway. Let me do some assumption. Probably you simply need to capture some currently rendered graphics from your application, but you need not all the layers blended together (as I understand, only what goes "under" the text). Yes, you cannot just "capture it" from already rendered screen, as information is already lost (more exactly, entropy increased). So you should preserve this data instead. This is how.

Abstract out the rendering of every separate layer into a method agnostic to a Graphics object. The parameter of each layer render function would be the two System.Drawing.Rectangle (or Size) and System.Drawing.Graphics (maybe some more, due to your application semantic). Let me call these functions RenderLayer. Now, have a command "Save Layer" (not "Layer" of course, some semantic name, like "Score images").

Call one of two of your RenderLayer methods from two different places. One place is already done: this is your control's Paint handler or overridden OnPaint: "lower" layer(s), first, upper next. Another place is a call from you "Save" handler. Let's assume you want to store it in file. Create a bitmap, calculate the required size, instantiate a bitmap, obtain its Graphics instance (in "using" block to ensure automatic Dispose) and then call your RenderLayer with this Graphics instance and proper Size or Rectangle. Close and save the bitmap. Something like that.

Is that clear now?

[EDIT]
I almost forgot: I wanted to suggest switching to WPF. Many problems we're discussing are already solved, and performance is better. I would forget about rendering whatsoever.
I fully support the idea suggested by Espen, please see his advice.
--SA

—SA
 
Share this answer
 
v2
Comments
Espen Harlinn 16-Mar-11 17:42pm    
My 5
Depending on how a control has been implemented:
"For some common controls, the default WM_PAINT message processing checks the wParam parameter. If wParam is non-NULL, the control assumes that the value is an HDC and paints using that device context." From http://msdn.microsoft.com/en-us/library/dd145213(v=vs.85).aspx

When it works - and it often does, he can send a WM_PAINT to the window below his label passing a handle to a memory DC.

This technique was used by the initial authors of the open source delphi RX library to provide similar functionality.
Sergey Alexandrovich Kryukov 16-Mar-11 18:47pm    
Thank you, Espen.
That's true, but with the abstraction provided by the class Graphics things are much simpler than that. And even simpler with WPF, as we both agree.
--SA
You could consider moving your application to WPF - my guess is that you would find it a lot more satisfying, as it seems that you enjoy creating graphics - it offers a far richer environment for playing around with fancy graphics.

Take a look at XAML Overview[^] for more information.

Regards
Espen Harlinn
 
Share this answer
 
Comments
Sergey Alexandrovich Kryukov 16-Mar-11 17:26pm    
Oh, I completely forgot that I forgot to recommend WPF instead. I added a new Answer, because now the discussion goes deep in design with persistence, capture and what not (take a look, by the way, makes sense).
My 5. I'll join you by referencing this.
--SA
Sergey Alexandrovich Kryukov 16-Mar-11 17:29pm    
OK, done. I supported your advice in my second Answer and referenced this one.
Teamwork? :-)
--SA
Espen Harlinn 16-Mar-11 17:33pm    
Thank you, SAKryukov!
Hanson Cheng 17-Mar-11 0:38am    
Thank you for replay. But WPF needs .net 3.0, my current application is running on .net 2.0.
Espen Harlinn 17-Mar-11 9:46am    
.Net 2.0, 3.0 and 3.5 - are in many ways the same thing, as the 3.0 and 3.5 uses the same runtime as 2.0 - think of as installing additional libraries :)
I figuted it out by study on http://www.codeproject.com/KB/GDI-plus/DesktopLyrics.aspx and http://www.vcskicks.com/splash-screen.php

I am trying my best to explain it bellow:

The Key is to use API.UpdateLayeredWindow(). It enable my application to display background images with transperancy.
The other trick is where I draw the text. instead of drawing the text on the Windows Form or Label directly, it needs draw it on a bitmap. The bitmap is not any component from tool box, it's in the memory.(All regular component won't be shown).

As the first article "To Create A COOL Desktop Lyrics Demo (A Gift To All People On CP)[^]"said, the steps are:
1. Create a compatible bitmap. 2. Draw the Text with the color and font on the bitmap. 3. Draw bitmap on the device content. 4. Invoke the UpdateLayeredWindows().



All those steps need do when OnPaint Event happen. Bellow is my codes to draw the Text of the Form on the desktop.
C#
#region FORM EVENTS -------------------------------------------------------
private void Form1_Load(object sender, EventArgs e)
{
    UpdateFormDisplay(this.BackgroundImage);
}
protected override void OnPaint(PaintEventArgs e)
{
    //Call our drawing function
    UpdateFormDisplay(this.BackgroundImage);
}
#endregion


        //Updates the Form's display using API calls
        public void UpdateFormDisplay(Image backgroundImage)
        {
            IntPtr screenDc = API.GetDC(IntPtr.Zero);
            IntPtr memDc = API.CreateCompatibleDC(screenDc);
            IntPtr hBitmap = IntPtr.Zero;
            IntPtr oldBitmap = IntPtr.Zero;
            Graphics g1 = this.CreateGraphics();
            SizeF thissize = g1.MeasureString(Text, this.Font);
            Bitmap bmp = new Bitmap( (int) thissize.Width,(int) thissize.Height);
            g1 = Graphics.FromImage(bmp);
            try
            {
                g1.SmoothingMode = SmoothingMode.AntiAlias;
                g1.InterpolationMode = InterpolationMode.HighQualityBicubic;
               LinearGradientBrush myBrush = new LinearGradientBrush(new Rectangle(0, 0, this.Font.Height,this.Font.Height), Color.White, Color.Blue, LinearGradientMode.Vertical);

                SolidBrush backBrush = new SolidBrush(Color.FromArgb(100,this.BackColor)); // draw the back color, 0 is transparent , 255 is solid back bolor.
                g1.FillRectangle(backBrush,0,0,thissize.Width,thissize.Height); // disable this line if you don't want background
                this.Size = new Size((int) thissize.Width, (int) thissize.Height);
                g1.DrawString(Text, this.Font, myBrush, 0, 0);
                hBitmap = bmp.GetHbitmap(Color.FromArgb(0));  //Set the fact that background is transparent  
                oldBitmap = API.SelectObject(memDc, hBitmap);
                //Display-rectangle
                Size size = bmp.Size;
                Point pointSource = new Point(0, 0);
                Point topPos = new Point(this.Left, this.Top);
                //Set up blending options
                API.BLENDFUNCTION blend = new API.BLENDFUNCTION();
                blend.BlendOp = API.AC_SRC_OVER;
                blend.BlendFlags = 0;  // Must be zero.
                blend.SourceConstantAlpha = 255;
                blend.AlphaFormat = API.AC_SRC_ALPHA;
                API.UpdateLayeredWindow(this.Handle, screenDc, ref topPos, ref size, memDc, ref pointSource, 0, ref blend, API.ULW_ALPHA);

                //Clean-up
                bmp.Dispose();
                API.ReleaseDC(IntPtr.Zero, screenDc);
                if (hBitmap != IntPtr.Zero)
                {
                    API.SelectObject(memDc, oldBitmap);
                    API.DeleteObject(hBitmap);
                }
                API.DeleteDC(memDc);
            }
            catch (Exception)
            {
            }
            finally
            {
                g1.Dispose();
                bmp.Dispose();
            }

        }
 
        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams cp = base.CreateParams;
                cp.ExStyle |= 0x00080000; // Required: set WS_EX_LAYERED extended style
                return cp;
            }
        }


C#
internal class API
   {
       public const byte AC_SRC_OVER = 0x00;
       public const byte AC_SRC_ALPHA = 0x01;
       public const Int32 ULW_ALPHA = 0x00000002;
       [StructLayout(LayoutKind.Sequential, Pack = 1)]
       public struct BLENDFUNCTION
       {
           public byte BlendOp;
           public byte BlendFlags;
           public byte SourceConstantAlpha;
           public byte AlphaFormat;
       }
       [DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
       public static extern bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst, ref Point pptDst, ref Size psize, IntPtr hdcSrc, ref Point pprSrc, Int32 crKey, ref BLENDFUNCTION pblend, Int32 dwFlags);

       [DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
       public static extern IntPtr GetDC(IntPtr hWnd);
       [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
       public static extern IntPtr CreateCompatibleDC(IntPtr hDC);
       [DllImport("user32.dll", ExactSpelling = true)]
       public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
       [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
       public static extern bool DeleteDC(IntPtr hdc);

       [DllImport("gdi32.dll", ExactSpelling = true)]
       public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
       [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
       public static extern bool DeleteObject(IntPtr hObject);
   }



/pre


To make the Text can be moved by mouse, the mousedown event needs to handled.


[DllImportAttribute("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
public const int WM_NCLBUTTONDOWN = 0xA1;
public const int HTCAPTION = 0x2;
[DllImportAttribute("user32.dll")]
public static extern bool ReleaseCapture();
private void frmSplash_MouseDown(object sender, MouseEventArgs e)
{
ReleaseCapture();
SendMessage(Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0);
}
 
Share this answer
 
v3
Comments
Hanson Cheng 18-Mar-11 1:29am    
Thank you everybody who worked on this question!

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900