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.
Related
I have a main form with some buttons, textboxes, labels, etc.
On a second form I would like to copy the text from the main forms textbox onto the second form.
Have tried:
var form = new MainScreen();
TextBox tb= form.Controls["textboxMain"] as TextBox;
textboxSecond.Text = tb.Text;
But it just causes an exception. The main screen textbox is initialised and contains text.
When I hover over form I can see all the controls are there.
What am I doing wrong?
Looking at the original code, there are two potential reasons for the NullReferenceException you are getting. First, tb is not defined in the code you provide so I am not sure what that is.
Secondly, TextBox textbox = form.Controls["textboxMain"] as TextBox can return null if the control is not found or is not a TextBox. Controls, by default, are marked with the private accessor, which leads me to suspect that form.Controls[...] will return null for private members.
While marking the controls as internal will potentially fix this issue, it's really not the best way to tackle this situation and will only lead to poor coding habits in the future. private accessors on controls are perfectly fine.
A better way to share the data between the forms would be with public properties. For example, let's say you have a TextBox on your main screen called usernameTextBox and want to expose it publicly to other forms:
public string Username
{
get { return usernameTextBox.Text; }
set { usernameTextBox.Text = value; }
}
Then all you would have to do in your code is:
var form = new MainForm();
myTextBox.Text = form.Username; // Get the username TextBox value
form.Username = myTextBox.Text; // Set the username TextBox value
The great part about this solution is that you have better control of how data is stored via properties. Your get and set actions can contain logic, set multiple values, perform validation, and various other functionality.
If you are using WPF I would recommend looking up the MVVM pattern as it allows you to do similar with object states.
PhoenixReborn is correct. The problem is that you are creating a new MainScreen, which means that new controls are created, so unless the text in your controls are initialized in the form constructor, they are going to be empty. Usually, the way to handle this is to pass the first form instance to the second form, like this:
SecondForm second = new SecondForm(this);
and in the second form:
public SecondForm (MainForm form)
{
// do something with form, like save it to a property or access it's controls
}
That way, the second form will have access to the first form's controls. You might consider making the properties you need to use public (in the designer properties pane). That way you can just do form.textboxMain.Text.
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.
I am having trouble with the MVVM pattern and Commands in my WPF app. The problem is not so much the MVVM pattern, but more the stuff that is going on on my GUI. I'll explain the situation:
My app can DoStuff to some files. I have a class with a function DoStuff(int limit). My user user interface has the following items:
A Button DoStuffBtn to start parsing.
A TextBox LimitTxt to fill in a limit.
A CheckBox LimitChk to enabled or disable the limit.
When you would "uncheck" LimitChk, then LimitTxt.Text = "" and LimitTxt.IsEnabled = false. When you would "check" LimitChk, then LimitTxt.IsEnabled = false again, but the text remains empty until you fill something in.
I have read many tutorials on Commands in WPF and MVVM but I just can't seem to pour my case into that mold. The example I gave is actually just a small part of my UI, but I can't seem to do this nicely either.
I keep running into questions like:
Do I need two Commands for LimitChk (enable and disable) or just one (toggle)?
If I bind an int to LimitTxt, what happens if I make it empty and disable it?
Is it a clean way to just use DoStuff(Int32.Parse(LimitTxt.Text)) when DoStuffBtn is pressed?
If I use two commands on LimitChk, what happens with the CanExecute() function of ICommand that determines whether LimitChk is enabled?
So the main question is: How would the situation I described fit into a nice pattern using Commands in WPF?
Some links on WPF, Commands and MVVM i've looked at:
http://www.devx.com/DevX/Article/37893/0/page/1
http://msdn.microsoft.com/en-us/magazine/cc785480.aspx?pr=blog
http://jmorrill.hjtcentral.com/Home/tabid/428/EntryId/432/MVVM-for-Tarded-Folks-Like-Me-or-MVVM-and-What-it-Means-to-Me.aspx
http://msdn.microsoft.com/en-us/magazine/dd419663.aspx
What I understand so far is that I have to keep as much as possible out of the UI. Even stuff like UI influencing the UI. I.e. unchecking LimitChk disables LimitText. Still, I think I should keep a difference between UI related information and actions and stuff that actually has to do with the actual work that has to be done.
I think you're getting confused... you don't need any commands here, you can just use bindings.
Do I need two Commands for LimitChk (enable and disable) or just one (toggle)?
You need none. Just create a LimitEnabled property in your ViewModel, and bind the CheckBox to it (IsChecked="{Binding LimitEnabled}")
If I bind an int to LimitTxt, what happens if I make it empty and disable it?
Disabling it has no effect. If you make the TextBox empty, the binding will fail because an empty string can't be converted to an int (at least not with the default converter)
Is it a clean way to just use Parse(Int32.Parse(LimitTxt.Text)) when ParseBtn is pressed?
You don't need to. Just create a Limit property in your ViewModel, and bind the TextBox to it. You might want to add an ExceptionValidationRule to the Binding so that it highlights invalid input.
The button is not necessary, the parsing will be done automatically when the TextBox loses focus (if you use the default UpdateSourceTrigger). If you want to customize the way it's parsed, you can create a custom converter to use in the binding.
Just some high level thoughts, leaving out superfluous stuff like Color and alignment attributes, WrapPanels, etc.
Your ViewModel has a a couple properties:
public bool? LimitIsChecked { get; set; }
public bool LimitTextIsEnabled { get; set; } //to be expanded, below
public ICommand ParseCommand { get; private set; } // to be expanded, below
public string LimitValue { get; set; } // further explanation, below
Your XAML has CheckBox and TextBox definitions something like:
<CheckBox Content="Limit Enabled" IsChecked="{Binding LimitIsChecked}" />
<TextBox Text="{Binding LimitValue}" IsEnabled="{Binding LimitIsEnabled}" />
<Button Content="Parse" Command="{Binding ParseCommand}" />
You'll want to initialize ParseCommand something like this:
this.ParseCommand = new DelegateCommand<object>(parseFile);
Now, let's fill in that LimitTextIsEnabled property too:
public bool LimitTextIsEnabled {
// Explicit comparison because CheckBox.IsChecked is nullable.
get { return this.LimitIsChecked == true; }
private set { }
}
Your parseFile method would then pass the value of the LimitValue property to the logic doing the actual parsing.
I declared the LimitValue property as string here to avoid cluttering up the code with an explicit converter, or other validation code. You could choose to handle that "LimitValue is a valid int" verification/conversion in several different ways.
Of course, I haven't implemented this in its entirety, but I wanted to outline a pattern where you are not using Commands to update the state of the other widgets. Instead, bind those attributes to properties that are managed in your ViewModel.
I did some googling and didn't find an answer to this puzzle.
Provided you have the following:
MySuperView
MySuperViewModel
MySuperView has two textboxes both bound to string properties on the ViewModel
and your using a DelegateCommand to bind your 'Save' button to the ViewModel using syntax such as:
ViewModel:
this.SaveOrderCommand = new DelegateCommand<object>(this.Save, this.CanSave);
View:
Command="{Binding SaveOrderCommand}"
How do you deal with UI Elements to make the User Interaction more pleasing. For example, lets say some lower level failure occurring during the save action of the DelegateCommand and you would like to trigger the tooltip of one of the TextBoxs. How would this typically occur?
I'd like to stick with as clean code-behind as possible but I am not adverse to putting UI specific code in there.
I would recommend that your ViewModel implement IDataErrorInfo so you can take advantage of validation stuff in WPF. You don't need to wait until someone clicks the save button, once the textbox gets updated it will be validated.
public string this[ColumnName]
{
if (Column == "TextProperty")
{
if(!ValidateTextProperty())
return "TextProperty is invalid";
}
}
void Save(object param)
{
if (CanSave)
{
if (string.IsNullOrEmpty(this["TextProperty"])
{
//Add Save code here
}
}
}
In your View:
<TextBox Text={Binding TextProperty, ValidateOnDataErrors="true",
UpdateSourceTrigger=PropertyChanged}/>
This will put a red box around the textbox and you can add a validation error template to the textbox style to add a tooltip see here
To show exceptions in a tooltip, I would add a property to the ViewModel that exposes the error message as a string, and bind that to your TextBox's ToolTip. Then in your Save method, you would start by setting that property to the empty string, and then doing all the real work inside a try..catch that, if an exception occurs, pushes the exception message into that property, so it automatically shows up in the ToolTip.
You would need to provide change notification for your property, either by making it a DependencyProperty or by using INotifyPropertyChanged.
Basically, you would want a create properties for your view to observe (usually through triggers) that would update your UI depending on what is happening in your code execution.
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.