?
Solved

Interface Issue / Question

Posted on 2003-03-13
22
Medium Priority
?
490 Views
Last Modified: 2010-04-04
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.
0
Comment
Question by:mdlittle
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 10
  • 10
  • 2
22 Comments
 

Author Comment

by:mdlittle
ID: 8134095
Also, a standard list box is out of the question because I want this to look like buttons.
0
 
LVL 3

Expert Comment

by:BTecho
ID: 8134110
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
 

Expert Comment

by:mfhobbs
ID: 8134114
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
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 

Author Comment

by:mdlittle
ID: 8134235
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
 

Expert Comment

by:mfhobbs
ID: 8134387
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
 
LVL 3

Expert Comment

by:BTecho
ID: 8140384
Hi , mdlittle

I busy at the moment, If its still needed I'll try and post a sample application early tommorow.
0
 
LVL 3

Accepted Solution

by:
BTecho earned 2000 total points
ID: 8140719
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
 

Author Comment

by:mdlittle
ID: 8140860
BTecho I am trying your sample now. Will get back shortly...
0
 

Author Comment

by:mdlittle
ID: 8140927
BTecho how do I remove the scroll bars from the dbctrlgrid
0
 

Author Comment

by:mdlittle
ID: 8141067
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
 
LVL 3

Expert Comment

by:BTecho
ID: 8141133
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
 
LVL 3

Expert Comment

by:BTecho
ID: 8141147
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
 
LVL 3

Expert Comment

by:BTecho
ID: 8141303
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
 

Author Comment

by:mdlittle
ID: 8141342
BTecho how do I remove the scroll bars from the dbctrlgrid
0
 
LVL 3

Expert Comment

by:BTecho
ID: 8141534
>>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
 
LVL 3

Expert Comment

by:BTecho
ID: 8141590
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
 

Author Comment

by:mdlittle
ID: 8143148
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
 

Author Comment

by:mdlittle
ID: 8143160
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
 
LVL 3

Expert Comment

by:BTecho
ID: 8153744
Hi mdlittle.

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

Thanks.
0
 

Author Comment

by:mdlittle
ID: 8155880
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
 
LVL 3

Expert Comment

by:BTecho
ID: 8156384
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
 

Author Comment

by:mdlittle
ID: 8159420
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

Featured Post

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This article explains how to create forms/units independent of other forms/units object names in a delphi project. Have you ever created a form for user input in a Delphi project and then had the need to have that same form in a other Delphi proj…
Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
Add bar graphs to Access queries using Unicode block characters. Graphs appear on every record in the color you want. Give life to numbers. Hopes this gives you ideas on visualizing your data in new ways ~ Create a calculated field in a query: …
Have you created a query with information for a calendar? ... and then, abra-cadabra, the calendar is done?! I am going to show you how to make that happen. Visualize your data!  ... really see it To use the code to create a calendar from a q…
Suggested Courses
Course of the Month10 days, 21 hours left to enroll

770 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question