Interface Issue / Question

I am trying to do something a little different and am not sure about how to approach it:

On a form on in a panel? on the form I want a list of buttons? with captions. These buttons and captions will have to be built at run time and the user will need the ability to scroll through these buttons and make a choice with either the mouse or the keyboard. Here is the issue though:

1. I don't want the traditional scroll bars.
2. The buttons? need to have .png or .jpg images and change images depending on the mouse (enter/leave) and keypress events
3. The list comprising these buttons could be large ~3000

Basically I want to create image buttons at run time and have the ability to scroll up and down through them at run time. The buttons have to support mouse events as well as keyboard events. When I say buttons I don't mean literally - they should look like buttons at run time. I did my best to explain my issue - give it a shot.
mdlittleAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

mdlittleAuthor Commented:
Also, a standard list box is out of the question because I want this to look like buttons.
0
BTechoCommented:
Hi there.

Very interesting question. I have an idea that you may like but I haven't tried it yet.

In theory you could use a TDBCtrlGrid, Set the number of columns you would want and just connect it to a database using a TTable and TDataSource, you can use a Paradox database which is simple to use and supports RecNo (you will find uses for it) you could have 2 fields ButtonID (Number field) and Caption (Alpha size=20 or how long a caption you would use). You would use the TPanel(you cant put a control like a TButton in a DBCtrlGrid) as a Button and put a TImage in the Panel as the state image, you would use the Panel's OnMouseEnter/OnMouseExit events to know what image to load.

Now you can use this freeware Panel or some others (http://www.torry.net/enhancedpanels.htm) which will support all mouse events OnMouseEnter, OnMouseLeave and etc. You can use the OnMouseUp and OnMouseDown events to change the bevel style, so you can mimic a button visual press.

Just the OnMouseEnter and OnMouseLeave events need to be added the rest the TPanel already has.
 
unit HighlightPanel;

interface
uses Messages, Controls, Classes, StdCtrls ,Graphics ,SysUtils,ExtCtrls;


type
 THighlightPanel = class(TPanel)
 private
   FOnMouseEnter: TNotifyEvent;
   FOnMouseLeave: TNotifyEvent;
 public
   procedure MouseEnter(var msg : TMessage); message CM_MOUSEENTER;
   procedure MouseLeave(var msg : TMessage); message CM_MOUSELEAVE;
 published
   property OnMouseEnter: TNotifyEvent read FOnMouseEnter write FOnMouseEnter;
   property OnMouseLeave: TNotifyEvent read FOnMouseLeave write FOnMouseLeave;
 end;

procedure Register;

implementation

{ THighlightPanel }

procedure THighlightPanel.MouseEnter(var msg: TMessage);
begin
 if Assigned(FOnMouseEnter) then FOnMouseEnter(self);
end;

procedure THighlightPanel.MouseLeave(var msg: TMessage);
begin
 if Assigned(FOnMouseLeave) then FOnMouseLeave(self);
end;

procedure Register;
begin
RegisterComponents('Samples', [THighlightPanel]);
end;

end.

as for the key events you can set the Forms KeyPreview to true or use the DBCtrlGrids key events you can read the Table's RecNo to know which button has focus . Also you can navigate the buttons by using the keyboard or mouse or using TTable's Next Prior,First and Last methods.

Using this method you can have 3000 or more buttons and it would be very easy to handle any situtation like adding and removing buttons, just add and delete the records ;) also this way the button configuration is saved for the next time the application is run .

Its a bit late here where I am, so I'll try and post more details if needed tomorrow.

P.S

I use a control from 1stClass (www.woll2woll.com) called fcButtonGroup , its very simple to use and you can create groups of buttons but its actually one control. You would just navigate them like you would many buttons, using FocusControl or SelectNext. And each button has all the Key and Mouse events you would need but I never tried adding 3000 buttons in one group.
0
mfhobbsCommented:
Hi,  I have vague memories of doing something like this.   Way back...

If you want a vertical scroller the easiest way is to use a TListBox but use the owner-draw functionality to do your own painting in each cell.  Some fiddling and you can 'draw' a button in a cell via the owner draw cell event.  You can also draw the caption and graphic inside the button within the cell the same way.

You can turn the scrollbar off I expect and just hook into any extra key presses you want too.

If you want the buttons to go down on a press, you'd have to call a paint on a mouse-down event and detect the mouse position and mouse status in the draw-cell event.

Cheers,
-Mat
0
Cloud Class® Course: Ruby Fundamentals

This course will introduce you to Ruby, as well as teach you about classes, methods, variables, data structures, loops, enumerable methods, and finishing touches.

mdlittleAuthor Commented:
OK! 2 great suggestions. Let me play a bit - any code samples you have would be helpful I am also will to "post" other questions with similar point values (2 or 3 more) For the one who helps me solve this issue, quickly.

Mike
0
mfhobbsCommented:
Hi Mike,  I found some code that you might be able to get some useful stuff from.  It illustrates the calls you may want to consider using for image display and simple text output.  As for drawing a 'button'... a 3-d effect for a panel is a FillRect leaving a couple of edges free, then lines in another color to fill in the two edges, a button is a bit more work I guess, or maybe print-screen a button, edit the bmp, and just display it as an image!

Cheers,
-Mat

The TListBox has these properties set:

    ItemHeight = ...whatever you want...
    Style = lbOwnerDrawFixed

The OnDrawItem is hooked to:

Example:

procedure TfrmTemplateObjects.ListBox1DrawItem(Control: TWinControl; Index: Integer; Rect: TRect; State: TOwnerDrawState);
begin
  ListBox1.Canvas.Brush.Color := ListBox1.Color;
  ListBox1.Canvas.Font.Color := ListBox1.Font.Color;
  ListBox1.Canvas.FillRect(Rect);
  if (Index >= 0) then
    ListBox1.Canvas.Draw(Rect.Left+2,Rect.Top+((ListBox1.ItemHeight-16) div 2),...mytbitmap...);
  ListBox1.Canvas.TextOut(Rect.Left+20,Rect.Top+2,ListBox1.Items[Index]);
end;


Or, a wacky example, includes image tiling (only read this if you have a lot of time on your hands!):

procedure Tmyform.myondrawitemeventhandler(Control: TWinControl;
  Index: Integer; Rect: TRect; State: TOwnerDrawState);
var
  vRect: TRect;
begin
  vRect := Rect;

  if (Index = (lbLinks.Items.Count-1)) then
    vRect.Bottom := lbLinks.Height;
  CoverCanvas(Image1,lbLinks.Canvas.handle,vRect,
         vRect.left + (lbLinks.ClientOrigin.x - Self.ClientOrigin.x), //         Left {+ Panel1.Left},
         vRect.Top + (lbLinks.ClientOrigin.y - Self.ClientOrigin.y)//lbLinks.Top {+ Panel1.Top}
         );
  SetBkMode(lbLinks.Canvas.Handle,TRANSPARENT);

  if (odSelected in State) or (odFocused in State) then //odSelected, odGrayed, odDisabled, odChecked odFocused, odDefault
  begin
    lbLinks.Canvas.Font.Color := clBlue;
    lbLinks.Canvas.Font.Style := [fsUnderline];
  end
  else
  begin
    lbLinks.Canvas.Font.Color := lbLinks.Font.Color;
    lbLinks.Canvas.Font.Style := [];
  end;
  lbLinks.Canvas.TextOut(Rect.Left + 3, Rect.Top, lbLinks.Items[Index]);
end;



procedure CoverCanvas(pFrom: TImage; pTo: THandle; pRect: TRect; pFromLeft, pFromTop: integer);
//Tile image pFrom into pTo
var
  vImageWidth, vImageHeight, vImage_L, vImage_T, vRect_L, vRect_T, vThisWidth, vThisHeight: integer;
begin
  vImageWidth := pFrom.Width;
  vImageHeight := pFrom.Height;

  //initialize image top coordinate
  vImage_T := pFromTop mod vImageHeight;
  //reset top rect coordinate
  vRect_T := pRect.Top;

  //do rows first
  while (vRect_T <= pRect.Bottom) do
  begin
    //initialize image left coordinate
    vImage_L := pFromLeft mod vImageWidth;
    //reset left rect coordinate
    vRect_L := pRect.Left;

    //calculate height of the section to be copied from the pFrom image
    vThisHeight := (pRect.Bottom - vRect_T) + 1;
    if (vThisHeight > (vImageHeight-vImage_T)) then //restrict to
      vThisHeight := (vImageHeight-vImage_T);

    //do columns
    while (vRect_L <= pRect.Right) do
    begin
      //calculate width of the section to be copied from the pFrom image
      vThisWidth := (pRect.Right - vRect_L) + 1;
      if (vThisWidth > (vImageWidth-vImage_L)) then
        vThisWidth := (vImageWidth-vImage_L);

      //draw
      BitBlt(pTo,vRect_L,vRect_T,vThisWidth,vThisHeight,
             pFrom.Canvas.Handle,vImage_L,vImage_T,SRCCOPY);

      vRect_L := vRect_L + vThisWidth;
      vImage_L := (vImage_L + vThisWidth) mod vImageWidth;
    end; //while columns

    vRect_T := vRect_T + vThisHeight;
    vImage_T := (vImage_T + vThisHeight) mod vImageHeight;
  end; //while rows
end;//CoverCanvas

0
BTechoCommented:
Hi , mdlittle

I busy at the moment, If its still needed I'll try and post a sample application early tommorow.
0
BTechoCommented:
Well why wait for the sample :-) I'll just outline the steps needed.

First you create the Paradox Table using Database Desktop, goto menu File|New|Table pick Paradaox7 , add 2 fields ButtonID (Number field) and Caption (Alpha size=20 or how long a caption you would use) make the ButtonID field the key field.

Now in the Delphi IDE.

Place a TDataSource and TTable on the Form, set the Table to point to the new table you have created by setting DatabaseName (Folder Name) and TableName. Set DataSource1.DataSet = Table1. Plave a TDBCtrlGrid on the Form and set the DataSource and ColCount properties you can also set the Selected Color property if you like. Now place the THighlightPanel component (I posted the source in my first post) inside the first panel on the DBCtrlGrid set the Panels Align to alClient. Place a TDBText on the Panel, just position it a little of center to the right, set the DataField to the "Caption" field. If you don't have a TDBText use a TDBEdit instead, just set turn off all the border styles for the DBEdit and set its color to match the Panels color .Next, Place a TImage on the Panel set the Picture to the off state image, place it a little off center to the left of the DBEdit.

Now for the code:

OnMouseDown event of the Panel
HighlightPanel1.BevelOuter:=bvLowered;

OnMouseUp event of the Panel
HighlightPanel1.BevelOuter:=bvRaised;

Then assign the above events for the DBText and TImage.

Now to change the TImage's picture:

OnMouseEnter & OnEnter events of the Panel
Image1.Picture.LoadFromFile('C:\StateOn.bmp');

OnMouseLeave & OnExit events of the Panel
Image1.Picture.LoadFromFile('C:\StateOff.bmp');


To navigate the buttons using the keyboard

On the DBCtrlGrid's OnKeyDown event:

if Key=VK_DOWN then begin
Table1.Next;
HighlightPanel1.SetFocus;
end

else if Key=VK_UP then begin
Table1.Prior;
HighlightPanel1.SetFocus;
end;

To add buttons, just add records.

var
ButtonID:integer;
begin
ButtonID:=Table1.RecordCount+1;
Table1.Append;
Table1.FieldValues['Caption'] := "Button Caption";
Table1.FieldValues['ButtonID'] := ButtonID;
Table1.Post;
end;
 
To search for a Button and Delete it,
var
ButtonID:integer;
begin

ButtonID:=10;
if Table1.FindKey([ButtonID]) then
 Table1.Delete;

end;


Thats seems to be it, not much to it. You can expand the idea, add a Graphic field to the table and each button can have a picture instead of a caption ;)


Cheers!

0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
mdlittleAuthor Commented:
BTecho I am trying your sample now. Will get back shortly...
0
mdlittleAuthor Commented:
BTecho how do I remove the scroll bars from the dbctrlgrid
0
mdlittleAuthor Commented:
BTecho

Well, this does have possibilities but I see some problems. Maybe I am doing something wrong but the mouseenter event only fires on the first and last button in the grid unless you click the button first the it works but the others stop working. Very strange. I also don't want the scroll bars.

I do have to tell you that this is closer that I have come to solve this problem and if I can figure out how to fix this then were looking good.
0
BTechoCommented:
Hi :)

I'll check the OnMouseEnter problem ASAP.

To get rid of the scrollbars

You can try this on the DBCtrlGrid's OnPaintPanel event, it may flicker
ShowScrollBar(DBCtrlGrid1.Handle, SB_VERT , false);

If it will flicker just put a panel over the scrollbars instead, set the hieght in the IDE and width on the OnCreate or OnShow events of the Form

Panel1.Width := GetSystemMetrics(SM_CYHSCROLL);
0
BTechoCommented:
P.S

Since you dont have the scrollbars. You can get the position in the grid using Table1.RecNo.

Example

Label1.Caption := 'Button '+IntToStr(Table1.RecNo)+
                  +' of '+IntToStr(Table1.RecordCount);
0
BTechoCommented:
P.S

Since you dont have the scrollbars. You can get the position in the grid using Table1.RecNo.

Example

Label1.Caption := 'Button '+IntToStr(Table1.RecNo)+
                  +' of '+IntToStr(Table1.RecordCount);
0
mdlittleAuthor Commented:
BTecho how do I remove the scroll bars from the dbctrlgrid
0
BTechoCommented:
>>BTecho how do I remove the scroll bars from the dbctrlgrid

Didn't the post dated 03/14/2003 07:39PM PST  help any?

After brain storming and searching this is the only solution I think is left, to fix the mouse focus issue. I can see that the Panel will only get the mouse enter/leave events if that Panel's record is active, currenly you need to click on a panel to then get those events triggered. So, we can mimic the click through code.

Now you must change the HighLightPanel's Align property to alNone or else this wont work becuase the event below won't fire. And you must resize the HighLightPanel that it should be smaller than the DBCtrlGrid's panel, the smaller you make the HighlightPanel the better this will work, a 7 pixel border should be fine enough though.

Use this on the DBCtrlGrid's OnMouseMove event:
mouse_event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);
mouse_event(MOUSEEVENTF_LEFTUP,0,0,0,0);


Change the code for the DBCtrlGrid OnKeyDown event to:

//so the mouse click triggered on the MouseMove event would not interfere with keyboard scrolling.
 
var
MouseCoor : TPoint;

begin

if(Key=VK_DOWN)or(Key=VK_UP)or(Key=VK_LEFT)or(Key=VK_RIGHT) then begin

 MouseCoor.x := DBCtrlGrid1.Left-1;
 MouseCoor.y := DBCtrlGrid1.Top+1;
 MouseCoor := ClientToScreen(MouseCoor);
 SetCursorPos(MouseCoor.x, MouseCoor.y);


 if Key=VK_DOWN then begin
  DBCtrlGrid1.DoKey(gkDown);
 end

 else ifKey=VK_UP then begin
  DBCtrlGrid1.DoKey(gkUp);
 end

 else if Key=VK_LEFT then begin
  DBCtrlGrid1.DoKey(gkLeft);
 end

 else if Key=VK_RIGHT then begin
  DBCtrlGrid1.DoKey(gkRight);
 end;

HighlightPanel1.SetFocus;

end;

end;


I hope this works well for you. Those two major issues should be solved.

If its ok I ask, you don't have to tell if you don't want to. Just curious... in what case would you use the interface you describe?
0
BTechoCommented:
Hello again!

About the scrollbars.

The ShowScrollBar(DBCtrlGrid1.Handle, SB_VERT , false);
would not work in this case, the flicker is to bad even when DoubleBuffering for the DbCtrlGrid is set to true.

So placing a panel over the scrollbars should work.

Sorry this
Panel1.Width := GetSystemMetrics(SM_CYHSCROLL);

Should be
Panel1.Width := GetSystemMetrics(SM_CXVSCROLL);


In any case this should resize it perfectly.

OnCreate event of the Form,

var
ScrollbarWidth : Integer;

begin

ScrollbarWidth := GetSystemMetrics(SM_CXVSCROLL);

Panel1.Left := DBCtrlGrid1.Left + DBCtrlGrid1.Width -ScrollbarWidth;
Panel1.Width := ScrollbarWidth;
Panel1.Top := DBCtrlGrid1.Top;
Panel1.Height := DBCtrlGrid1.Height;

end;

The other solutions is to create a inherited component from the DBCtrlGrid and do something there, not sure what it is yet.

Kindest regards!
0
mdlittleAuthor Commented:
BTecho

Well, this does have possibilities but I see some problems. Maybe I am doing something wrong but the mouseenter event only fires on the first and last button in the grid unless you click the button first the it works but the others stop working. Very strange. I also don't want the scroll bars.

I do have to tell you that this is closer that I have come to solve this problem and if I can figure out how to fix this then were looking good.
0
mdlittleAuthor Commented:
Oops - ignore the last post. I refreshed my browser and it posted the question again. That is also what happened above with the scrollbar question.

I am trying you suggestion now.
0
BTechoCommented:
Hi mdlittle.

Just wondering... did you try my suggestion yet? If yes, does it work well for you?

Thanks.
0
mdlittleAuthor Commented:
BTecho -

I am still having problems with the mouseover, etc but I can resolve them on my own. The point is you pointed me in the right direction. The dbGrid idea is perfect since I am going to have a lot of data anyway. I am going to award you the points for pointing me in the right direction.

Also, I found a really nice set of tools from:

http://www.jfactivesoft.com

their tools provide a really nice grid and other very cool interface tools. Some of which I am going to use in my project. Check them out. Thanks for all your help.
0
BTechoCommented:
Hi mdlittle.

Really glad I could help :-)

<<I am still having problems with the mouseover
That is a toughy... seems few other people are having a similar problem with the mouse and DBCtrlGrid. We could use a plain DBGrid just as you mentioned but then the only step left is to somehow integrate the button features you want.
http://makeashorterlink.com/?B6B6258D3

<<Also, I found a really nice set of tools from: http://www.jfactivesoft.com

Didn't know it was ok to suggest non freeware third party tools, not many people like to hear they have to pay for something to get what they want ;-) I for one say why not, if it decreased development time, I'm all for it.

If I may add another DBGrid you may want to tryout, I use it and am very happy with it , has many features of the DevExpress grid but much easier to use and learn. Its called Infopower a DB control pack from www.Woll2Woll.com. I use v3000 but v4000 is soon to be released:

http://www.woll2woll.com/infopower/WhatsNewIn4000.htm

Best wishes and goodluck with your project!


0
mdlittleAuthor Commented:
Btecho -

I too have v4000 from woll2woll. I'm with you - if I can buy it I will.

Thanks a bunch for the help.

0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Delphi

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.