Hi all!

I'll keep this post a little shorter then I normally do, lack of time to post and all of that.

I am making a widget-type program. You know, no borderstyle, no taskbar, opacitycontrol, stuff like that.

It's going to be an RSS-aggregator by the way.

Now for my problem:

When I hover over the widget I would like it to raise the opacity to 1 (by doing an enormous load of this.Opacity += 0.01 through a timer) and when leaving the form, it should lower it the same way to what has been set up.

Now, I had the incrementing down, but the decrementing wouldn't work at all.

Here is the code:

using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Xml;

namespace RSSWidget
{
	/// <summary>
	/// RSS Widget, mainly used for Slashdot.org
	/// </summary>
	public partial class MainForm : Form
	{
		#region Variables
		TrackBar TransparencyTrackBar;
		Timer OpacityRaiseTimer;
		Timer OpacityLowerTimer;
		double TransparencyFormOpacityValue = 1;
		#endregion
		
		public MainForm()
		{
			InitializeComponent();
			this.FormBorderStyle = FormBorderStyle.None;
			this.Size = new Size(200, 300);
			this.BackColor = Color.FromArgb(0, 0, 100);
			this.ShowInTaskbar = false;
			this.MouseHover += new EventHandler(MainForm_MouseHover);
			this.MouseLeave += new EventHandler(MainForm_MouseLeave);
			this.LostFocus += new EventHandler(MainForm_LostFocus);
			
			RightClick();
			
			OpacityRaiseTimer = new Timer();
			OpacityRaiseTimer.Interval = 7;
			OpacityRaiseTimer.Tick += new EventHandler(OpacityRaiseTimer_Tick);
			
			OpacityLowerTimer = new Timer();
			OpacityLowerTimer.Interval = 7;
			OpacityLowerTimer.Tick += new EventHandler(OpacityLowerTimer_Tick);
		}

		void MainForm_LostFocus(object sender, EventArgs e)
		{
			if(this.Opacity >= TransparencyFormOpacityValue)
			{
				OpacityLowerTimer.Start();
			}
			else
			{
				OpacityLowerTimer.Stop();
			}
			
			this.FormBorderStyle = FormBorderStyle.None;
		}

		void MainForm_MouseLeave(object sender, EventArgs e)
		{
			if(this.Opacity >= TransparencyFormOpacityValue)
			{
				OpacityLowerTimer.Start();
			}
			else
			{
				OpacityLowerTimer.Stop();
			}
			
			this.FormBorderStyle = FormBorderStyle.None;
		}

		void MainForm_MouseHover(object sender, EventArgs e)
		{
			//
			// This has a known bug, if you open TransparencyForm, then hover over the MainForm,
			// the opacity will keep rising, not matter how you set the TrackBar.
			// The workaround is to close the TransparencyForm and reload the TransparencyForm.
			//
			
			if(this.Opacity != 1.0)
			{
				OpacityRaiseTimer.Start();
			}
			else
			{
				OpacityRaiseTimer.Stop();
			}
			
			this.FormBorderStyle = FormBorderStyle.FixedToolWindow;
		}
		
		void OpacityLowerTimer_Tick(object sender, EventArgs e)
		{
			this.SuspendLayout();
			
			this.Opacity -= 0.01;
			
			this.ResumeLayout();
		}

		void OpacityRaiseTimer_Tick(object sender, EventArgs e)
		{
			this.SuspendLayout();
			
			this.Opacity += 0.01;
			
			this.ResumeLayout();
		}

		private void RightClick()
		{
			ContextMenu contextMenu = new ContextMenu();
			MenuItem m1 = new MenuItem();
			MenuItem m2 = new MenuItem();
			MenuItem m3 = new MenuItem();
			m1.Text = "&Transparency";
			m2.Text = "&Options";
			m3.Text = "&Exit";
			contextMenu.MenuItems.Add(m1);
			contextMenu.MenuItems.Add(m2);
			contextMenu.MenuItems.Add(m3);
			m1.Click += new EventHandler(m1_Click);
			m2.Click += new EventHandler(m2_Click);
			m3.Click += new EventHandler(m3_Click);
			this.ContextMenu = contextMenu;
		}

		void m2_Click(object sender, EventArgs e)
		{
			Form OptionsForm = new Form();
			OptionsForm.Size = new Size(300, 300);
			OptionsForm.Text = "Options";
			
			OptionsForm.Show();
		}

		void m1_Click(object sender, EventArgs e)
		{
			Form TransparencyForm = new Form();
			TransparencyForm.Size = new Size(120, 50);
			
			TransparencyTrackBar = new TrackBar();
			TransparencyTrackBar.Size = new Size(100, 30);
			TransparencyTrackBar.Location = new Point(10, 10);
			TransparencyTrackBar.Minimum = 0;
			TransparencyTrackBar.Maximum = 90;
			TransparencyTrackBar.Text = "Transparency";
			TransparencyTrackBar.Value = (int)(RSSWidget.MainForm.ActiveForm.Opacity * TransparencyTrackBar.Maximum);
			TransparencyTrackBar.Scroll += new EventHandler(TransparencyTrackBar_Scroll);
			
			TransparencyForm.Controls.Add(TransparencyTrackBar);
			TransparencyForm.Show();
		}

		void TransparencyTrackBar_Scroll(object sender, EventArgs e)
		{
			this.Opacity = ((Convert.ToDouble(TransparencyTrackBar.Value) / 100) + 0.1);
			TransparencyFormOpacityValue = ((Convert.ToDouble(TransparencyTrackBar.Value) / 100) + 0.1);
		}

		void m3_Click(object sender, EventArgs e)
		{
			this.Close();
		}
	}
}

Does anybody have an idea why this won't work? I'm testing it under Windows 7, if that makes any difference at all.


Thank you for any help in advance!

you are asking your application to do a lot of changing, but you aren't telling i that it needs to repaint. after you change the opacity you should call either this.Invalidate(true); which will cause the form to repaint when but only as seen fit by the system. this is typically a good enough approach, but. since you are being aggressive about it, you will need to call this.Refresh(); which will cause an immediate repainting but will probably flicker if not doublebuffered.

a further note, you will want to stop your opacity at .99 not ever reaching 1. when you switch between partial opacity and opaque, where will be an unavoidable flicker. the easiest way around this is just going almost opaque, .99 opacity looks opaque, without the flicker.

good luck with you app.

Hi Diamonddrake,

Thanks for the info, I will see if this will solve my problems.

Couple of things though. I was told that the SuspendLayout() and ResumeLayout() were to cancel that flickering. But I will at least keep it from reaching 1.0, thanks for that hint.

Then another thing. In what way am I to use the this.Refresh() and this.Invalidate(true) ? and where do I use them?


Thanks for the help so far, it is really appreciated!

suspend and resume layout just stop the form from updating while making a lot of changes. since you are only making one change, it won't help you in your case.

you would use this.Refresh(); immediately after you change the opacity.

like...

void OpacityLowerTimer_Tick(object sender, EventArgs e)
		{
                       //not needed unless a lot of chaning occurs
			//this.SuspendLayout();
                        
			
			this.Opacity -= 0.01;
                        this.Refresh();
			


			//this.ResumeLayout();
		}

		void OpacityRaiseTimer_Tick(object sender, EventArgs e)
		{
                       //not needed unless a lot of chaning occurs
			//this.SuspendLayout();
			
			this.Opacity += 0.01;

                        this.Refresh();
			
			//this.ResumeLayout();
		}

That should do it. but it might flicker without double buffering.

another thing to note, you don't stop your timers in the timer tick, the method you attempt to stop it if you happen to be hovering over it at the right time, that's not the best idea. you should have it check in the tick events if you are at the high or low values example

double topopacity = 0.99;
double bottomopacity = 0.2;

void OpacityLowerTimer_Tick(object sender, EventArgs e)
		{
                       //not needed unless a lot of chaning occurs
			//this.SuspendLayout();
                        
			if(this.Opacity > bottomopacity)
                        {
			this.Opacity -= 0.01;
                        this.Refresh();
                        }
                     else
                       {
                         OpacityLowerTimer.Stop();
                       }

			//this.ResumeLayout();
		}

		void OpacityRaiseTimer_Tick(object sender, EventArgs e)
		{
                       //not needed unless a lot of chaning occurs
			//this.SuspendLayout();
			
                       if(this.Opacity < topopacity)
                       { 
			this.Opacity += 0.01;

                        this.Refresh();
                         }
                        else
                       {
                        OpacityRaiseTimer.Stop();
                       }
			
			//this.ResumeLayout();
		}

That will fix your know bug too.

Edited 7 Years Ago by Diamonddrake: n/a

First of all, I'd like to thank you Diamonddrake. I have used your suggestions, though I have modified some of it, and they worked brilliant!

The this.Refresh() seems to do exactly as I want it.
I have changed this.MouseHover to this.Click, and I have also changed this.MouseLeave to this.LostFocus. This way it seems more logical and intuitive.

I have also removed double bottomopacity and in its place made a double UserOpacity (initialized to 0.8, but it is changed by the OpacityTrackBar) then I let OpacityLowerTimer_Tick check if this.Opacity was higher than UserOpacity)

Works like a charm!

I will leave this open for another 12 hours so everybody gets a chance to ask more things, after that, it will be marked solved.

Thanks for the help!

Edited 7 Years Ago by ctrl-alt-del: n/a

I'm glad I could be of help, I spend most of my time in C# trying to make things look the way I want them, so I can help with a lot of drawing related issues.

Best of luck with your app, I would love to see it when you are done!

Thank you! I'll post it after it is done. At the moment I am configuring the UI, the functional part of it is mostly done already.

Do you ever read Slashdot.org? (It's hard to imagine a programmer not doing that, but it takes all kinds right?)

If you have any tips or cool tricks UI-wise, I would love to hear them! (if you don't want to post them, you ca always send me a personal message)

Thanks again.

I actually never heard of Slashdot.org but I just checked it out, and it looks pretty cool, I look forward to seeing your app.

when it comes to UI stuff with GDI the possibilities are endless, its hard to just say hey you could do something like this!!! But if you want to get into some more powerful stuff try looking into the api call ExLayeredWindow or just layered windows in general, it allows for some better looking forms but requires a lot of work.

I will post the program here when it is done, and will certainly look into those things!

Thank you again, I will now mark this solved.

Little tidbit of information. the reason why setting the opacity of the form to 1 causes a flicker when changed from a transparent value is because the opacity property actually uses WS_EX_LAYERED window style message and the setlayeredwindowatrributes flag to modify the way the form is displayed. when the value goes to opaque the form is then reverted back to a standard window call by calling setwindowlong and removing the WS_EX_LAYERED style bit, thus requiring windows to reinterpret how it should paint the window. since this usually happens for an effect, a refresh is called and a flicker occurs because windows is not yet ready to paint that window.

the simple solution to prevent that from happening is to never set the opacity to 1, since .99 is the 252 that's practically a 255(opaque) bit setting.

a complicated way to work around that would be to write a class that would set the window's WS_EX_LAYERED bit and have a function that would submit the SetLayeredWindowAttributes transparency bit then manually tell the windows api to redraw the window with RedrawWindow(hwnd...) this way you could always have the window ready to be transparent, but allow a full opaque setting of 255 on the transparency bit.

Sadly, as amazing as layered windows are, you don't see a lot of good articles on it. There is something called a perpixelalpha blend that allows you to set the transparency of a form based on the alpha channel of every single pixel in an image. It's complicated, but allows for some amazing effects. Sadly. It requires a lot of attention if one would intend to use it in a standard application.

guess this has went on far enough. If I ever get the patience for it I plan to write a Code Project article on quality form fading using such a proposed custom class.

Look forward to seeing your work.

Thanks for the information.

This is the reason I love asking things on this forum. First of all you get a little respect. As I can see you know a lot about programming (from my point of view anyway), but you still go out of your way to try and explain why I should do things.
And secondly because of all the extra info I get. I am not nearly at a level that I will be overwriting stuff, of managing flags and what not. But I do so very much like to know these things beforehand, that way, when I do start running into those things, I have a little register in the back of my head that has a little info on the subject.

Again, thanks for the help. UI part is almost done, then I will hook the functional part to that, and will upload it here!

(I'll see if I can make it work with Daniweb too, that would be aa neat trick)

I decided to goahead and write that article for code project. Its still pending moderator approval but the link is :
http://www.codeproject.com/KB/cs/DDFormsFader.aspx

I wrote a user32 wrapper class that handles all the fading directly without messing with the .net wrapper at all. It works unbelievably well, and its incredibly easy to use. You might want to archive this for future use, and if you get a second check it out. I wrote more comments then code, so it should be really easy to follow and its very clean code.

still looking forward to seeing that app :)

Comments
very nice! Nothing worse than the dreaded flicker :p

Bookmarked! Respect earned, and rep to follow shortly :p

Nice article, i can guarantee your code will be popping up in my apps from now on :D

This question has already been answered. Start a new discussion instead.