Click here to Skip to main content
15,880,651 members
Articles / Programming Languages / C++/CLI
Article

AnimatedRollupControl with C++/CLI

Rate me:
Please Sign up or sign in to vote.
4.89/5 (5 votes)
24 Aug 2008CPOL23 min read 33.2K   1.3K   20   1
3dsMax style rollup control animated in C++/CLI
AnimatedRollupControl1.JPG

Introduction

Who is this article for? Generally it is for intermediate level coders familiar with C++/CLI in Visual Studio C++ 2008. To get the most out of this article, intermediate level here means you will find it more suitable if you:

  1. Know how to add new Items such as a Form or UserControl to a project.
  2. Can use the Visual Studio Designer to create a user interface.
  3. Can easily become familiar with the code that gets generated by the Visual Studio Designer when creating Forms/Controls based user interfaces (So you can write/modify a little yourself).
  4. Have some knowledge of the Model-View-Controller (MVC) design pattern terminology. Caution: MVC terminology is used/abused as a discussion aide in this article and not as a strict architecture to be adhered to (adherence to the pattern is your choice).

At the very least, if you just want to use the sample code in another project, you should be able to follow the typical process of including a new class (object) in a project, instantiating an instance of an object and using it (it’s outlined in the summary of this article). Ok…now on with the Show!

My main aim was to use rollup controls in a Windows Forms based application. A secondary interest was to ‘see how far we have come’ in our progression towards programming nirvana (What?); that is, to see how much more easy it would be to write a .NET based version of a rollup control, or more strictly speaking, using C++ with the Common Language Infrastructure (CLI). So this article presents an example of how I implemented a C++/CLI version of a Rollup Control. There are two rollup controls in the sample provided; one is standard (i.e. with instantaneous rollup/rolldown) and one contains animated rollups (i.e. slower, smoother rolling up/down) with rollup speed control. The download source was created using Visual C++ 2008 Express Edition (and messed around with a bit in the professional edtion)

For those of you that want to get straight to using the control without wading through this article, you can download the zipped solution and go to the summary section near the bottom of the page for the bare essential instructions on how to include the Rollup Control in your own application. This also applies if you have a short attention span and start getting bored reading the article.

Background

Rollup controls are also known as rollout controls. Roll-up or roll-in and roll-down or roll-out: These terms are interchangeable in this article. Rollup controls have a little history at CodeProject. There are two other older articles I know of that deal with rollups; one uses MFC, while the other uses WTL. So this is a third article on rollups, this time using C++/CLI Windows Forms. For those not familiar with rollup controls or sophisticated programs like 3dsMax, a rollup control is a useful tool for providing additional functionality to the users of your program in a very compact presentation, freeing up the screen space from a possible sea of toolbars, toolpads and “floaters” as the complexity of your application increases. A rollup control has two main features:

  1. Rollup Pages. A rollup control typically contains a number of panels or pages that can be collapsed and expanded (or rolled up and rolled down).
  2. A scrollbar. When the amount of screen space your pages take up, exceeds the permitted viewing area you have allocated, a scrollbar appears to allow you to scroll through to any page of interest that is not currently in your viewing pane.

These two features make it possible to present functionality that could otherwise take up much more screen space than you would like to permit.

Using the Code

Here is an overview of the steps we take to put a rollup control into an application:

  1. Add RollupCtrl.h, RollupCtrl.cpp, RollupCtrlPage.h, RollupCtrlPage.cpp, DialogPage.h and DialogPage.cpp to your project.
  2. Declare and instantiate a RollupCtrl object. Set some of its properties: the main properties being the Location and Size.
  3. Design your rollup pages on a dialog panel in Windows Form Designer.
  4. Declare and create instances of your dialog panels. Set some of their properties.
  5. Pass each dialog panel to the rollup control via its CreatePage() method: so that it creates a rollup page with your page design inside and adds it to its viewing panel.
  6. Add the rollup control to its host Form or Control.

You can place the control in a Form or in any suitable control. In the sample project one rollup control is added to the form, while the other one is placed in a tab control. Now let’s take a look at the details of this rollup control.

Panels, Panels and more Panels!

You guessed it…, the rollup control is System::Windows::Forms::Panel based, except for the use of one UserControl. The Panel is ideal for us since it is designed for grouping controls, while the UserControl provides us with a suitable visual surface on which to create the contents of a rollup page in the Windows Forms Designer (e.g. typically by dragging labels, textboxes and other controls onto the surface from the toolbox to create a page design). This UserControl is also referred to as the dialog panel throughout this article. Before we go any further, let’s take a look at the definitions of the classes that make up the rollup control. There are three classes:

RollupCtrl: Derived from Panel. This is the host control that holds expandable pages. I like to think of it a viewport or window into our list of rollup pages, because that’s just about all it is: a bordered rectangle.

RollupCtrlPage: Derived from Panel. This is the Collapsible/Expandable page. It simply consists of a button (to collapse/expand the page), a group box and a dialog panel.

DialogPanel: Derived from System::Windows::Forms::UserControl. This is the panel on which we create the contents to go into a rollup page (we typically drag controls on to the panel in the Forms Designer).

Example Use: The code snippet below will create a rollup control that contains one expandable/collapsible page of type RollupPageDialog. It then adds the rollup control to a tabbed page. You may also notice some minor bells and whistles; you can:

  • Set the controls AnimatedRoll property to true for a smoothly animated rolling effect when pages open/close.
  • Set the speed that animated pages close/open at, and you can,
  • Set the speed that animated pages close/open at, when the “Close All” or “Open All” options are selected by the user.

For a more comprehensive example, see the InitializeRollupCtrls() method in Form.h of the sample project.

C++
#include "RollupCtrl.h"
#include "RollupPageDialog.h" // include the header file of the dialog panel we
                              // created in the Designer
...
private: RollupCtrl^ rollupCtrl2; 
... 
this->rollupCtrl2 = gcnew RollupCtrl();
// 
// rollupCtrl2 on Tab page
// 
this->rollupCtrl2->Location = System::Drawing::Point(10, 10);
this->rollupCtrl2->Text = L"rollupCtrl2";
this->rollupCtrl2->Size = rollupCtrlSize;
this->rollupCtrl2->TabIndex = 2; 
...
...
/// The DialogPanel RollupPageDialog (derived from DialogPanel) is created
/// in the Designer before the next line can happen
RollupPageDialog^ rollupPageSample = gcnew RollupPageDialog();
rollupPageSample->Text = L"rollupPageSample";
rollupPageSample->Name = L"rollupPageSample";

//give the page a reference to this form
rollupPageSample->ClientForm = this;  

this->rollupCtrl2->CreatePage(rollupPageSample);
//
// rollupCtrl2 Added to Tab Page
//
this->tabPage1->Controls->Add(this->rollupCtrl2);

// Animated Rollup Page Properties.
// Set Page Rollup Speed: the bigger the number the faster the rollup speed
// Note: some values may not roll up neatly. There are no limits! So try a value
// of 200 or more at your peril?!
//
this->rollupCtrl2->RollupSpeed = 2; 
//
// also you can rollup/down faster when user selects "Close All" or "Open All" from the 
// context menu. The default value for this is value of the RollupSpeed property 
//
this->rollupCtrl2->RollupSpeedAll = 100; 
//
// turn on animated rolling
// 
this->rollupCtrl2->AnimatedRoll = true; 
this->rollupCtrl2->ResumeLayout(false);

All we have to do now to actually see something in the control is to create and add the rollupSamplePage class that contains the rollup page data. It is the last essential step (or two) to be covered in the process of using the rollup control.

How to Create the Contents of a Rollup Page in the Designer

There are a number of ways to create your rollup page contents. Here is the one provided in the sample solution:

To design a rollup page in Visual C++ 2008 Express Edition, do the following:

  1. Add a new Form item to the Project.
  2. Derive the form from the DialogPanel class. e.g. include DialogPage.h in the new form’s header file and replace ”public System::Windows::Forms::Form” in the new form’s class definition to “public DialogPanel”.

Now, when you view the DialogPanel form in the Designer it should now look like a typical blank UserControl dialog pane or surface, and it’s ready for you to create a new page on. Just drag to size, and create your new page.

That concludes the basic information about the rollup control. The next section contains information about some extraneous issues that arose during the creation of the rollup control that may be of interest.

Points of Interest

The instructions for using the rollup control that has just been described are simple compared to the issues that arose during its development. Some of those are described below, as well as some other points of interest.

Rollup Page Content Creation Alternatives

The use of the DialogPanel is optional. It’s a simple, convenient example of two typical features of user interface development. The DialogPanel could be viewed as a ‘quick and dirty’ example of two UI features, depending on how zealous you are about the Single Responsibility Principle. The two UI features usually needed in one form or another are:

  1. A visual surface on which to design a dialog page.
  2. An interface to the Model. The terminology here belongs to the Model-View-Controller (MVC) architectural types of patterns. The Model is the actual location of the stored data that is presented visually in the View (where the view is made up of the texttboxes, buttons and other controls that are presented to users in the rollup pages).

Some Visual Surface Options

  • In Visual C++ 2008 we could add a new UserControl item to the project as the design surface for each page we create. You can replace all references to DialogPanel objects with references to UserControl objects, and delete the DialogPanel altogether. There are only 2 or 3 references that need to be changed, and that can succesfully be modified with the Edit->Find and Replace menu optionin the IDE. When you use this option you forgo the code reuse benefits that inheritance may provide.

  • Create you own DialogPanel type class that inherits from UserControl.

Hopefully the main point to take away from these options is that it is easy to modify the supplied demo source in a way that best suits you.

Some Model Interface Options

First, let’s just take a brief look at what the DialogPanel has in this regard. Each page in the sample project contains an instance of a DialogPanel as a member. It provides the design surface for us to create a rollup page on and it contains an example of an interface between the page and the Model. The Model in this case is simply Form1, or more specifically a couple of text boxes in the application’s main form and all they do is display some results of the user interaction with the View (i.e. information such as the rollup page name, control name and state (if applicable) are displayed as the user interacts with controls on a page). The DialogPanel stores a reference to Form1 in the ClientForm property and the interface is all the public methods that belong to the form. You can see an example of the client form property being set with the following line from the code snippet above:

C++
//give the page a reference to this form
rollupPageSample->ClientForm = this;

In the demo code there is only one method our rollup page(s) are interested in:

C++
void UpdateInfo(String^ msg1, String^ msg2)

It’s a (bad?) generic name for a function, but it is for a generic example (so stands as such). I would expect you to be more meaningful with your method name choices. If the ClientForm property of a page is set to Form1 then UpdateInfo() is used to pass two String messages to the form: when the user clicks on a control in the page, and when the mouse leaves a control on the page. Other public access methods can be added to Form1 to pass additional data to it as needed.

Other options:

  • If you don’t already have a mature application with a system to manage user interactions, then researching Model-View-Controller/Presenter (MVC/MVP) and Model-View-Adaptor (MVA) design patterns may stimulate your thinking.
  • As more and more rollup pages are added to the rollup control, it is obvious that communication between Views and Models in general may increase or become more complex. You may feel more comfortable creating an interface as…, well, an interface between each Model and View. This technique is mentioned for you look into (and/debate with friends) the merits of its use as an alternative, and will not be discussed any further here.

If I had to declare my approach to design patterns in general, such as MVC/MVP/MVA mentioned above, it would be best described as egalitarian. Egalitarian?: for example, design patterns like MVC are great tools because they provide solutions to problems that are so generic to software design that a general pattern really does articulate related development issues very sussinctly (maybe this sentence could have been more sussinct!). A design pattern should be used for what it is, a generic tool. It is a generic tool to be applied to your specific design needs as you see fit. So, when I see the mention of the misuse and abuse of design patterns, I cannot help thinking such discussions have a lot more to do with the personal needs of the discussion participants than with the actual design pattern topic. This phenomenon is even more evident if you observe the group dynamics at play when you are part of a team of developers.

ContextMenu

Right click in the RollupCtrl to see a context menu with options to close and open pages. I am thinking I should add an option for the user to be able to change the animated rollup speed (what? …more bells and whistles!).

Scrollbar

The RollupCtrl scrollbar used is the default one that windows provides, and not the smaller, tighter, ‘better’, owner painted ones I normally seen in rollup controls. Maybe it should be changed? But I’m not going to change it.

Mouse Cursors with .NET

How well does the .NET framework support custom Mouse Cursors? It handles them very poorly apparently according to this old link (circa 2005). There seems to be a few suggested solutions, most of them don’t seem to work in the general case, except maybe for the last suggestion in the link. I went with the simplest solution and hung it out in a folder somewhere to be loaded at run time each time it’s needed. For example, the code below shows that when the mouse enters the viewing area it changes to the Pan.Cur (picture of a hand) cursor to indicate scrolling by dragging is ‘active’. Note that it seems as if thePan.Cur file is loaded in each instance! This certainly isn’t an ideal situation.

C++
void RollupCtrlPage::RollupCtrlPage_MouseEnter(Object^ sender, System::EventArgs^ /*e*/)
{

 if(bHasFocus == false)
  bHasFocus = ((RollupCtrl^)(this->Parent))->ContainsMouse();
 if(bHasFocus) 
 { 
 this->Cursor = gcnew System::Windows::Forms::Cursor("Pan.Cur"); 
 this->Focus(); 
 }
}

Even though this doesn’t seem to cause any degradation in the performance of the application today, it might one day. So if anyone has any information that is more up-to-date, or that you think we can benefit from, that is about handling custom cursors more efficiently, lets us know (or if I get around to finding and using a better method myself I’ll give you an update).

Smooth Flicker Free Animated Rollup of Pages (Eliminating a Minor Irritation that causes Big Problems)

One of the lasting impressions pre .NET MFC has left with me is that its behaviour can be temperamental, idiosyncratic and even downright eccentric. .NET and Forms Controls also has its fair share of idiosyncrasies, but to a lesser degree than MFC (or so it seems so far). This eccentric or unexpected behaviour can quickly arise when you start creating elements or features that step out the boundaries of stock standard, ordinary use of controls. For beginners, this eccentric or unexpected behaviour is also usually a sign that it is time to join the ranks of intermediate level programmers: Perhaps leading to painting your own controls, or at the very least, to finding other API calls that will provide you with greater control over your programs behaviour in order to achieve your goal.

Enter the flickering control problem. Flickering controls seems to be a hot topic in some circles, probably because it is usually one of the first unexpected behaviours that arises when beginners attempt the unordinary. And its biggest impact is its unerring ability to make the best application look and feel amateurish. If you do enough research into the subject you will discover the major cause of flickering is drawing over the same pixel twice. And the solution is just as simple: just make sure you do not draw over the same pixel twice. So, the problem and the solution seem well defined. However, the method used to solve the problem is not so well defined. The good thing about this is that you are free to use any method you like to eliminate flickering. But this freedom calls for wisdom. Without a wise choice you could end up writing a lot more code unnecessarily: There are a number of very well known strategies ranging from double buffering with simple one line flag/property settings to more complex GDI+ off screen device context use, plus managing all the rectangles, clipping and painting ourselves. Some strategies can be used at the .NET level, while others require access to the underlying Win32 API.

As you may have guessed by now, my first attempt at implementing something as unordinary as smoothly animated rollup pages produced some flickering at times (surprise,surprise…not). So here’s a brief of how flickering was eliminated: But firstly, I am a lazy programmer: I avoid writing monotonous, repetitive and redundant code. Secondly, I’m pretty dumb too: I don’t even pretend to know anything (ok…that’s not true, I do pretend sometimes); I am always searching for better ways of doing things. So, it is with this attitude that I attempted to eliminate flickering from the rollup control. The last thing I wanted to do was write mountains of redundant painting code or mashal some Win32 into my application just to remove the flickering. I tried the usual suspects like double buffering properties and flags, all to no avail. So, I just thought I would try something different before getting serious with painting the controls myself. I know that I am drawing over the same pixel twice during the rollup animation, but where? And if I slowed down the animation enough I could actually see the two different positions the controls were being drawn at. So I went looking for any code that were likely to update the position of controls more than once in the same method. Here it is, in RollupCtrlPage.cpp:

C++
/// Moves the page relative to the Rollup Control 
/// Called on all pages at a time to simulate scrolling 
/// of the canvas panel 
/// 
void Scroll(int yDelta) 
{ 
 this->Location = System::Drawing::Point(0, y-yDelta); 
 Update(); 
} 
/// 
/// recalculate the absolute position of the page on the canvas panel 
/// 
void ReCalcLayout(int yAdjustment)
{ 
 y += yAdjustment;
 this->Location = System::Drawing::Point(0, y); //this line is omitted to remove
                                                //flickering! 
}

These were the only methods that modified the page location (apart of initialization code) and they are ultimately called in the RollupCtrl::AdjustLayout() method. And what I see flickering does seem to match up to the two different calls on the Location property. “Maybe only setting the Location once would work” I thought. Removing the line

C++
this->Location = System::Drawing::Point(0, y);

from ReCalcLayout() did eliminate the flickering (the y variable stores the page location but it isn’t actually set until the Control::Location property is set). So what did I learn? Probably not much really. I have to assume the control is drawn after each time the Location property is set at least in this case and it should be something to look out for in future. However, the same cannot be said for the controls within the page. For example, the Location property of the data panel is set in the RollupCtrlPage::Resize() method, but is not properly drawn until Update() is called in the Scroll() method above.

Is this a better way to eliminate flickering, than doing all the painting ourselves? That’s up to you. I consider myself lucky (at being lazy) to eliminate the flickering without going a little deeper and painting controls myself, as any serious programmer would attest I should have done(I am hoping it doesn’t flicker when you run the sample, because I’ll deserve all the criticism I get for not painting controls myself: full of confidence aren’t I ). So, as a warning: ‘don’t try this at home kiddies’, it’s a good example of bad programming practice. Do the right thing: Paint your own controls when you start doing things out of the ordinary like animated rollups (and if you want to avoid the scorn of the ‘serious programmer’).

UserControl Inheritance and /clr Anomaly

Using managed and unmanaged code in your project (with the /clr property selected) can be problematic if you inherit from UserControl. The result: expect to see something like this in the Designer instead of a blank surface on which to create your rollup page dialog:

AnimatedRollupControl2.JPG

“This is an expected behavior given a known CLR limitation. The same problem happens for user controls and inherited forms. The workaround is to compile the mixed-mode code as a DLL not as an EXE.” WenYuan Wang [MSFT] - 18 Jun 2007 04:35 GMT, Microsoft Online Community Support at this link. So if you do your research it’s not an anomaly, but expected behaviour.

There are two other workarounds I have used:

  1. Change the Common Language Runtime support property to /clr:pure to design the page. Change it back to /clr to run the program. Just make sure the file you want to see in the Designer is closed before recompiling with clr:pure so it refreshes and loads properly, and you should see the expected blank surface to create you page on. This is a long shot, since the compiler may complain violently about the presence of unmanaged code, depending on you application.
  2. In Visual Studio versions except Express Edition: Don’t inherit from UserControl. Just add a new UserControl item to the project for each rollup page design you want to create. You’ll get everything you get when you inherit from UserControl, plus a designer surface and no error box will appear when using /clr. However, the downside is that we forgo the code reuse benefits that inheritance could provide. There are always alternatives: e.g. perhaps a more sensible design would be to give the UserControl one purpose (of providing the designer surface), and implement an interface as an interface between the View and Model. However, design is really out of the scope of this article. We are mainly interested in rollup controls here. So sticking with the simple example in the sample project, where the UserControl provides two things: the design surface and interface methods, you could replace all references to DialogPanel objects to UserControl objects and delete the DialogPanel class. You can add ClientForm type methods to interace with the Model if you like, or (I would expect you to ultimately) implement your own preferred strategy for View-Model interaction.

So, there are 3 options there to choose from as a solution to workaround the behaviour produced by the User Control inheritance and /clr anomaly.

You will find other points of interest as comments in the example source code. Many of the comments are targetted directly towards the readers of this article and thus, would not normally exist. So, I hope the comments are more of a help than a hindrance.

Summary

I wish every article had this section; the summary; for the times you’re in the mood for just the basic info; skip the article, and get the code up and running ASAP.

Here is an overview of the steps we take to put a rollup control into our application:

  1. Add RollupCtrl.h, RollupCtrl.cpp, RollupCtrlPage.h, RollupCtrlPage.cpp and DialogPage.h to your project.
  2. Declare and instantiate a RollupCtrl object. Set some of its properties: the main properties being the Location and Size.
  3. Design your rollup pages on a dialog panel in Windows Form Designer.
    • Add a new Form item to the Project.
    • Derive the form from the DialogPanel class. e.g. include DialogPanel.h in the new form’s header file and replace ”public System::Windows::Forms::Form” in the new form’s class definition to “public DialogPanel”.
    • Now, when you view the DialogPanel form in the Designer it should now look like a typical Blank UserControl dialog pane or surface, and it’s ready for you to create a new page on. Just drag to size, and create your new page.
  4. Declare and create instances of your dialog panels. Set some of their properties.
  5. Pass each dialog panel to the rollup control: so that it can create a rollup page with your page design inside and add it to its viewing panel.
  6. Add the rollup control to its host Form or Control.

You can place the rollup control in a Form or in any other suitable control. In the sample project one rollup control is added to the form, while the other one is placed in a tab control. To review some example code, scroll up to the first code block in the article (containing an example of steps 2,4,5 and 6) or for a more comprehensive example see the InitializeRollupCtrls() method in Form.h of the sample project. If you get stuck, or have problems, reviewing the Points of Interest section above may help.

And Finally…

Finally, you may recall my secondary interest in the article was to find out how much more easily a rollup control would be to implement in C++/CLI compared to older technology, such as pre NET MFC (i.e. recall the: ‘see how far we have come’ in our progression towards programming nirvana). Well, without counting the lines, there seemed to be a lot less code to write (which is not always a good thing, for example, people that aim to impress by deliberately writing terse and cryptic code can be needlessly difficult to work with). And if we ignore the bulk of the standard initialization code (auto-generated and hand written) there definitely is a lot less code to write. A lot less code implies it should be faster to implement, and the less code you write, the less chance there is of producing errors (That’s how our programming theory works, right?). But the main plus is that the code we need to write is easy to understand (this is very important for maintenance, and hopefully beneficial to article reading coders like you and me to read). So, needless to say, I am suitably impressed.

PS. A Few Words about Smoothly Animated Controls

During the course of developing the rollup control, the continual testing and use of the smoothly animated pages had a suprisingly profound effect on me (it’s pretty sad when a UI control can have that effect on a person!). They just ‘feel’ so much better to use than the standard instantaneous rollup control. If you try out the sample, at times you might notice a slight jittering or rumbling/wavering effect in an animated page as its rolling up (it only seems to be evident when closing a page and the scrollbar button is located at the bottom i.e. maximum value, of the scrollbar control). I even liked that enough to leave it [as a feature!?](Of course, you can try eliminating it if you don’t like it). I hope you get as much enjoyment out of using the rollup control as I did in making it.

I cannot remember if I actually have ever seen or used smoothly animated type controls like this outside of a video game (so, if you know of any windows apps that do have them, let us know). It’s going to be difficult for me not to use these animated type controls in the future. Then again, I suspect it might just be a novelty that will eventually wear off.

License

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


Written By
Australia Australia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionDownload is corrupt Pin
firehawk20-Apr-12 20:19
firehawk20-Apr-12 20:19 

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.