How to set the defaul event for a user control? - c#

I created a user control with an event. I do not want to add the following code every time I add a new instance of the control to a form.
Control1.Operate += new MyControl.OperateEventHandler(Control1_Operate)
Instead I would like to simply double-click the control and have the above code added by the IDE to the Designer file. By default the Control1_Load event is assigned in the Designer file. Is it possible to change this?

For C# use:
[DefaultEvent("Click")]
public class MyClass : BaseClass
{
//code
}
MS Code:
http://msdn.microsoft.com/en-us/library/system.componentmodel.defaulteventattribute.aspx

It seems you want to set a DefaultEvent for your control.
Use DefaultEventAttribute on your Control class.
A similar question exists:
How can I set the default event to be edited for my custom control in Visual Studio?
Hope it helps!

The behavior you are looking for is more like what you have for buttons. You double click on it and you have the event handler method generated for you. But in here, the event handler code is generated on the fly. In your cae, you want to attach, a pre-defined method with the event of the User Control. Try defining some custom properties or event for your usercontrol and then set the default value. For e.g. : http://msdn.microsoft.com/en-us/library/yhzc935f.aspx .

Related

Context-sensitive help for menu items in Windows Forms

I am implementing context-sensitive help for an existing WinForms app built in Visual Studio .NET. I have added a HelpProvider to the form and set the HelpNamespace property to a wonderful .chm that covers every control and menu item on the form. I have set the necessary HelpKeyword on all the controls that derive from Control and so far all is great: F1 works perfectly.
My problem is that I can't work out how to do it for menu items. These use the ToolStripMenuItem class, which does not derive from Control and so has no HelpKeyword property. How should I provide context-sensitive help for individual menu items? Mr. Google has not been very forthcoming.
Using F1 is not a common way of providing help for menu items. Menu items usually use ToolTip, or show some help text in StatusBar or usually their comprehensive helps comes with Help content of main page.
I prefer to use one of above mentioned solutions, but here for learning purpose, I'll show what you can do using HelpRequested event of the form.
To handle help for form and controls, you can rely on the HelpRequested event of the form and controls.
Here you can rely on Form event to solve the problem. Since you have a HelpProvider on form, you should know HelpProvider handles HelpRequested event of all controls internally and, for controls having ShowHelp set to true, it sets Handled to true and prevents bubbling the event up so you can not have your custom code for handling help event if ShowHelp is true. So you should set ShowHelp for controls to false and just use HelpProvider as a help key holder.
To solve the problem using the HelpRequested event of the form, you should follow these steps:
For ToolStripMenuItems, use the Tag property as the help key holder.
For other controls, if you use HelpProvider to assign HelpKey, don't forget to set ShowHelp to false.
Handle the HelpRequested event of the form.
In the body of event handler, check if there is an active menu item on your form, then use the Tag property of the active item to show help. If there is not any active menu, use the ActiveControl property of the form to show the help.
Example
Here is a step by step example of how you can show help for menu items using F1 key. To do so, follow these steps:
Create Form, Menu and Controls - Create a Form and put some controls and a MenuStrip having some menu and sub menus on the form.
Configuring HelpProvider - Put a HelpProvider control on form and for each control assign suitable key to HelpKeyword property of control. Also set ShowHelp for each control to false. We will handle help in code.
Configuring Help for Menu - For a ToolStripMenuItem use its Tag property to store the help keyword.
Creating a helper method to find descendants of the Menu - Add a class to your application having the following code. In the following code, I've introduced an extension method to get all sub ToolStripMenuItem of a MenuStrip:
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
public static class ToolStripMenuItemExtensions
{
public static List<ToolStripMenuItem> Descendants(this MenuStrip menu)
{
var items = menu.Items.OfType<ToolStripMenuItem>().ToList();
return items.SelectMany(x => Descendants(x)).Concat(items).ToList();
}
public static List<ToolStripMenuItem> Descendants(this ToolStripMenuItem item)
{
var items = item.DropDownItems.OfType<ToolStripMenuItem>().ToList();
return items.SelectMany(x => Descendants(x)).Concat(items).ToList();
}
}
Handling the Helprequested event to show help - Handle the HelpRequested event of the form and implement the algorithm which I described above using the following code:
private void Form1_HelpRequested(object sender, HelpEventArgs hlpevent)
{
string keyword = "";
var selectedMenuItem = this.menuStrip1.Descendants()
.Where(x => x.Selected).FirstOrDefault();
if (selectedMenuItem != null)
keyword = selectedMenuItem.Tag?.ToString();
else if (ActiveControl != null)
keyword = helpProvider1.GetHelpKeyword(ActiveControl);
if (!string.IsNullOrEmpty(keyword))
Help.ShowHelp(this, "Help.chm", HelpNavigator.Index, keyword);
}
Note
For testing the solution you don't need a chm file having index and so on. You can simply show the helpkeyword in Text property of form. It means the solution is working and after that you can create suitable chm file.
You can use one of the other overloads of ShowHelp method of Help class based on your requirement.
There are HelpKeyword and HelpString extended properties for controls, pay attention which one you are using and get the same one in the HelpRequested event.
Don't forget to set ShowHelp to false. If you forget this step, the event will be handled internally in Helpprovider.
Don't forget to assign a help keyword to Tag property of menu items. To make it more friendly for future, you can simply create an extender provider that adds a help keyword property to menu items.

C# WndProc event handler

I added a form control to my form in a designer. But I need to override that control's WndProc. Can I do that without creating a new control extending the old one? Because when I extend the old one like this my designer won't work anymore:
partial class ThatControlWithWndProc : TheControlIActuallyWant {}
Or how can I get my designer to work with this new control that I created and not throw me an error?
If you just edit the .Designer.cs file directly to refer to your overridden control, and ensure that it follows the rules for controls to be designer-compatible (like having a default constructor, and not relying on any other initialisation) you should be fine.

How to declare an event mandatory

I have a user control that has a contextmenustrip that is displayed on every right click. When the user select an item on this menu, I have to pass this info to the parent control so it can react according to this.
So I need the parent to mandatory subscribe to this event. Is there a way to tell that?
If there is no way to do that, should I throw an exception or just check for null value (of the event handler) and do nothing?
Thanks.
There is no way to enforce this at compile time - meaning that there is nothing in the .NET framework/C# language that can perform static checks at compilation to ensure that this logic is implemented in your code.
At run-time you can perform the necessary validation. For instance you might inspect the invocation list of the delegate to ensure that the parent is in that list.
You can create a specific constructor for your user control to prevent instantiation unless an event handler is given, like this.
public partial class MyUserControl : UserControl {
public MyUserControl (ToolStripItemClickedEventHandler handler) {
InitializeComponent ();
myContextMenuStrip.ItemClicked += handler;
}
}
Do note though: this won't work very well with Visual Studio's form designer, as the form designer generally expects parameter-less constructors. Instead, you need to manually create the control instance from code.

Is there a dynamic-creation-friendly LinkLabel Alternative?

I'm trying to dynamically create a link from a Windows Form to our website when certain conditions are met (it's a warning message with further information in our online manual).
Currently I'm finding LinkLabel quite unwieldy to use in this situation: having to set up LinkClicked handlers on the fly for a straightforward hyperlink seems inelegant.
Is there a wrapper or alternative that fulfills the following requirements?:
Inherits from System.Windows.Forms.Control (so I can use it in a TableLayoutPanel)
Has reasonably low setup (no strange LinkClicked function pre-visit checking, for example)
Isn't bound to a specific browser
What is your problem with the LinkClicked event handler? You would have to do the same for almost any control in order to do anything useful.
Anyway, it would be trivial to implement yourself - create a class that inherits from LinkLabel, add a string URL property (you may need an attribute to make this show in the designer properties panel if you want to set it that way) and provide an event handler that opens the browser with that URL.
Then you can just add the control in the designer (or at runtime), set the URL property and it will work without having to set event handlers.
Did you use the LinkClicked event instead of OnClick? Then you can use this in the event handler:
(sender as LinkLabel).LinkVisited = true;
System.Diagnostics.Process.Start("http://example.com");
It's not bound a specific browser - opens in the user's default browser. The setup is low - just instantiate the LinkLabel, add an event hookup to LinkClicked (which is one two-line method) and add it to the page. What's unwieldy about this approach?
In the end I used LinkLabel.Links.Add to modify the link destination dynamically..

IExtenderProvider and WinForms designer file

C# 3.5 Winforms...
So I’ve recently discovered the IExtenderProvider and how it can be used to extend controls with additional properties.
In a prototype project that I setup i put a break point on the ‘set’ method for my extendee property and as the form loads I could see the ‘set’ method executing for every control on the form; which is exactly what I wanted. Following the successful prototype I implemented the extender component into my main project. All the forms in my project inherit from a base form which I’ve added my extender component to. On the base form I set the modifier of the extender component to public so that its accessible by the form inheriting this base form.
Doing the same thing before i added a break point on the ‘set’ method for my extendee property but the method doesn’t execute for the controls in the form (but only for the controls in the base form). HELP!
I should probably add at this point that i’ve source controlled my forms and so most of them are checked-in (ie lock from modification). For the forms that I’ve checked out and modified the provider property; I’ve noticed in the designer file that all controls have an additional statement which calls the ‘set’ method of the provider property.
this.MyProvider1.SetMyProperty(this.txtTextBox1, false);
Am I right in thinking that for the extender component to work it has to physically modify the designer file or should it be able to cope with locked files and therefore call the set method dynamically? I guess if it does have to modify the designer file then this isn’t a problem for new forms or forms that get modified after the extender component was added to the project – but it would be problem when you have 101 forms all locked by source-safe...
I’d appreciate any thoughts...
At what point does the extender provider (IExtenderProvider) extend the 'type' (in my case a winforms control) that the extender was intended for; at design time or at run time?
The designer is responsible for showing you the properties of the extender in the property editor
Method bool CanExtend(object) from the IExtenderProvider interface
Am I right in thinking that for the extender component to work it has to physically modify the designer file or should it be able to cope with locked files and therefore call the set method dynamically?
It has to physically modify the designer file, and write the extended properties there
I guess if it does have to modify the designer file then this isn’t a problem for new forms or forms that get modified after the extender component was added to the project – but it would be problem when you have 101 forms all locked by source-safe...
This is is not a problem for new forms, and not for old forms.
If you want to set some extended properties, open the old form and set the extended properties (a check out of the file is necessary)
This really does confirm my suspicions, many thanks. But this does leave a problem in that the components are only extended if some physical change is made to the old form.
I was trying to hijack the Set property method to also add and remove an event handler to the component (if the component was a control). Image the property is a Boolean and when set to false it adds the event handle and therefore the default behaviour (setting to true doesn’t add and event handler)
To cut a long story short the controls which were part of newly added forms automatically have an event handler added even without me explicitly setting the property to false but the designer file of the old forms never modifier so the event handler wasn’t added.
As some background, I was trying to add a global event handler for all controls
Global event handler for all controls for User Help
The theme here is to add context help to my forms here’s example of the extender ( the event handler is added as part of the end initialiser)
public partial class HelpProvider : Component, IExtenderProvider, ISupportInitialize
... other code of the extender omitted ...
#region ISupportInitialize Members
public void BeginInit()
{
// do nothing
}
public void EndInit()
{
if (DesignMode)
return;
foreach (Component item in _disableOnlineHelp)
{
if (item == null)
continue;
if (GetDisableOnlineHelp(item)) // developer has decide to set property to TRUE
continue;
Control control = item as Control;
if (control != null)
continue;
control.HelpRequested += new HelpEventHandler(HelpProvider_HelpRequested);
_toolTip.SetToolTip(control, GetHelpText(control));
}
}
#endregion
#region DisableOnlineHelp Provider Property
public virtual bool GetDisableOnlineHelp(Component component)
{
object flag = _disableOnlineHelp[component];
if (flag == null)
return false;
return (bool)flag;
}
public virtual void SetDisableOnlineHelp(Component component, bool value)
{
_disableOnlineHelp[component] = value;
}
#endregion
One issue might be the foreach loop in the EndInit method:
Control control = item as Control;
if (control != null)
continue;
If the item is, in fact, a Control, you get out of the loop before executing this code:
control.HelpRequested += new HelpEventHandle(HelpProvider_HelpRequested);
_toolTip.SetToolTip(control, GetHelpText(control));
so you never add the Event Handler or the ToolTip, to any Control. Oops :)
Thanks,
John

Categories