Skip to content

StevenThuriot/Nova.Bindings

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

22 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Nova Bindings Build status

  • Model first approach to WPF view designs.
  • Decouple view markup from your data. Instead, define metadata on your (view)models to shape your view through the use of attributes.
  • You can even go a step further and retrieve the metadata at startup from your server's database. This allows you to change field types and labels without having to redeploy.
  • Contains:
    • LabelFor markup extension
    • ValueBinding markup extension

These markup extensions will resolve the property path passed to them and configure your labels and controls accordingly.

nova bindings

#Usage

Merge Nova.Bindings's pack URI to your project's ResourceDictionary.

<ResourceDictionary.MergedDictionaries>
		<ResourceDictionary Source="pack://application:,,,/Nova.Bindings;component/ValueEditor.xaml" />
</ResourceDictionary.MergedDictionaries>

##Views

Views become as simple as the following Label and Editor. It will be the only editor you'll ever use again!

A change in the model? No problem! The Bindings will take care of it for you!

<TextBlock   Grid.Column="0" Text="{LabelFor Model.Property}" />
<ValueEditor Grid.Column="1" Value="{ValueBinding Model.Property}" />

The LabelFor binding has a property AppendColon, default true, which will append a colon to the label.

The ValueBinding binding has two properties that can be set:

  • Mode ( == BindingMode, default BindingMode.Default )
  • Converter ( Default null )

##Implementation

Implement IHaveSettingsManager on your ViewModel. The bindings will try to resolve this Manager (from the control up) to try and find the control's settings.

###ViewModel

public class ViewModel : IHaveSettingsManager
{
	public ISettingsManager SettingsManager { get; private set; }
	
	public ViewModel()
	{
		SettingsManager = new NovaSettingsManager();
	}
}

###Settings Manager

Next, create a settings manager.

public class NovaSettingsManager : ISettingsManager
{
	private readonly Dictionary<string, IDefinition> _definitions;
	private readonly DefinitionFactory _factory;
	
	public NovaSettingsManager()
	{
		_definitions = new Dictionary<string, IDefinition>();
		
		//TODO;
		_factory = new ComboBoxFactory(); //Good starter since it's a special case.
		_factory.SetSuccessor<RadioButtonFactory>()
			    .SetSuccessor<CheckBoxFactory>()
			    .SetSuccessor<DatePickerFactory>()
			    //.......... 
			    .SetSuccessor<TextBoxFactory>(); //Decent Fallback in case nothing matches.
	}
	
	public IDefinition GetDefinition(string id)
	{
		IDefinition definition;
		
		if (_definitions.TryGetValue(id, out definition))
		{
			return definition;
		}
		
		definition = _factory.Create(id);
		_definitions.Add(id, definition);
		return definition;
	}
}


//Sample [Chain Of Responsilibity](https://www.dofactory.com/net/chain-of-responsibility-design-pattern)
abstract class DefinitionFactory
{
	protected DefinitionFactory _successor;
	
	public DefinitionFactory SetSuccessor(DefinitionFactory successor)
	{
		return _successor = successor;
	}

	public DefinitionFactory SetSuccessor<T>()
		where T : DefinitionFactory, new()
	{
		return _successor = new T();
	}
	
	public IDefinition Create(string id)
	{
		var definition = CreateDefinition(id);
		
		if (definition != null)
			return definition;
		
		if (_successor != null)
		{
			definition = _successor.Create(id);
			
			if (definition != null)
				return definition;
		}
		
		throw new NotSupportedException(id);
	}
	
	protected abstract IDefinition CreateDefinition(string id);
}

###Meta Data

The id the GetDefinition method receives is determined by the metadata you have to place on the model. (Hence model first) This is done by adding attributes.

	[Settings("PersonName")]
	public string Name { get; set;}

In this specific case, the id will be PersonName. Sometimes, fields can have multiple definitions, depending on the situations. Imagine a class where a second property determins if the decorated property is shown as a combobox or a normal text field. This can't be decorated by a simple attribute.

In this case, we can use a second provided attribute.

	[DynamicSettings("SomeDynamicProperty")]
	public string Property { get; set;}

When using Dynamic Settings, the class that has said property must implement IHaveDynamicPropertySettings.

public class Model : IHaveDynamicPropertySettings
{
	public string ProvideDynamicSettings(string field)
	{
		if (field == "SomeDynamicProperty")
		{
			if (SecondProperty == "Something")
			{
				return "PersonName";
			}
			else
			{
				return "PersonList";
			}
		}
		
		return field;
	}
}

The string returned will be the id used in the settings manager to find the correct IDefinition.

###Definitions

####Default

A definition has the following properties:

public interface IDefinition
{
	string Id { get; }
	string Label { get; }
	string Editor { get; }
}
  • Id is the id from the GetDefinition method.
  • Label is the text used for the LabelFor extension.
  • Editor is the type of editor that needs to be used.

This will do for most settings. Two control types require a specific interface to be implemented;

####ComboBox

public interface IComboBoxDefinition : IDefinition
{
    IEnumerable ItemsSource { get; }
}

####RadioButton

public interface IRadioButtonDefinition : IDefinition
{
    string GroupName { get; }
}

To make life easier, constants are available! The Editor is defined as a string rather than an enum to make it easier to add your own implementations!

Nova.Bindings.ValueEditor.Definitions.ValueTextEditor
Nova.Bindings.ValueEditor.Definitions.ValueCheckBoxEditor
Nova.Bindings.ValueEditor.Definitions.ValueRadioButtonEditor
Nova.Bindings.ValueEditor.Definitions.ValueComboBoxEditor
Nova.Bindings.ValueEditor.Definitions.ValueDatePickerEditor 

###Templates

A template can easily be added by adding a similar template into the App's Resource Dictionary. Samples can be found here.

Note that the Template keys are the same as the Editor constants that IDefinition supplies!

<ControlTemplate x:Key="ValueTextEditor" TargetType="n:ValueEditor">
    <TextBox x:Name="PART_ValueEditor"
             Text="{Binding Value, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}" />
</ControlTemplate>

About

Model first approach to WPF view designs.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages