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;
Related
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.
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
For some reason, the AlwaysOnTop attribute for a form's design isn't properly working.
Here's the context: we are trying to have a form that stays on top of every other one when opened. Simple no? Also, we don't want to set the WindowType to Popup according to my superior for some other reason (if you have any idea why, please let me know).
So my question is, is there any parameter/security feature somewhere that somehow restricts the forms to be on top at any time?
Even WinApi's setForegroundWindow returns false with the form's hWnd. Any ideas?
Oh, also we're running on Dynamics AX 4.0.
If you want your form to have modal behavior, then call the wait method from the form itself!
public void run()
{
super();
this.wait(true);
}
The true parameter triggers the modal mode. This works on all versions of AX.
The wait may be called from the caller instead, but that is less attractive as most forms are called through menu items.
formRun.init();
formRun.run();
formRun.wait(true);
I managed this case long time ago with the lostFocus event and the setFocus method. I didn't find a proper way to make a form stay on top (I think AX prevents this specifically to avoid locking a terminal) but it worked fine this way: When the form lost focus, set the focus on the form.
I don't have the code as it was on an old project. It was for a PDA project but I think you can't ever avoid user on changing form with Alt+Tab.
This is an interesting point, please keep us updated.
EDIT:
Someome in twitter got an cute solution for modal forms. I'm pretty sure it will make the trick for you. In the init method of the form:
public void run()
{
super();
element.wait(true);
// Execution will resume at this point, only after
// the user has closed the form.
}
Source: http://gotdax.blogspot.com.es/2013/08/modal-forms-in-dynamics-ax.html
What I did to solve this was by making the form modal through WinAPI. The code below is a copy from a saved text so it might need some polishing. (Also keep in mind that it might not be working as of AX2009.)
void setFormModal(int _thisHWND, boolean _bModal)
{
DLL _winApiDLL;
DLLFunction _EnabledWindow;
DLLFunction _getTop;
DLLFunction _getNext;
DLLFunction _getParent;
void local_enableWHND(int _lHWND)
{
int lnextWnd;
lnextWnd = _getTop.call(_getParent.call(_lHWND));
while (lnextWnd)
{
if (lnextWnd != _lHWND)
_enabledWindow.call(lnextWnd, (!_bModal));
lnextWnd = _getNext.call(lnextWnd, 2);
}
}
;
_winApiDLL = new DLL('user32');
_getNext = new DLLFunction(_winApiDLL, "GetWindow");
_EnabledWindow = new DLLFunction(_winApiDLL, "EnableWindow");
_getTop = new DLLFunction(_winApiDLL, "GetTopWindow");
_getParent = new DLLFunction(_winApiDLL, "GetParent");
_getParent.returns(ExtTypes:: DWORD);
_getParent.arg(ExtTypes:: DWORD);
_EnabledWindow.returns(ExtTypes:: DWORD);
_EnabledWindow.arg(ExtTypes:: DWORD, ExtTypes:: DWORD);
_getTop.returns(ExtTypes:: DWORD);
_getTop.arg(ExtTypes:: DWORD);
_getNext.returns(ExtTypes:: DWORD);
_getNext.arg(ExtTypes:: DWORD, ExtTypes:: DWORD);
local_enableWHND(_thisHWND);
local_enableWHND(_getParent.call(_thisHWND));
}
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.
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.