What windows messages are used by Delphi to notice changes in a combo box? - delphi

I have a Delphi application A, which I need to control from a .NET application B.
Among other things, I need to automate this process:
User selects item X from a combo box.
Application A notices the change and reacts by displaying a certain panel.
This works fine, if I do it manually.
But when the application B selects a combo box value, no panel is displayed.
This is the problem.
Potential cause of it:
When I select a combo box item, a certain windows message is fired. Some Delphi routine reacts to this message.
When I select a combo box item programmatically, the only message I send is CB_SETCURSEL and the Delphi app seems to ignore it.
Hence I assume that I can fix the problem, if I
get to know what windows messages are used as a basis for notification about combo box value changes (e. g. OnChange) and
send that windows message from C# application.
Therefore my question: What are the windows messages, on whose occurrence OnChange (and other events that notify the Delphi application on changed combo box selection) are fired?
Update 1: Started to implement the solution proposed by David Heffernan
private const int CB_SETCURSEL = 0x14E;
private const int WM_COMMAND = 0x0111;
private const int CBN_SELCHANGE = 0x001;
private const int CN_COMMAND = 0xBD11;
private int MakeWParam(int l, int h)
{
return (l & 0xFFFF) | (h << 16);
}
...
IntPtr comboBoxHandle = new IntPtr(comboBox.Current.NativeWindowHandle);
SendMessage(comboBoxHandle, CB_SETCURSEL, (Int32)myIndexInComboBox, 0);
SendMessage(comboBoxHandle, CN_COMMAND, MakeWParam(0, CBN_SELCHANGE), 0);
At the moment, it doesn't work.
Update 2:
I noticed a very strange thing.
If I invoke CB_SETCURSEL only, the desired item is selected in the combo box.
If I invoke CB_SETCURSEL and then (after 5 seconds) CN_COMMAND, then nothing is selected in the combo box.
This means - CB_SECURSEL selects the item and CN_COMMAND undoes it.
Update 3: Styles of the combo box according to Spy++:
WS_CHILDWINDOW
WS_VISIBLE
WS_CLIPSIBLINGS
00000243
Extended styles:
WS_EX_LEFT
WS_EX_LTRREADING
WS_EX_RIGHTSCROLLBAR
Class styles:
CS_VREDRAW
CS_HREDRAW
CS_DBLCLKS
Update 4: When I select the combo box item manually, I see following messages in the Spy++ output:
<00177> 0195085E S message:0xBD33 [Custom:WM_APP+15667] wParam:6801164A lParam:0195085E
<00178> 0195085E R message:0xBD33 [Custom:WM_APP+15667] lResult:4610165A
Unfortunately, I couldn't find documentation for this message.
Update 5: I noticed that the reaction to combo box selection change does occur, but only after a relatively long time (30 seconds to 1 minute). When I do the same thing manually, the reaction occurs instantaneously.
Potential cause of this behaviour: The thread of the .NET application makes the thread of the Delphi application wait for it. Note that the UI interaction code in the .NET app is executed in a separate thread (not the UI thread).

You should follow the CB_SETCURSEL message by sending the combo box a WM_COMMAND message with NotifyCode equal to CBN_SELCHANGE. It's the CBN_SELCHANGE that triggers the OnChange event.
In Delphi the code would look like this:
SendMessage(ComboHandle, CB_SETCURSEL, NewSelectionIndex, 0);
SendMessage(ComboHandle, WM_COMMAND, MakeWParam(0, CBN_SELCHANGE), ComboHandle);
Or you could use the CN_COMMAND message instead which would perhaps be a little more direct:
SendMessage(ComboHandle, CB_SETCURSEL, NewSelectionIndex, 0);
SendMessage(ComboHandle, CN_COMMAND, MakeWParam(0, CBN_SELCHANGE), 0);
You'll want to translate that into whichever .net language you are using, but I'm sure that's easy for you.

When the user selects a ComboBox item by hand, the control receives a CBN_SELCHANGE notification, which then triggers the TComboBox.OnChange event. When you select a ComboBox item programmably, no CBN_SELCHANGE notification is sent. This is documented behavior:
http://msdn.microsoft.com/en-us/library/windows/desktop/bb775821.aspx
The CBN_SELCHANGE notification code is not sent when the current selection is set using the CB_SETCURSEL message.

Related

setColumnOrder() on Grid in Vaadin Flow seems to cause the getSortOrder() list to increment infinitely in the event of the sort listener

I believe I have found a bug in the Vaadin Grid that can be replicated with the code below.
I would like to save the grid's sorting order whenever it's changed and then restore it when the grid is loaded. The main issue is that the GridSortOrder list just keeps incrementing in size for the event in the sort listener rather than replacing the sortorder list. I believe this is a bug within Vaadin 14-20 because it only happens if I call setColumnOrder() in the reset method below (removing that call it works as expected). In other words calling setColumnOrder() seems to add the list of GridSortOrder to the event in the sort listener when it should be replacing it. This is actually also true if you do grid.getSortOrder() in the listener, meaning it's not just the event but also the grid that is compromised.
This can be replicated consistently with the code below. You will first need to change the sort order of Description column by clicking on the column header. Then just click on Reset button 5-10 times. Once this is done click on the Description column header to change the sort order one more time. The System.out for the listener will show a size of a list of 20 GridSortOrder instead of the expected 2 GridSortOrder. There will be replications of columns over and over.
Grid<Car> grid = new Grid<>();
grid.addColumn(Car::getName)
.setSortable(true)
.setComparator(Car::getName)
.setKey("Name")
.setHeader("Name");
grid.addColumn(Car::getPrice)
.setSortable(true)
.setComparator(Car::getPrice)
.setKey("Price")
.setHeader("Price");
grid.addColumn(Car::getDescription)
.setSortable(true)
.setComparator(Car::getDescription)
.setKey("Description")
.setHeader("Description");
grid.setItems(listOfCars);
grid.setColumnReorderingAllowed(true);
grid.setMultiSort(true);
grid.addSortListener(event -> {
System.out.println("Event: " + event.getSortOrder().size() + " : " + event.isFromClient());
System.out.println("Grid: " + grid.getSortOrder().size() + " : " + event.isFromClient());
});
defaultSort(grid);
Button resetButton = new Button("Reset", click -> {
System.out.println("Reset: " + grid.getSortOrder().size());
defaultSort(grid);
grid.setColumnOrder(
grid.getColumnByKey("Name"),
grid.getColumnByKey("Price"),
grid.getColumnByKey("Description"));
});
add(grid, resetButton);
private void defaultSort(Grid grid) {
grid.sort(List.of(
new GridSortOrder<>(grid.getColumnByKey("Name"), SortDirection.ASCENDING),
new GridSortOrder<>(grid.getColumnByKey("Price"), SortDirection.ASCENDING)
));
}
If I remove grid.setColumnOrder() from the above then the List<GridSortOrder> works as expected and is two in the event listener. What's especially interesting is that event.getSortOrder().size() will increase by 2 every time the resetButton is clicked (but you'll only see it in the sort listener). It doesn't matter if the setColumnOrder() is before or after the call to defaultSort().
With that in mind if you debug grid.setColumnOrder() it seems to call updateClientSideSorterIndicators(sortOrder) which is what I believe is the cause and is where it's being added to the GridSortOrder list. That being said I'm not sure how to proceed from here so that I can reset the grid's setting when clicking on the Reset button.
FYI: This is an issue for me because I'm trying to save the state of the Grid to the database so that when the user reloads the screen (a very complex one) all their grid settings including the sorting order, the column order, and so on are preserved. However with this bug it's impossible to save the sorting order.
PS: This seems to be true in both Vaadin 14 and Vaadin 20.

DCEF4 virtual keyboard is not showing up

I am using CEF4Delphi from https://github.com/salvadordf/CEF4Delphi
I am editing PopupBrowser2 example. There is Chromium1 component. I have added event, that is supposed to notify when keyboard is requested:
procedure TForm1.Chromium1VirtualKeyboardRequested(Sender: TObject; const browser: ICefBrowser;
input_mode: TCefTextInpuMode);
begin
caption := 'kbd';
end;
Sadly, when example loads google page, I click on search edit box and event is not called. How can I make event to be called?
TChromium.OnVirtualKeyboardRequested is one of the events of ICefRenderHandler and that handler is only used by browsers in off-screen mode (OSR mode).
The PopupBrowser2 demo uses browsers in normal mode (a.k.a. "windowed mode") which means that none of the ICefRenderHandler events will be triggered.
With PopupBrowser2 you will have to use a different event called GlobalCEFApp.OnFocusedNodeChanged
Check the node.name with the HTML tag names that should show the virtual keyboard like "input" or "textarea" inside GlobalCEFApp.OnFocusedNodeChanged.
That event is executed in the render process and you will have to send a process message to the main browser process to show the keyboard.
The DOMVisitor demo shows you how to send that process message with the some parameters if you need them :
https://github.com/salvadordf/CEF4Delphi/blob/132edb2e8895d998d3e3810982c95b9f845d78f8/demos/Delphi_VCL/DOMVisitor/uDOMVisitor.pas#L305
The browser process will receive that message in the TChromium.OnProcessMessageReceived event as you can see here :
https://github.com/salvadordf/CEF4Delphi/blob/132edb2e8895d998d3e3810982c95b9f845d78f8/demos/Delphi_VCL/DOMVisitor/uDOMVisitor.pas#L432
Notice that TCefProcessMessageRef is created by the "New" function with a name. When you implement TChromium.OnProcessMessageReceived in your application you'll have to compare that message.name has the same name value before handling it.

TabControl - How to force event OnGetImageIndex

I have a Delphi VCL form application (for Windows) with a TabControl component, where tabs are created at run time. Images property is linked to a ImageList, and I am using the OnGetImageIndex event to set the image index for each tab, based on a status information. At certain point, when status change, I need to update this images (indexes). My first idea is to call:
TabControl1.Invalidate;
But it doesn't work. I also tried another approachs without success:
TabControl1.Repaint;
RedrawWindow(TabControl1.Handle, nil, 0, RDW_ERASE or
RDW_INVALIDATE or RDW_ALLCHILDREN);
Please, how can I force OnGetImageIndex for each tab? Thanks!
You can call the protected UpdateTabImages method which sends a TCM_SETITEM for each tab.
type
TAccessTabControl = class(TTabControl);
...
TAccessTabControl(TabControl1).UpdateTabImages;

EmbeddedWB TAB key

I use EmbeddedWB in edit mode and need to insert tab (4 * ) when user presses TAB key. I've trapped OnKeyDown event and did the following:
if (Key = VK_TAB) then
begin
EditDesignerMsg.InsertHTML(' ');
EditDesignerMsg.EmbeddedWB.SetFocusToDoc;
end;
The problem is that this moves focus from the control to another control as usual with TAB in Windows. I want to keep the focus within the web browser control and only move away to previous control if the user presses Shift + TAB.
How can this be done?
Thanks to TLama, I've managed to do this by intercepting CM_DIALOGKEY message and applying the message handler which inserts specified HTML code at that point and then eats the message by setting AMessage.Result := 1;. More details how to implement this message handler can be found here:
Intercept TAB key and suppress it

Powerpoint Protected Mode with Disabled Ribbon - Automated in Delphi

I am looking for a way to protect powerpoint presentations via Automation using Delphi.
In Word, I can issue this command:
If (WordDocument.ProtectionType = wdNoProtection)
Then WordDocument.Protect (3, VarTrue, VarProtectPass);
In Excel, I can issue this command:
{ If the file was NOT protected, then protect it }
For SheetIndex := 1 To ExcelWorkbook.Sheets.Count Do Begin
VarSheet := SheetIndex;
{ Connect to the work sheet }
ExcelWorksheet.ConnectTo (ExcelWorkbook.Worksheets.Item [VarSheet] As _Worksheet);
ExcelWorksheet.Protect ()...
The problem is that I cannot find a way to do this using the OfficeXP.pas components. We are running Office 2010 on various systems. Any ideas?
What I need is to open powerpoint up with the presentation,
1) For MenuItem1, Disallow any editting by the user. Also, disallow them to re-save it somewhere else.
2) For MenuItem2, Allow the user to edit the presentation.
One approach to this would be via an add-in that traps events:
Trap the PresentationBeforeSave event and if need be, cancel the save.
Trap the WindowSelectionChange event, test to see what's selected (it's passed by the event) and in most cases, DE-select the selection. If the user can't select something, they can't edit/change it.
The event handling routines can be enabled/disabled based on state variables; you might trap the PresentationOpen event, test to see if the new presentation is one of your "protected" ones and if so, set boolIsProtected = True; your other event handlers could test this variable and stop processing the event if False.

Resources