Introduction
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.
Background
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 HorizPanel
and 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);
btn9Panel1.Enable();
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;
Using the Dx
and 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 Dx
and 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 BtnDown
is 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 MouseOver
, MouseDown
, Background
, Border
, and Checked
colors while you can see the effect it has (the designer doesn't show the MouseOver
or 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 HorizPanel
and 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.
History
The 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 Start()
, Suspend()
, Sleep()
, or Stop()
methods to initiate or terminate scrolling, as you'll see if you use WinDbg
or 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.