Adding custom UI behavior to controls via a Component - c#

In my project, I have several controls that display a set of points on the screen. I would like to provide "lasso selection" functionality for all of these controls. To do this, I wrote a class called "Lasso", which takes care of tracking mouse movements, displaying the lasso, raising an event when the lasso is closed, etc. So far so good. But, in order to use the lasso, I must write code like this:
public partial class MyControl : UserControl {
private Lasso lasso;
public MyControl() {
InitializeComponent();
lasso = new Lasso();
lasso.HookEvents(this);
}
}
The "lasso.HookEvents" method takes care of hooking up the lasso to the mouse events for the host control.
What I would like to do is somehow provide a way for people to simply drag the "Lasso" component from the Toolbox onto their UserControl or Form at design time, and not worry about writing any code themselves; the lasso should magically "just work". Is there a way to do that ? I tried extending my Lasso class from Component, but I couldn't figure out how to get the Designer to generate the "lasso.HookEvents(this)" invocation automatically.

If possible, just capture the mouse events in the Lasso object. If that's not possible or doesn't work: In your Lasso class, add a handler to the Loaded event, and in the handler run this.HookEvents(this.Parent);.

Related

Is there a way to stop the WinForms designer defaulting to control Load event

This drives me nuts. In the Visual Studio WinForms designer, if you double-click a control it will produce a XXXX_Load handler for that control. This is never, ever, what you want. You want Click event or a Changed event or more likely to go to the existing Click event. You do NOT was a new Load event. It is worse because a double click can happen by accident. You click once, then again - bingo a Load event handler. I use a pen-tablet combo and double clicks are very easy!
Can someone tell me if there is a way to stop this happening. I would like a Click handler. I would settle for nothing happening.
One way to achieve that is inheriting the control and modifying the DefaultEventAttribute.
Specifies the default event for a component.
[DefaultEvent("Click")]
public class MyRadionButton : RadionButton {
}
But some care/extra work may need to be done when you override a control. You need to keep that in mind when using this approach.
In addition to Mehrzad's answer, you can completely disable it by passing an empty string to the DefaultEvent attribute:
[DefaultEvent("")]
public class MyCustomControl : UserControl
{
}
This works because the default control designer uses the ComponentDesigner.DoDefaultAction when the control is double clicked. By default, this uses reflection to look up the method signature of the default event. If it can't find it, it doesn't do anything.

Manually redrawing a custom control when WPF requests an arrangement

I have a custom control that has a refresh method, something similar to this:
public class MyControl : Canvas
{
// Dependency property for "data" used to draw the control here
public void Refresh()
{
Children.Clear();
// Using data, draw the control
Children.Add(new Line(...));
Children.Add(new Rectangle(...));
// etc.
}
}
Right now, I have to call Refresh() manually each time I want the look of the control to update. My dependency properties are set up for FrameworkPropertyMetadataOptions.AffectsArrange, so WPF knows that modifying the properties will affect the arrangement of the control and that it should be redrawn. So here's the question:
What does WPF use to tell a custom control that it should be redrawn? Is it an event, or an override, and how should it be used? I've tried handling various events and overrides and nothing seems to work. So, what's the correct way to do this? I want to replace / wrap the Refresh() method above in something "automatic" that WPF will handle automatically.

Load like event in Windows Forms for any control like textbox, checkbox?

In Windows Forms when a UserControl or Form first time becomes visible the Load event is fired.
http://msdn.microsoft.com/en-us/library/system.windows.forms.usercontrol.load.aspx
Is there any such event for controls like Checkbox, TextBox, Label ?
No. You could use the HandleCreated event, it is fired when the native window for the control is created. The first event you can rely on to run after the class constructor ran. It is triggered when the parent adds the control to its Controls collection and the control becomes visible.
Beware however that it this event can fire more than once. Controls may be re-created when certain properties get reassigned, the kind that requires the native CreateWindowEx() function to be called with new style flags. So you'll at least need to carry around a bool flag that keeps track of this.
Also note that setting properties of a control after the native window is created is pretty inefficient. All Winforms controls were designed to allow properties to be set before the native window is created. Whatever code you are generating almost surely should use the class constructor instead. Either of the derived control itself. Or in the code of the parent, much like InitializeComponent() does for a form or user-control.
The same is true for the existing Load event. It tends to be over-used due to the VB6 legacy where the Load event was very important. In Winforms however it is only required for code that depends on the final location and size of a control or form. Which may be different from the design properties due to form scaling. Any other code belongs in the constructor.

Form Designer and Namespaces

I have a static event in a DLL that I use frequently - Toolkit.Dialogs.ExitConfirm
The only way I can use this event is by modifying the line that adds the event in form.Designer.cs. Example:
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Form1_FormClosing);
becomes
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(Toolkit.Dialogs.ExitConfirm);
If I try to add it via the Properties->Events page, it gives me this error: 'Toolkit.Dialogs.ExitConfirm' is not a valid identifier.
Is there a way to allow Form Designer to reference events from other classes/namespaces?
Edit: some people work better with visual cues, so here's some images to define the issue: http://imgur.com/a/RaLMg
The first image shows how I have to make it work in Visual Studio - an event that calls an event.
The second image shows what I'm actually trying to do.
The third image is the error that occurs when I key in the method name by hand.
I have a static event in a DLL
You don't, you just have a method. FormClosing is the event, your method can be the event handler method if it has the proper signature. The designer simply doesn't support what you try to do, you'll have to stop trying. There are two sane solutions, both involve writing code in the form class. First you can do it in the constructor:
public Form1() {
InitializeComponent();
this.FormClosing += Toolkit.Dialogs.ExitConfirm;
}
Or the sane one since it doesn't make sense for a class to listen to its own events:
protected override void OnFormClosing(FormClosingEventArgs e) {
Toolkit.Dialogs.ExitConfirm(this, e);
if (!e.Cancel) base.OnFormClosing(e);
}
Which has the great advantage of working properly when you derive another form from this one. Which is also a strong hint to what you are probably really should do. It looks like you are trying to write common code for dialogs. The "Toolkit" namespace suggests as much. Make it work well by having this toolkit implement a base form class instead. Now you can design your form class without any code or event handlers:
public partial class Form1 : Toolkit.Dialogs.BaseDialog {
// etc
}
With the assumption that Toolkit.Dialogs.BaseDialog is a class derived from Form that overrides OnFormClosing(). Maybe it should also have a public property named "ConfirmOnClose" of type bool. which enables the "ExitConfirm" logic. You can set that property in the designer without trouble.
The WinForms designer isn't designed to do that. I'm a little surprised it doesn't lose your event the next time you make a change in the designer.
A few ideas on other ways you could make this work:
You could make a Form class that hooks the event for you, and descend all your other forms from that base class. Then you'd get the behavior everywhere.
You could make a utility method that hooks the event for you, and call it from each form's constructor.
You could make an extension method that hooks the event and then shows the form, and call your extension method everywhere you show your forms (instead of calling Show).
The base class is probably the simplest solution, as long as you aren't already using form inheritance for some other purpose.
You can call this Method 'Toolkit.Dialogs.ExitConfirm' On form closing event of your application form and pass required param to Toolkit.Dialogs.ExitConfirm

Winform & user control - C#

I have a Form and a UserControl. The UserControl has a menu, and the form has a tabstrip (General, Food, Vitamins etc).
In the UserControl, I have the following code: (Form name is frmForm, the tab names in the form are tabGeneral,tabFood, tabVitamins)
frmForm fm=new frmForm();
fm.tabMain.Selected=tabVitamins;
I call these line from the UserControl to capture the tab to get selected on the form, but it does not select the vitamins tab.
Where am I going wrong? I have access specifier as Protected Internal for tabs in the form.
Please advice.
Thanks,
Karthick
When you write new frmForm(), you're creating a completely new instance of frmForm, which is then discarded.
To get the frmForm instance that holds your control, call the FindForm() method and cast to frmForm.
For example:
frmForm myForm = FindForm() as frmForm;
if(myForm != null)
myForm.tabMain.SelectedTab = myForm.tabVitamins;
If the control is on some other form, this code won't do anything.
By the way, Hungarian notation is frowned upon in .Net.
Your form should probably be named something like MainForm.
SLaks has correctly pointed out your fundamental error, and given you a valid example of a way, via a call to the method 'FindForm, to get the Form the UserControl is sited on.
It may be valuable to you to keep in mind that a UserControl (and all Controls) also has a 'Parent property, but, of course, a UserControl could be placed inside another Control on a Form (like your UserControl could be inside a Panel on the Form) : in that case the UserControl's Parent would be the control it's inside on the Form (like, a Panel), not the Form itself, but 'FindForm will do the right thing to get you the Form it's on.
However you are calling a Method every time you use 'FindForm, and "best practice" suggests that what you want to do is to "inject" a reference to the Form into the UserControl at run-time so that it can always access its Form property easily, without calling a 'Method.
In your example, on a practical level, this (calling the Method) may make almost no difference in performance, but, imho, as you get to a place with WinForms and .NET where you might have a UserControl that will need access to its Parent Form very frequently, this will pay off, and it's a better way to structure your code in the long run, for maintenance.
Wes showed you one way you can "embed" (inject) the UserControl's hosting Form : using an overloaded constructor for the UserControl. But that requires you to modify the Designer.cs file in standard WinForms, and I strongly advise you against that, even though it will work. Particularly if you are just "getting your feet on the ground" in .NET, I strongly advise you against modifying it, or anything having to do with the Form's constructor and its internal call to : InitializeComponent();
Also, as you progress with WinForms you are going to meet many situations where you are going to want instances of "objects" (a Control, a Form, an instance of a Class) to contain references to other instances of "objects.
If you can understand and use one simple use of "injection" here, you are going to make progress to make yourself ready to handle more complex .Net programming in the future.
Another way is to put a Public Property in the UserControl that can be set in code from the MainForm. In the UserControl something like :
private frmForm ParentForm;
public frmForm UCParentForm
{
set { ParentForm = value; }
}
So then in your main form's code, perhaps in the Load event like this :
private void frmForm_Load(object sender, EventArgs e)
{
TheUserControl.UCParentForm = this;
}
or when you need to, you set the UserControl's 'ParentForm property once. So you have eliminated using the method 'FindForm().
In this case, if you only want access to a specific control on the UserControl's Parent Form, like a TabControl, you might consider that you want to make the Property you set of type TabControl, rather than Form : the same coding technique shown above can be used in the UserControl :
private TabControl mainFormTabControl;
public TabControl MainFormTabControl
{
set { mainFormTabControl = value; }
}
imho, it is when you are creating UserControls dynamically at run-time, using an overloaded constructor, as Wes suggests, is the best strategy. And using overloaded constructors has many, many others uses in .NET that you'll get into.
good luck !
You should not be creating a new frmForm() inside the user control. You could pass a reference to the frmForm to the user control.
In your user control constructor try something like this.
private frmForm fm;
public YourUserControl(frmForm fm)
{
this.fm = fm;
}
Then you could use.
fm.tabMain.Selected=tabVitamins;
Does that help?

Categories

Resources