A panel makes a nice container because things 'stay put' on a panel; and hooking up a panel's
Click() event to a timer activated by scroll/pan buttons on that panel makes quick work of both organizing the button placement and coordinating timer start/stop events with mouse down/up events on individual buttons, provided you have a handy class like the
Btn9Panel containing constructors and methods which are ready to lay out all of the details as soon as the Visual Studio designer sees it, and before it gets to your toolbox.
Most of the CodeProject articles I've seen deal with intricacies which take hours to ferret out, and although sometimes there are rather obvious and simplistic solutions for beginners, most of the articles deal with relatively complex or convoluted scenarios. Obviously, a lot of the readers don't have time to fool around with simple stuff once they get done with the really tricky stuff, so I figured I ought to offer some time-saver code... Maybe something to give a poor guy a chance to get fifteen minutes of extra sleep some night.
Using the Code
All it takes is to drag a
Btn9Panel out of your toolbox onto a form to see what it does. The
VertPanel classes subclass the
Btn9Panel, and you can drag all three onto a form and use the designer to change the dimensions or
FlatAppearance elements offered when
FlatStyle.Flat is in use.
There isn't anything really tricky enough about the code to make an issue of it here. The
NineBtnCtrl.DLL source code is so thoroughly documented (even over-documented) that you should view it in the Visual Studio IDE so that you can toggle the outlining on and off for each region as you go through it. Completely collapsing it and then examining one section at a time will take a lot less time than trying to go through it here.
The only code you need to 'wire it up' after you drag one of these panels onto a form is this:
btn9Panel1.Click +=new EventHandler(btn9Panel1_Click);
following your form's
InitializeComponent() statement. And the panel
Click() event handler is also required:
private void btn9Panel1_Click(object sender, EventArgs e)
The example in Btn9Form.cs only carries out two operations to illustrate 'scrolling' in the handler:
ii = ii + btn9Panel1.Dx; jj = jj + btn9Panel1.Dy;
Dy increments of [+|-]1 furnished by the .DLL code each time the timer ticks will allow you to pan, scroll, change array indexes, or whatever you need to do; and as long as the mouse button is held down, the timer will keep on generating these increments and calling your code. So all you need to do is to decide how you like your scrolling done; and if you don't want to use the timer for some reason, you can call
SetDxDy() to set
Dy for you, so you can use the same code you've got to scroll programmatically as well as in response to the mouse button events. Call
btn9Panel1.Disable() whenever you want to shut down the timer response to the button presses; and be sure to check, as noted in the .DLL source code, whether
true before calling precisely controlled code in your application if you will be enabling or disabling the timer during critical periods.
The example Windows Form in Btn9Form.cs gives you an opportunity to run a form containing a button matrix and to change the
Checked colors while you can see the effect it has (the designer doesn't show the
MouseDown colors on the design surface; only in the Properties window); and after you exit the Form, you can use the Properties dialog in the designer to set the default colors or panel dimensions, although you could also hard-code the new values into the .DLL source code and recompile it if you're that devoted to your predilections.
Btn9Form.cs also shows you the panel name, button name, button index, and direction (W, NW, N, etc.) while two integer values are incremented/decremented during a button press. The two subclasses having only two buttons will only scroll in the N-S or E-W directions, as you might surmise, provided your scroller code follows the usual map conventions.
Points of Interest
I initially wrote some scrolling code for two separate projects which I intended ultimately to merge but which do not interoperate. One of the segments is run analogously to a modal dialog, although the threading model doesn't see it that way and there are no calls to
BackgroundWorker classes since I tied the two together. I had initially used a
BackgroundWorker in one and a second
Thread in the other, with a rather tricky set of semaphores used to enable, configure, start, control, stop, and disable the worker thread in the former segment; and a much simpler but still analogous set of semaphores used in the latter segment, even though the latter provided bidirectional scrolling and the former did not. The difference was that the latter scrolling events were started and stopped by eight buttons (in the same form as the
Btn9Panel you see featured here) which scrolled only a single graphical image window, whereas the former scrolling events were started and stopped by twelve buttons (in the same form as the
VertPanel you see featured here) and would, on demand, scroll in tandem a virtual window into an
Array and the visible window in a
PictureBox corresponding to that and containing a bitmap representation of the array contents.
Although in merging the two segments, I replaced both the
BackgroundWorker and the second
Thread with a single timer, I didn't merge and subclass the arrangement of the buttons as you see them in the
VertPanel, nor did I create a separate class for the bidirectional
Btn9Panel. These classes were created especially for this CodeProject article, just because I thought the idea of distilling the functionality embodied by those implementations into a single, easy-to-use and easy to set-up toolbox item was worth a shot.
Btn9Panel development work stemmed from my project, "The CQvis MultiGradient Palette Tool", which is a freeware program which you can download from a number of sites. That has a
Thread doing the scrolling in it, but does not use the
Stop() methods to initiate or terminate scrolling, as you'll see if you use
PEBrowse Professional to examine it. That second process thread runs continuously the whole time the program is running, and its behavior is governed entirely by semaphores.