AxAcroPDF swallowing keys, how to get it to stop? - c#

The AxAcroPDF swallows all key-related events as soon as it gets focus, including shortcuts, key presses, etc. I added a message filter, and it doesn't get any key-related messages either. It's a COM component, could that be relevant?
Is there any way to catch these before the control starts swallowing them?

Hans is correct, the Acrobat Reader spawns two child AcroRd32 processes which you have no direct access to from within your managed code.
I have experimented with this and you have three viable options:
You can create a global system hook, and then look for and filter out / respond to WM_SETFOCUS messages sent to your child AcroRd32 windows. You can accomplish some of this from within C# by using a wrapper library, such as the one here: http://www.codeproject.com/KB/system/WilsonSystemGlobalHooks.aspx
You also need to identify the correct processes as there may be more than one instance of your application, or other instances of AcroRd32. This is the most deterministic solution, but because your application will now be filtering messages sent to every single window in existence, I generally don't recommend this approach because then your program could negatively affect system stability.
Find an alternate PDF viewing control. See this answer for a few commercial components: .net PDF Viewer control , or roll your own: http://www.codeproject.com/KB/applications/PDFViewerControl.aspx
Find an acceptable hack. Depending on how robust your application needs to be, code such as the following may be suitable (it was suitable for my case):
DateTime _lastRenav = DateTime.MinValue;
public Form1()
{
InitializeComponent();
listBox1.LostFocus += new EventHandler(listBox1_LostFocus);
}
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
axAcroPDF1.src = "sample.pdf"; //this will cause adobe to take away the focus
_lastRenav = DateTime.Now;
}
void listBox1_LostFocus(object sender, EventArgs e)
{
//restores focus if it were the result of a listbox navigation
if ((DateTime.Now - _lastRenav).TotalSeconds < 1)
listBox1.Focus();
}

I might finally have a ridiculously simple answer. So far in testing this is working.
Having suffered from this problem for quite some time and having built a complex system of each custom control recording which of them last had focus and using a timer to flip focus back (when acropdf grabbed it) I revisited this problem and read a great number of answers (looking for recent solutions). The information gleaned helped me with the idea.
The idea is to disable the (acropdf) control whilst it is loading as in the following example (code reduced for clarity)
AxAcroPDF_this.Enabled = False
AxAcroPDF_this.src = m_src
Then on a timer, after say 1 second.
AxAcroPDF_this.Enabled = True
Basically the idea is to tell Windows not to let users use the acropdf control until allowed, so asking Windows to prevent it from getting focus (because users are not allowed in there).
So far this is holding up, I will edit this if anything changes. If it doesn't work completely for you then maybe the idea points into a useful direction.

It is an out-of-process COM component, that's the problem. Completely in violation of Windows SDK requirements as laid out in SetParent(). Once its window gets the focus, the message loop in the acroread.exe process gets all the messages, your message filter cannot see any messages anymore.
Technically it is fixable by using SetWindowsHookEx() to inject a DLL into the process and monitor messages with WH_GETMESSAGE. But you can't write such a DLL in the C# language.
Major suck, I know. There never seems to be any lack of it with that program.

For some reason Tim's answer, disabling the AxAcroPDF control directly, didn't work in my case. The Leave event on the previously-selected Textbox would never fire, either.
What is working is nesting the AxAcroPDF control inside of a disabled GroupBox. Since the users of my application need to only see the PDF, not interact with it, the GroupBox's Enabled property is set to False in the designer.

Related

HelpProvider ignores form's HelpTopic on some controls

I've hit a frustrating issue with a software project I'm working on. I've got help set up on various forms such that if I press F1, the application's CHM file is opened.
I want it to always open to the topic related to the current form, however it currently opens to a different location depending on what part of the form is in focus. If some fields are in focus, it opens to the top of the first page of the help document; and if other fields are in focus it will open, correctly, to the page and heading related to the current form.
I have the following designer code for the various forms, and I'm only setting HelpKeyword on the form itself, not any of the form's controls.
this.helpProvider.HelpNamespace = #"Path\To\ChmFile.chm";
this.helpProvider.SetHelpKeyword(this, "TopicName.htm#heading_name");
this.helpProvider.SetHelpNavigator(this, System.Windows.Forms.HelpNavigator.Topic);
From experimentation, I've determined that items with either SetShowHelp(false) or Enabled = false are the only ones that show the correct chm help location. This applies when pressing F1 with them in focus (if they can hold focus), or pressing on them with the "what's this" help cursor.
As an example:
If focus is on the main dialog's first control (a TextBox which has a HelpString, which implicitly sets ShowHelp) and I press F1, I'll get sent to the overall application help page (undesired behaviour).
If focus is on a main dialog TextBox which does not have an associated help string, and I press F1, I'm shown the Dialogs help page, at the heading for the main dialog (desired behaviour).
The only workaround I've found for this so far is to set both HelpTopic and HelpNavigator on every control with a HelpString, but this is very heavy-handed and difficult to maintain.
Clarification
I am intentionally using both the "What's this?" help and the F1 help on the same forms. I will not accept a solution that says to disable "what's this" help for all controls on my form in order to allow the F1 help to work. It is a requirement for this application that interactive controls have a help tooltip, and that each dialog has a help section in the help document.
If there is no way to get these two help features to work together nicely, I will accept an effective workaround that does not sacrifice maintainability as my above workaround does.
It was only after your clarification that I was able to fully understand your question and reproduce the problem. This behavior was not known to me before.
Same undesired behaviour using Visual Studio 2019 on Windows 10 PC - it does not matter if it is coded in the Form_Load event or with the help of the IDE (Integrated Development Environment) in the designer code.
After some hours of experimenting (with the reproduced problem) I hopefully narrowed it down without fully knowing the real reason.
I have done the following steps and thoughts - FYI - experimentally (see special notes in the list below):
The default value of the HelpNavigator enumeration is AssociateIndex. If you accidentally set SetShowHelp = True and a required property is missing, the call to the Index tab may fail, corrupt something and goes to the CHM's home page topic. If you don't have an index tab in your CHM, another problem arises.
I deleted or renamed the file hh.dat several tines to reset all (!) CHM windows on my system to their default settings. Windows will create a new version of hh.dat when you next open any .chm file. You'd find hh.dat at C:\Users\%username%\AppData\Roaming\Microsoft\HTML Help. BUT - - no success with the existing problem in the first test phase.
For some more test only (code is not required later) I have tried if the Form3_HelpRequested is triggered. BUT - that did not work in the first test phase.
private void Form1_HelpRequested(object sender, HelpEventArgs hlpevent)
{
// do whatever you're gonna do here
DialogResult dr = MessageBox.Show("HelpRequested on Form1 was fired!\n\nOpen CHM help?","Test case", MessageBoxButtons.YesNo);
switch (dr)
{
case DialogResult.Yes:
// FALSE will also open any associated help file
hlpevent.Handled = false;
break;
case DialogResult.No:
// TRUE will prevent windows from also opening any associated help file
hlpevent.Handled = true;
break;
}
Last as a hard step I deleted the HelpProvider component, have inserted this again and set all properties correctly a second time. NOW - it is working for me. Controls with the property ShowHelp=True now show the assigned topic and controls with the property ShowHelp=False now show the help topic of the form as expected.
You know - this can be a complex step and should be done in a test environment first. Make sure that all properties are set correctly and that the topic is accessible in the CHM via HelpKeyword.
// Tell the HelpProvider what controls to provide help for, and what the help string is.
this.helpProvider1.SetShowHelp(this.cityTextBox, true);
this.helpProvider1.SetHelpNavigator(this.cityTextBox, HelpNavigator.Topic); // make sure to set "Topic"
this.helpProvider1.SetHelpKeyword(this.cityTextBox, #"/Garden/flowers.htm");
this.helpProvider1.SetHelpString(this.cityTextBox, "Enter the city here.");
After giving the city textbox focus and F1 the help viewer window is shown. Using the "What's this" ? button in a second step is resulting in:
tl;dr
The definition of the properties via designer code or program code is more a decision based on personal preferences. I myself prefer to set values in the program code rather than via the controls properties window of Visual Studio.
If you add here all the properties of the controls for the help functionality as code, you can easily comment out for fixing problems. In a form with many controls, it is easier to save parts of the code externally and insert them again later. But as I said - everybody likes it different.
// set F1 help topic for first form
private void Form1_Load(object sender, EventArgs e)
{
helpProvider1.SetHelpNavigator(this, HelpNavigator.Topic);
helpProvider1.SetHelpKeyword(this, #"/HTMLHelp_Examples/Jump_to_anchor.htm#SecondAnchor");
}
BTW - the "What's this?" help for large programs means a high effort and in my experience it is used less and less in help authoring (CHM's). This kind of help has a long history from the times of Visual Basic 6, for example. Today you often find only one help topic for a form or a dialog in which the single controls are explained. The problem you describe then does not appear at all.
FYI - in earlier days a ALIAS and MAP files was required and maybe used today by TopicId. The purpose of the two files is to ease the coordination between developer and help author. The mapping file links an ID to the map number - typically this can be easily created by the developer and passed to the help author. Then the help author creates an alias file linking the IDs to the topic names.
Creating Context-Sensitive Help for Applications
Where to specify topic id in c# windows application

Sending paste message to active window's focused control

I want to intercept a hot-key (specifically CTRL V) which will interact with my application in a certain way. I am able to globally register the hotkey using the RegisterHotKey method. When I press CTRL V I want the focused window/control to also receive the WM_PASTE message. I've tried sending it trough SendMessage but it didn't seem to work. I eventually ended up Unregistering the HotKey, sending ^v to the current window, then re-Registering the hotkey.
private static void Hook_KeyPressed(object sender, KeyPressedEventArgs e)
{
_hook.KeyPressed -= Hook_KeyPressed;
_hook.Dispose();
SendKeys.Send("^v");
_hook = new KeyboardHook();
_hook.RegisterHotKey(global::ClipMaster.ModifierKeys.Control, Keys.V);
_hook.KeyPressed += Hook_KeyPressed;
}
This does the trick, but it blocks the mouse (and the window) for around half a second. I'm also afraid it won't work in certain applications, although I don't know what example I could give.
I'm not sure what you're trying to achieve with that hook since it's impossible to understand it from the code example.
Assuming you do it for auditing reasons (or monitoring of the sort), I'd suggest to review the return value of the WM_HOTKEY message. It can "say" that the message wasn't processed, hence allowing further processing of the Ctrl-V by other logic (e.g. other hooks).
Also, i'd advise to use alternative way to re-send Ctrl-V than SendKeys class which has know timing issues. Did you consider posting a message instead?
I've also found alternative approach to the hooking itself by using lower level key pressing for hooking.
Let me know if it helped.

In Idle cannot access RichTextControl or IME will not work

When reading some RichTextControl properties inside Application.Idle some IME won't work.
Given this simple code:
_richTextControl = new RichTextControl();
Application.Idle += delegate(object sender, EventArgs e) {
btnCopy.Enabled = _richTextControl.SelectionLength > 0;
btnPaste.Enabled = _richTextControl.CanPaste();
};
It'll work fine with most IME I tried but, at least, for Chinese (Traditional, Taiwan) with Microsoft Chinese Traditional Array (6.0) and Chinese Traditional DaYi (6.0) it'll prevent IME to work properly (I tried only with Windows 7, in English and Taiwanese). You can start typing but when Idle code is executed (reading SelectionLength property and invoking CanPaste() method, here I wrote them both but each one is enough to reproduce this) it'll stop working (you type but pop-up window is closed immediately and nothing is sent to RichTextControl).
I checked MSDN about EM_GETSELTEXT and EM_CANPASTE but it says nothing about this issue. I also tried to replace SelectionLength property manually sending EM_GETSELTEXT with SendMessage() but it doesn't change this odd behavior (however I checked WordPad - which uses rich editor - and it works properly even if (AFAIK) MFC commands use idle time to update UI status).
Of course I can move some code into SelectionChanged event and update UI both from Idle and from SelectionChanged (and it'll work) or I may update some flags in SelectionChanged and change UI in Idle but this will break existing code and it'll force a big change in UI library (all commands have not knowledge of Idle or SelectionChanged, they simply return true in a CanExecute() method when their target control allows their execution). If nothing else is viable then I may derive a class from RichTextControl to make these methods/properties callable in Idle (using flags updated elsewhere) but (to avoid a big refactoring) I would know if it's a knows issue, a bug related to IME itself or if there is something else I missed.

How to distinguish between User Control load on form and load when runtime

I created a user control using C# for windows form application. This user control has some properties. In runtime, if the user does not enter values for this properties I want to show a message box and exit the application.
The problem is when I write the checking code in the Load event of User Control. When I drag & drop it on the form the message box will appear.
private void UserControl1_Load(Object sender, EventArgs e)
{
if (_getFirstPageArgument==null || _getFirstPageArgument.Length==0)
{
throw new Exception("Some Message");
}
}
How do I distinguish between load on the form and load on run time?
I fear there is a larger problem here. But to solve your immediate problem (if I understand correctly...) There is a form attribute called DesignMode. When you are in the visual studio design mode, this will be true. At runtime, this will be false.
For beginners, #Nimas case can be a good study point to understand that Visual Studio actually runs and executes parts of our code even when we are in design time, which is why the constructor is invoked. Even "DesignMode" property is not 100% reliable. You can find an interesting note here related to that http://weblogs.asp.net/fmarguerie/archive/2005/03/23/395658.aspx
If you only want to know when the type itself has been loaded into the runtime (not a specific instance), you can put code into the static constructor for that class.
If I'm misinterpreting your question, please clarify using a timeline when you want specific events to happen.

How can I create a Delphi TSpeedButton or SpeedButton in C# 2.0?

How can I create a Delphi TSpeedButton or SpeedButton in C# 2.0?
Using a Button and setting the TabStop property to false only works when tapping through the form...
If you need (as I did) a button that does not get selected when clicking on it, there is only one way I have found to do it.
The way I did it, was to subclass the Button class and in the constructor calling the SetStyles and thereby setting Selectable to false, like so:
public class ButtonNoFocus : Button
{
public ButtonNoFocus()
: base()
{
base.SetStyle(ControlStyles.Selectable, false);
}
}
This worked out for me, and is perfect if you e.g. have a control-panel with buttons that perform actions to a selected object...
I'm wondering if you want to create a control like a TSpeedButton, or you just need same kind of end result ...
Programming one from scratch is certainly possible, but I'd only tackle that as a learning exercise.
Assuming you want to achieve a similar end result ...
Delphi's TSpeedButton had a differences from the standard TButton that developers found useful - it was flat, didn't take focus, and it consumed fewer resources than a regular button (because it didn't have an underlying Windows Handle).
Which of these are important to you?
If you just want a flat button that doesn't accept focus, use a regular Button with FlatStyle=Flat (or PopUp) and TabStop=false. You can configure a glyph by setting either the Image property, or a combination of ImageList and ImageIndex/ImageKey.
An alternative to this would be to look for an existing button component that comes close to your needs - one place to look might be the Krypton Toolkit (free to use, see http://www.componentfactory.com/toolkit_buttoncontrols.php).
If you're wanting to reduce the number of resources consumed by your application, it's likely you'll get a better return looking elsewhere.
Back in the days of Windows 3.1 (Delphi 1) and Windows 95 (Delphi 2), the number of available handles was strictly limited, with a maximum number available system wide. Today, with Windows XP and Vista, the number of available handles is far far higher, and the number is per process, not system wide. Unless you're creating thousands upon thousands of buttons, you're very unlikely to come anywhere close to running out.
Does this help? Looks like you would have to handle the OnPaint event, and not take focus...
The regular .net 2.0 button supports part of what a TSpeedbutton Does:
The Glyph: Image
Flat : FlatStyle
It does not handle:
Down
Group
These two are related, you could inherit from the button, and ownerdraw it, adding Down and Group features.
Codeproject has an example of ownerdraw buttons.

Categories

Resources