show icon only in the column header of a ListView - delphi

I want to show a icon (up or down) when i sort a column (asc or desc) of a ListView, so I assingned the SmallImages property to a ImageList (with two icons for up and down). My problem is that when I enter data, the firtst icon in the ImageList is shown in every line of the ListView.
PS
(ListView.ViewStyle:=vsReport)

You can set the ImageIndex on each of the TListItems to -1 to stop them showing images, but you will still have the gap where the image should be.
with ListView1.Items.Add do
begin
Caption := 'Foo';
ImageIndex := -1;
end;

Related

Delphi properly position balloon hint associated with a listview item

How do I ensure a balloon hint that I want to associate with a listview item is properly positioned so that it is next to the item in question, and always shows the full ballon text on screen?
For example, if I enter an invalid character when editing a file name in Windows Explorer, a balloon pops up saying what the invalid characters are. The entire balloon is always on screen even if the list item is near a screen edge or partially off screen. The tail is always positioned at the middle bottom of the list item. The bubble is usually to the bottom right of the tail, but may be above it or to the left if the list item is near the bottom and/or right edges of the screen.
Primarily, I am unable to get the bubble and tail to stay close to the list item.
procedure TForm1.ListEdited(Sender: TObject; Item: TListItem;
var S: string);
var
AHint: string;
R: TRect;
B : TBalloonHint;
begin
if TRegEx.IsMatch(S, '[\\/:*?"<>|]') then
begin
AHint := 'A file name cannot contain any of the following' + sLineBreak +
'characters: \/:*?"<>|';
R := Item.DisplayRect(drBounds);
R.TopLeft := ClientToScreen(R.TopLeft);
R.BottomRight := ClientToScreen(R.BottomRight);
B := TBalloonHint.Create(Self);
B.Description := AHint;
B.HideAfter := 5000;
B.ShowHint(R);
S := TRegEx.Replace(S, '[\\/:*?"<>|]', '');
end;
end;
I've tried the various overloads of ShowHint, as well as the JEDI balloon hint component. I've also adjusted the Top property of the rectangle, which may position the balloon better when the item is in a certain area of the screen, but the ballon is then off position when the item is on some other part of the screen.
Delphi 10.3 Rio, Win 7 x64.
DisplayRect gives client coordinates relative to the listview containing the item, not the form. Hence when converting to screen coordinates, you have to use the listview as the base, not the form:
R := Item.DisplayRect(drBounds);
R.TopLeft := ListView1.ClientToScreen(R.TopLeft); // <--
R.BottomRight := ListView1.ClientToScreen(R.BottomRight); // <--

How to add a Timage to a TScrollBox in Firemonkey XE6?

Firstly sorry if this has come up before but I am struggling to find anything on the matter.
I'm trying to add a number of TImage's to a scrollbox which is meant to hold the images and allow the user to scroll across them. This creation is done in run time.
The images are stored in an array of TImage.
Below is the code I have to create the images.
procedure TfrmMain.CreateSolutionImages(ImageCount: Integer);
var
I: Integer;
ImageScale: double;
begin
if sbSolutionImages.ComponentCount > 0 then //destroy the images already in the scrollbox
sbSolutionImages.DestroyComponents;
SetLength(SolutionImages,0); //clear the array of images
SetLength(SolutionImages,ImageCount); //SolutionImages is an array of timage
ImageScale:= ((sbSolutionImages.Width - 20)/Guillotine.StockWidth);
for I := 0 to ImageCount - 1 do
begin
if not Assigned(SolutionImages[I]) then //if not assigned then create and set the parent to the scrollbox
begin
SolutionImages[I]:= TImage.Create(sbSolutionImages);
SolutionImages[I].Parent:= sbSolutionImages;
SolutionImages[I].Width:= trunc(Guillotine.StockWidth * ImageScale); //set image dimentions and positions
SolutionImages[I].Height:= trunc(Guillotine.StockHeight * ImageScale);
SolutionImages[I].Position.X:= 10;
if I = 0 then
begin
SolutionImages[I].Position.Y:= 10;
end
else
begin
SolutionImages[I].Position.Y:= SolutionImages[I-1].Position.Y + SolutionImages[I-1].Height + 20;
end;
end;
//forgot to include these lines
SolutionImages[I].Bitmap.SetSize(Round(SolutionImages[I].Width),Round(SolutionImages[I].Height));
SolutionImages[I].Bitmap.Clear(TAlphaColors.White);
end;
end;
What is happening is that the scrollbox (sbSolutionImages) is reporting that it contains the images, i.e. componentcount increases, however it is not drawing the images and no scrollbars appear, which should logically happen as some of the images won't be in the viewable region.
Any help would be greatly appreciated.
Add a TLayout as a child of the TScrollBox.
Set the Width and Height as appropriate (and set Position=(0,0)).
Add your images as children on the TLayout.
The TScrollBox will then know the bounds of the TLayout and will set it's scroll bars based on this.
Ok sorry. It was a simple stupid issue.
I forgot to set the sizes on the bitmaps of all the images.
Still within the for loop I needed to add.
SolutionImages[I].Bitmap.SetSize(Round(SolutionImages[I].Width),Round(SolutionImages[I].Height));
SolutionImages[I].Clear(TAlphaColors.White);
Ok so it appears that I am still having a problem. The scrollbars are not coming up and trying to the resize the scrollbox (I have a slider between two panels, one is the parent of the scrollbox and the other holds other components) either does nothing (nothing moves) or causes the slider to shoot off the screen to the left, thus hiding everything "off" the application window.
As I am not familiar with firemonkey, this is boggling. I could've done this easily in VCL however we are trying to explore the "acclaimed power" of firemonkey.

Trying to make an Editbox move up and down with a button click

hi i'm trying to make an editbox move down from 300 by 30 when a button is clicked, and upon clicking the same button again, make editbox move back up by 30 to its original position. However when i click the button ive made it just moves up by 30 each time, where am i going wrong? here is my code,
procedure TfrmProject.Button3Click(Sender: TObject);
begin
if Edit1.Top = 300 then
Edit1.Top := Edit1.Top + 30 else
Edit1.Top := Edit1.Top - 30;
end;
EDIT: I have realised that due to my form being long and having a vertical scrollbar, the property Top of the editbox changes in response to where i am on the form, i.e if i'm at the top of my form the Top property of the edit box increases (the editbox is near the bottom of the form), therefore my new question is how could i ensure the editbox only moves between 2 fixed points, as following the recent suggestions the edit box moves between two points with a 30 distance between them, but their positions on the form change.
This works perfectly fine for me.
Create a new Delphi VCL Forms Application
Drop a TEdit and a TButton on the new form. Set the Top' property of each to50using theObject Inspector`.
Double-click Button1, and paste the following code to replace the newly generated TForm1.Button1Click event:
procedure TForm1.Button1Click(Sender: TObject);
begin
if Edit1.Top = 50 then
Edit1.Top := Edit1.Top + 30
else
Edit1.Top := 50;
end;
Run the application. Repeatedly clicking Button1 makes Edit1 move up and down from 50 to 80.
This means your comparison is wrong. Set the Button1.Top explicitly to the original coordinate (300 in your code) instead of reducing by 30.
Then the original setting of the Top property was not 300. Or the movement is not (fully) allowed due to constraint, align or anchor settings of the edit control or those of adjacent controls.
Possible solutions:
When Top should always be 300:
Set Edit1.Top to 300. And make sure there is movement possible.
When 299 < Top < 330:
Change the comparison to:
if Edit1.Top < 330 then
When Top should remain undesided:
Use the Tag property of the edit control (or a private variable, or...) to remember in which direction it has to move:
procedure TForm1.Button1Click(Sender: TObject);
const
MoveNorth = 0;
MoveSouth = 1;
begin
if Edit1.Tag = MoveNorth then
Edit1.Top := Edit1.Top + 30 else
Edit1.Top := Edit1.Top - 30;
if Edit1.Tag = MoveNorth then
Edit1.Tag := MoveSouth else
Edit1.Tag := MoveNorth;
end;
Use Ken's solution.
Here's a little trick for you.
Place a label with no caption on the form with the Top property set to 0 and the anchors set to [akLeft, akTop]. Use that label as a place holder, so you always know where the top of the form is. When it's off the screen at the top from scrolling, the Top property will actually become negative.
Now, use the label's Top property as your starting point, so to put the edit box at 300 pixels from the top:
Edit1.Top := Label1.Top + 300;
That's the easy way. I figure the proper way is to use the position of the vertical scroll bar like this:
Edit1.Top := 300 - Self.VertScrollBar.Position;

add TMenuItem bitmap

I want to add a bitmap to a TMenuItem created dynamically. With this code it doesn't work, I don't have the image on my menu. What's wrong?
procedure TForm3.FormCreate(Sender: TObject);
var
item : TmenuItem;
icon : TIcon;
begin
item := TMenuItem.Create(PopupMenu1);
item.Caption := 'coucou';
icon := TIcon.Create;
icon.LoadFromFile('d:\SmallIcon.ico');
icon.Height := 16;
icon.Width := 16;
item.Bitmap.Canvas.Draw(0,0,icon);
PopupMenu1.Items.Add(item);
end;
The Bitmap property on TMenuItem isn't the way to go here. You really should use image lists instead. This will allow you to share images between your UI elements in a manageable fashion.
Add the icon to a TImageList.
Set the Images property on the menu (i.e. PopupMenu1) to refer to the image list.
Set the image index of the menu item to the index of the icon in the list, i.e. 0 if it's the first image.
Of course, you really ought to be using actions too, in which case you simply need to set the ImageIndex for the action and the framework takes care of assigning it to the menu item.
As an aside, I would note that the Delphi implementation of Vista themed menus has a large number of subtle bugs, many related to drawing of images. However, these bugs are relatively minor in visual impact.
Add the line
item.Bitmap.SetSize(16,16);
as third one. Then it works.
So your code would look like this:
var
item : TmenuItem;
icon : TIcon;
begin
item := TMenuItem.Create(PopupMenu1);
item.Caption := 'coucou';
item.Bitmap.SetSize(16,16); // <--- set size of bitmap
icon := TIcon.Create;
icon.LoadFromFile('d:\SmallIcon.ico');
icon.Height := 16;
icon.Width := 16;
item.Bitmap.Canvas.Draw(0,0,icon);
PopupMenu1.Items.Add(item);
end;
Although I agree with David. Better use a TImageList.
a) You can't set TIcon dimensions once they have an image in them -- if your loaded icon isn't already 16x16 you'll get an exception, b) You don't indicate if your parent menu uses a TImageList (if so, you can't set individual images), c) by default, I don't think tmenuitem bitmaps have a particular size/color depth or anything else. You need to properly create a TBitmap to assign to the TMenuItem.Bitmap (assuming your parent menu doesn't use TImageLists).

Is it possible to make a TListView search by the caption of a different column in Delphi?

When you set the Caption of a TListItem it seems to always set the Text for the first column in the row. When you start typing in the ListView it will search & select the closest match based on the caption of the first column.
I have a situation where I need the caption of the first row to be empty, but still need the search functionality to work as normal (in this case the data I would be searching for may be in the 2nd/3rd column).
Is this possible without using any 3rd party controls?
Depending on why you want the caption/ first column to be blank, you could move the text you want to search for into the caption, and then have a blank sub-item. Then swap the column order in code like so
//Move the 1st sub-item left one column
ListView1.Columns[1].Index := 0;
This would look almost the same, with the exception that if you don't have RowSelect set to true the highlighted caption will be in the wrong column. This would allow you to search as required and use the FindCaption method in code.
procedure TForm1.FormCreate(Sender: TObject);
var
I: Integer;
li : TListItem;
begin
//Add data to the list view for demo
for I := 0 to 10 do
begin
li := ListView1.Items.Add;
li.Caption := intToStr(Random(10000));
li.SubItems.Add('');
li.SubItems.Add('Col2');
//addimages so you can see which column is which
li.SubItemImages[0] := 0;
li.ImageIndex := -1;
end;
//move column 2 left one, this is the important bit
ListView1.Columns[1].Index := 0;
end;
alt text http://img265.imageshack.us/img265/3489/captureqg.jpg
If it's bound to a dataset, then you can do your own search and then move the dataset cursor to the row that you want. Just off the top of my head, because I just did one of those.
Update: Use the OnCompare handler, and do your own comparision on whatever criteria you want. i.e. you get to decide whether item1 < item2 or not.
Here's a nice writeup:
http://www.latiumsoftware.com/en/delphi/00011.php

Resources