Custom ElementConvention assessing view model instance

Mar 18, 2011 at 5:43 PM

Hi,

I need to be able to apply a convention to TextBoxes that apply a value to their MaxLength.  Ive started out with the following;

Snippet

 ConventionManager.AddElementConvention<TextBox>(TextBox.TextProperty, "Text""TextChanged")
                .ApplyBinding = (viewModelType, path, property, element, convention) =>
                                    {
                                        if (!ConventionManager.SetBinding(viewModelType, path, property, element,
                                                                          convention))
                                            return false;
                                        var textBox = (TextBox) element;

The issue im having is that the MaxLength value (and other business rules) is stored in the instance of viewModelType, nested within a private child KeyValuePair. This data can therefore only be accessed via a method.

At this point the textBox.DataContext is also null so can't access it via that route. I need the instance of viewModelType.

Any help would be greatly appreciated.

Regards

Mar 19, 2011 at 9:36 AM

The ConventionManager operates providing conventional WPF/SL bindings for elements based on VM *type*; bindings are created just after view instantiation, while the actual VM instance is hooked later.
So, it's normal to not have the DataContext at that stage.

You could hook (at least in WPF) an event handler to get notified of DataContext change, but it would seem a messy approach to me; it would also make difficult to have the UI updated if the business rules would dynamically change.
In my opinion the easiest and most effective solution would be to expose the business rules through properties, so that they could be hooked via regular bindings.
To avoid over-poisoning the VM interface with tons of explicit properties, you might choose to expose a name/value dictionary (which is supported by the binding infrastructure).

For example, something along this path should work (I hope to have well understood you scenario :-) )

public class MyViewModel {
	public string AProperty {get;set;}
	public string SomeOtherProperty {get;set;}

	//the dictionary is loaded according to business rules, indexed by property name
	public IDictionary<string, PropertyMetadata> PropertyMetadata {get; set;}
}

public class PropertyMetadata {
	public int MaxLenght {get;set;}
	//... other metadata 
}


 ConventionManager.AddElementConvention<TextBox>(TextBox.TextProperty, "Text", "TextChanged")
                .ApplyBinding = (viewModelType, path, property, element, convention) =>
                                    {
                                        if (!ConventionManager.SetBinding(viewModelType, path, property, element,
                                                                          convention))
                                            return false;
                                        
                                        var binding = new Binding("PropertyMetadata["+property.Name+"].MaxLenght");
                                        BindingOperations.SetBinding(element, TextBox.MaxLenght, binding);
				}

 

Mar 21, 2011 at 12:26 PM

Thanks marcoamendola, thats right I dont need the instance of the VM. I'll use this approach fully or maybe even a ValueConvertor into the binding to access the relevant business rule.

Best Regards

David