ActiveX in CPropertyPage can't be used by keyboard - activex

in my sample project (c++ vs10) i placed an ActiveX control in an Dialog (used as PropertyPage). The Dialog is connected to a CPropertyPage derived class called CTestPage. If i open a PropertySheet with DoModal, where the CTestPage is added, the ActiveX control appears, but i cant handle it by keyboard. If i click the ActiveX control (e.g. Calendar Control 8.0) the control receives the click and works fine. But no keyboard stroke is handled by the control. I tested ten different ActiveX controls on my developer machine -> always the same behavior.
If i place the ActiveX control in a normal Dialog everything works fine.
Any hints what i do wrong?
Thanks a lot
heribert
PS. Yes, AfxEnableControlContainer is called in InitInstance.

A PropertyPage sends WM_GETDLGCODE to every control to check out, which keyboard input can be handled by the control.
All the ActiveX controls i have tested results with 0 as answer on the request! Cool, so no one will receive any keyboard input.
The ActiveX Control i will use is written by me in c#. The ActiveX layer is needed to offer the complex c# control to c++.
But the underlying complex c# control returns with a 0 on a WM_GETDLGCODE message, too.
So i subclassed the c# control like the following code and now it works fine!!
internal class SubclassedComplexControl : ComplexControl
{
[SecurityPermission(SecurityAction.LinkDemand,
Flags = SecurityPermissionFlag.UnmanagedCode)]
protected override void WndProc(ref System.Windows.Forms.Message m)
{
const int WM_GETDLGCODE = 0x0087;
const int DLGC_WANTARROWS = 0x0001;
const int DLGC_WANTALLKEYS = 0x0004;
const int DLGC_WANTCHARS = 0x0080;
const int VK_ESCAPE = 0x1B;
const int VK_RETURN = 0x0D;
if (m.Msg == WM_GETDLGCODE)
{
if (m.WParam.ToInt32() == VK_RETURN || m.WParam.ToInt32() == VK_ESCAPE)
m.Result = (IntPtr) DLGC_WANTALLKEYS;
else
m.Result = (IntPtr)(DLGC_WANTARROWS | DLGC_WANTCHARS);
return;
}
base.WndProc(ref m);
}
}

Related

How to remove an application from ALT + TAB? [duplicate]

I've been a .NET developer for several years now and this is still one of those things I don't know how to do properly. It's easy to hide a window from the taskbar via a property in both Windows Forms and WPF, but as far as I can tell, this doesn't guarantee (or necessarily even affect) it being hidden from the Alt+↹Tab dialog. I've seen invisible windows show up in Alt+↹Tab, and I'm just wondering what is the best way to guarantee a window will never appear (visible or not) in the Alt+↹Tab dialog.
Update: Please see my posted solution below. I'm not allowed to mark my own answers as the solution, but so far it's the only one that works.
Update 2: There's now a proper solution by Franci Penov that looks pretty good, but haven't tried it out myself. Involves some Win32, but avoids the lame creation of off-screen windows.
Update:
According to #donovan, modern days WPF supports this natively, through setting
ShowInTaskbar="False" and Visibility="Hidden" in the XAML. (I haven't tested this yet, but nevertheless decided to bump the comment visibility)
Original answer:
There are two ways of hiding a window from the task switcher in Win32 API:
to add the WS_EX_TOOLWINDOW extended window style - that's the right approach.
to make it a child window of another window.
Unfortunately, WPF does not support as flexible control over the window style as Win32, thus a window with WindowStyle=ToolWindow ends up with the default WS_CAPTION and WS_SYSMENU styles, which causes it to have a caption and a close button. On the other hand, you can remove these two styles by setting WindowStyle=None, however that will not set the WS_EX_TOOLWINDOW extended style and the window will not be hidden from the task switcher.
To have a WPF window with WindowStyle=None that is also hidden from the task switcher, one can either of two ways:
go with the sample code above and make the window a child window of a small hidden tool window
modify the window style to also include the WS_EX_TOOLWINDOW extended style.
I personally prefer the second approach. Then again, I do some advanced stuff like extending the glass in the client area and enabling WPF drawing in the caption anyway, so a little bit of interop is not a big problem.
Here's the sample code for the Win32 interop solution approach. First, the XAML part:
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300"
ShowInTaskbar="False" WindowStyle="None"
Loaded="Window_Loaded" >
Nothing too fancy here, we just declare a window with WindowStyle=None and ShowInTaskbar=False. We also add a handler to the Loaded event where we will modify the extended window style. We can't do that work in the constructor, as there's no window handle at that point yet. The event handler itself is very simple:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
WindowInteropHelper wndHelper = new WindowInteropHelper(this);
int exStyle = (int)GetWindowLong(wndHelper.Handle, (int)GetWindowLongFields.GWL_EXSTYLE);
exStyle |= (int)ExtendedWindowStyles.WS_EX_TOOLWINDOW;
SetWindowLong(wndHelper.Handle, (int)GetWindowLongFields.GWL_EXSTYLE, (IntPtr)exStyle);
}
And the Win32 interop declarations. I've removed all unnecessary styles from the enums, just to keep the sample code here small. Also, unfortunately the SetWindowLongPtr entry point is not found in user32.dll on Windows XP, hence the trick with routing the call through the SetWindowLong instead.
#region Window styles
[Flags]
public enum ExtendedWindowStyles
{
// ...
WS_EX_TOOLWINDOW = 0x00000080,
// ...
}
public enum GetWindowLongFields
{
// ...
GWL_EXSTYLE = (-20),
// ...
}
[DllImport("user32.dll")]
public static extern IntPtr GetWindowLong(IntPtr hWnd, int nIndex);
public static IntPtr SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong)
{
int error = 0;
IntPtr result = IntPtr.Zero;
// Win32 SetWindowLong doesn't clear error on success
SetLastError(0);
if (IntPtr.Size == 4)
{
// use SetWindowLong
Int32 tempResult = IntSetWindowLong(hWnd, nIndex, IntPtrToInt32(dwNewLong));
error = Marshal.GetLastWin32Error();
result = new IntPtr(tempResult);
}
else
{
// use SetWindowLongPtr
result = IntSetWindowLongPtr(hWnd, nIndex, dwNewLong);
error = Marshal.GetLastWin32Error();
}
if ((result == IntPtr.Zero) && (error != 0))
{
throw new System.ComponentModel.Win32Exception(error);
}
return result;
}
[DllImport("user32.dll", EntryPoint = "SetWindowLongPtr", SetLastError = true)]
private static extern IntPtr IntSetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
[DllImport("user32.dll", EntryPoint = "SetWindowLong", SetLastError = true)]
private static extern Int32 IntSetWindowLong(IntPtr hWnd, int nIndex, Int32 dwNewLong);
private static int IntPtrToInt32(IntPtr intPtr)
{
return unchecked((int)intPtr.ToInt64());
}
[DllImport("kernel32.dll", EntryPoint = "SetLastError")]
public static extern void SetLastError(int dwErrorCode);
#endregion
Inside your form class, add this:
protected override CreateParams CreateParams
{
get
{
var Params = base.CreateParams;
Params.ExStyle |= WS_EX_TOOLWINDOW;
return Params;
}
}
It's as easy as that; works a charm!
I've found a solution, but it's not pretty. So far this is the only thing I've tried that actually works:
Window w = new Window(); // Create helper window
w.Top = -100; // Location of new window is outside of visible part of screen
w.Left = -100;
w.Width = 1; // size of window is enough small to avoid its appearance at the beginning
w.Height = 1;
w.WindowStyle = WindowStyle.ToolWindow; // Set window style as ToolWindow to avoid its icon in AltTab
w.Show(); // We need to show window before set is as owner to our main window
this.Owner = w; // Okey, this will result to disappear icon for main window.
w.Hide(); // Hide helper window just in case
Found it here.
A more general, reusable solution would be nice. I suppose you could create a single window 'w' and reuse it for all windows in your app that need to be hidden from the Alt+↹Tab.
Update: Ok so what I did was move the above code, minus the this.Owner = w bit (and moving w.Hide() immediately after w.Show(), which works fine) into my application's constructor, creating a public static Window called OwnerWindow. Whenever I want a window to exhibit this behavior, I simply set this.Owner = App.OwnerWindow. Works great, and only involves creating one extra (and invisible) window. You can even set this.Owner = null if you want the window to reappear in the Alt+↹Tab dialog.
Thanks to Ivan Onuchin over on MSDN forums for the solution.
Update 2: You should also set ShowInTaskBar=false on w to prevent it from flashing briefly in the taskbar when shown.
Here's what does the trick, regardless of the style of the window your are trying to hide from Alt+↹Tab.
Place the following into the constructor of your form:
// Keep this program out of the Alt-Tab menu
ShowInTaskbar = false;
Form form1 = new Form ( );
form1.FormBorderStyle = FormBorderStyle.FixedToolWindow;
form1.ShowInTaskbar = false;
Owner = form1;
Essentially, you make your form a child of an invisible window which has the correct style and ShowInTaskbar setting to keep out of the Alt-Tab list. You must also set your own form's ShowInTaskbar property to false. Best of all, it simply doesn't matter what style your main form has, and all tweaking to accomplish the hiding is just a few lines in the constructor code.
Why so complex?
Try this:
me.FormBorderStyle = FormBorderStyle.SizableToolWindow
me.ShowInTaskbar = false
Idea taken from here:http://www.csharp411.com/hide-form-from-alttab/
see it:(from http://bytes.com/topic/c-sharp/answers/442047-hide-alt-tab-list#post1683880)
[DllImport("user32.dll")]
public static extern int SetWindowLong( IntPtr window, int index, int
value);
[DllImport("user32.dll")]
public static extern int GetWindowLong( IntPtr window, int index);
const int GWL_EXSTYLE = -20;
const int WS_EX_TOOLWINDOW = 0x00000080;
const int WS_EX_APPWINDOW = 0x00040000;
private System.Windows.Forms.NotifyIcon notifyIcon1;
// I use two icons depending of the status of the app
normalIcon = new Icon(this.GetType(),"Normal.ico");
alertIcon = new Icon(this.GetType(),"Alert.ico");
notifyIcon1.Icon = normalIcon;
this.WindowState = System.Windows.Forms.FormWindowState.Minimized;
this.Visible = false;
this.ShowInTaskbar = false;
iconTimer.Start();
//Make it gone frmo the ALT+TAB
int windowStyle = GetWindowLong(Handle, GWL_EXSTYLE);
SetWindowLong(Handle, GWL_EXSTYLE, windowStyle | WS_EX_TOOLWINDOW);
Why trying so much codes?
Just set the FormBorderStyle propety to FixedToolWindow.
Hope it helps.
I tried setting the main form's visibility to false whenever it is automatically changed to true:
private void Form1_VisibleChanged(object sender, EventArgs e)
{
if (this.Visible)
{
this.Visible = false;
}
}
It works perfectly :)
if you want the form to be borderless, then you need to add the following statements to the form’s constructor:
this.FormBorderStyle = FormBorderStyle.None;
this.ShowInTaskbar = false;
AND you must add the following method to your derived Form class:
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
// turn on WS_EX_TOOLWINDOW style bit
cp.ExStyle |= 0x80;
return cp;
}
}
more details
In XAML set ShowInTaskbar="False":
<Window x:Class="WpfApplication5.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
ShowInTaskbar="False"
Title="Window1" Height="300" Width="300">
<Grid>
</Grid>
</Window>
Edit: That still shows it in Alt+Tab I guess, just not in the taskbar.
I tried This. It works for me
private void Particular_txt_KeyPress(object sender, KeyPressEventArgs e)
{
Form1 frm = new Form1();
frm.Owner = this;
frm.Show();
}
Don't show a form. Use invisibility.
More here: http://code.msdn.microsoft.com/TheNotifyIconExample
Solution for those who want a WPF window to stay visible while hidden from the Alt+Tab switcher:
Hide a WPF window from Alt+Tab
Personally as far as I know this is not possible without hooking into windows in some fashion, I'm not even sure how that would be done or if it is possible.
Depending on your needs, developing your application context as a NotifyIcon (system tray) application will allow it to be running without showing in ALT + TAB. HOWEVER, if you open a form, that form will still follow the standard functionality.
I can dig up my blog article about creating an application that is ONLY a NotifyIcon by default if you want.
Form1 Properties:
FormBorderStyle: Sizable
WindowState: Minimized
ShowInTaskbar: False
private void Form1_Load(object sender, EventArgs e)
{
// Making the window invisible forces it to not show up in the ALT+TAB
this.Visible = false;
}>

TStringGrid doesn't recognize VK_DELETE as a keypress

I have a number of TStringGrids inside TTab's and have been using
if (Key==VK_RETURN) for years (its an old app) as the trigger to execute code pertaining the the cell entry.
I would now like the option to delete the Cell contents, by writing a empty string to it, so tried using
if (Key==VK_DELETE)
yet the Delete Button does not trigger the OnKeyPress event at all in my case.
I was hoping to capture the event via the following code pertaining to the specific active Stringrid1
void __fastcall TPagesDlgLoadEditorFixed::StringGrid1KeyPress(TObject
*Sender, char &Key)
{
int Grid = 1;
EntriesOnStringGrid(Key, Grid);
}
and with the KeyPress then passed on code that is common to all the StringGrids, as follows:
void TPagesDlgLoadEditorFixed::EntriesOnStringGrid(char &Key, int Grid)
{
if (Key==VK_RETURN)
{
//code works fine
}
if (Key==VK_DELETE)
{
//code has no effect
}
}
However whilst in debug mode, one can see that the StringGrid1KeyPress event is not triggered at all by the "Delete" button.
Any advise would be greatly appreciated.
It's generally better to use OnKeyUp and OnKeyDown when checking for virtual key codes instead of OnKeyPress.
So using that should fix your problem and then it will detect VK_DELETE when it's pressed or let go.

Use TMonthCalendar for months and years only

My point is I want to keep the calendar always on the months view, like this:
Expected TMonthCalendar view:
So that when I click in a month, instead of showing the days of the month, it stays in this screen and call the event.
Prior to Vista, the underlying Win32 MonthCal control that TMonthCalendar wraps has no concept of views at all, so you can't do what you are asking for in XP and earlier, unless you find a 3rd party calendar that supports what you want on those Windows versions.
However, in Vista and later, the underlying MonthCal control is view-aware (but TMonthCalendar itself is not). You can manually send a MCM_SETCURRENTVIEW message to the TMonthCalendar's HWND to set its initial view to MCMV_YEAR, and subclass its WindowProc property to intercept CN_NOTIFY messages (the VCL's wrapper for WM_NOTIFY) looking for the MCN_VIEWCHANGE notification when the user changes the active view. You can't lock the control to a specific view, but you can react to when the user changes the active view from the Year view to the Month view, and then you can reset the calendar back to the Year view if needed.
For example:
class TMyForm : public TForm
{
__published:
TMonthCalendar *MonthCalendar1;
...
private:
TWndMethod PrevMonthCalWndProc;
void __fastcall MonthCalWndProc(TMessage &Message);
...
public:
__fastcall TMyForm(TComponent *Owner)
...
};
#include "MyForm.h"
#include <Commctrl.h>
#ifndef MCM_SETCURRENTVIEW
#define MCMV_MONTH 0
#define MCMV_YEAR 1
#define MCM_SETCURRENTVIEW (MCM_FIRST + 32)
#define MCN_VIEWCHANGE (MCN_FIRST - 4) // -750
typedef struct tagNMVIEWCHANGE
{
NMHDR nmhdr;
DWORD dwOldView;
DWORD dwNewView;
} NMVIEWCHANGE, *LPNMVIEWCHANGE;
#endif
__fastcall TMyForm(TComponent *Owner)
: TForm(Owner)
{
if (Win32MajorVersion >= 6)
{
SendMessage(MonthCalendar1->Handle, MCM_SETCURRENTVIEW, 0, MCMV_YEAR);
PrevMonthCalWndProc = MonthCalendar1->WindowProc;
MonthCalendar1->WindowProc = MonthCalWndProc;
}
}
void __fastcall TMyForm::MonthCalWndProc(TMessage &Message)
{
PrevMonthCalWndProc(Message);
if (Message.Msg == CN_NOTIFY)
{
if (reinterpret_cast<NMHDR*>(Message.LParam)->code == MCN_VIEWCHANGE)
{
LPNMVIEWCHANGE lpNMViewChange = static_cast<LPNMVIEWCHANGE>(Message.LParam);
if ((lpNMViewChange->dwOldView == MCMV_YEAR) && (lpNMViewChange->dwNewView == MCMV_MONTH))
{
// do something ...
SendMessage(MonthCalendar1->Handle, MCM_SETCURRENTVIEW, 0, MCMV_YEAR);
}
}
}
}
If you are using C++Builder 10.1 Berlin or later, look at the newer TCalendarView and TCalendarPicker components. They both have a DisplayMode property that you can set to TDisplayMode::dmYear for the current view, and an On(Calendar)ChangeView event to react to view changes by the user.

How to determine which component has the program focus in C++Builder

I am using C++Builder XE4 32bit VCL platform. I am writing for Windows OS.
I have a MainForm with a lot of components on it. When I press a keyboard arrow key and the Form's OnShortCut event is triggered, how do I determine which component has the program focus?
I have different actions which must be taken based on which component has the focus.
void __fastcall TMainForm::FormShortCut(TWMKey &Msg, bool &Handled)
{
//determine which component has the focus.
}
Use the global Screen->ActiveControl property:
Indicates which control currently has input focus on the screen.
Read ActiveControl to learn which windowed control object in the active form currently receives the input from the keyboard.
void __fastcall TMainForm::FormShortCut(TWMKey &Msg, bool &Handled)
{
TWinControl *ctrl = Screen->ActiveControl;
if (ctrl == Control1)
{
// do something...
}
else if (ctrl == Control2)
{
// do something else...
}
// and so on...
}
Or, you can use the Form's own ActiveControl property:
Specifies the control that has focus on the form.
Use ActiveControl to get or set the control that has focus on the form. Only one control can have focus at a given time in an application.
If the form does not have focus, ActiveControl is the control on the form that will receive focus when the form receives focus.
void __fastcall TMainForm::FormShortCut(TWMKey &Msg, bool &Handled)
{
TWinControl *ctrl = this->ActiveControl;
if (ctrl == Control1)
{
// do something...
}
else if (ctrl == Control2)
{
// do something else...
}
// and so on...
}

ActiveX freezes IE

I created an activeX control to perform some actions, which take about 1 minute. During this action IE freezes completely. Is there a way to call the activeX control so that IE does not freeze?
Thank you
You would have the same problem in any ActiveX host, not just IE. If you don't want to block the UI thread, then you need to change your ActiveX control to do its work on a secondary thread.
I had a simulair problem with my activeX that freezed IE. I managed to do a workaround, by letting my activeX control create new threads on client. Even tho the thread is doing its work, the activeX returns that its done and will unblock UI.
The tricky part is to know when the thread is done. You can find out by have a boolean property in your activeX, that you set to true when the thread is completed. From javascript you can keep invoke that property until its set to true.
Javascript:
var myActiveXObject = new ActiveXObject("myActiveX");
function startWork()
{
myActiveXObject.startWork();
setTimeout(checkIsDone(), 1000);
}
function checkIsDone()
{
if(myActiveXObject.checkIsDone())
workComplete();
else
setTimeout(checkIsDone(), 1000);
}
ActiveX:
private bool blnIsDone;
[ComVisible(true)]
public void startWork()
{
blnIsDone = false;
Thread myThread = new Thread(delegate()
{
ThreadedWork();
});
myThread.Start();
}
private ThreadedWork()
{
//Do work
blnIsDone = true;
}
[ComVisible(true)]
public bool checkIsDone()
{
return blnIsDone;
}
This is a threading issue dealing with IE, so I don't think there is any way to do this.

Resources