.NET (non-visual) component - c#

I need to create a non-visual component, FooComponent, that will do some management for all controls of type Bar that resides in its form.
I have the following constraints:
The FooComponent can only be added to forms.
Only one FooComponent per form is allowed.
FooComponent should register to the form closing event, and when it fires and to some function on all Bar's and sent the e.Cancel value based on the returned values.
#1 and #2 above should be enforced on run-time as well as design time.
#3 event registration should be made automatically and not by the FooComponent's users.
I searched Google and MSDN for some help and read about Component and ComponentDesigner classes, but I didn't find anything for the rescue.
What should I do?

(1) To control that the component can only be added to a form, use a FooComponent constructor that is passed a form, and don't define the default constructor. It's called like:
FooComponent component = new FooComponent(this);
where the component is created from within the form itself. By not-defining the default constructor, this:
FooComponent component = new FooComponent();
will not compile.
(2) Expose a FooComponent property on the form itself, and in the constructor of the FooComponent, set the passed form's FooComponent to this.
(3) Same thing, in the constructor for the FooComponent, register with the closing event for the form you passed
Put it all together and you get:
public class MyForm : Form {
public FooComponent OwnedComponent { get; set; }
}
public class FooComponent {
public FooComponent (MyForm OwnerForm) {
OwnerForm.OwnedComponent = this;
OwnerForm.FormClosing += MyCallback;
}
private void MyCallback(object sender, FormClosingEventArgs e) {
...
}
}
EDIT
Unfortunately, if you need the default constructor, and if it has to be a true drop-on-the-form Component, there's no way to enforce that a component is only created on a Form, or that the Form only has one instance of the component (not from within the component, anyway).
The problem is twofold:
(1) Dropping a component doesn't add the component to the form, it adds it to the form's components collection. So even if you could get a handle to the parent/owner, it will never be a form.
(2) As Neil pointed out, dropping a component onto a form calls the default constructor, which passes no parameters, and, of course, none of the component's properties (such as site or container) are populated.
Possibly helpful: A component can be designed to be notified when it is created in a couple of ways:
(1) By implementing a constructor that takes an IContainer parameter. When the component is dropped on a form, the generated code will call this constructor, instead. However, it will only do this at runtime, not design time. But the container will be a handle to the form's components collection.
public FooComponent(IContainer container) {...}
(2) By implementing ISupportInitialize. When the component is dropped on a form, the generated code will additionally call BeginInit() and EndInit(). In EndInit(), you can access properties such as the Site and Container. Again, you'll only get this at runtime, not designtime, and throwing an exception here won't stop the component from being created.
Old, but excellent articles on Components and Controls from MSDN Magazine by Michael Weinhardt and Chris Sells.
April 2003 Building Windows Forms Controls and Components with Rich Design-Time Features
May 2003 Building Windows Forms Controls and Components with Rich Design-Time Features, Part 2
These are now .chm help files. You will need to unblock in the file's property page to enable reading the contents after downloading.

I don't think it's possible to define exactly what a contained class can be contained within. I've certainly never seen an instance where I've gotten an error (or even a warning) for setting up a property of one type in another, even in WinForms.
Something you might be able to do is to define a Form-derived ancestor for your forms that contains a reference to your (internally-visible) FooComponent, initializes one on instantiation, and attaches the handlers. For best results it should be parameterless and the only constructor overload, so it forms the base for any constructor your consumers come up with. Then, just make it a house rule that forms derive from your ancestor class and not directly from Form (you might be able to use a code inspection tool like FxCop or similar to enforce this when code is committed to source control). Your users now get a FooComponent in every Form they create, cannot create their own (it's internal and should be in another project with your Form ancestor) and don't have to do anything other than derive from the new class to make their forms behave the way you want.

You are asking for a lot. In general, making components aware of the form they are dropped on is quite difficult. This answer can help you get the event handler implemented. You'll need to implement ISupportInitialize to get the EndInit() call to setup the event handler.
Preventing multiples is quite hard too, I can only think of a custom designer that can step in early enough to prevent the 2nd one from being added.

Related

How to update Templates?

Currently I'm creating a really big project in Visual Studio 2012, where there are some common settings for each form ("Cancel" and "Save" buttons, Methods that change in every form but have the same name, font sizes and types, form color etc.) it will save me a lot of time if I could do all the design a single windows form and when I edit or modify it, have the changes reflected in the other forms as well.
Let's say I need 10 forms, to create them I would choose this default format and have my menu and basic objects already placed and designed; then after 10 forms I decided to move a button a bit, but don't want to go to every form and move it; just change it in the original format, refresh and all my forms will have that button in the new location.
I used Templates as recommended by Can one set the default properties for new WinForms created in Visual Studio?. But I still have the issue that if I change something in the template it won't refresh in every other form created with the template to that point.
I've already thought of changing the InitializeComponent in the WinForm default format, but this is not recommended and I wouldn't want any errors from this later on.
Any ideas?
Thanks in advance
Inheritance will work for your solution.
Create "base" form with all "common" controls
Create new "derived" form and change form to inherit from your "base" form.
If you have some common logic in base form, which need to be "overridden" in derived forms - put it to the virtual method
// Base form
protected virtual void Close()
{
// Base logic
}
private void CloseButton_Click(object sender, EventArgs e)
{
Close();
}
// In derived form - just override "Close" method
protected override void Close()
{
// custom logic - will be executed when "Close" button clicked
}
In base form leave empty space for custom controls. Because you will not be able access baseform controls through designer in derived form.
Another approach - Model-View-ViewModel(MVVM)
- Introduce own UserControl with common controls(view) which have property - instance of ViewModel.(Viewmodel will contains behaviour logic and possibility to change "base" settings.)
- Add this user control to all "derived" forms and set UserControl.ViewModelProperty to instance which will represent logic for this particular form.
Without knowing "full" context of your goals - difficult to suggest more, but I am pretty sure you can build maintainable relations between forms, which can share common logic and view.
No, there is nothing you can do. Once you use a template to create a project or a file, it becomes a one-off. You have to edit it manually, or use a text editor that is powerful enough to employ a find and replace with pattern matching and capture group insertion.

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

Drag and drop the user control won't work in .net windows project

I created a user control and it shows up on the tool box as form components. Then when I try to drag and drop the user control on to a form , I get this visual studio error.
" The specified named connection is either not found in the configuration ,not intended to be used with the entity client provider or not valid."
Why am I getting this error?
But some other user controls I can drag and drop which are under the same project. I don't know what I missed in creating this user control.
Beware that code in the UserControl class runs at design time. The constructor, the OnLoad method and Load event. But also methods like OnPaint(). If this code does anything that depends on the environment being setup properly, that code is liable to throw an exception and cause the designer to change its mind about adding the control to the form. That certainly seems to be the case when you get a "not found in the configuration" error, there is no configuration file yet.
Use the DesignMode properly to skip such code. Like this:
protected override void OnLoad(EventArgs e) {
if (!this.DesignMode) {
// Do stuff...
}
base.OnLoad(e);
}
As Hans says, you might need to use the DesignMode property in the Constructor or OnLoad. Also, make sure any public properties that use the connection have this attribute:
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public string Foo
{
get;
set;
}
That way, the designer won't attempt to set them when you add the control to a form. This is always a good habit to get into anyway for properties that you won't be setting at design time.
this error show if you put the code of loading data from database into the constructor of userControl.
"loading data or initialize entity framework"
so the solution is to move the code of loading data from constructor to a method. you can call it "loadData".
and call this method "loadData" in the constructor of the parent form

How can I easily keep consistent UI settings in C# Winform application?

I have a lot of different UserControls and would like to maintain consistent UI settings (mainly colors and fonts). My first try was this:
public class UISettings
{
//...
public void SetupUserControl(ref UserControl ctrl)
{
ctrl.BackColor = this.BackColor;
}
}
to be called in every control like this:
settings.SetupUserControl(ref this);
As this is read-only it cannot be passed by ref argument so this does not work. What are other options to keep consistent UI without manually changing properties for every item?
Inheritance! If you have a form or control that will constantly be using the same styles and you want to set that as your base, just create your own user controls that inherit from a form/control. By default all of your forms will inherit from "Form". Instead of inheriting from the default form, create a new user control that inherits from Form, and then have that as your base class.
CustomForm : Form // Your custom form.
Form1 : CustomForm // Inherit from it.
...the same works for components. If you want a button to have the same styles across the board, create a user control and have it inherit from the button control -- then use the custom control.
Whenever you want to make a change to your base styles, or any settings, simply change your custom controls settings -- your new forms/controls will automatically be updated!
Do the same thing. Don't pass it by ref. UserControl is a reference object already, so there's no need to pass it into your method using the ref keyword.
You may also want to consider a recursive method that will find all the UserControls on the form and pass it into your method.
How about a base class which provides such settings?
Two answers:
You don't need ref, controls are objects are reference types. Just drop it.
Create a Base UserControl and derive your controls form that base. You can still do that, just edit the class definitions of the controls. For new controls you can follow the Wizard.
A tip: setup the styling in the baseControl. Then make sure the derived controls don't override, the best way to do that is scanning the *.Designer.cs files and remove all settings that you know should come from the base.

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