Wednesday, February 25, 2009

XAF Usability: Limit SpinEdit values with XAF Rules

In this new XAF Usability post, we will show how you can limit max and min values dynamically when editing integer numeric properties with SpinEdit controls.

Say your project has a XAF entity called Customer with an integer property PaymentDay aimed to store the number of the day of the month that each Customer schedules payments to his providers. As days of month are known in advance, and limited in number, a range rule is defined at property level to set max and min values for such property.


Once written down, your source code might look like this:

public partial class Customer : BaseObject
{
[...]
private int _PaymentDay;
[DisplayName("Payment day")]
// Define a rule to set min and max value for the property
[RuleRange("Customer.PaymentDay.RR", "Save", 0, 31)]
public virtual int PaymentDay
{
get { return _PaymentDay; }
set { SetPropertyValue("PaymentDay", ref _PaymentDay, value); }
}
[...]
}

Ideally, XAF might set Max and Min properties according to the rule range when rendering that kind of property thru a SpinEditor. But the truth is that it won’t do it, so you will be the one who have to. You can do it by adding a new ViewController to Module.Win project of your Solutions. Such ViewController would target any DetailView in your application, checking for every IntegerPropertyEditor created. When a property editor of that kind is added to the DetailView, it will use Application ValidationModule’s RulesWithoutSerializer method to get the rule associated to the property corresponding to the PropertyEditor being created. If the type of Rule is RuleRange, then its Min and Max values are used to set IntegerPropertyEditor’s Min and Max properties, as shown in the following code:

public partial class CustomSpinRuleRange : ViewController
{
public CustomSpinRuleRange()
{
InitializeComponent();
RegisterActions(components);
// Target ViewController to any DetailView in your application
this.TargetViewType = ViewType.DetailView;
}

protected override void OnActivated()
{
// Suscribe LayoutManager’s ItemCreated event handler to be able to check for every item created in the DetailView
((DevExpress.ExpressApp.Win.Layout.WinLayoutManager)((View as DetailView).LayoutManager)).ItemCreated += new EventHandlerdevexpress.expressapp.win.layout.ItemCreatedEventArgs(CustomSpinRange_ItemCreated);
base.OnActivated();
}

void CustomSpinRange_ItemCreated(object sender, DevExpress.ExpressApp.Win.Layout.ItemCreatedEventArgs e)
{
// For every new item created in the DetailView, check if it’s an IntegerPropertyEditor
if (e.DetailViewItem != null && e.DetailViewItem is DevExpress.ExpressApp.Win.Editors.IntegerPropertyEditor)
{
DevExpress.ExpressApp.Win.Editors.IntegerPropertyEditor lIntegerPropertyEditor = (DevExpress.ExpressApp.Win.Editors.IntegerPropertyEditor)e.DetailViewItem;
if (lIntegerPropertyEditor != null)
{
//If a PropertyEditor is being created, check if has an associated RuleRangeAttribute
DevExpress.Persistent.Validation.RuleRangeAttribute lRuleRangeAttribute = ((DevExpress.ExpressApp.Editors.PropertyEditor)
(lIntegerPropertyEditor)).MemberInfo.FindAttribute();
if (lRuleRangeAttribute != null)
{
// If a RuleRangeAttribute is was found, get an instance of the Application’s ValidationModule
// to gain access to validation rules
ValidationModule lModule = (ValidationModule)Application.Modules.FindModule(typeof(ValidationModule));
if (lModule != null)
{
DevExpress.Persistent.Validation.IRule lIRule;
// Use ValidationModule’s RulesWithoutSerializer method to get the rule associated to
// the property corresponding to the PropertyEditor being created in a safely manner
if (lModule.RulesWithoutSerializer.TryGetValue(lRuleRangeAttribute.Name, out lIRule))
{
// Once the rule was gotten, set PropertyEditor’s Min and Max values to RuleRange’s ones
DevExpress.Persistent.Validation.RuleRange lRuleRange = lIRule as DevExpress.Persistent.Validation.RuleRange;
lIntegerPropertyEditor.Control.Properties.MinValue = Convert.ToInt32(lRuleRange.Properties.MinimumValue);
lIntegerPropertyEditor.Control.Properties.MaxValue = Convert.ToInt32(lRuleRange.Properties.MaximumValue);
}
}
}
}
}
}
}

When the application is run and users edit Payment day property, they are not allowed to spin value under 0 or above 31. On the other side, if they type a value out of the valid range, a validation exception will raise when Customer entity is saved as XAF checks rule “Customer.PaymentDay.RR” for “Save” context.

1 comment:

  1. Hi

    Thanks for the useful tip but it does not compile:

    DevExpress.ExpressApp.Validation.ValidationModule' does not contain a definition for 'RulesWithoutSerializer' and no extension method 'RulesWithoutSerializer' accepting a first argument of type 'DevExpress.ExpressApp.Validation.ValidationModule' could be found (are you missing a using directive or an assembly reference?)

    XAF 2010.2.6

    ReplyDelete