What is an efficient way to change buttons (bands) in the CoolBar (the red rectangle) while switching among items in the TreeView (the purple rectangle). I want to use one set of buttons for every item in the list view.
Thanks for help and advices!
I'd create the CoolBands I'd need and assign each to the Data pointer of the TTreeNode for which it is to be used. Then in the TreeView's OnChanging handler, I'd "remember" the TreeNode that is currently selected and switch visibility on the CoolBands in the OnChange handler:
procedure TProbeerForm.TreeView1Changing(Sender: TObject; Node: TTreeNode;
var AllowChange: Boolean);
begin
FOldNode := TreeView1.Selected;
end;
procedure TProbeerForm.TreeView1Change(Sender: TObject; Node: TTreeNode);
begin
TCoolBand(FOldNode.Data).Visible := False;
TCoolBand(Node.Data).Visible := True;
end;
Related
Using Delphi 10.3:
In an owner-drawn TComboBox with Style=csOwnerDrawFixed, I want the owner drawn items in the DropDown list to be different from the static part of the combo. To discriminate between the two cases, I check for odComboBoxEdit in the State parameter, as described here:
How to draw the static part of the combobox
procedure TStylePanel.TargetArrowComboDrawItem(Control: TWinControl; Index: Integer; Rect: TRect; State: TOwnerDrawState);
begin
if (odComboBoxEdit in State) then
begin
// Paint static control
end
else
begin
// Paint item in dropped down list
end;
end;
This works well as long as there's no custom VCL style active. However, with a custom style, this no longer works reliably. Checking the source in Vcl.StdCtrls.pas for TComboBoxStyleHook, it seems to me that the cause is in this combination:
procedure TComboBoxStyleHook.WMPaint(...)
procedure TComboBoxStyleHook.DrawItem(...)
When there's no edit handle (which is the case for csOwnerDrawFixed), DrawItem() assembles a TDrawItemStruct that will never contain ODS_COMBOBOXEDIT, as a result the CN_DRAWITEM handler will never have odComboBoxEdit set.
I could override TComboBoxStyleHook, but I'd need a way to detect if the item is the static item or an item in the list.
As a workaround, I check for Combo.DroppedDown, but that's not the same: even when dropped down, I want the static part to be painted differently than the items in the list.
So the question is, how can I detect (in the custom draw handler or in the style hook) that the custom drawn item is the static area rather than an item in the list?
I was able to get it working by adding a stylehook for TComboBox that unconditionally includes ODS_COMBOBOXEDIT. The assumption is that TComboBoxStyleHook.DrawItem is only called by TComboBoxStyleHook.WMPaint when it needs to custom draw the static item, the drop down list is not handled there. There seem to be no unwanted side effects.
type
TComboBoxStyleHookFix = class(TComboBoxStyleHook)
strict protected
procedure DrawItem(Canvas: TCanvas; Index: Integer; const R: TRect; Selected: Boolean); override;
end;
procedure TComboBoxStyleHookFix.DrawItem(Canvas: TCanvas; Index: Integer; const R: TRect; Selected: Boolean);
var
DIS: TDrawItemStruct;
begin
FillChar(DIS, SizeOf(DIS), 0);
DIS.CtlType := ODT_COMBOBOX;
DIS.CtlID := GetDlgCtrlID(Handle);
DIS.itemAction := ODA_DRAWENTIRE;
DIS.hDC := Canvas.Handle;
DIS.hwndItem := Handle;
DIS.rcItem := R;
DIS.itemID := Index;
DIS.itemData := SendMessage(ListHandle, LB_GETITEMDATA, 0, 0);
if (Control is TComboBox) and (TComboBox(Control).Style = csOwnerDrawFixed) then
DIS.itemState := ODS_COMBOBOXEDIT;
if Selected then
DIS.itemState := DIS.itemState or ODS_FOCUS or ODS_SELECTED;
SendMessage(Handle, WM_DRAWITEM, Handle, LPARAM(#DIS));
end;
procedure InitComboStyleHookFix();
begin
TCustomStyleEngine.RegisterStyleHook(TComboBox, TComboBoxStyleHookFix);
end;
I want to show some rows from tcxgrid in different color (Depend upon column value ).
I did changes for it but its not getting reflected on grid after running my project.
procedure TfrmMessaging.cxGrid1DBTableView1CustomDrawCell(..);
Var
i : Integer;
begin
For i := 0 To cxGrid1DBTableView1.ViewData.RowCount - 1 Do
Begin
If cxGrid1DBTableView1.ViewData.Rows[i].Values[4] = '1' Then
Begin
cxGrid1.Canvas.Brush.Color := clRed;
End;
End;
end;
In above code I have used cxGrid1DBTableView1CustomDrawCell event of tcxgrid.
Thanks in advance.
If you are using a data-aware view (as it seems) you need to use the DataController instead of the ViewData to get to the records.
As stated in DevExpress help for TcxGridDBTableView (bold format is mine):
The TcxGridDBTableView object represents the data-aware version of the grid Table View. It inherits all functionality from its ancestor, except for data binding settings. The DataController.DataSource property of the TcxGridDBTableView provides the connection between the View and a TDataSet or its descendant.
Besides that, the OnCustomDrawCell event fires for every cell, so you do not need to iterate the rows.
Following code should help you:
procedure TfrmMessaging.cxGrid1DBTableView1CustomDrawCell(
Sender: TcxCustomGridTableView; ACanvas: TcxCanvas;
AViewInfo: TcxGridTableDataCellViewInfo; var ADone: Boolean);
begin
if Sender.DataController.GetValue(AViewInfo.GridRecord.RecordIndex, 4) = '1' then
ACanvas.Brush.Color := clRed;
end;
Normally the easiest path for stuff like that are cxStyles. Drop a style repository on the form, add one or more styles to it and assign them in the object inspector or in an event handler (OnGetContentStyle etc.).
One advantage over custom drawing is that styles are considered for various calculations while owner drawn cells aren't handled specially and sometimes aren't autosized correctly etc.
How I change the color of the grid
procedure TfrmNewOffer.GrdOffDetailViewRemarkCustomDrawCell(
Sender: TcxCustomGridTableView; ACanvas: TcxCanvas;
AViewInfo: TcxGridTableDataCellViewInfo; var ADone: Boolean);
var
backgroundColorCode: Variant;
textColorCode: Variant;
begin
inherited;
if assigned(AViewInfo) and assigned(AViewInfo.GridRecord) then
begin
backgroundColorCode := AViewInfo.GridRecord.Values[GrdOffDetailViewBackColorCode.Index];
textColorCode := AViewInfo.GridRecord.Values[GrdOffDetailViewTextColorCode.Index];
if not VarIsNull(backgroundColorCode) then
begin
ACanvas.Brush.Color := backgroundColorCode;
end;
if not VarIsNull(textColorCode) then
begin
ACanvas.Font.Color := textColorCode;
end;
end;
end;
IN TMS string grid I used to use this to paste the caption of the popup menu into the grid's cell :
var
s:string;
begin
s:=(Sender as TmenuItem).Caption;
s:=stringReplace(s,'&','',[rfReplaceAll]);
with AdvStringGrid1 do
Cells[Col,Row]:=s;
I never used this before in a cxGrid so I am totally new to this. I have linked cxGridpopUpMenu1 to my grid,added a classic PopUpMenu so it gets used by the cxGridpopUpMenu1,added some items in the popup menu and thats it. popup menu fires on right click in the grid ok, but how do you paste the value of the menuitem into the cell??
+ Is there a way to assign popopmenu to a particular column ?
I'd do it like this:
procedure TForm1.MenuItem1Click(Sender: TObject);
var
s: string;
begin
Assert(Sender is TMenuItem);
s := StripHotKey(TMenuItem(Sender).Caption);
cxGrid1TableView1.DataController.Edit;
cxGrid1TableView1.Controller.FocusedColumn.EditValue := s;
end;
This can be done combining two event handlers:
The OnPopUp handler of your TcxGridPopupMenu.
An OnClick handler for all your popup menu items.
The idea is to use the OnPopup to store a reference to the item (column) and record clicked, while the OnClick would apply the value to the cell.
Code is as following:
//in private section of your form
fItem: TcxCustomGridTableItem;
fRec: TcxCustomGridRecord;
procedure TForm1.cxGridPopupMenu1Popup(ASenderMenu: TComponent;
AHitTest: TcxCustomGridHitTest; X, Y: Integer; var AllowPopup: Boolean);
begin
if AHitTest is TcxGridRecordCellHitTest then
begin
fItem := TcxGridRecordCellHitTest(AHitTest).Item;
fRec := TcxGridRecordCellHitTest(AHitTest).GridRecord;
end;
end;
procedure TForm1.MenuItem1Click(Sender: TObject);
var
s : string;
begin
s := (sender as tmenuItem).Caption;
gridView.DataController.Values[frec.Index, fitem.Index] := StripHotKey(s);
end;
As #DavidHeffernan suggested, notice the use of StripHotKey that removes the accelerator character mark from the menu caption.
Its a dumb question maybe but I have a popup menu which is linked with many TTreeViewItems. The problem is that the TTreeView.Selected property never gets set on right click. The GetMousePos is prone to returning the next or the previous TTreeViewItem's coordinates. How can I get the Item which actually triggered the popup?
You can use OnPopup event of TPopupMenu like this:
procedure TForm7.PopupMenu1Popup(Sender: TObject);
var
aNode: TTreeNode;
p: TPoint;
begin
p := TreeView1.ScreenToClient(PopupMenu1.PopupPoint);
aNode := TreeView1.GetNodeAt(p.X, p.Y);
if aNode <> Nil then
caption := aNode.Text;
end;
seems TPopupMenu.PopupPoint returns (0,0) point when you click item in PopupMenu (In Delphi XE2, docwiki says that it is used internally to set position of menu, and seems it is set to 0 when menu dissapears).
so in this situation, seems to me, the easiest way is to handle TreeView.OnMouseDown where you can save reference to selected item, and then use it in Popup item event handler;
so, in the exmaple code below i've added FClickedItem : TTreeViewItem into the form class;
procedure TSampleForm.SampleTreeViewMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single);
begin
if button = TMouseButton.mbRight then
FClickedItem := SampleTreeView.ItemByPoint(x,y)
else FClickedItem := nil;
end;
procedure TSampleForm.TestMenuItemClick(Sender: TObject);
begin
if Assigned(FClickedItem) then
ShowMessage(Format('Item `%s (%s)` was selected!', [FClickedItem.Text, FClickedItem.Name]))
else ShowMessage('there is nothing to show');
end;
UPDATE: i've just browsed the source code, private variable TPopupMenu.FPopupPoint (readonly property) is not used in implementation code, thats why it is always = (0,0)
Is there a fast way to create 5 custom hints for 5 SubItems of Item of Tree View?
I have TreeView, 1 Item and 5 SubItems. I need a special hint for each SubItem (for first one - "F1", second one -"F2" and so on).
I can not apply this to my purpose: http://delphi.about.com/od/vclusing/a/treenode_hint.htm?
It sounds like you just want the OnHint event:
procedure TMyForm.TreeView1Hint(Sender: TObject; const Node: TTreeNode; var Hint: string);
begin
Hint := Node.Text;
end;
Sometimes this method can be a bit crude and offer up a Node that you aren't obviously hovering over. If you want more control you can use GetNodeAt and GetHitTestInfoAt:
procedure TMyForm.TreeView1Hint(Sender: TObject; const Node: TTreeNode; var Hint: string);
var
P: TPoint;
MyNode: TTreeNode;
HitTestInfo: THitTests;
begin
P := TreeView1.ScreenToClient(Mouse.CursorPos);
MyNode := TreeView1.GetNodeAt(P.X, P.Y);
HitTestInfo := TreeView1.GetHitTestInfoAt(P.X, P.Y) ;
if htOnItem in HitTestInfo then begin
Hint := MyNode.Text;
end else begin
Hint := '';
end;
end;
The definition of THitTests is as follows:
type
THitTest = (htAbove, htBelow, htNowhere, htOnItem, htOnButton, htOnIcon,
htOnIndent, htOnLabel, htOnRight, htOnStateIcon, htToLeft, htToRight);
THitTests = set of THitTest;
As you can see this gives you a lot of fine grained control over when and what you show as a hint.
I would set the hint of the component in response to OnMouseMove (or that other event that gives you mouse coordinates, from which you can get the item the mouse is over - I might have mistaken the name and at the moment I have no Delphi with me).