views:

138

answers:

4

You all know the "You've got new answers!" notification bar on SO. I'd like the same thing in a Form, preferably just as smooth. Is there a simple way? Or do I have to completely create this myself?

My searches did not yield any good results, only lots of progress bars and popups in the system notification area, but that's not what I'm looking for.

The messages I want to display belong to a Form, not to the whole application

+1  A: 

If you want it constrained to a particular form, it should be easy enough to put a Panel on the form with its Dock set to DockStyle.Top, then place a label for the description and a button that hides it.

Adam Robinson
Yes, but... some sliding would look more cool ;)
mafutrct
@mafutrct: It would, but animation and Winforms don't exactly go hand-in-hand. While something like that would be trivial in WPF, it's going to take a good bit of code to do in Winforms, and is likely to be jerky.
Adam Robinson
@Adam: The past hour of fiddling with multiple threads and overriding methods that crash the forms designer confirm that. +1
mafutrct
@mafutrctg, do not use a thread, use a `System.Windows.Form.Timer`. That runs in the UI thread and does not require marshaling.
Dour High Arch
@Dour: After removing some requirements, I was able to do it without any of the hassle I described. I'm using a single, shortlived thread with a few simple Invokes now, and it works fine.
mafutrct
@mafutrct: You might want to consider using `ThreadPool.QueueUserWorkItem` rather than creating short-lived threads.
Adam Robinson
+3  A: 

You could simply animate a panel dropping down from the top of the client area of the form.

Increasing the y coordinate of the panel in a timed loop. The panel would start invisible and slowly become visible. (The panel would start at -panel.height and work its way down to 0.)

So I have to do everything myself, basically? :\ ok. And I guess I also have to watch for resizes of the parent form myself as well.
mafutrct
Yes, you have to do everything yourself, including watching for resize events, however, in the end, it is not all that difficult.
The only problem with this approach is that the panel will then be covering the top of the form, unless you add code to slide the rest of the form contents (on anothe panel of course) down at the same time. This is doable but it interferes with docking and is a nuisance.
Charles
You do not have to "do everything yourself". Your panel will not cover the top of the form, you do not have to slide anything else in the form, and it does not interfere with docking.
Dour High Arch
+1  A: 

It's not difficult to do with a panel or a UserControl, but the fiddly part is making the contents of the form slide down as the bar slides down. To simplify that I would use a SplitContainer. The top splitpanel contains the notification bar and the splitter distance is initially 0. Slide the bar into view by incrementing the SplitterDistance property. Doing it this way means you don't have to worry about making the other contents of the form slide down (which is a hassle because it prevents you from using docking).

The only downside to using SplitContainer I can think of is that the animation of the bar will be slightly different: the text won't scroll down with the bar, it will be revealed in place as the bar slides down. If this bothers you, you can fix it by having the text (or your panel/custom control) slide down as you increase the splitter distance (only a couple more lines of code).

Showing the bar:

    for (int i = 0; i <= 33; i++)
    {
        splitContainer1.SplitterDistance = i;
        Thread.Sleep(5);
        Refresh();
    }

Hiding the bar:

    for (int i = 33; i >= 0; i--)
    {
        splitContainer1.SplitterDistance = i;
        Thread.Sleep(5);
        Refresh();
    }

Of course, if you don't mind the notification bar simply covering the top part of your form, then you can just do the whole thing very easily with a panel. :)

Charles
I do prefer to slide the rest of the form down. A SplitContainer seems like a reasonable way to do this. How would you handle multiple messages appearing at the same time?
mafutrct
This will, by the way, block the UI while the animation is taking place, which is exceedingly frustrating (preventing me from doing work while you perform a functionally useless operation). If animation can be done without interfering with the user, then it's nice eye candy. If it interferes with the user, you're sacrificing usability for useless fluff.
Adam Robinson
Each message would be on its own Panel, and you would stack the Panels in the SplitPanel. So when you add a new message on top of an existing message, just increase the SplitterDistance again. (Obviously you will be instantiating the notification bar panels at runtime - this is a good argument for creating a custom notification bar control (UserControl)).
Charles
A splitter is for the user to resize panels and is not suitable if you want to change the size of panels programmatically. Dock the notification panel to Top; you do not have to fiddle with anything to make the content panel slide down.
Dour High Arch
@Adam: okay, so make a suggestion to overcome that.
Charles
@Charles: Do it in another thread, use a timer, etc. Neither of these are good solutions, which is why the solution is, basically, don't do it in Winforms.
Adam Robinson
@Dour: sorry but you're wrong on both counts. There is no problem programatically moving the splitter. And docking doesn't allow a smooth sliding effect. I've done this.
Charles
@Charles, I never said moving a splitter is impossible programmatically, I said Splitters are for the user to resize the UI. It is commendable that you were able to get it to work, but it is unnecessary. I have been using sliding panels for notifications for years and have never needed splitters or threads, and my panels animate smoothly.
Dour High Arch
+1  A: 

Create two panels in your form, a notification panel docked to top, and below that a content panel anchored to top. In your Form.Load, set the height of the notification panel to zero. Don't set the height to zero in Design View, you won't be able to click on the notification panel to edit it.

Then when you get a notification, draw the contents in the notification panel and create a System.Windows.Form.Timer that increases the height of the notification panel by a few pixels every few dozen milliseconds or so. Stop when the panel is of the desired height. Do the same with a negative height to hide the panel.

This does not require repainting or recalculating sizes or positions of anything, does not overdraw anything, and looks slick. I have done this and it works.

Dour High Arch
Ah, setting the height instead of the top to a negative value (which does not work with docking). Nice, I'll try this.
mafutrct
@mafutrct, make sure your sliding panel's Z-order is bigger than your content panel so it gets drawn first. Form draws panels in reverse Z-order.
Dour High Arch
This permanently locates the top edge of the content panel at a distance from the top of the form equal to the shown-height of the notification panel. So instead of the sliding the content down as the notification slides in, you're just reserving space at the top of the form for the notification.
Charles
@Charles, that is correct. If you want the content panel to fill up the space when the notification panel is gone, set the content panel to Dock Top as well. Set its Z-order smaller than the notification panel's so it gets drawn below the notification panel. Note that if you do that, the position of the content panel will change and its contents will move on the screen when the notification appears. You may want to put the notification panel at the bottom to avoid this.
Dour High Arch