Dailycode.info

Short solution for short problems

Disallow non numeric characters in an asp.net textbox

A very simple javascript that will block the entry of non numeric characters:

function isNumberKey(evt) {

    var charCode = (evt.which) ? evt.which : event.keyCode

    if (charCode > 31 && (charCode < 48 || charCode > 57))

        return false;

 

    return true;

}

Simply put this on the onkeydown event:

<dx:ASPxTextBox runat="server" ID="txtTaskSortOrder" Width="50px" Value='<%# Eval("TaskSortOrder")%>'

MaxLength="5" onkeypress="return isNumberKey(event);">

</dx:ASPxTextBox>

 

If the user enters non numeric characters, the entry will be withhold and not allowed in the textbox.


Track changes without binding controls

When you are working with windows forms .net without binding your controls to a data object, you still need to check if a control is changed or not. For example if a user closes a form without saving it, you might want to tell the user this and ask him to save or not.

This post will show you a very simple implementation that will easy the development effort. To use this, you will need to create control wrappers for every control that you want to auto implement the track changes functionality.

Let’s begin with creating or modifying an interface that is implemented by all controls that need change tracking. I will add the property IsDirty to the interface. The interface is called: IControlBase

 

#region Properties

 

bool IsDirty { get; set; }

 

#endregion

 

Next we implement the interface in our controls and set the IsDirty flag whenever a value is changed. I will give 2 examples:

Textbox:

#region Implementation of IControlBase

 

public bool IsDirty

{

    get;

    set;

}

#endregion

protected override void OnKeyPress(KeyPressEventArgs e)

{

    IsDirty = true;

    base.OnKeyPress(e);

}

 

Combobox:

#region Implementation of IControlBase

 

public bool IsDirty

{

    get;

    set;

}

 

 

protected override void OnTextChanged(EventArgs e)

{

    IsDirty = true;

    base.OnTextChanged(e);

}

 

So this will make sure that the events we want will set the IsDirty flag. For the combobox I had the problem that the event was raised when I filled it, so I created a function that will clear all IsDirty flags after loading the form:

 

public static void ClearIsDirty(Control currentControl)

{

 

    foreach (Control isDirtyctrl in currentControl.Controls)

    {

        if (isDirtyctrl.Controls.Count > 0)

        {

            ClearIsDirty(isDirtyctrl);

        }

        if (isDirtyctrl.Parent as IControlBase != null)

        {

            ((IControlBase)isDirtyctrl.Parent).IsDirty = false;

        }

    }

}

 

After the page is loaded and all controls are filled, I simply run this function and pass the parent control to it. So it will clear itself and all child controls. The control could be a panel or the form itself:

GlobalFunctions.ClearIsDirty(tpGeneral);

 

Then the last piece of code needs to be written when the user leaves the form without saving. So the onleave of your control is captured and then we call a second function that will check all controls of the form on their IsDirty flag. Here is the function that does the trick, put this function somewhere in your general functions namespace.

private static bool _isDirty=false;

 

public static bool CheckIsDirty(Control currentControl)

{

 

    if ((_isDirty == true))

    {

        return _isDirty;

    }

 

    foreach (Control isDirtyctrl in currentControl.Controls)

    {

        if (isDirtyctrl.Controls.Count > 0)

        {

            CheckIsDirty(isDirtyctrl);

        }

        else

        {

            if (isDirtyctrl.Parent as IControlBase != null)

            {

                if ((((IControlBase)isDirtyctrl.Parent).IsDirty == true))

                {

                    _isDirty = true;

                    return _isDirty;

                }

            }

        }

    }

    return _isDirty;

}

 

Here is the implementation of this function (I check 2 tabpages on the IsDirtyFlag, if the user wants to save I save the project. At the end I clear the IsDirty flags):

private void tbcontProject_Leave(object sender, EventArgs e)

{

    if (GlobalFunctions.CheckIsDirty(tpGeneral) || GlobalFunctions.CheckIsDirty(tpInfo))

    {

        DialogResult res = MessageBox.Show("Information has been changed, do you want to save?", "Information changed", MessageBoxButtons.YesNo);

        if (res == DialogResult.Yes)

        {

            SaveProject();

        }

        GlobalFunctions.ClearIsDirty(tpGeneral);

        GlobalFunctions.ClearIsDirty(tpInfo);

    }

 


No validation with the "Cancel" button

I was having a hard time with some forms that had control validation occuring. Especially when the "Cancel" button was pressed, the validation still occured. I was looking for a property on the button called autovalidate or something, but I looked over the CausesValidation property. If you set this to false, the validation will be ignored! Thank god! Found this here: http://stackoverflow.com/questions/1882523/how-to-skip-validating-after-clicking-on-a-forms-cancel-button

 


Required field validation in WPF using MVVM

 

This article will describe simple validation handling for WPF and MVVM.

I first implemented input validation myself. This was OK, but the only down side was that the CanSave method of the relay command was not updated when I entered a value, only when I the field lost focus the update came and the Save button was enabled. This makes it hard to work if the Save button is the next action after filling in the value.

So I will discuss the implementation of the IDataErrorInfo interface. This actually will reflect in your Model, ViewModel and View. We will tell the View that ErrorHandling is done and that the property changed event will be the trigger to do the validation. Also, some styling is applied to visualize the error s. Then the Model will have to implement the actual validation sets telling the viewmodel whether the model is valid or not. Last the view model will have handle the actual save command and check the validation.

So let’s start from the bottom and work our way up. The model:

For the demo I used a simple popup screen that adds a record to the audio type list. The record consists of a name. This field we will use to implement the validation. An empty field is not allowed.

First implement the IDataErrorInfo interface to your class.

public class AudioTypeEnt: IDataErrorInfo

 

This  will expose 2 methods which we will use to handle the validation.

#region IDataErrorInfo Members

 

string IDataErrorInfo.Error { get { return null; } }
 
string IDataErrorInfo.this[string propertyName]
{
    get { return this.GetValidationError(propertyName); }
}
 
#endregion // IDataErrorInfo Members

 

Then we will need to explain what fields need to be validated and how:

string GetValidationError(string propertyName)

{

    if (Array.IndexOf(ValidatedProperties, propertyName) < 0)

        return null;

 

    string error = null;

 

    switch (propertyName)

    {

        case "Name":

            error = this.ValidateName();

            break;

 

        default:

            Debug.Fail("Unexpected property being validated on Audio Type: " + propertyName);

            break;

    }

 

    return error;

}

 

string ValidateName()

{

    if (IsStringMissing(this.Name))

    {

        return Strings.AudioTypeEnt_MissingName;

    }

    return null;

}

 

 

static bool IsStringMissing(string value)

{

    return

        String.IsNullOrEmpty(value) ||

        value.Trim() == String.Empty;

}

 

So here we checked if the Name property is not null or empty or has some spaces. If any of these, we will return the error string coming from a resource file telling that the name field is still missing entry.

Last what we need to implement in the model is the IsValid method. This will be called be the viewmodel to check the validity of the record.

/// <summary> 

/// Returns true if this object has no validation errors. 

/// </summary> 

public bool IsValid

{

    get

    {

        foreach (string property in ValidatedProperties)

            if (GetValidationError(property) != null)

                return false;

 

        return true;

    }

}

 

static readonly string[] ValidatedProperties =

{

    "Name"

};

 

So let’s move on to The view model. There we simply check the data and trigger a requery so the relaycommands can reflect this.  Also here the view model will have to implement the IDataErrorInfo

 interface:  public class AddAudioTypeViewModel : WorkspaceViewModel, IDataErrorInfo

 

#region Error Handling

 

public string Error

{

    get { return (AudioTypeRecord as IDataErrorInfo).Error; }

}

 

public string this[string propertyName]

{

    get

    {

        string error = null;

 

 

        error = (AudioTypeRecord as IDataErrorInfo)[propertyName];

 

        // Dirty the commands registered with CommandManager,

        // such as our Save command, so that they are queried

        // to see if they can execute now.

        CommandManager.InvalidateRequerySuggested();

 

        return error;

    }

}

 

#endregion

 

We implement the interface methods. You can do some extra validation here, but we just pass on the error and trigger the Requery by calling the CommandManager.InvalidateRequerySuggeted method.

Then we can tell the CanSave method what to check. And when a save is triggered we will again check if the record is valid, if so, just save it.

 

/// <summary>

/// Saves the audio to the repository.  This method is invoked by the SaveCommand.

/// </summary>

public void Save()

{

    if (!AudioTypeRecord.IsValid)

        throw new InvalidOperationException(Strings.AudioTypeEnt_MissingName);

    else

    {

/// Saves the audio to the repository

        GenericSingleton<RepositoryHold>.GetInstance().AudioRep.AddAudioTypeRec(new AudioTypeEnt() { Name = Name, Valid = true });

        base.OnPropertyChanged("DisplayName");

        RequestClose();

    }

}

 

/// <summary>

/// Returns true if the audio type is valid and can be saved.

/// </summary>

bool CanSave

{

    get { return AudioTypeRecord.IsValid; }

}

 

Last we have to tell the view that which field needs to be checked and what will trigger the validation check. This has to be done in the binding:

<TextBox Text="{Binding Name, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}"

 

If you like, you can add a content presenter to show the errors that come from the view model:

<ContentPresenter

      Grid.Row="1" Grid.Column="1"

      Content="{Binding ElementName=txtName, Path=(Validation.Errors).CurrentItem}" Margin="10,0" />

 

Finally we define a template for the validation errors in the view:

<DataTemplate DataType="{x:Type ValidationError}">

                <TextBlock

          FontStyle="Italic"

          Foreground="Red"

          HorizontalAlignment="Left"

          Margin="0,1"

          Text="{Binding Path=ErrorContent}"

          />

 

And that should do it!

 

Nothing filled in:

After the first character is entered the save button is enabled:


Using client side validation in .Net

In this next example I will briefly explain the use of validators in ASP.Net

When we create web applications, there is always a need to perform client-site validation on user input. .Net really facilitates this with the use of validators. I will show the use of a regularexpression validator.

I have a form that needs some numeric input. It has to be a positive number. When the input is wrong, I don't want my button event to fire. Since I'm using AJAX no postback will be fire anyway, but still the button event is fired. So this is how to correctly handle a situation like this:

First place the input and button on the screen, next to the input you can place the regularexpression validator.

The ASP code looks like this:

<tr>
                        <td>
                            <asp:Label ID="Label5" runat="server" Text="Lot number"></asp:Label>
                        </td>
                        <td>
                            <asp:TextBox ID="txtLotNumber" runat="server" ValidationGroup="Numeric">0
			    </asp:TextBox>    
                        </td>
                        <td>
                            <asp:RegularExpressionValidator ID="RegularExpressionValidator3" 
				runat="server" 
                                ControlToValidate="txtLotNumber" 
                                ErrorMessage="Please provide a positive number" 
				ValidationExpression="^\d+$" 
                                ValidationGroup="Numeric"></asp:RegularExpressionValidator>
                        </td>
                    </tr>
                    <tr>
                        <td><asp:Button ID="btnSave" Text="Save" runat="server" 
				OnClick="btnSave_Click" 
                                ValidationGroup="Numeric" Width="100px" /></td>
                    </tr>

The validationexpression is: ^\d+$ More on these expressions you can find here. As you can see I called the validationgroup numeric. This is because we only have numeric validation on this button event. If you have several validations that need to be performed with this button event, then you'll need a more general validation group name, keep in mind that you can only use 1 validationgroup for the button. When the button is in the same validationgroup as the regularexpressionvalidator, the button event will only be fired when the validations are valid. You can use several validationgroups on one page, in case you have more then 1 event that needs validation. No postback or event are triggered making the user expirience on your site better. No time wasted on refreshing screens or manually perfomring validations. No problems with control states.  

After the regular expression was checked, I needed to check on required fields. This is not possible using regular expression validators, so I had to add a required field validator. Using the designer of VS 2008, I added this validator. Then when I tried out the validators, it appeared that the second validator had a space between the control to validate. I googled and found out that you need to set the Display to dynamic. In the following example I changed the first 2 controls, the last I didn't change, you can see the result at the picture below the code:

<tr>
    <td>
        <asp:Label ID="Label3" runat="server" Text="Amount"></asp:Label>
    </td>
    <td>
        <asp:TextBox ID="txtAmount" runat="server" ValidationGroup="SaveArrival"></asp:TextBox>    
    </td>
    <td>
        <asp:RegularExpressionValidator ID="RegularExpressionValidator2" runat="server" 
            ControlToValidate="txtAmount" ErrorMessage="Please provide a positive number" 
            ValidationExpression="^\d+$" ValidationGroup="SaveArrival" 
            Display="Dynamic"></asp:RegularExpressionValidator>
        <asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server" 
            ControlToValidate="txtAmount" ErrorMessage="Amount is required!" 
            ValidationGroup="SaveArrival" Display="Dynamic"></asp:RequiredFieldValidator>
    </td>
</tr>
<tr>
    <td>
        <asp:Label ID="Label4" runat="server" Text="K number"></asp:Label>
    </td>
    <td>
        <asp:TextBox ID="txtKNumber" runat="server" ValidationGroup="SaveArrival">0</asp:TextBox>    
    </td>
    <td>
        <asp:RegularExpressionValidator ID="RegularExpressionValidator1" runat="server" 
            ControlToValidate="txtKNumber" ErrorMessage="Please provide a positive number" 
            ValidationExpression="^\d+$" ValidationGroup="SaveArrival" 
            Display="Dynamic"></asp:RegularExpressionValidator>
        <asp:RequiredFieldValidator ID="RequiredFieldValidator2" runat="server" 
            ControlToValidate="txtKNumber" ErrorMessage="K number is required!" 
            ValidationGroup="SaveArrival" Display="Dynamic"></asp:RequiredFieldValidator>
    </td>
</tr>
<tr>
    <td>
        <asp:Label ID="Label5" runat="server" Text="Lot number"></asp:Label>
    </td>
    <td>
        <asp:TextBox ID="txtLotNumber" runat="server" ValidationGroup="SaveArrival">0</asp:TextBox>    
    </td>
    <td>
        <asp:RegularExpressionValidator ID="RegularExpressionValidator3" runat="server" 
            ControlToValidate="txtLotNumber" 
            ErrorMessage="Please provide a positive number" ValidationExpression="^\d+$" 
            ValidationGroup="SaveArrival"></asp:RegularExpressionValidator>
        <asp:RequiredFieldValidator ID="RequiredFieldValidator3" runat="server" 
            ControlToValidate="txtLotNumber" ErrorMessage="Lot number is required!" 
            ValidationGroup="SaveArrival"></asp:RequiredFieldValidator>
    </td>
</tr>
 

 

As you can see I used SaveArrival as ValidationGroup, because I now have more then 1 validation type.