Create Error Provider Like Component.

digitaldan3

Member
Joined
Jun 29, 2005
Messages
7
Programming Experience
3-5
I need to create an Error Provider like component that the Icon is clickable.

I have the task of creating a way for users to save the criteria entered in our report forms so that the next time the report is opened the criteria is populated with these values.

The developer will decide which controls the user is allowed to save and also which controls are always saved and which controls are never saved. Each of these modes will have its own icon.

If the user is allowed to choose to save a controls value the icon can be clicked and the icon will be changed to a "Checked" icon and back if clicked again. (Basically the icon will be an image of a check box checked and another of a check box unchecked)

If someone can get me pointed in the right direction that would be great!

Thanks

Dan
 
I had a go with this because I thought it sounded interesting. It was a lot more work than anticipated, so you get a real freebee here ;)

First let's explain some things, the base starting point is to write a class that inherits Component, and not Control, that's pretty basic. Named it "SaveProvider".

Then about the ErrorProvider component, a key functionality of this component is that it dynamically adds a property to all controls in form, you add one provider to form and can set custom Error text for any control targeting this component. How is that done? The first thing I did was to use .Net Reflector to read the code for the ErrorProvider control, it's always useful to see how others are doing stuff, especially if you don't have a clue where to start looking. The internal code is quite complex, but it was easy to find out that it decorates a ProvideProperty attribute for the Error property. The help page explain that IExtenderProvider interface is needed, it only has one member and the content I got from Reflector and the ErrorProvider class, basically it filters which controls to provide this property for. Then the help page explains the special way to write this property with the Get/Set methods, not very different from a regular property.

The component you asked about should provide three value options as I see it (NoSave, AlwaysSave, CanSave), which in basic code translates to an enumeration, I defined this as a Mode enum, and defined the extender property as Savable. These are the three values the developer chooses at design time. If Savable mode is CanSave then user can choose at runtime if it should be saved by clicking the checkbox icon, I added this later as a UserSave property (Boolean).

Now getting deeper into things, since this component provides this property for any number of controls it naturally needs to manage the state for all these controls, a lookup table is required. I used an internal Dictionary where each control instance is the key. Several values needs to be kept for each control so I defined an internal class ControlItem to keep these, and this is the value object in the Dictionary. Also this loosely based on the ErrorProvider code.

Using a ControlItem class was also derived from the fact that I needed to paint the icons for each control the provider attached to, not an easy task that took some thinking. The internal code for ErrorProvider is very complex at this point, it basically uses the WM_Paint message and does all kinds of fancy stuff with unmanaged device contexts. I took a slightly different approch by using the Paint event of the control parent, since each control is wrapped in its own ControlItem class it is fairly simple to handle this even with each possibly having their own parent container, or the same, no need to worry about which event is already handled or not.

Much about the same control events is used similar to ErrorProvider, (ParentChanged, VisibleChanged, LocationChanged, HandleCreated, HandleDestroyed) ie whenever it matters where to paint the icon, or matters which controls events to listen to. The parent controls Paint and MouseDown events handles the core functionality for this component.

That's pretty much it. Then a fair amount of time was spent debugging, much can go wrong in designer where it suddenly crashes because your class is accessing objects that has ceased to exist, surprises are abound (and why was it still painting there!?...). More than a few times I had to go into form designer code and manually remove the component to continue development. IDisposable support was added for the ControlItem class to release handlers, and a trick was finally needed to make the component Dispose when the form was (the special IContainer constructor).

Hopefully with the help of these descriptions and reading the code a few times this all will make sense, and the component be of use. Opposite of the ErrorProvider I added the icon in front of the control. Attached is the component class and a very basic sample project.

As for saving the states of the saveable controls, that's up to you, or the client code, I left a short code sample in the form load event that shows how to set the UserSave state for a control, which you'd do at startup when this state info is retrieved from your storage, and a sample loop through the controls to get the needed info about Savable mode and UserSave state at runtime.
 

Attachments

  • vbnet35-ProviderComponent.zip
    19.1 KB · Views: 68
Thanks John!

Another piece of the puzzle solved!

Because I want to add this functionality to my ReportForms but do not want to touch existing code or fire control events on the existing controls, I am planning to overlay a transparent panel over the form and display the Image Controls on top of that. Fun part is that some of these reports conatain a tab controls so I will have to make the tab area clickable on the transparent panel and also show only the controls on the selected tab.
 
Because I want to add this functionality to my ReportForms but do not want to touch existing code or fire control events on the existing controls
Somewhat confusing, but in case this is related to this thread and the provider component I must comment that neither is the case with this component. The provider can be configured independently without changing any existing code, and it does not raise any events, it just handles events that is already raised by the controls (well ok it invalidates the icon area, and cause Paint of that). The natural thing to do is to just add one provider to form as in sample then select the controls and set the Savable property, but it can also be done in code with some calls like this:
VB.NET:
Me.SaveProvider1.SetSavable(Me.TextBox2, SaveProvider.Mode.AlwaysSave)
By the way, I found one bug, in SaveProvider.UserSave property the setter should set the 'value' (now 'True'):
VB.NET:
table(c).UserSave = value
 
Sorry for the confusion. What I am trying to accomplish with the transparent panel is to lock the UI down so that it can't be changed when the user selects the controls they want to save. When in Save Mode the user will not be able to change a value of a control or fire any events. I need to make a snap shot of the state of each control I plan on saving. When the user is done saving or cancelling the saving the criteria then the UI will be responsive again.
 
John thanks for all the help. I am developing a proof of concept using standard .net controls and once that is done I will be migrating it to our application. We have over 400 reports in this application and it is growing! One of the issues we were having is one client wants to have certain fields defaults one way and another something different and it our system configuration forms are going to get out of control.

Another piece of the puzzle will be that I will allow the user to save multiple versions of the criteria with one listed as a default. For example they could setup a report for each territory. That way they don't have to configure the report each time they want it run a certain way.

I added the saving of defaults to our search control for our data entry forms this was easy because I save all the control values. Report forms are differnt because there may be fields that should not save. Like Current Accounting Period that should always be populated as normal and not saved. Plus we have reports that if a check box check changes other controls are disabled or visibility is changed. That is why I need to lock down the UI>
 
Back
Top