Implement Generic Max Length validation in WPF Entity Framework

Rohit.Agrawal
Posted by in WPF category on for Intermediate level | Points: 250 | Views : 4666 red flag
Rating: 5 out of 5  
 1 vote(s)

In Win-form implementing "Maximum Length validation is easy using EDMX file. But in WPF it is bit tricky to achieve the same validation by reading EDMX file.

This article would show you how to achieve the same maximum length validation in WPF project using Entity Framework.


 Download source code for Implement Generic Max Length validation in WPF Entity Framework

Introduction

Project: WPF using Entity Framework & MVVM Architecture with MEF.

This article is about how could we implement length check (MaxLength) validation in WPF, based on the actual allowed length of the column in database (through Entity Framework). We will be using style and tag property to achieve this effect in a generic way. The implementation is a little tricky but not impossible. 


Objective

When I say MVVM, I mean no code behind your XAML file. I have seen lots of MVVM examples use two classes by just naming “ViewModel” and “View” in the same project. Since MVVM is much more than naming two files, so I decided to use MVVM sample kit from CodePlex. If you need complete MVVM with MEF framework, you can download it from here. The download provides a working sample of MVVM using MEF, and thus gives a base for demonstrating the solution. 


Problem

As compared to WinForms, implementation of MaxLength in WPF is not generic enough. I have looked around and read many articles, but could not find a generic way to implement MaxLength checking for text boxes.

Since this looks challenging, I gave a try to implement MaxLength in WPF in a generic way so that we don’t need to go to each and every form and implement MaxLength validation.


Before starting, we need to understand...

Why it is difficult in WPF to Validate against EDMX Max Length ?

  • To check MaxLength, two things are required “Entity Name” (Table name) and Binding “Field Name”.
  • We can extract Binding Field from control “Textbox” but we do not have mechanism to extract associated entity name used in its View Model. Thus at runtime we can not find the acceptable length of associated binding in the database.

Solution

Solution, in a nuthsell is to store the entity name somewhere during the binding, and refer to this entity name to fetch information about the field's properties like MaxLength etc. 

This could be achieved in many ways, by keeping global list of items used ina  dictionary(key/value) format, etc. But for sake of optimization I've tried to reuse existing property "Tag" for the purpose. This will store the broken piece of the puzzle, i.e. the Entity name using View Model so that our incomplete pair of Entity Name/ Field name would be completed.

Tag is an age old property in .Net that is available for most of the controls. This property is rarely used by developer. At least I never had to use/manipulate this property in any of the projects that I've done so far in my career. In the current development project too, this property stays untouched throughout the modules. When I researched within our .NET community, I found similar conclusions for the Tagproperty, and its limited use.

Since my project has not used this property, I have decided to use this property for our Textbox max length implementation so that I can fill the gap of EntityName.

I have created one string property named “EntityTableKey” in my view model base class. This property would be set for Tag in Textbox style. One important point to be noted here is if needed you can declare this property as an "object" type also (to pass an array / list or some collection) because Tag property is an Object type property. 

Using the code

 public string EntityTableKey { get; set; }
Now you must be getting an idea what we are going to do further. This EntityTableKey property needs to be set with the Entity Name in constructor of our view model. In the below code, my entity table name is Book so "EntityTableKey" property is set to “Book” this is an indication that in EDMX file the code would search textbox bound property in entity whose name is Book

[ImportingConstructor] //Forget about this codeline if you are not using MEF.

internal BookViewModel(IBookView view)

      : base(view)

      {

        EntityTableKey = "Book";

      }

Now, we will move to our control's resource file where we have defined all our style. Here, we are going to bind this EntityTableKey property to tag in our textbox base style.

So till here, we have created our mapping of entity name to the textbox control, but how do we use this and utilize this EntityTableKey property for our max length? Pretty simple… 

<Style TargetType="TextBox">

     <Setter Property="Width" Value="Auto"/>

     <Setter Property="MinHeight" Value="20"/>

      <Setter Property="Margin" Value="2"/>

      <Setter Property="Tag" Value="{Binding EntityTableKey}"/>      

      </Style>

Do not give x:Key for your textbox style, this way all these styling changes would be defaulted to textbox control.

Now the extraction part, we need to use some kind of converter where we can pass the whole control and then extract the tag value and bind property. Since simple Binding property do not pass control.

Passing Control to Converter Using Style

So here is the main implementation of our whole article, we need to use multibinding to pass our control (textbox) to our code class. For this, we will use a converter which would be called and process the current control to extract the bound field and tag property. Once we have bound field name and the entity name, then it is very easy to search in our entity and get the max length.

So let’s see how this can be achieved. 

      <Setter Property="MaxLength">

      <Setter.Value>

      <MultiBinding Converter="{StaticResource maxlen}">

      <Binding RelativeSource="{RelativeSource Mode=Self}" />

      <Binding Path="Tag"

      RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type TextBox}}" />

      </MultiBinding>

      </Setter.Value>

      </Setter>

Code for Max Length Converter 

public class MaxLenSetter : IMultiValueConverter

{

     public object Convert(object[] value, Type targetType, object parameter, 

CultureInfo culture)

        {

            int maxLen = 0;

            var currentControl = (value[0] as TextBox);

            if (currentControl != null && currentControl.Tag != null)

            {

try

{

var propertyName = currentControl.GetBindingExpression

(TextBox.TextProperty).ParentBinding.Path.Path;

var boundproperty = propertyName.Split('.');

        if (boundproperty.Length > 0)

                propertyName = boundproperty[boundproperty.Length - 1];

else

                propertyName = boundproperty[0];

                  maxLen = Waf.BookLibrary.Library.Domain.BookLibraryEntities.GetMaxLengths

(currentControl.Tag.ToString(), propertyName);

currentControl.ToolTip = maxLen > 0 ? "Maximum length allowed is " + maxLen : null;

}

catch

{

//nothing to do.

}

            }

            return maxLen;

}

Fetching Maximum Length value from EDMX file. 

public static int GetMaxLengths(String entitySetName, string propertyName)

        {

            int maxLength = 0;

//sometime control rendring is earlier then entity initializing specially in Release mode duet o code optomization.

            while (MaxLenEntity == null)

                System.Threading.Thread.Sleep(1000);

            MetadataWorkspace workspace = MaxLenEntity.MetadataWorkspace;

            ReadOnlyCollection<EntityType> entities =           

workspace.GetItems<EntityType>(DataSpace.CSpace);

            foreach (EntityType entity in entities)

            {

if (entity.Name.Equals(entitySetName))

{

foreach (EdmProperty property in entity.Properties)

{

  if (property.TypeUsage.EdmType.Name.Equals("String"))

      {

if (property.Name == propertyName && property.TypeUsage.

Facets["MaxLength"].Value != null)

          {

              int res = 0;

              if (int.TryParse(property.TypeUsage.Facets["MaxLength"]

.Value.ToString(),out res))

                                {

                                    maxLength = res;

                                    break;

                                }

                            }

                        }

}

break;

}

    }

Benefits

Making a generic property, we can set its value in constructor of each ViewModel class. This class exposes the properties which are used in binding the text controls. Since MVVM with MEF works on one to one mapping mechanism, this means every view is having one view model. We just need to set this “EntityTableKey” property with our entity table name and the rest will be done automatically.

What if Binding Property Belongs to Two Different Tables in same View ?

Now what if your ViewModel is exposing properties from two different tables? Let’s say a complex view is having 10 textbox controls and two textboxes are using some field which belongs to other table?

It is very simple, just explicitly set the textbox tag property with your entity name. XAML always overrides all style properties with the properties used explicitly on control. 

Conclusion

Though the MaxLength validation is not straight forward, the above solution brings a step closer to implement it using Textbox tag property. There is a way to use some dependency property but I wanted to keep this implementation as simple as possible. Few technical guys may not agree on using tag property for this purpose and I am open to hear from them.

At last before finishing my article, I would like to thank CodePlex community for their great work on MVVM & MEF solution. After using their MEF solution, I am now a diehard fan of MVVM & MEF.

Thanks for reading the article & happy coding! 


Reference

WAF.CodePlex.com


Page copy protected against web site content infringement by Copyscape

About the Author

Rohit.Agrawal
Full Name: Rohit Agrawal
Member Level: Starter
Member Status: Member
Member Since: 3/26/2014 3:57:13 AM
Country: India
Rohit Agrawal Email : Roheet.Agrawal@gmail.com
http://www.dotnetfunda.com
.net WPF/WinForm Professional. Working in IT industry from Last 7 Years.

Login to vote for this post.

Comments or Responses

Login to post response

Comment using Facebook(Author doesn't get notification)