GorillaApe
GorillaApe

Reputation: 3641

Custom error message in WPF / MVVM

I read most of the articles and i was happy that i could use attributes (with IDataErrorInfo) for validaton. That was great. But i wasted hours and still no good solution in order to display custom messages in cases that IDataErrorInfo isnt fired because casting has failed. No matter what error appears and it makes no sense and i want to translate it. Image

Should i apply a custom convertor or a custom validation rule ?

Upvotes: 2

Views: 2797

Answers (4)

hbarck
hbarck

Reputation: 2944

You might want to check out this blog post: http://wpfglue.wordpress.com/2012/05/06/checking-property-types-automatically/

It contains examples on how to set up ValidationRules so that type conversion errors are caught before they happen, and are translated into meaningful and localized error messages. IDataErrorInfo won't help you here, unless you really want to wrap all your properties into strings as suggested, which I wouldn't like to do. The reason is that IDataErrorInfo only is queried after setting the property on the bound object succeeded, which won't happen if the types don't match.

Upvotes: 1

blindmeis
blindmeis

Reputation: 22445

i use string properties in my viewmodels, so i could handle every input with idataerrorinfo. of course i have to parse my string properties to the right type when calling a service or put the value to my model.

another way would be to forbid input in your view. eg. with numeric only textbox.

or using Behaviors(Blend Sdk) like:

<TextBox Text="{Binding MyDecimalProperty}">
        <i:Interaction.Behaviors>
            <Behaviors:TextBoxInputBehavior InputMode="DecimalInput"/>
        </i:Interaction.Behaviors>
 </TextBox>

.cs

public class TextBoxInputBehavior : Behavior<TextBox>
{
    const NumberStyles validNumberStyles = NumberStyles.AllowDecimalPoint |
                                               NumberStyles.AllowThousands |
                                               NumberStyles.AllowLeadingSign;
    public TextBoxInputBehavior()
    {
        this.InputMode = TextBoxInputMode.None;
    }

    public TextBoxInputMode InputMode { get; set; }

    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.PreviewTextInput += AssociatedObjectPreviewTextInput;
        AssociatedObject.PreviewKeyDown += AssociatedObjectPreviewKeyDown;

        DataObject.AddPastingHandler(AssociatedObject, Pasting);

    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        AssociatedObject.PreviewTextInput -= AssociatedObjectPreviewTextInput;
        AssociatedObject.PreviewKeyDown -= AssociatedObjectPreviewKeyDown;

        DataObject.RemovePastingHandler(AssociatedObject, Pasting);
    }

    private void Pasting(object sender, DataObjectPastingEventArgs e)
    {
        if (e.DataObject.GetDataPresent(typeof(string)))
        {
            var pastedText = (string)e.DataObject.GetData(typeof(string));

            if (!this.IsValidInput(this.GetText(pastedText)))
            {
                System.Media.SystemSounds.Beep.Play();
                e.CancelCommand();
            }
        }
        else
        {
            System.Media.SystemSounds.Beep.Play();
            e.CancelCommand();
        }
    }

    private void AssociatedObjectPreviewKeyDown(object sender, KeyEventArgs e)
    {
        if (e.Key == Key.Space)
        {
            if (!this.IsValidInput(this.GetText(" ")))
            {
                System.Media.SystemSounds.Beep.Play();
                e.Handled = true;
            }
        }
    }

    private void AssociatedObjectPreviewTextInput(object sender, TextCompositionEventArgs e)
    {
        if (!this.IsValidInput(this.GetText(e.Text)))
        {
            System.Media.SystemSounds.Beep.Play();
            e.Handled = true;
        }
    }

    private string GetText(string input)
    {
        var txt = this.AssociatedObject;
        var realtext = txt.Text.Remove(txt.SelectionStart, txt.SelectionLength);
        var newtext = realtext.Insert(txt.CaretIndex, input);

        return newtext;
    }

    private bool IsValidInput(string input)
    {
        switch (InputMode)
        {
            case TextBoxInputMode.None:
                return true;
            case TextBoxInputMode.DigitInput:
                return CheckIsDigit(input);

            case TextBoxInputMode.DecimalInput:
                if (input.Contains("-"))
                    if (input.IndexOf("-") == 0 && input.LastIndexOf("-")==0)
                        return true;
                    else
                        return false;
                if (input.ToCharArray().Where(x => x == ',').Count() > 1)
                    return false;

                decimal d;
                return decimal.TryParse(input,validNumberStyles,CultureInfo.CurrentCulture, out d);
            default: throw new ArgumentException("Unknown TextBoxInputMode");

        }
        return true;
    }

    private bool CheckIsDigit(string wert)
    {
        return wert.ToCharArray().All(Char.IsDigit);
    }
}

public enum TextBoxInputMode
{
    None,
    DecimalInput,
    DigitInput
}

Upvotes: 0

Rohit Vats
Rohit Vats

Reputation: 81333

In case you want to customize the message, only way to do is implementing your own Validation rule. Refer to this link here for the code Custom Validation.

Upvotes: 0

Mohnkuchenzentrale
Mohnkuchenzentrale

Reputation: 5885

I don't know how you do Validation right now. But consider using the IDataErrorInfo Interface.

You can find an example here

http://codeblitz.wordpress.com/2009/05/08/wpf-validation-made-easy-with-idataerrorinfo/

Upvotes: 0

Related Questions