ASP.NET – Create Validators for CheckBoxLists

in .NET Development, ASP.NET, C#

To validate ASP.NET CheckBoxLists, you need to write an own validator. A CustomValidator will not work, because it can’t take a CheckBoxList as a single object in the ControlToValidate property. ASP.NET will generate sequential IDs for the CheckBoxes in a CheckBoxList object, so there is no chance to validate it as “single control”. That works for non-listing objects, like textfields, datepickers etc., but not for list controls.

Here you find three different validators, each has its own purpose:

  • RequiredMinimumOneValidatorForCheckBoxList. This validator is used to check, if  – in minimum – one checkbox is checked.
  • RequiredMaximumOneValidatorForCheckBoxList. This validator can be used to test, if only one checkbox is checked (either…or).
  • RequiredAllValidatorForCheckBoxList. This validator tests, if all checkboxes are checked in the checkboxlist

Here is the code:

RequiredMinimumOneValidatorForCheckBoxList


public class RequiredMinimumOneValidatorForCheckBoxList : BaseValidator
{
private ListControl _listControl;

/// <summary>
/// Constructor
/// </summary>
public RequiredMinimumOneValidatorForCheckBoxList()
{
base.EnableClientScript = false;
}

/// <summary>
/// Checks, if we can find the control to validate
/// </summary>
/// <returns></returns>
protected override bool ControlPropertiesValid()
{
Control ctrl = FindControl(ControlToValidate);
if (ctrl != null)
{
_listControl = (ListControl)ctrl;
return (_listControl != null);
}

return false;
}

/// <summary>
/// Checks, if in minimum one checkbox has been checked by the user
/// </summary>
/// <returns></returns>
protected override bool EvaluateIsValid()
{
return _listControl.SelectedIndex != -1;
}
}

RequiredMaximumOneValidatorForCheckBoxList

public class RequiredMaximumOneValidatorForCheckBoxList : BaseValidator
{
private ListControl _listControl;

/// <summary>
/// Constructor
/// </summary>
public RequiredMaximumOneValidatorForCheckBoxList()
{
base.EnableClientScript = false;
}

/// <summary>
/// Checks, if we can find the control to validate
/// </summary>
/// <returns></returns>
protected override bool ControlPropertiesValid()
{
Control ctrl = FindControl(ControlToValidate);
if (ctrl != null)
{
_listControl = (ListControl)ctrl;
return (_listControl != null);
}

return false;
}

/// <summary>
/// Checks, if only one checkbox has been checked by the user
/// </summary>
/// <returns></returns>
protected override bool EvaluateIsValid()
{
int intSelCount = 0;
foreach (ListItem item in _listControl.Items)
{
if (item.Selected)
{
intSelCount++;
if (intSelCount > 1)
{
return false;
}
}
}
return true;
}
}

RequiredAllValidatorForCheckBoxList


public class RequiredAllValidatorForCheckBoxList : BaseValidator
{
private ListControl _listControl;

/// <summary>
/// Constructor
/// </summary>
public RequiredAllValidatorForCheckBoxList()
{
base.EnableClientScript = false;
}

/// <summary>
/// Checks, if we can find the control to validate
/// </summary>
/// <returns></returns>
protected override bool ControlPropertiesValid()
{
Control ctrl = FindControl(ControlToValidate);
if (ctrl != null)
{
_listControl = (ListControl)ctrl;
return (_listControl != null);
}

return false;
}

/// <summary>
/// Checks, if all checkboxes have been checked by the user
/// </summary>
/// <returns></returns>
protected override bool EvaluateIsValid()
{
foreach (ListItem item in _listControl.Items)
{
if (!item.Selected)
{
return false;
}
}
return true;
}
}

For a short Test, add this code to the your page or user control codebehind:

protected override void OnInit(EventArgs e)
{
base.OnInit(e);

RequiredMaximumOneValidatorForCheckBoxList validator = new RequiredMaximumOneValidatorForCheckBoxList();
validator.ControlToValidate = "ID of your CheckBoxList";
validator.ErrorMessage = "The error message, that will be shown in case of a validation error";
Controls.Add(validator);
}
0 Comments

gMapMaker+ – with support for TTQV

in .NET Development, GPS-Navigation

gMapMaker+

gMapMaker+ enables you to download maps from OpenStreetMap, OpenAerialMap, Google, Yahoo or MSN as large scaled image files, that can be used in GPS applications like TTQV or OziExplorer.

gMapMaker was originally developed by Damien Debin. Since Damien no longer has time to contribute to the development of gMapMaker, I decided to add some new features on the latest version 0.7.

The updated version gMapMaker+ V1.0 is available for download here (SetupSourcecode).

gMapMaker+

Features of gMapMaker + V1.0

The list below shows the added features in comparison to the latest version gMapMaker 0.7:

  • Enhanced graphical user interface: Completely revised graphical user interface, integration of a map dialog to select map sections.
  • Bugfixes: Several download url’s have been corrected and modified
  • Support for TTQV: New option “Build image and TTQV cal” has been added. This option creates a calibration file, which can be imported in TTQV 4x and higher.
  • Support for ECW file format: Along with JPEG, PNG and TIFF, gMapMaker supports also the ECW file format. See installation manual to properly configure this feature.
  • Downloads history: The download settings of all successfully downloaded image files are stored in a logfile and displayed in the history section.
  • Settings: Most of the settings available in gMapMaker can be done in the GUI now
  • Test Button for memory allocation: This button enables you to check the graphical memory for allocation, before start downloading.

Installation manual

  1. Download latest gMapMaker+ setup file
  2. Run Setup, follow the instructions of the wizard
  3. [OPTIONAL] For ECW support, you must download the ECW compressor of ERDAS first
  4. [OPTIONAL] Install ECW JPEG 2000 Compressor 7.0. After the installation, start gMapMaker+ and enter the path to the ECW compressor binary in the settings under ECWFullPath, i.e. “C:\Program Files\Earth Resource Mapping\ECW JPEG 2000 Compressor 7.0\Bin\ecw_compress_free.exe”.
  5. Installation is finished

Terms of use

This software is for private use only. Before downloading map images, read the terms of use of the provider and ensure, you have the right to download and store the file locally on your computer.

Licence

gMapMaker + is distributed under GNU Public Licence. The program can be downloaded in binary form and as well as source code. Redistribution and modifications of the program are allowed. For more information, see the GPL licence terms.
0 Comments

odbMaker – Program for creating TTQV-compatible Geo Names databases

in .NET Development, GPS-Navigation, Uncategorized

The internal geo names databases of Touratech TTQV are really usefull for planning routes. Unfortunatly TTQV provides no functionality to edit or update the geo names database.I created a windows tool, that converts Geo Names data from NGA  to TTQV compatible Geo Names Databases. You can download this tool, named odbMaker, at this location: http://www.nichtswieweg.ch/pages/tipps-und-tricks/ttqv-quo-vadis/odbmaker.php Have fun!P.S. The program text is hold in German, but it’s quite intuitive. Just download the country files from NGA, store them on your computer. Run odbMaker, select the downloaded file or files and click on “Erstellen..”. After entering a destination filename, the creation process will start.

0 Comments

.NET Transliteration API Project

in Uncategorized

I’m thinking of initializing an .NET API project with the goal to provide Transliteration Services within the .NET framework. The class libraries are basically already done (see details below), but there’s still a lot of work to do.

Who wants to help on this project? I need people having background information about languages that can be transliterated from and into the latin alphabet and .NET developers helping to build such a framework for .NET.

This is my idea of implementing  Transliteration services in .NET:

1. First, we need a Transliteration character mapping table, that we later can use as our source. For this reason, create an UTF-8 encoded XML file with the following structure:


<?xml version="1.0" encoding="utf-8" ?>
<Transliteration Name="ISO9-1995">
<Chars IsUpper="true">
<Char>
<Source>Ә</Source>
<Destination>A̋</Destination>

</Char>

</Chars>
<Chars IsUpper="false">
<Char>
<Source>а</Source>
<Destination>a</Destination>
</Char>

</Chars>
</Transliteration>

Please note, in this example there are 2 Chars-Elements: one with IsUpper=”true” and the other with IsUpper=”false”. I’m not sure if those attributes will be relevant later in our project, but we will see.
The name property contains the Transliteration name, in this case ISO9:1995 is the source of the character set.

2. Create a new Visual Studio C# class library project and name it “Transliterations”
3. Create an Transliteration XML-File (with the structure above) and place in your characters of the language you want to transliterate. The source value contains the character to search for, the destination value contains the character to replace with.
3. Save the XML-File in your Visual Studio project in the subfolder “Transliteration” with the filename according to a “standard”, e.g. Hindi
4. Create a new Class “Transliteration” and Copy the following code into the newly created class:


using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Xml;

namespace urmanet.ch.Transliteration
{
public class Transliterator
{
/// <summary>
///
/// </summary>
private static SortedList<TransliterationType, Transliterator> _instances;
private Hashtable _charValuePairs;
private string _name;

/// <summary>
/// Private constructor
/// </summary>
private Transliterator(){}

/// <summary>
/// Private constructor
/// </summary>
/// <param name="parTransliterationType">Type of transliteration</param>
private Transliterator(TransliterationType parTransliterationType)
{
CharValuePairs = new Hashtable();

string myEmbeddedResourceName = string.Format(
"{0}.Transliteration.{1}.xml",GetType().Namespace, parTransliterationType);

Stream myResourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(
myEmbeddedResourceName);

if (myResourceStream == null)
throw new Exception("The given transliteration source file could not be found.");

XmlDocument myXmlDocument = new XmlDocument();
using (StreamReader myStreamReader = new StreamReader(myResourceStream))
{
myXmlDocument.LoadXml(myStreamReader.ReadToEnd());
XmlNodeList myCharNodes = myXmlDocument.SelectNodes("//Chars/Char");

if (myCharNodes == null)
throw new Exception("There were noch char pairs found in the given literation file.");

foreach (XmlNode myCharNode in myCharNodes)
{
CharValuePairs.Add(
myCharNode["Source"].InnerText,
myCharNode["Destination"].InnerText);
}

_name = myXmlDocument.SelectSingleNode("//Transliteration").Attributes["Name"].InnerText;
}
}

/// <summary>
///
/// </summary>
/// <param name="parTransliterationType">Type of transliteration</param>
/// <returns></returns>
public static Transliterator GetTransliterator(TransliterationType parTransliterationType)
{
if (_instances == null)
_instances = new SortedList<TransliterationType, Transliterator>();

if (!_instances.ContainsKey(parTransliterationType))
{
_instances.Add(
parTransliterationType,
new Transliterator(parTransliterationType));
}

return _instances[parTransliterationType];
}

/// <summary>
///
/// </summary>
public Hashtable CharValuePairs
{
protected set { _charValuePairs = value; }
get { return _charValuePairs; }
}

/// <summary>
///
/// </summary>
/// <param name="parString"></param>
/// <returns></returns>
public string Transliterate(string parString)
{
string myReturnValue = parString;
char[] myChars = parString.ToCharArray();
foreach (char myChar in myChars)
{
string myKey = myChar.ToString();
if (CharValuePairs.ContainsKey(myKey))
{
myReturnValue = myReturnValue.Replace(
myChar.ToString(), CharValuePairs[myKey].ToString());
}
}

return myReturnValue;
}

/// <summary>
///
/// </summary>
public string Name
{
private set { _name = value; }
get { return _name; }
}
}
}

Now, you need to extend the enumeration TransliterationType with the name of your created Transliteration xml file.


namespace urmanet.ch.Transliteration
{
public enum TransliterationType
{
ISO91995,
Hindi
}
}

And that’s the way how the Transliteration classes are used within .NET:

Transliterator myTransliterator = Transliterator.GetTransliterator(TransliterationType.ISO91995);
string mySourceText = "Абыйская Низменность";
string myLatinText1 = myTransliterator.Transliterate(mySourceText);
1 Comment

Transaction exception while using ADO.NET entities

in .NET Development, Uncategorized

The following ADO.NET code will throw following exception “New transaction is not allowed because there are other threads running in the session.”:


var customer = ... ;
using (var entities = new ApplicationEntities())
{
var myQuery = from o in entities.Orders
where
o.Customers.ID == 3
select o;

foreach (var order in orders)
{
var newOrder = (Orders)order.Clone();
newOrder.Customers = customer;

entities.AddToOrders(newOrder);
entities.SaveChanges();

}
}

The exception occurs because the foreach loop opens an transaction to query the order entities. The transaction stays open until the end of the loop is reached. To avoid this exception, just use the saveChanges()-method outside the foreach loop, see example below:


var customer = ... ;
using (var entities = new ApplicationEntities())
{
var myQuery = from o in entities.Orders
where
o.Customers.ID == 3
select o;

foreach (var order in orders)
{
var newOrder = (Orders)order.Clone();
newOrder.Customers = customer;

entities.AddToOrders(newOrder);

}

//This is the right place to call the saveChanges method

entities.SaveChanges();

}

Another solution would be, to store the query result in an object, for example a list or an array and iterate trough the array instead of the IQueryable list.

0 Comments

Reopening model in ADO.NET entity designer impossible?

in .NET Development, Uncategorized

The designer of the ADO.NET entity framework seems to be in very early stadium. One of its known bugs is the reopening issue, which means, if the designer was once openend and closed, it isn’t possible to re-open the model again in the designer view. Double clicking the .edmx file will take no effect.

One  solution to re-open the entity model again is:

  • Right click the .edmx file in Visual Studio 2008 and choose “Open with…”
  • In the list, choose “XML Editor”
  • After opening the file in its XML view, close the view
  • Now, double click the .edmx file and the designer opens again!
0 Comments

Deep Cloning/Copying of ADO.NET Entity Objects

in .NET Development, Uncategorized

This is my first post on urmanet.ch.

Today I was experimenting with the ADO.NET Entity Framework and I was really surprised of the lack of support for cloning entity objects. After doing some research in the Internet I finally figured out that this feature might be added in the next release of .NET. However, this seems to take too long for me and so I tried to solve this problem by enhancing the EntityObject Class with an Extension Method.

Extension methods are new in C# 3.0 and were mostly used for integrating LINQ in existing classes. In my opinion, enhancing the EntityObject with such an Extension Method seemed to be the best solution to add cloning functionalty. The code which is published here should work, but there are certainly things left and I would appreciate if someone gave me suggestions and feeback to improve the code below. Thank you in advance!

Now, lets get into the code! For testing purposes, I created 3 database tables named Customers, Orders and OrderItems. These 3 tables are related togethter with foreign keys constraints and the ideas was, to deeply copy entities with all their related properties from top to the bottom (see picture below as example).

ADO.NET Entity Framework Model Example

ADO.NET Entity Framework Model Example

To test the code below, just create an ADO.NET Entity Model from an existing data source. The object context in this example is called TestEntities.

Used Namespaces

First, import these namespaces in your project:

  using System;  using System.Collections.Generic;  using System.Data;  using System.Data.Objects.DataClasses;  using System.Diagnostics;  using System.Linq;  using System.Reflection; 

Example of using the code in your project

This is an example of using the Clone() method in your code. The following example shows an iteration of Customers objects. Within the loop, the current Customer object makes a call to its Clone() member method which creates an exact copy of its caller object.The copy will be added tho the corresponding object context and at the end, all copied objects will be stored back in the datasource.

internal class Program
{
private static void Main(string[] args)
{
//Create an instance of the Object Context
var entities = new TestEntities();

//Get all customers from the datasource
var customers = from o in entities.Customers
select o;
//Iterate through the customers collection
foreach (var customer in customers)
{
//Create a deep copy of the customer object and add it to the Object Context
var myNewCustomer = (Customers) customer.Clone();
entities.AddToCustomers(myNewCustomer);
}

//Submit changes to the underlying datasource
entities.SaveChanges();
}
}

The Extension Method “Clone()” for the EntityObject Class

Everytime an (derived) object of the type “EntityObject” is accessed, the method “Clone()” will be available in the Intellisense of Visual Studio (see Screenshot).

Visual Studio Intellisense for Extension Methods

Visual Studio Intellisense Appearance for Extension Methods

Code Explanation

The parameter “this EntityObject entityObject” of the Clone() method transports the object which was calling the method and not the base class like it may seems to be. After getting the calling class, some properties of the object must be filtered out, because they cannot be copied in the new object, e.g. EntityKey or EntityState. What we really want, is the content of the object, not its state variables or information, which is only used by the entity itself.

After that, we need to know, if the property is a normal type like a string, integer, datetime etc. or a collection of related entities. In latter case, we need to call some functions by using reflection to get the original collection and – because this method works with infinite levels of depth – we make use of recursive calls.

The new object now contains exactly the same values like the original object, excepting the key field, which will be empty or set to 0. This is necessary for adding the new object to the object context of the entiy model, otherwise we’ll get an exception error telling us, that the key already exists. The default key value is null – for string keys – or 0 – for numbers.


///
/// This class is used to store self references for
/// back tracking
///
public class SelfReferencesTracking
{
public string EntitySetName;
public EntityObject NewEntityObject;
public EntityKey OriginalKeys;
}

///
/// Extension method class for the EntityObject class
///
public static class EntityObjectExtension
{
//Enable tracking
private static readonly List _tracking =
new List();

///
/// These method makes a 1:1 copy of the original entity object
///
/// The original entity object /// The copied entity object
public static EntityObject Clone(this EntityObject entityObject)
{
//Get constructor for new object
var newEntityObject = entityObject.GetType().GetConstructor(
new Type[0]).Invoke(new object[0]);

_tracking.Add(new SelfReferencesTracking
{
EntitySetName = entityObject.EntityKey.EntitySetName,
OriginalKeys = entityObject.EntityKey,
NewEntityObject = (EntityObject)newEntityObject
});

//Copy all properties and its values of the given type
var properties = entityObject.GetType().GetProperties();
foreach (var property in properties)
{
try
{
var propertyValue = property.GetValue(entityObject, null);
PropertyInfo myProperty = property;
if (entityObject.EntityKey.EntityKeyValues.Where(x => x.Key == myProperty.Name).Count() == 0)
{
//Ignore all properties of these types
if (property.PropertyType != typeof(EntityKey) &&
property.PropertyType != typeof(EntityState) &&
property.PropertyType != typeof(EntityReference<>))
{
//Check, if the property is a complex type (collection), in that
//case, some special calls are necessary
if (property.GetCustomAttributes(
typeof(EdmRelationshipNavigationPropertyAttribute), false).Count() == 1)
{
//Check for self referencing entities
if (propertyValue.GetType() == entityObject.GetType())
{
//Get the self referenced entity object
var selfRefrencedEntityObject =
(EntityObject)property.GetValue(entityObject, null);

//This variable is used to store the new parent entity objects
EntityObject newParentEntityObject = null;

//This loops might be replaced by LINQ queries... I didn't try that
foreach (
var tracking in
_tracking.Where(
x =>
x.EntitySetName == selfRefrencedEntityObject.EntityKey.EntitySetName)
)
{
//Check, if the key is in the tracking list
foreach (
var newKeyValues in selfRefrencedEntityObject.EntityKey.EntityKeyValues)
{
//Iterate trough the keys and values
foreach (var orgKeyValues in tracking.OriginalKeys.EntityKeyValues)
{
//The key is stored in the tracking list, which means, this is
//the foreign key used by the self referencing property
if (newParentEntityObject == null)
{
if (orgKeyValues.Key == newKeyValues.Key &amp;amp;&amp;amp;
orgKeyValues.Value == newKeyValues.Value)
{
//Store the parent entity object
newParentEntityObject = tracking.NewEntityObject;
}
}
else
{
break;
}
}
}
}

//Set the value to the new parent entity object
property.SetValue(
newEntityObject,
newParentEntityObject, null);
}
else
{
//Entity collections are always generic
if (propertyValue.GetType().IsGenericType)
{
//Don't include self references collection, e.g. Orders1, Orders2 etc.
//Check for equality of the types (string comparison)
if (!propertyValue.GetType().GetGenericArguments().First().FullName.Equals(
entityObject.GetType().FullName))
{
//Get the entities of the given property
var entities =
(RelatedEnd)property.GetValue(entityObject, null);

//Load underlying collection, if not yet done...
if (!entities.IsLoaded) entities.Load();

//Create a generic instance of the entities collection object
var t = typeof(EntityCollection<>).MakeGenericType(
new[] { property.PropertyType.GetGenericArguments()[0] });

var newEntityCollection = Activator.CreateInstance(t);

//Iterate trough the entities collection
foreach (var entity in entities)
{
//Add the found entity to the dynamic generic collection
var addToCollection = newEntityCollection.GetType().GetMethod("Add");
addToCollection.Invoke(
newEntityCollection,
//new object[] {(EntityObject) entity});
new object[] { Clone((EntityObject)entity) });
}

//Set the property value
property.SetValue(
newEntityObject,
newEntityCollection,
null);
}
}
}

}
else
{
//Common task, just copy the simple type property into the new
//entity object
property.SetValue(
newEntityObject,
property.GetValue(entityObject, null), null);
}
}
}
}
catch (InvalidCastException ie)
{
//Hmm, something happend...
Debug.WriteLine(ie.Message);

continue;
}
catch (Exception ex)
{
//Hmm, something happend...
Debug.WriteLine(ex.Message);

continue;
}
}

return (EntityObject)newEntityObject;
}
}

 

Features

  • Support for copying entity objects from top to the bottom (copying of all children and their values)
  • Support for copying self-referenced entity objects (original relations will be tracked in an internal tracking list and copied entity objects will have the same relationship to the new parent entity object)
  • Simple access because it’s an extension methods, no parameters are required

Pending Tasks

As mentioned above, this code is the first try to implement Clone() functionaity into the ADO.NET Entity Framework. I tested it just with the example database model above (up to 3 Tables) for seeing, if it’s gonna work or not.

Pending tasks are among others:

  • Support for bottom-up relationships like child entities containing foreign keys to other (upper) entities (not yet tested)

Conclusion

The published code should give you an idea of how to implement Cloning()-funcionality in the ADO.NET Entity Framework. If you have any suggestions, please feel free to post your comments here!

6 Comments