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
Related
I'm having a strange issue that maybe being caused by my ignorance.
I have a treeview with an .AfterSelect and any time that i change the design of my form (in the deign view) the code gets removed for some reason.
here is my code
this.lstTreeView.AfterSelect += LstTreeView_AfterSelect; < this is the code that gets removed
this.lstTreeView.Location = new System.Drawing.Point(194, 56);
this.lstTreeView.Name = "lstTreeView";
this.lstTreeView.Size = new System.Drawing.Size(220, 498);
this.lstTreeView.TabIndex = 6;
this is the code that it allows to work.
private void LstTreeView_AfterSelect(object sender, System.Windows.Forms.TreeViewEventArgs e)
{
TreeNode CurrentNode = e.Node;
string fullpath = CurrentNode.FullPath;
MessageBox.Show(fullpath);
NrDirSearch(fullpath);
}
if anyone can give me some advice on why the .AfterSelect is being removed that would be really helpful.
I suggest you:
in the windows form designer, click the tree view to select it
in the properties grid click the lightning bolt and scroll to find the AfterSelect event
right click the name AfterSelect and choose reset
hit save all
Close out of the soution entirely/shut down visual studio
restart/reload the solution
Go back to the AfterSelect event as above, the box for which should be empty
click the drop down and choose your existing event handler
save all, quit and restart vs and check that the setting stayed
If you're finding it didn't stick, check that you don't have your designer open in another program e.g. A text editor that keeps autosaving an old version of the file that lacks the event handler?
Incidentally, the above process is how you add event in Design view - click the relevant control, lightning bolt, scroll to event wanted, double click the name of the event and you will be transported to your own code behind with a new named eventhandler created and ready to be filled
If you don't write any code in it, and go back to the designer and Reset the event as per the bulleted list instructions then your event handler method in your code will disappear. If you write code into the event handler then it is not removed when doing a reset, only empty handler methods are removed during reset
Side note: be careful with Undo if you see a message saying something like "performing this undo will cause a loss of work elsewhere" it usually indicates that the windows form design or designer.cs code will change as a result of actioning the undo
Designer files are safe to edit manually and it's sometimes necessary if the contents have gotten into a state where they are crashing the designer. I most often encounter this when deleting event handler s from my code that are still referenced in the designer. A screen appears saying a problem is preventing the forms designer from appearing, indicating the error line in the designer file. I have additionally in the past edited the designer directly to set large numbers of properties without the faff of using the designer - be mindful not to have a windows forms designer open at the same time as editing the designer.ca file because the forms designer will probably overwrite your changes. So long as you keep in mind that opening the same file in any two different editors at the same time can lead to conflict and loss of work, and take steps to ensure that edits in one editor are reflected in another before proceeding with further edits in the other editor, you'll be fine :)
Edit: having said that paragraph above, Mickey D made me realise an important point I'd overlooked:
The designer.cs file is read by the forms designer and uses to build the contents of the form, buttons, properties etc. As such if you are going to edit the designer.cs in a text editor you should limit your edits to only those things the forms designer can make use of, understand, represent and preserve when it next writes the file. Adding a line to set a button to enabled is fine. Removing a line that is causing it to crash is also good. Putting 27 methods that implement your entire program's database access strategy in is not a good idea as it will not be heeded or used to build the form when the designer reads the file and hence lost when the designer writes the file. If you're unsure of the difference between what will and won't be preserved stick to removing or fixing existing lines only rather than adding new lines of code
You should never[1] modify *.designer.cs files. They are code generated. Any changes you make are subject to being overwritten.
Instead either use the WinForms GUI Forms Designer to visually setup event handlers or you can do so in code in your form’s code-behind .cs file.
There are plenty of resources on the Net on how to use the WinForms designer.
[1] see Caius Jard's comment below for an exception to the rule that I concur with
I am new to C# windows application.I would like to add custom help file using help provider from the toolbar in Microsoft visual studio 2010 for windows application.
I can add my help file and that is working fine. I'd like to context-sensitive help and that is working fine with Help.ShowHelp(). In the properties window we have a HelpNavigator property where it has options like .Topic, .TopicId, .AssociateIndex, .TableOfContents, .Index, .Find. I have chosen .TopicId but i am wondering where to specify my topic ID. I have two questions
Is that possible ony by writing code in .cs file of the application?
If yes,then what is the purpose of help navigator property?
Short story - no, you can achieve this (F1-Help) without writing code (but sometimes coding is a better solution).
The HelpNavigator property is an enumeration that specifies the Help command to use when retrieving Help from the Help file for the specified control (see also: Help for controls with VB .NET).
Connecting a CHM help file with your application and providing context-sensitive help for controls has a small learn curve.
Below are (code) examples that demonstrate using context-sensitive help by F1 and how to open the help viewer by TopicId.
F1 - Help
Add a HelpProvider component to the form. This will add properties
like .HelpKeyword, .HelpNavigator, .HelpString, .ShowHelp.
Set the full path to your CHM file to the
HelpProvider.HelpNamespace property.
To enable the help ? button on the form's caption area, set the
values of the following form properties HelpButton = True,
MaximizeBox = False, MinimizeBox = False.
Use the control properties mentioned above to provide help for a
control when it has focus and F1 was pressed or the ?
button was clicked by the user. For example, set the button1
HelpKeyword property to 20010 and its HelpNavigator property to
.TopicId as shown in the screenshot below.
... and the resulting help viewer window:
Open the Help Viewer
Following code is used to open a Help Viewer and a topic by TopicId 10000:
private void btnTopicId_Click(object sender, EventArgs e)
{
Help.ShowHelp(this.btnOpenHelpShowTopic, helpProvider1.HelpNamespace, HelpNavigator.TopicId, #"10000");
}
I have a simple WinForm whose only function is to display a message passed to it. The message is passed as a string and is displayed in a textbox. It is set to be a FixedToolWindow (and no matter how else I set it I get the same behavior in the end).
The form is called with:
PswCoordFailDisplay coord1 = new PswCoordFailDisplay(inputString);
coord1.Show();
Here are two screenshots: top is of the designer; bottom is how it is actually painted.
This is part of a fairly complicated application with about 10 other WinForms. They all act like they're supposed to, except this one.
The changes from design are:
the Form's dimensions change
the textbox changes to occupy the entire width of the form (possibly in response to the size of the text that fills it -- although ScrollBars is set to Both)
the Close button changes its text to "OK" and migrates to the bottom right
the form's Title is gone
the X-closer is different
when I turn ControlBox to False, the X-closer appears regardless, and in the same form as in the bottom example
I pretty much take all the defaults that a WinForm provides when creating the WinForm, except for a couple of items that shouldn't make this kind of difference.
What on earth is happening? I mean, it still works fine, but it's not what the design calls for!
If you pay close attention to the pictured screenshots presented in the question, you will notice that the bottom one shows a MessageBox, not the intended WinForm. As commenters T McKeown and Grant Winney pointed out, there is no way for the elements of the form to go that far off the rails unless the form was coded that way. Since it wasn't coded that way, the expected form is NOT being called. There is a very superficial resemblance, of course, which accounted for my confusion.
If I had showed the complete calling code, a sharp eye would have seen this quickly:
switch (opRes.OpResult)
{
case OperationResult.Result.Succeeded :
MessageBox.Show("Password coordination succeeded.");
LoadUser();
break;
case OperationResult.Result.PartialSuccess :
MessageBox.Show(String.Format("Password coordination partially succeeded: {0}{1}", Environment.NewLine, opRes.Notation));
LoadUser();
break;
default :
MessageBox.Show("Password coordination failed.");
PswCoordFailDisplay coord1 = new PswCoordFailDisplay(opRes.Notation);
coord1.Show();
break;
}
As you can clearly see, with a "partial success" control would be passed to the second case in the switch statement, not the default. The second case was supposed to be coded with the new WinForm, but I forgot to do it! And so there you see the problem:
READ YOUR DARNED CODE BEFORE POSTING YOUR PROBLEM ON STACKOVERFLOW!
Less embarrassment that way.
ETA: Also, if I had posted the above code in my question, as #Rockster suggested, someone would have noticed it immediately, perhaps saving a step or two.
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.
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.