Here is the deal, I have some collections to insert in a validation class and I'm trying to make it able to insert into the Visual Studio's Editor's IDE with all the context explained (if not enough just ask me) I want to the PropertyGrid to show its EventsTab like this in Visual Studio's IDE
But the problem is I have checked the Winforms source code and for what I could understand I have found that in order to insert the Button for EventsTab we have to call ShowEventsButton(true)
a private method of the PropertyGrid but that won't work (called it with reflection).
So what I have tried so far:
AddRefTab
passing EventsTab
with parameters (private method)SetupToolbar
with the true parameter (private method)RefreshTabs
TooltipItems
to Visible
ShowEventsButton(true)
Edit
I have two classes in this schema, one of them is the Validation
class which implements the IValidation
interface and it has the the events which I want to show in the property grid.
The other class is a ControlValidator
which derives from a component and has a list of Validation
Edit 2:
To be more specific what I'm trying to accomplish is to make use of the CollectionEditor
(same window that is used to edit the columns of a DataGridView) in this CollectionEditor
I want to make it show the EventsTab
in its PropertyGrid
(actually I want to make it be exactly like the PropertyTab
of the Visual Studio IDE but I'm not in that part yet) so the events and property that I want to show is of a list/collection of the ControlValidator
(component)
Let me try to make it clearer:
As you can see the ControlValidator
has a collection of the IValidation
interface and it is the component so it should and is working like this:
As you can see it does not show the events tab in the IDE because it does not have any events but if you double click the validations property of the controlValidator it opens the CollectionEditor which have a PropertyGrid in there
And as a PropertyGrid I'm trying to make it show the eventsTab (which I'm not able to make it show in any of the ways I listed up there)
If I understand correctly, you want to provide a way for the events for a Custom class to show in the VS IDE Property Editor.
This can be done fairly simply with only a few changes to your class. First, you need to understand that VS actually compiles your forms for use in the IDE designer. Specifically, all that designer code in InitializeComponent
needs to be compiled in order to create/draw your form.
This means in order to for your Class events (and props) to show in the IDE, your class needs to be a Component
(or Control
) so it can be compiled like any other for the IDE property editor. It cant be a class you instance at runtime because that code is not examined for the form editor.
I've done this several times with classes that otherwise act as normal classes at runtime but with the added ability to set properties and so forth in the IDE via the from designer (a class with ExtenderProvider
abilities, for example) . The changes to your class start with something like this:
class Validator:Component, ISupportInitialize
{
// fake props
public string Foo { get; set; }
public int Bar { get; set; }
public event ValidatingEventHandler Validating;
public delegate void ValidatingEventHandler(object sender, EventArgs e);
public event ValidatedEventHandler Validated;
public delegate void ValidatedEventHandler(object sender, EventArgs e);
public Validator(string foo, int bar)
{
}
public Validator()
{
}
// ISupportInitialize methods
public void BeginInit()
{
}
public void EndInit()
{
}
}
Compile and the Validator will be available in the toolbox. Drag one to the form/Component tray. Select it, open the Properties Editor et voila the properties and events show in the IDE:
Validator Component Properties (click for larger image). The Events View:
Double click the desired event and VS will add the skeleton event handler for you to edit.
Simply having your class inherit from Component does most of the work. There are a few things related to Components which are required or may come up. Most of these are simple issues and fixes.
As a Component, your Validator class will have to have a simple (parameterless) ctor. VS has no idea how to instance it using the (string foo, int bar)
version above.
This can cause problems if you have code which relies on those params to initialize other things in the constructor. The answer for that is ISupportIntialize
.
This interface provides support for a BeginInit and EndInit method which VS will invoke from the designer code since it now takes care of setting your Component's properties:
//
// validator1
//
this.validator1.Bar = 0;
this.validator1.Foo = null;
this.validator1.Validating += new Validator.ValidatingEventHandler(this.validator1_Validating);
...
this.Text = "Form1";
this.Load += new System.EventHandler(this.Form1_Load);
this.tableLayoutPanel1.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.validator1)).EndInit();
this.ResumeLayout(false);
...
private Validator validator1; // note: no params
As you can see, one of the last things the designer code does, after all the properties have been set, is invoke EndInit
on your class/Component. Any initialization ctor code using foo
or bar
can be simply be moved to there.
The Properties as well as events for your class will now show in the IDE. If you wish to hide any of these (perhaps it only has meaning when there are actual items to be validated), you can keep them from showing in the Property Pane using the Browsable
attribute:
[Browsable(false)]
public string Foo { get; set; }
In order to be certain that VS does serialize those you wish to be managed via the IDE, use the DesignerSerialization
attribute:
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public int Bar { get; set; }
Use DesignerSerializationVisibility.Hidden
for any property you do not wish VS to serialize.
Finally, the OP mentions a "collection of items" which the Validator
works on. If there is a collection class property on Validator (such as Items
), the default collection editor will display for that property. This may or may not be desirable depending on what the items are. You can [Browsable(false)]
on this to hide it.
If you do wish to add/edit items via the IDE, there is more work to do, but that is another kettle of fish.
This is in regards to the new, emerging desire to access Item Events in the CollectionEditor and (apparently) have code created for them when clicked (created where, I am not sure). There are a number of problems with this both as a design and in practice. I am assuming a structure like this:
Form.Validator.Items<ValidationItems>
First, the property grid used in the CollectionEditor
is an internal one. It is not hard to find on the EditorForm
, but simply enabling the event view would not magically give it the power to know what to do when you click an event. Since it is working with the collection, it knows little about the Type owning the collection, and less about the Form hosting the Type.
The events view is off in the CollectionEditor because it cant add event code to the form (the great-grandparent object of the Items!), because there is no form level object to reference. It cant/wont add events to your collection class code because your code doesnt not have designer files. Finally, the main requirement for a collection property is that the collection implement IList
. If your collection property is a plain List or Collection variable, there is no code file for it to modify.
To enable form events for the items it needs to be reconfigured. As an Item Class, they 'belong' to something else - the Validator class/component. If the Items are also reworked into a Component
, form level events become possible:
[Serializable]
public class ValidationItem: Component
{
[DefaultValue("")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string DisplayName { get; set; }
[DefaultValue(-1)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public int Value { get; set; }
public event ValidatingEventHandler Validating;
public delegate void ValidatingEventHandler(object sender, EventArgs e);
public ValidationItem()
{
Name = "New Item";
Value = 0;
}
}
Now, the Items will exist as a form component and be stored in the Validator's Items collection - similar to a DataGridView's columns. To edit/add events, you still cannot do so from the CollectionEditor for the same reasons as before, but they are available from the IDE Properties Window.
Select the Item Component and use the Property Window as normal to add the event code to the designer and to the form (very much as with the Validator
component in the original) :
// (Designer)
// validationItem1
//
this.validationItem1.DisplayName = "New Item";
this.validationItem1.Value = 0;
this.validationItem1.Validating += new ValidationItem.ValidatingEventHandler(this.validationItem1_Validating);
...
private ValidationItem validationItem1; // persistent form var
in the form:
private void validationItem1_Validating(object sender, EventArgs e)
{
}
As a component, all the earlier points about them now applies to the items as well.
a) If there are a lot of Items, there will be a lot of ValidationItems
in the Component Tray. The first thing I would ask myself is whether I need the Validator Class and all these items as individual form level objects.
b) As with controls, if you remove one, it will remove code from the designer, but not the form. Duplicate code can result as you change/update your items (collection contents tend to be more volatile):
private void validationItem1_Validating(object sender, EventArgs e)
...
private void validationItem1_Validating_1(object sender, EventArgs e)
...
private void validationItem1_Validating_2(object sender, EventArgs e)
This approach seems "noisy" to me. The CollectionClass is perfectly capable of hooking up to the Items' events which are then bubbled up to the form in the form of one single Validator
event. Why have 15 or even 5 events to maintain when one may work? Shouldn't consolidating all that be part of the job of the Validator
class? Ex:
private void validator1_ValidationComplete(object sender,
ValidationCompleteEventArgs e)
{
}
ValidationCompleteEventArgs
could report which item did or did not validate, when, why, provide a CancelRemaining
capability or whatever the form needs to know. This of is not unlike a ListBox
or DataGridView
which reports events on behalf of the stuff it contains.