Validating an ASP.NET user control from its parent page - c#

I have an asp.net page with a button. This button generates and inserts a user control into the page, so many controls could exist on one page. I need to validate that a certain dynamically generated control inside the generated control exists.
So..Page has 0 to N Control1’s. Each Control 1 can have 0 to N Control2’s. When SaveButton is clicked on Page, I need to make sure there are at least 1 Control2’s inside every Control1.
I’m currently between two options:
• Dynamically insert CustomValidators for each control that is generated, each of which would validate one Control1.
• Do the validation manually (with jQuery), calling a validation function from SaveButton.OnClientClick.
Both are sloppy in their own way – which is why I’m sharing this with you all. Am I missing the easy solution?
Thanks in advance.. (btw – anything up to and including .NET 3.5 SP1 is fair game)

Hmm i like the Interface idea suggested by digiguru but i would use the interface on the container Control1 instead of the sub controls as it seems like the more logical place for the code to live. Heres my take on it:
public interface IValidatableControl
{
bool IsValidControl();
}
then implement this on your Control1
public class Control1 : IValidatableControl
{
... Other methods
public bool IsValidControl()
{
foreach(object c in this.Controls)
{
if(c.GetType() == "Control2")
return true;
}
return false;
}
}
There are probably better ways to write this but it should give you enough of an idea to get started.

If you are adding user controls on the fly, you could make each control implement the same interface with a Validate function. That way you can load the controls into a placeholder in each parent control on the page. When the page is submitted, simply loop through the controls in the placeholder, cast them to the interface class and then call the validate function. I doesn't use custom validators, but you can build up a list of validation errors using the object returned from the validate function, you can render this collection of validation errors whichever way you like.

I think you could do it by assigning a public property in Control1 that references the existence of Control2's ID, and then decorate Control1's class with ValidationProperty. I'm thinking something along these lines:
[ValidationProperty("Control2Ref")]
public partial class Control1 : UserControl
{
public string Control2Ref
{
get { return FindControl("Control2"); }
}
// rest of control 1 class
}
And then you should be able to point a RequiredFieldValidator at an instance of Control1.

One method you could try is creating and maintaining a simple xml structure that represents your custom control hierarchy. Insert or delete from this structure any time you create or destroy a custom user control. Upon save, validate that the control hierarchy represented in the xml structure is correct. You could save the xml in the Session object to persist it across postbacks.

Related

Validating user input / Give .NET controls status OK or NOK

I'm thinking about the best way to validate user input.
Let's imagine some TextBoxes, CheckBoxes or whatever .NET control you please, where the user input has to be validated as OK or NOK. As soon as the user's filled up all required fields he submits via a button.
Now I have to know which fields were previously confirmed as OK and which as NOK. By now I've always handled such cases by declaring a global bool variable for every control to tell me so. But I don't like that...
I'm pretty sure there must be another way! What I would like to do is expanding these .NET controls with a OK or NOK property called status or similar. Can you do that? And if so how do you do it? Is something like that already existing?
Thank you for your response!
You have some useful features in windows forms to perform validation and show error messages including:
IDataErrorInfo Interface
Validating Event of Controls
ErrorProvider Component
ValidateChildren Method and AutoValidate Property of Form
Using above options:
You can perform validation when you are using data-binding to model classes.
You van perform validation when you don't use data-binding.
You can show error messages and an error icon near the controls which are in invalid states.
You can decide to prevent the focus change from invalid controls or let the focus change.
You can show a validation summary for your form.
You can also apply DataAnnotations Validations in Windows Forms
IDataErrorInfo Interface
In cases which you have some model classes, the best fit for validation and providing error messages in windows forms is implementing IDataErrorInfo. It's supported by data-binding mechanisms and some windows forms control like DataGridView and ErrorProvider.
To keep things simple you can write validation rules in your class and return error messages using IDataErrorInfo properties. Even if you want to apply a more advanced scenario like using validation engines, at last it's better to implement IDataErrorInfo to gain most consistency with widows forms.
You will use an ErrorProvider to show error messages. It's enough to bind it to your data source and it shows errors automatically.
Validating Event of Controls
In cases that you don't have model classes and all validations should be done against controls, the best option is using Validating event of controls. There you can set e.Cancel = true to set the control state as invalid. Then you can prevent focus change or use the state of control in getting validation summary.
In this case you will use an ErrorProvider to show errors. It's enough to set an error for a control in Validating event this way: errorProvider1.SetError(control1, "Some Error") or you can set an empty error message to remove validation error.
ErrorProvider Component
In both cases when you use databinding or when you use Validating event, as mentioned above, ErrorProvider shows and error icon with a tooltip that shows error message for you near the controls. (DataGridView uses its own mechanism to show errors on rows and cells, without using an ErrorProvider.)
You can also use the component to get a validation summary for your form using GetError method of the component which return the error message of each control.
ValidateChildren Method and AutoValidate Property of Form
You can use ValidateChildren method of form or your container control to check if there is a validation error for your controls or not.
Based on the value of AutoValidate property of your form, it prevents focus change or let the focus change from invalid controls.
Save the names of your controls to be validated into an array and then just loop through them. You can also set a validation function onto them, if you want to.
var elements = new[] {
new { Control = textBox1 },
new { Control = textBox2 }
};
foreach (var elem in elements)
{
elem.Control.BackColor = string.IsNullOrWhiteSpace(elem.Control.Text) ? Color.Yellow : Color.White;
}
Wrap your Elem array into class objects to add a "ok" property.
It really depends how deep you want to delve into that rabbit hole...
You need to decide on the validation statuses - if it's simply a case of Yes/No, then Boolean/bool will suffice, otherwise you should consider creating an enumeration to hold your validation statuses.
You will need to decide whether you want to extend the controls that require validation, or just use the control's Tag property to store the validation status (personally I think that using Tag to do this is hideous).
An Example:
// Provides your validation statuses.
public enum ControlValidation
{
Ok,
NotOk
}
// Provides a contract whereby your controls implement a validation property, indicating their status.
public interface IValidationControl
{
ControlValidation ValidationStatus { get; private set; }
}
// An example of the interface implementation...
public class TextBox : System.Windows.Forms.TextBox, IValidationControl
{
public ControlValidation ValidationStatus { get; private set; }
...
protected override void OnTextChanged(EventArgs e)
{
ValidationStatus = ControlValidation.Ok;
}
}
All winforms components have a "spare" property which you can use: Tag. It's an object and you can assign whatever to it: it's not used for anything by the framework, and it's useful for cases like this.
If this is going to be generalized, you can just derive your controls and add your properties, but for a one-time single-property, Tag could perfectly work.
// OK
myTextBox.Tag = true;
// NOK
myTextBox.Tag = false;
// Undefined
myTextBox.Tag = null;
To check:
if(myTextBox.Tag is bool)
{
var isOk = (bool)myTextBox.Tag;
if(isOk)
{
// It's OK
} else {
// It's NOK
}
} else {
// It's undefined
}
All that said, I use Tag for simple things and simple logics. If you plan to have more properties or it's a generalized thing... either use the validation mechanisms explained in the other answers, or derive your controls:
public class MyTextBox : System.Windows.Forms.TextBox
{
public bool ValidationOK { get; set; }
}
And change the controls to MyTextBox (if you already have them, open the designer.cs file and change all instances of System.Windows.Forms.TextBox to <yourNamespace>.MyTextBox), etc.

Correct UserControl Usage?

I just started breaking up my GUI application into UserControls. I have a TabControl with a bunch of TagePages. Obviously my MainForm.cs file was filled up with tons of events and controls etc and it got very messy quick.
So a previous question gained me the insight of how to create a UserControl. I intend on creating a UserControl for each TabPage and I was wondering how I can interact with Components on the main form or other UserControls.
Here is an example of a TabPage that I have made using a UserControl, which needs to Enable or Disable a button depending which TabPage is currently selected. Is this proper usage or is there a better way?
public partial class TabDetails : UserControl
{
private RequestForm fRequestForm;
public TabDetails()
{
InitializeComponent();
}
public void CustomInitialization(RequestForm pRequestForm)
{
fRequestForm = pRequestForm;
pRequestForm.TabControl_Main.SelectedIndexChanged += SelectedTabIndexChanged;
}
private void SelectedTabIndexChanged(object pSender, EventArgs pEvents)
{
fRequestForm.Button_SubmitRequest.Enabled = fRequestForm.TabControl_Main.SelectedTab != fRequestForm.Tab_Details;
}
}
In the MainForm.cs constructor I call:
this.tab_Details1.CustomInitialization(this);
This doesn't look like a good use of a user control. The user control should not decide how things in the form should behave when something is changed in the user control. A user control should be unaware of its container and should operate in any container.
The user control should notify the form that something has changed without telling what's the internal implementation and the form should decide what to do.
Example:
A user control named "NameUserControl" consists of TitleComboBox, FirstNameTextBox and LastNameTextBox. The user control wants to notify when one of the values has changed.
Wrong Way:
Create events:
TitleComboBox - SelectedIndexChanged.
FirstNameTextBox, LastNameTextBox - TextChanged.
The problems here:
You expose the internal controls behavior. What will happen if you want to change the TitleComboBox to TextBox? You'll have to change the event name and implementation.
You expose the fact that you use exactly 3 different controls. What will happen if you want to use the same text box for first and last name? You'll have to delete one event and change the name of the other.
Good Way:
Create only a single event: NameChanged and expose 1 property of FullName or three different properties for the values.
Either way the form subscribe to the event and decide what to do next.
Another thing to think about: the more you add more functionality to your user control, you either make it less reusable or you make its code more complex. For example, if you add validation inside the user control, you'll find one day that you need it without validation, so you'll add a property "bool ValidateData" or it will be so complicated that you'll need to build another control. One way to solve that is to build very small user controls, but combine them in one or more bigger user controls that fit all your current needs.

C# Using a form to load other user controls and have access to a base control or property

Currently I have a C# program with a windows form and then a user control template put onto the form. The user control template is really just used as a placeholder. I have a series of other controls which inherit from this user control template.
Each of those controls have navigation buttons like 'Continue' and 'Back' on them and each control knows which control needs to be loaded next. However what I need to figure out is an easier way to have variables that are global to these controls.
The only workaround I have is that I pass the form to each control when they are loaded and use variables inside of the form to read and write to. What would be the proper way to have each of these user control screens be built off of a base control which contained objects all of the controls could get to?
Sorry for the rambling nature of the post but I've been thinking about this problem all morning.
Here is some of the code:
Most of what I have written was based on hiding and showing the user controls so that content in the controls wouldn't be lost during navigation. I won't be needing to do that as eventually it will be loading the fields of data from a database.
Code for initially loading control from form click:
conTemplate1.Controls.Clear();
conInbound Inbound = new conInbound(this);
Inbound.Dock = DockStyle.Fill;
Inbound.Anchor = (AnchorStyles.Left | AnchorStyles.Top);
conTemplate1.Controls.Add(Inbound);
Code for Continue button inside of one of the controls:
if ((Parent.Controls.Count - 1) <= Parent.Controls.IndexOf(this))
{
UserControl nextControl = new conPartialClear();
nextControl.Dock = DockStyle.Fill;
Parent.Controls.Add(nextControl);
this.Hide();
Parent.Controls[Parent.Controls.IndexOf(this) + 1].Show();
}
else
{
this.Hide();
Parent.Controls[Parent.Controls.IndexOf(this) + 1].Show();
}
The best-practice for communicating from a control to a parent is to use events, and for communicating from a parent to a control is to call methods.
However, if you don't want to or can't follow this practice, here's what I would recommend.
Each UserControl has a ParentForm property that returns the Form that contains the control. If you know that the UserControl will always be attached to MyParentForm, you just cast the ParentForm and then you can access all public controls, methods, etc.
Here's what I mean:
public class conTemplate
{
public MyParentForm MyParentForm
{
get
{
return (MyParentForm)this.ParentForm;
}
}
}
This way, you can easily access any public members of MyParentForm. Your conInbound class could have code such as this.MyParentForm.GlobalSettings.etc..., and could even have access to any public controls.
I'm not totally sure I understand your problem. It sounds like you want the user control to "do something" with it's parent form. If that's the case, you may want to consider adding events to the UC and then handle them on the form itself.
Basically, for your UC's "continue", you'll have an event that's fired when it's pressed. You'll want to handle that in your form. I'm not real sure about the syntax from memory, or I'd work something out for you code-wise. But I think that's the route you'll want to take. Think of your UC like any other windows form control. If you add a button to your form, you assign it it's event method. Do the same with the UC.
I found this and thought it may be helpful. Scroll down to where it talks about UC's and events.
http://www.akadia.com/services/dotnet_user_controls.html
Hope this helps.
EDIT after new info from OP.
You could declare a global variable inside the UC of type yourForm and then set that variable to the ParentForm at run-time, if I'm understanding you correctly.
So, inside your UC Class, you could do:
private parentFormInstance;
then inside the constructor of the UC, you could set it as such:
parentFormInstance = this.ParentForm; (or whatever the property name is).
This allows you at design-time to use:
parentFormInstance.DoSomething();
without the compiler yelling at you.
Just basic advice, but if you can go back and make it easier on yourself, even if it takes some additional time re-working things, it'd be worth it. It may save you time in the long run.

Is there an elegant / simple way to handle the logical flow for a wizard UI

I have a WebForm that consists of a few dozen UI elements (all server controls). I have turned the whole page into a wizard-like user experience by separating the UI elements into distinct steps using asp:Placeholder controls and setting their visibilities between postbacks. Only one placeholder is visible at a time.
If the UI experience consisted solely of moving from Step 1 through Step 10, it's pretty trivial to create a generic Next / Prev button handler to move through the steps. However, there are a handful of UI elements that determine which steps (wizard panes) get displayed. (two radio buttons, two checkboxes on different pages)
Is there an elegant or simple way to achieve this flow logic without creating a dedicated Click handler for every Next/Prev button on the page? If there is a pattern for this, it eludes me.
Take a look at this: jQuery Form Wizard
I recently converted a form into a wizard and it works very nice. It might just work for you.
Create a Master Page
Add your nav buttons to the master page.
Create an abstract base class that inherits from Web.UI.Page.
public abstract class WizardPage: Page
{
public abstract void NextStep();
public abstract void PreviousStep();
}
Add abstract methods for NextStep and PreviousStep.
Create each wizard step as a separate page that inherits from the base class.
Implement NextStep and PreviousStep step on each page with the appropriate navigation.
Wire your buttons on the master page to call the active page's method...
protected void NextButton_Click(object sender, EventArgs e)
{
if (Page is WizardPage)
{
WizardPage wizPage = (WizardPage)Page;
wizPage.NextStep();
}
}
All that being said -- I'd check out the wizard control.
I've used the Wizard control a number of times to handle the type of situation you describe. It takes a bit of study, like anything else, but can be made to provide a nice user experience. Controls that should be shown at each step of the wizard are declared inside a WizardStep template and it works out just great.

What is the best way to access elements of parent control from a child control?

I have a parent control (main form) and a child control (user control). The child control has some code, which determines what functions the application can perform (e.g. save files, write logs etc.). I need to show/hide, enable/disable main menu items of the main form according to the functionality. As I can't just write MainMenu.MenuItem1.Visible = false; (the main menu is not visible from the child control), I fire an event in the child control and handle this event on the main form. The problem is I need to pass what elements of the menu need to be shown/hidden. To do this I created an enum, showing what to do with the item
public enum ItemMode
{
TRUE, FALSE, NONE
}
Then I created my eventargs which have 6 parameters of type ItemMode (there are 6 menu items I need to manage). So any time I need to show the 1st item, hide the 2nd and do nothing with the rest I have to write something like this
e = new ItemModeEventArgs(ItemMode.TRUE, ItemMode.FALSE, ItemMode.NONE, ItemMode.NONE, ItemMode.NONE, ItemMode.NONE);
FireMyEvent(e);
This seems like too much code to me and what's more, what if I need to manage 10 items in future? Then I will have to rewrite all the constructors just to add 4 more NONEs.
I believe there's a better way of doing this, but I just can't figure out what it is.
you could create an EventArgs which takes an ItemMode[] or a List<ItemMode> or a Dictionary<string, ItemMode> for those items (instead of the current 6 arguments) - that way you don't need to change much when adding more items...
The chain child->parent can be reversed. In such scenario requests will be passed from the mainform to its child controls.
Controls participating in the command processing must implement a special interface:
interface ICommandHandler
{
bool CanInvoke(int commandId);
void InvokeCommand(int commandId);
bool UpdateCommand(int commandId, MenuItem item);
}
The advantage of this approach is that only active controls must be traversed, not all the children.
The weak point - UpdateCommand() method, which could be called from Application.Idle event or timer.
hope this helps
Well, I can't speak to a "best" way unless except in specific cases, since there are often several equally good ways. My first thought, though, would be to create a class that has a property which the parent assigns a reference of its MainMenu, and which has functions for enabling/disabling individual menus or items. In a very simple case, this could be as simple as passing a list of strings like "OptionsScreen=enabled" etc. and then inside the class manually handling those cases, to something more generic like passing strings such as "mnuToolsOptions=enabled" and then finding the menu item via the .Name property. So, on startup, create an instance of your menu handler class, then do something like MenuHandlerHelper.MenuToHandle = MainMenuStrip;.
On the child side, you could perhaps have your classes that update the MainMenu be derived UserObjects that derive from a common one you create that has a public MyMainMenuHandlerHelper MenuHandlerHelper property, and set that in your Parent form's constructor so the Child controls could call the menu updating function. Or, you could have an event that just passed back a List<string> containing all the rules, and fire that as you are doing now.
This is a very simple idea, and doesn't handle things like possible collisions, so you would probably either want to throw an exception (easiest). You might also want to have rule priorities (easy), or try to chain functionality (could be hard to determine orders and such).
I would be happy to implement some examples of my thinking if you can constrain the problem a little for me (desired collision handling, etc.) and I actually wanted to see what some basic code would look like and try perhaps to test a couple of ideas, so if those come to anything I will post the code here for those as well.
If you want to handle all changes from the user control: you could inherit your own user control class and add a reference to the form/collection of menu entries you want to be able to modify. You would pass this reference to its constructor and then you'll be able to easily modify the menu from inside your user control
If, on the other hand, you would like to manage this on an event basis in your form, you could implement your own EventArgs class, but I would do it like this:
class ItemModeEventArgs
{
MenuItemClass target;
EnumType change;
}
So basically for each menu item a separate event is risen. Every event args knows about what item menu is changing and how it is changing. Ofc, if you only have two states for the menu items, the 'change' field is kinda useless.
This way you don't have to hardcode functions with n parameters where n is the number of menu items.
There truly are many ways this could be done. The easiest way, although some will shout "bad practice", would be to just pass a pointer to the main menu when the control is created. Your control would have some code like this:
MenuStrip MainMenu;
internal void SetMainMenu(MenuStrip mainMenu)
{
MainMenu = mainMenu;
}
and when you create the control:
void CreateControl()
{
MyUserControlType MyControl = new MyUserControlType();
MyControl.SetMainMenu(mainMenuStrip); //or whatever you called your main menu
}
This will give your child form unlimited access to the mainform's menu (which is why it's technically a bad practice). From the child form you can access the submenus by name, eg:
if (MainMenu != null)
{
ToolStripMenuItem fileMenu =
(ToolStripMenuItem)MainMenu.Items["fileToolStripMenuItem"];
fileMenu.DropDownItems["exportFileToolStripItem"].Visible = false;
}
If you created the control in the designer, then you can add the SetMainMenu call into the .design file, or add it in the Form's load event.

Categories