Commits

Anonymous committed 0b17546

Work on common UI/MVVM abstractions.

Comments (0)

Files changed (36)

UserProjects/sprucely/CSharp/Composer/Composer/Common/Collections/BindableEnum.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Composer.Common.Collections
+{
+	/// <summary>
+	/// A databindable enum with a display name.
+	/// </summary>
+	public class BindableEnum
+	{
+		/// <summary>
+		/// Gets or sets the underlying value.
+		/// </summary>
+		/// <value>The underlying value.</value>
+		public int UnderlyingValue { get; set; }
+
+		/// <summary>
+		/// Gets or sets the display name.
+		/// </summary>
+		/// <value>The display name.</value>
+		public string DisplayName { get; set; }
+
+		/// <summary>
+		/// Gets or sets the value.
+		/// </summary>
+		/// <value>The value.</value>
+		public object Value { get; set; }
+
+		/// <summary>
+		/// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
+		/// </summary>
+		/// <returns>
+		/// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
+		/// </returns>
+		public override string ToString()
+		{
+			return DisplayName;
+		}
+
+		/// <summary>
+		/// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
+		/// </summary>
+		/// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
+		/// <returns>
+		/// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
+		/// </returns>
+		/// <exception cref="T:System.NullReferenceException">
+		/// The <paramref name="obj"/> parameter is null.
+		/// </exception>
+		public override bool Equals(object obj)
+		{
+			var otherBindable = obj as BindableEnum;
+
+			if (otherBindable != null)
+				return UnderlyingValue == otherBindable.UnderlyingValue;
+
+			if (obj is int)
+				return UnderlyingValue.Equals((int)obj);
+
+			return false;
+		}
+
+		/// <summary>
+		/// Serves as a hash function for a particular type.
+		/// </summary>
+		/// <returns>
+		/// A hash code for the current <see cref="T:System.Object"/>.
+		/// </returns>
+		public override int GetHashCode()
+		{
+			return UnderlyingValue.GetHashCode();
+		}
+	}
+}

UserProjects/sprucely/CSharp/Composer/Composer/Common/Collections/BindableEnumCollection.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using ReactiveUI;
+using System.Reflection;
+using System.ComponentModel;
+
+namespace Composer.Common.Collections
+{
+	/// <summary>
+	/// A collection of <see cref="BindableEnum"/> based on an <see cref="Enum"/>.
+	/// </summary>
+	/// <typeparam name="T">The Enum type.</typeparam>
+	public class BindableEnumCollection<T> : ReactiveCollection<BindableEnum>
+		where T : struct, IConvertible
+	{
+		#region < Fields >
+
+		private static KeyedList<T, EnumInfo> _enumInfos;
+
+		#endregion < Fields >
+
+		#region < Constructors >
+
+		static BindableEnumCollection()
+		{
+			_enumInfos = new KeyedList<T, EnumInfo>(x => x.Value);
+			var type = typeof(T);
+			if (!type.IsEnum)
+				throw new ArgumentException("This class only supports Enum types.");
+
+			var fields = type.GetFields(BindingFlags.Static | BindingFlags.Public);
+
+			foreach (var field in fields)
+			{
+				var att = field.GetAttributes<DescriptionAttribute>(false)
+					.FirstOrDefault();
+
+				var value = field.GetValue(null);
+				_enumInfos.Add(new EnumInfo { Value = (T)value, Name = att != null ? att.Description : field.Name });
+			}
+		}
+
+		public BindableEnumCollection(bool prefill)
+		{
+			if (prefill)
+				Prefill();
+		}
+
+		/// <summary>
+		/// Initializes a new instance of the <see cref="BindableEnumCollection&lt;T&gt;"/> class.
+		/// </summary>
+		public BindableEnumCollection(params T[] values)
+		{
+			Prefill(values);
+		}
+
+		#endregion < Constructors >
+
+		#region < Methods >
+
+		private static BindableEnum GetBindableEnum(EnumInfo enumInfo)
+		{
+			return new BindableEnum
+			{
+				Value = enumInfo.Value,
+				UnderlyingValue = Convert.ToInt32(enumInfo.Value),
+				DisplayName = enumInfo.Name
+			};
+		}
+
+		public void Add(T value)
+		{
+			var enumInfo = _enumInfos[value];
+			Add(GetBindableEnum(enumInfo));
+		}
+
+		public void AddRange(IEnumerable<T> values)
+		{
+			foreach (var value in
+				from value in values
+				let enumInfo = _enumInfos[value]
+				select GetBindableEnum(enumInfo))
+			{
+				base.Add(value);
+			}
+		}
+
+		private void Prefill(params T[] limitToValues)
+		{
+			var hashSet = new HashSet<T>(limitToValues.Distinct());
+
+			foreach (var value in
+				from enumInfo in _enumInfos
+				where limitToValues.Length == 0 || hashSet.Contains(enumInfo.Value)
+				select GetBindableEnum(enumInfo))
+			{
+				base.Add(value);
+			}
+		}
+
+		#endregion < Methods >
+
+		#region < Nested Types >
+
+		private class EnumInfo
+		{
+			#region < Fields >
+
+			public string Name;
+			public T Value;
+
+			#endregion < Fields >
+		}
+
+		#endregion < Nested Types >
+	}
+}

UserProjects/sprucely/CSharp/Composer/Composer/Common/Collections/KeyedList.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Composer.Common.Collections
+{
+	/// <summary>
+	/// Implements KeyedCollection using a <see cref="Func<TItem, TKey>"/> to retrieve an item's key. 
+	/// </summary>
+	public class KeyedList<TKey, TItem> : SynchronizedKeyedCollection<TKey, TItem>
+	{
+		#region < Fields >
+
+		private Func<TItem, TKey> _getKey;
+
+		#endregion < Fields >
+
+		#region < Constructors >
+
+		public KeyedList(Func<TItem, TKey> getKey, int dictionaryCreationThreshold = 1)
+			: base(new object(), EqualityComparer<TKey>.Default, dictionaryCreationThreshold)
+		{
+			this._getKey = getKey;
+		}
+
+		#endregion < Constructors >
+
+		#region < Methods >
+
+		public void AddOrUpdate(TItem item)
+		{
+			if (!Contains(item))
+				Add(item);
+		}
+
+		public new bool Contains(TItem item)
+		{
+			return base.Contains(_getKey(item));
+		}
+
+		public TKey GetKey(TItem item)
+		{
+			return _getKey(item);
+		}
+
+		public bool RemoveItem(TItem item)
+		{
+			return base.Remove(_getKey(item));
+		}
+
+		public bool TryGetValue(TKey key, out TItem item)
+		{
+			if (Dictionary == null)
+			{
+				item = default(TItem);
+				return false;
+			}
+
+			return Dictionary.TryGetValue(key, out item);
+		}
+
+		protected override TKey GetKeyForItem(TItem item)
+		{
+			return _getKey(item);
+		}
+
+		#endregion < Methods >
+	}
+}

UserProjects/sprucely/CSharp/Composer/Composer/Composer.csproj

       <SpecificVersion>False</SpecificVersion>
       <HintPath>..\lib\System.Reactive.Windows.Threading.dll</HintPath>
     </Reference>
+    <Reference Include="System.ServiceModel" />
     <Reference Include="System.Windows.Forms" />
     <Reference Include="System.Windows.Interactivity">
       <HintPath>..\lib\System.Windows.Interactivity.dll</HintPath>
       <DependentUpon>App.xaml</DependentUpon>
       <SubType>Code</SubType>
     </Compile>
+    <Compile Include="Common\Collections\BindableEnum.cs" />
+    <Compile Include="Common\Collections\BindableEnumCollection.cs" />
     <Compile Include="Common\Collections\EnumerableExtensions.cs" />
+    <Compile Include="Common\Collections\KeyedList.cs" />
     <Compile Include="Common\Collections\ReadOnlyDictionary.cs" />
     <Compile Include="Common\Interfaces.cs" />
     <Compile Include="Common\TypeUtilities.cs" />
     <Compile Include="Graph\CellEffector.cs" />
     <Compile Include="Graph\EffectorMessage.cs" />
+    <Compile Include="ORM\ConnectionInfo.cs" />
     <Compile Include="ORM\EnumMappingConvention.cs" />
     <Compile Include="ORM\StoreConfiguration.cs" />
     <Compile Include="ORM\BaseEnumMap.cs" />
     <Compile Include="UI\Controls\ConnectionAdorner.cs" />
     <Compile Include="UI\Controls\Connector.cs" />
     <Compile Include="UI\Controls\ConnectorAdorner.cs" />
+    <Compile Include="UI\Controls\Dialog\ButtonConverter.cs" />
+    <Compile Include="UI\Controls\Dialog\ButtonPanel.cs" />
+    <Compile Include="UI\Controls\Dialog\Dialog.cs" />
+    <Compile Include="UI\Controls\Dialog\DialogChrome.cs" />
     <Compile Include="UI\Controls\GraphCanvas.cs" />
     <Compile Include="UI\Controls\GraphItem.cs" />
     <Compile Include="UI\Controls\DragThumb.cs" />
     <Compile Include="UI\Controls\PathFinder.cs" />
     <Compile Include="UI\Controls\RelativePositionPanel.cs" />
     <Compile Include="UI\Controls\RubberBandAdorner.cs" />
+    <Compile Include="UI\Converters\Common.cs" />
+    <Compile Include="UI\Converters\ConverterFactory.cs" />
     <Compile Include="UI\Converters\EqualityToBooleanConverter.cs" />
+    <Compile Include="UI\Converters\LambdaConverter.cs" />
+    <Compile Include="UI\Converters\ValueConverterException.cs" />
     <Compile Include="UI\DispatcherHelper.cs" />
+    <Compile Include="UI\Elements\ButtonModel.cs" />
+    <Compile Include="UI\Elements\ControlState.cs" />
     <Compile Include="UI\Elements\IShortcut.cs" />
     <Compile Include="UI\Elements\Menu\MenuModel.cs" />
     <Compile Include="UI\Elements\Tree\ITreeNodeViewModel.cs" />
     <Compile Include="UI\Elements\Tree\ITreeViewModel.cs" />
-    <Compile Include="UI\Elements\Tree\MdiViewModelFactory.cs" />
     <Compile Include="UI\Elements\Tree\TreeNodeViewModelBase.cs" />
     <Compile Include="UI\Elements\Tree\TreeViewModelBase.cs" />
     <Compile Include="UI\Interaction\Prompt\Answer.cs" />
     <Compile Include="UI\Interaction\Prompt\IPromptResult.cs" />
+    <Compile Include="UI\Interaction\Prompt\IQuestionDialog.cs" />
+    <Compile Include="UI\Interaction\Prompt\MessageBoxResult.cs" />
+    <Compile Include="UI\Interaction\Prompt\Prompt.cs" />
+    <Compile Include="UI\Interaction\Prompt\PromptExtensions.cs" />
+    <Compile Include="UI\Interaction\Prompt\PromptResultBase.cs" />
+    <Compile Include="UI\Interaction\Prompt\QuestionDialogView.xaml.cs">
+      <DependentUpon>QuestionDialogView.xaml</DependentUpon>
+    </Compile>
+    <Compile Include="UI\Interaction\Prompt\QuestionDialogViewModel.cs" />
     <Compile Include="UI\Interaction\Prompt\ShowFileDialogResultBase.cs" />
     <Compile Include="UI\Interaction\Prompt\ShowFileOpenDialogResult.cs" />
     <Compile Include="UI\Interaction\Prompt\ShowFileSaveDialogResult.cs" />
     <AppDesigner Include="Properties\" />
   </ItemGroup>
   <ItemGroup>
+    <Page Include="Themes\generic.xaml">
+      <SubType>Designer</SubType>
+      <Generator>MSBuild:Compile</Generator>
+    </Page>
+    <Page Include="UI\Controls\Dialog\ResourcesForButtonPanel.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
+    <Page Include="UI\Controls\Dialog\ResourcesForDialog.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
+    <Page Include="UI\Interaction\Prompt\QuestionDialogView.xaml">
+      <SubType>Designer</SubType>
+      <Generator>MSBuild:Compile</Generator>
+    </Page>
     <Page Include="UI\Screens\GraphEditor\GraphEditorView.xaml">
       <SubType>Designer</SubType>
       <Generator>MSBuild:Compile</Generator>

UserProjects/sprucely/CSharp/Composer/Composer/ORM/ConnectionInfo.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Composer.UI.Framework;
+using ReactiveUI;
+
+namespace Composer.ORM
+{
+	public class ConnectionInfo : ReactiveObject
+	{
+		private string _filePath;
+
+		public ConnectionInfo()
+		{
+			this.ObservableForProperty(x => x.FilePath)
+				.Subscribe(x => raisePropertyChanged("FileName"));
+		}
+
+		public string FileName
+		{
+			get 
+			{
+				if (string.IsNullOrEmpty(_filePath))
+					return null;
+				return System.IO.Path.GetFileName(_filePath);
+			}
+		}
+
+		public string FilePath
+		{
+			get { return _filePath; }
+			set { this.RaiseAndSetIfChanged(x => x.FilePath, ref _filePath, value); }
+		}
+	}
+}

UserProjects/sprucely/CSharp/Composer/Composer/ShellViewModel.cs

 	using System;
 	using System.Collections.Generic;
 	using System.ComponentModel.Composition;
-	
-	using Composer.UI.Framework;
+	using System.Linq;
+
 	using Composer.Graph;
 	using Composer.ORM;
-	using Composer.UI.Screens;
-	using ReactiveUI;
-	using System.Linq;
-	using ReactiveUI.Xaml;
-using Composer.UI.Elements;
+	using Composer.UI.Elements;
+	using Composer.UI.Framework;
 	using Composer.UI.Interaction;
 	using Composer.UI.Interaction.Prompt;
+	using Composer.UI.Screens;
+
+	using ReactiveUI;
+	using ReactiveUI.Xaml;
 
 	[Export(typeof(IShell))]
 	public class ShellViewModel : Screen, IShell
 	{
 		public ReactiveCommand CmdCreateComponent { get; protected set; }
-
+		public ConnectionInfo ConenctionInfo { get; private set; }
 
 		public ShellViewModel()
 		{
+			this.DisplayName = "Composer";
+
+			this.ConenctionInfo = new ConnectionInfo();
+			this.ConenctionInfo.ObservableForProperty(x => x.FileName)
+				.Subscribe(info => this.DisplayName = "Composer" + (!string.IsNullOrEmpty(info.Value) ? ": " + info.Value : string.Empty));
+
 			CmdCreateComponent = new ReactiveCommand(
 				this.ObservableForProperty(x => x.CanCreateComponent, val => val));
 
 
 		private void NewDatabase()
 		{
+			string folderName = null;
 			Answer answer = Answer.Cancel;
-			Show.FileOpenDialog(result => answer = result.Answer)
-				.Execute();
+			Show.FolderBrowserDialog(result =>
+			{
+				answer = result.Answer;
+				folderName = result.SelectedPath;
+			})
+			.WithDescritpion("Select location for new database")
+			.Execute();
+
+			if (answer != Answer.Ok || string.IsNullOrEmpty(folderName))
+				return;
+
+			string fileName = null;
+			var inputResult = Show.InputBox("Enter name for new database", "DataBase Name", x => answer = x.Answer);
+			inputResult.Execute();
 
 			if (answer != Answer.Ok)
 				return;
 
 		private void OpenDatabase()
 		{
+			string fileName = null;
 			Answer answer = Answer.Cancel;
-			Show.FileOpenDialog(result => answer = result.Answer)
+			Show.FileOpenDialog(result => 
+				{
+					answer = result.Answer;
+					fileName = result.FileName;
+				})
+				.WithTitle("Open Cosa database")
+				.WithCheckFileExists(true)
+				.WithCheckPathExists(true)
+				.WithFilter("Cosa database files (*.cosadb)|*.cosadb|All files (*.*)|*.*")
+				.WithFilterIndex(0)
 				.Execute();
 
-			if (answer != Answer.Ok)
+			if (answer != Answer.Ok || string.IsNullOrEmpty(fileName))
 				return;
+
+			this.ConenctionInfo.FilePath = fileName;
 		}
 
 		private object CreateComponent()

UserProjects/sprucely/CSharp/Composer/Composer/Themes/generic.xaml

+<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
+
+    <ResourceDictionary.MergedDictionaries>
+        <ResourceDictionary Source="/Composer;component/Composer/UI/Controls/Dialog/ResourcesForButtonPanel.xaml" />
+        <ResourceDictionary Source="/Composer;component/Composer/UI/Controls/Dialog/ResourcesForDialog.xaml" />
+    </ResourceDictionary.MergedDictionaries>
+</ResourceDictionary>

UserProjects/sprucely/CSharp/Composer/Composer/UI/Controls/Dialog/ButtonConverter.cs

+namespace Composer.UI.Controls
+{
+	using System;
+	using System.ComponentModel;
+	using System.Globalization;
+	using System.Linq;
+	
+	using Composer.Common.Collections;
+	using Composer.UI.Elements;
+	
+	using ReactiveUI;
+
+	public class ButtonConverter : TypeConverter
+	{
+		#region < Methods >
+
+		public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
+		{
+			if (typeof(string).IsAssignableFrom(sourceType))
+				return true;
+			return base.CanConvertFrom(context, sourceType);
+		}
+
+		public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
+		{
+			var theString = value.ToString();
+			var parts = theString.Split(new[] { ' ', ',' }, StringSplitOptions.RemoveEmptyEntries);
+
+			var collection = new ReactiveCollection<ButtonModel>();
+			collection.AddRange(
+				from part in parts
+				select new ButtonModel(part)
+				);
+
+			return collection;
+		}
+
+		#endregion < Methods >
+	}
+}

UserProjects/sprucely/CSharp/Composer/Composer/UI/Controls/Dialog/ButtonPanel.cs

+namespace Composer.UI.Controls
+{
+	using System.ComponentModel;
+	using System.Windows;
+	using System.Windows.Controls;
+	using Composer.Common.Collections;
+	using Composer.UI.Elements;
+	using ReactiveUI;
+
+	public class ButtonPanel : ItemsControl
+	{
+		#region < Fields >
+
+		public static DependencyProperty ButtonsProperty = 
+			DependencyProperty.Register(
+				"Buttons",
+				typeof(IReactiveCollection<ButtonModel>),
+				typeof(ButtonPanel),
+				new PropertyMetadata(ButtonsChanged)
+				);
+
+		#endregion < Fields >
+
+		#region < Constructors >
+
+		public ButtonPanel()
+		{
+			Visibility = Visibility.Collapsed;
+			DefaultStyleKey = typeof(ButtonPanel);
+		}
+
+		#endregion < Constructors >
+
+		#region < Properties >
+
+		[TypeConverter(typeof(ButtonConverter))]
+		public IReactiveCollection<ButtonModel> Buttons
+		{
+			get { return GetValue(ButtonsProperty) as IReactiveCollection<ButtonModel>; }
+			set { SetValue(ButtonsProperty, value); }
+		}
+
+		#endregion < Properties >
+
+		#region < Methods >
+
+		private static void ButtonsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+		{
+			var list = e.NewValue as IReactiveCollection<ButtonModel>;
+			var panel = (ItemsControl)d;
+
+			panel.Items.Clear();
+
+			if (list != null)
+				list.Apply(x => panel.Items.Add(x));
+
+			panel.Visibility = panel.Items.Count < 1
+								   ? Visibility.Collapsed
+								   : Visibility.Visible;
+		}
+
+		#endregion < Methods >
+	}
+}

UserProjects/sprucely/CSharp/Composer/Composer/UI/Controls/Dialog/Dialog.cs

+namespace Composer.UI.Controls
+{
+	using System;
+	using System.Windows;
+	using System.Windows.Controls;
+
+	[TemplatePart(Name = Dialog.PART_SummaryContent, Type = typeof(ContentPresenter))]
+	[TemplatePart(Name = Dialog.PART_Content, Type = typeof(ContentPresenter))]
+	public class Dialog : DialogChrome
+	{
+		#region < Fields >
+
+		public static DependencyProperty SummaryContentProperty;
+		public static DependencyProperty SummaryContentTemplateProperty;
+
+		const string PART_Content = "PART_Content";
+		const string PART_SummaryContent = "PART_SummaryContent";
+
+		#endregion < Fields >
+
+		#region < Constructors >
+
+		static Dialog()
+		{
+			SummaryContentProperty = DependencyProperty.Register(
+				"SummaryContent" /*name of property*/,
+				typeof(object) /*type of property*/,
+				typeof(Dialog) /*type of 'owner' - our control's class*/,
+				new UIPropertyMetadata(null) /*default value for property*/);
+
+			DefaultStyleKeyProperty.OverrideMetadata(
+					typeof(Dialog),
+					new FrameworkPropertyMetadata(typeof(Dialog)));
+		}
+
+		#endregion < Constructors >
+
+		#region < Properties >
+
+		public object SummaryContent
+		{
+			get
+			{
+				return base.GetValue(SummaryContentProperty);
+			}
+			set
+			{
+				base.SetValue(SummaryContentProperty, value);
+			}
+		}
+
+		#endregion < Properties >
+
+		#region < Methods >
+
+		public override void OnApplyTemplate()
+		{
+			base.OnApplyTemplate();
+			ContentPresenter content;
+			content = base.GetTemplateChild(PART_Content) as ContentPresenter;
+			if (content == null)
+				throw new NotImplementedException(
+					string.Format("Template part \"{0}\" is not implemented", PART_Content));
+			content = base.GetTemplateChild(PART_SummaryContent) as ContentPresenter;
+			if (content == null)
+				throw new NotImplementedException(
+					string.Format("Template part \"{0}\" is not implemented", PART_SummaryContent));
+		}
+
+		#endregion < Methods >
+	}
+}

UserProjects/sprucely/CSharp/Composer/Composer/UI/Controls/Dialog/DialogChrome.cs

+namespace Composer.UI.Controls
+{
+	using System.ComponentModel;
+	using System.Windows;
+	using Composer.UI.Elements;
+	using ReactiveUI;
+
+	public class DialogChrome : Window
+	{
+		#region < Fields >
+
+		public static DependencyProperty ButtonsProperty = 
+			DependencyProperty.Register(
+				"Buttons",
+				typeof(IReactiveCollection<ButtonModel>),
+				typeof(DialogChrome),
+				null
+				);
+
+		#endregion < Fields >
+
+		#region < Constructors >
+
+		public DialogChrome()
+		{
+			DefaultStyleKey = typeof(DialogChrome);
+		}
+
+		#endregion < Constructors >
+
+		#region < Properties >
+
+		[TypeConverter(typeof(ButtonConverter))]
+		public IReactiveCollection<ButtonModel> Buttons
+		{
+			get { return GetValue(ButtonsProperty) as IReactiveCollection<ButtonModel>; }
+			set { SetValue(ButtonsProperty, value); }
+		}
+
+		#endregion < Properties >
+
+		#region < Methods >
+
+		public override void OnApplyTemplate()
+		{
+			base.OnApplyTemplate();
+
+			var chrome = GetTemplateChild("Chrome") as UIElement;
+			if (chrome != null && Title == null) chrome.Visibility = Visibility.Collapsed;
+		}
+
+		#endregion < Methods >
+	}
+}

UserProjects/sprucely/CSharp/Composer/Composer/UI/Controls/Dialog/ResourcesForButtonPanel.xaml

+<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:cal="clr-namespace:Composer.UI.Framework"
+    xmlns:ctrls="clr-namespace:Composer.UI.Controls"
+                    >
+
+    <Style TargetType="{x:Type ctrls:ButtonPanel}" >
+        <Setter Property="Template">
+            <Setter.Value>
+                <ControlTemplate TargetType="{x:Type ctrls:ButtonPanel}">
+                    <Border Margin="0 6 0 0">
+                        <ItemsPresenter />
+                    </Border>
+                </ControlTemplate>
+            </Setter.Value>
+        </Setter>
+        <Setter Property="ItemsPanel">
+            <Setter.Value>
+                <ItemsPanelTemplate>
+                    <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" />
+                </ItemsPanelTemplate>
+            </Setter.Value>
+        </Setter>
+        <Setter Property="ItemTemplate">
+            <Setter.Value>
+                <DataTemplate>
+                    <Button 
+                        Content="{Binding Content}"
+                        Margin="2 0 0 0"
+                        MinWidth="60"
+                        IsDefault="{Binding IsDefault}"
+                        IsCancel="{Binding IsCancel}"
+                        ToolTipService.ToolTip="{Binding ToolTip}"
+                        cal:Message.Attach="{Binding Action}" />
+                </DataTemplate>
+            </Setter.Value>
+        </Setter>
+    </Style>
+
+
+    <!--<Style TargetType="CalCtrls:ButtonPanel" x:Key="buttonPanel">
+        <Setter Property="Template">
+            <Setter.Value>
+                <ControlTemplate TargetType="CalCtrls:ButtonPanel">
+                    <Border Margin="0 6 0 0">
+                        <ItemsPresenter />
+                    </Border>
+                </ControlTemplate>
+            </Setter.Value>
+        </Setter>
+        <Setter Property="ItemsPanel">
+            <Setter.Value>
+                <ItemsPanelTemplate>
+                    <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" />
+                </ItemsPanelTemplate>
+            </Setter.Value>
+        </Setter>
+        <Setter Property="ItemTemplate">
+            <Setter.Value>
+                <DataTemplate>
+                    <Button 
+                        Content="{Binding Content}"
+                        Margin="2 0 0 0"
+                        MinWidth="60"
+                        ToolTipService.ToolTip="{Binding ToolTip}"
+                        rm:Message.Attach="{Binding Action}" />
+                </DataTemplate>
+            </Setter.Value>
+        </Setter>
+    </Style>-->
+
+
+</ResourceDictionary>

UserProjects/sprucely/CSharp/Composer/Composer/UI/Controls/Dialog/ResourcesForDialog.xaml

+<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+                    xmlns:converters="clr-namespace:Composer.UI.Converters"
+                    xmlns:ctrls="clr-namespace:Composer.UI.Controls"
+                    x:Name="root">
+
+    <Style BasedOn="{StaticResource {x:Type Window}}" TargetType="{x:Type ctrls:Dialog}">
+        <Setter Property="IsTabStop" Value="false" />
+        <Setter Property="HorizontalAlignment" Value="Center" />
+        <Setter Property="VerticalAlignment" Value="Center" />
+        <Setter Property="HorizontalContentAlignment" Value="Center" />
+        <Setter Property="VerticalContentAlignment" Value="Center" />
+        <Setter Property="SizeToContent" Value="WidthAndHeight" />
+        <Setter Property="ShowInTaskbar" Value="False" />
+        <!--  <Setter Property="WindowStartupLocation" Value="CenterOwner" />  -->
+        <!--  Causes an exception  -->
+        <Setter Property="Template">
+            <Setter.Value>
+                <ControlTemplate TargetType="{x:Type ctrls:Dialog}">
+                    <DockPanel x:Name="ContentRoot" Background="{TemplateBinding Background}">
+                        <Border x:Name="Chrome"
+                                BorderBrush="#FFFFFFFF"
+                                DockPanel.Dock="Top">
+                            <Border.Background>
+                                <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,0.528">
+                                    <GradientStop Offset="1" Color="#FFE5E8EB" />
+                                    <GradientStop Offset="0" Color="#FFFEFEFE" />
+                                </LinearGradientBrush>
+                            </Border.Background>
+                            <ContentControl Margin="4,6,4,6"
+                                            HorizontalAlignment="Stretch"
+                                            VerticalAlignment="Center"
+                                            IsTabStop="False"
+                                            Visibility="{Binding SummaryContent,
+                                                                 Converter={x:Static converters:Common.CollapsedWhenNull}}">
+                                <ContentPresenter x:Name="PART_SummaryContent"
+                                                  HorizontalAlignment="Stretch"
+                                                  VerticalAlignment="Center"
+                                                  Content="{TemplateBinding SummaryContent}" />
+                            </ContentControl>
+                        </Border>
+                        <ctrls:ButtonPanel Margin="4"
+                                           Buttons="{TemplateBinding Buttons}"
+                                           DockPanel.Dock="Bottom"
+                                           Style="{DynamicResource {x:Type ctrls:ButtonPanel}}" />
+                        <Border Background="{TemplateBinding Background}">
+                            <ContentPresenter x:Name="PART_Content"
+                                              HorizontalAlignment="Stretch"
+                                              VerticalAlignment="Stretch"
+                                              Content="{TemplateBinding Content}"
+                                              ContentTemplate="{TemplateBinding ContentTemplate}" />
+                        </Border>
+                    </DockPanel>
+                </ControlTemplate>
+            </Setter.Value>
+        </Setter>
+    </Style>
+
+
+
+</ResourceDictionary>

UserProjects/sprucely/CSharp/Composer/Composer/UI/Converters/Common.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows.Data;
+using System.Windows;
+using System.Collections;
+using Composer.UI.Elements;
+using Composer.Common.Collections;
+
+namespace Composer.UI.Converters
+{
+	public static class Common
+	{
+		#region < Visibility converters >
+
+		public static readonly IValueConverter CollapsedWhenTrue = ConverterFactory.New(
+			() => Common.CollapsedWhenTrue,
+			(bool value) => value ? Visibility.Collapsed : Visibility.Visible);
+
+		public static readonly IValueConverter CollapsedWhenFalse = ConverterFactory.New(
+			() => Common.CollapsedWhenFalse,
+			(bool value) => !value ? Visibility.Collapsed : Visibility.Visible);
+
+		public static readonly IValueConverter HiddenWhenTrue = ConverterFactory.New(
+			() => Common.HiddenWhenTrue,
+			(bool value) => value ? Visibility.Hidden : Visibility.Visible);
+
+		public static readonly IValueConverter HiddenWhenFalse = ConverterFactory.New(
+			() => Common.HiddenWhenFalse,
+			(bool value) => !value ? Visibility.Hidden : Visibility.Visible, CheckTargetType.No);
+
+		public static readonly IValueConverter CollapsedWhenNull = ConverterFactory.New(
+			() => Common.CollapsedWhenNull,
+			(object value) => value == null ? Visibility.Collapsed : Visibility.Visible);
+
+		public static readonly IValueConverter HiddenWhenNull = ConverterFactory.New(
+			() => Common.HiddenWhenNull,
+			(object value) => value == null ? Visibility.Hidden : Visibility.Visible);
+
+		public static readonly IValueConverter CollapsedWhenEmpty = ConverterFactory.New(
+			() => Common.CollapsedWhenEmpty,
+			(object value) =>
+			{
+				if (value == null)
+					return Visibility.Collapsed;
+				var enumerable = value as IEnumerable;
+				if (enumerable == null || !enumerable.Cast<object>().Any())
+					return Visibility.Collapsed;
+				return Visibility.Visible;
+			}
+			);
+
+		public static readonly IValueConverter CollapsedWhenNotNull = ConverterFactory.New(
+			() => Common.CollapsedWhenNotNull,
+			(object value) => value != null ? Visibility.Collapsed : Visibility.Visible);
+
+		public static readonly IValueConverter CollapsedWhen0 = ConverterFactory.New(
+			() => Common.CollapsedWhen0,
+			(object value) => Convert.ToInt32(value) == 0 ? Visibility.Collapsed : Visibility.Visible);
+
+		public static readonly IValueConverter VisibilityFromControlState = ConverterFactory.New(
+			() => Common.VisibilityFromControlState,
+			(ControlState value) => value == ControlState.Hidden ? Visibility.Collapsed : Visibility.Visible);
+
+		public static readonly IValueConverter IsEnabledFromControlState = ConverterFactory.New(
+			() => Common.VisibilityFromControlState,
+			(ControlState value) => (bool)(value == ControlState.Enabled));
+
+		#endregion < Visibility converters >
+
+		#region < Enum converters >
+
+		public static readonly IValueConverter EnumToBindableEnum = ConverterFactory.New(
+			() => Common.EnumToBindableEnum,
+			(object value) => new BindableEnum
+			{
+				Value = value,
+				UnderlyingValue = (int)value,
+				DisplayName = Enum.GetName(value.GetType(), value)
+			},
+			(BindableEnum value) => value.Value,
+			CheckTargetType.No);
+
+		#endregion < Enum converters >
+	}
+}

UserProjects/sprucely/CSharp/Composer/Composer/UI/Converters/ConverterFactory.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Linq.Expressions;
+using System.Windows.Data;
+using System.Globalization;
+
+using Composer.Common;
+
+namespace Composer.UI.Converters
+{
+	public static class ConverterFactory
+	{
+		#region < LambdaConverter >
+
+		public static LambdaConverter<TSource, TTarget> New<TSource, TTarget>(
+			Expression<Func<IValueConverter>> converterLocation,
+			Func<TSource, TTarget> convert,
+			CheckTargetType checkTargetType = CheckTargetType.Yes)
+		{
+			return new LambdaConverter<TSource, TTarget>(
+				(value, culture) => convert(value),
+				null,
+				checkTargetType,
+				converterLocation.GetMemberInfo());
+		}
+
+		public static LambdaConverter<TSource, TTarget> New<TSource, TTarget>(
+			Expression<Func<IValueConverter>> converterLocation,
+			Func<TSource, TTarget> convert,
+			Func<TTarget, TSource> convertBack,
+			CheckTargetType checkTargetType = CheckTargetType.Yes)
+		{
+			return new LambdaConverter<TSource, TTarget>(
+				(value, culture) => convert(value),
+				(value, culture) => convertBack(value),
+				checkTargetType,
+				converterLocation.GetMemberInfo());
+		}
+
+		public static LambdaConverter<TSource, TTarget, TParam> New<TSource, TTarget, TParam>(
+			Expression<Func<IValueConverter>> converterLocation,
+			Func<TSource, TParam, TTarget> convert,
+			CheckTargetType checkTargetType = CheckTargetType.Yes)
+		{
+			return new LambdaConverter<TSource, TTarget, TParam>(
+				(value, parameter, culture) => convert(value, parameter),
+				null,
+				checkTargetType,
+				converterLocation.GetMemberInfo());
+		}
+
+		public static LambdaConverter<TSource, TTarget, TParam> New<TSource, TTarget, TParam>(
+			Expression<Func<IValueConverter>> converterLocation,
+			Func<TSource, TParam, TTarget> convert,
+			Func<TTarget, object, TSource> convertBack,
+			CheckTargetType checkTargetType = CheckTargetType.Yes)
+		{
+			return new LambdaConverter<TSource, TTarget, TParam>(
+				(value, parameter, culture) => convert(value, parameter),
+				(value, parameter, culture) => convertBack(value, parameter),
+				checkTargetType,
+				converterLocation.GetMemberInfo());
+		}
+
+		public static LambdaConverter<TSource, TTarget> New<TSource, TTarget>(
+			Expression<Func<IValueConverter>> converterLocation,
+			Func<TSource, CultureInfo, TTarget> convert,
+			CheckTargetType checkTargetType = CheckTargetType.Yes)
+		{
+			return new LambdaConverter<TSource, TTarget>(
+				convert,
+				null,
+				checkTargetType,
+				converterLocation.GetMemberInfo());
+		}
+
+		public static LambdaConverter<TSource, TTarget> New<TSource, TTarget>(
+			Expression<Func<IValueConverter>> converterLocation,
+			Func<TSource, CultureInfo, TTarget> convert,
+			Func<TTarget, CultureInfo, TSource> convertBack,
+			CheckTargetType checkTargetType = CheckTargetType.Yes)
+		{
+			return new LambdaConverter<TSource, TTarget>(
+				convert,
+				convertBack,
+				checkTargetType,
+				converterLocation.GetMemberInfo());
+		}
+
+		public static LambdaConverter<TSource, TTarget, TParam> New<TSource, TTarget, TParam>(
+			Expression<Func<IValueConverter>> converterLocation,
+			Func<TSource, TParam, CultureInfo, TTarget> convert,
+			CheckTargetType checkTargetType = CheckTargetType.Yes)
+		{
+			return new LambdaConverter<TSource, TTarget, TParam>(
+				convert,
+				null,
+				checkTargetType,
+				converterLocation.GetMemberInfo());
+		}
+
+		public static LambdaConverter<TSource, TTarget, TParam> New<TSource, TTarget, TParam>(
+			Expression<Func<IValueConverter>> converterLocation,
+			Func<TSource, TParam, CultureInfo, TTarget> convert,
+			Func<TTarget, object, CultureInfo, TSource> convertBack,
+			CheckTargetType checkTargetType = CheckTargetType.Yes)
+		{
+			return new LambdaConverter<TSource, TTarget, TParam>(
+				convert,
+				convertBack,
+				checkTargetType,
+				converterLocation.GetMemberInfo());
+		}
+
+		#endregion < LambdaConverter >
+	}
+}

UserProjects/sprucely/CSharp/Composer/Composer/UI/Converters/LambdaConverter.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Globalization;
+using System.Reflection;
+
+namespace Composer.UI.Converters
+{
+	internal enum ConvertAction
+	{
+		Convert,
+		ConvertBack
+	}
+
+	internal static class LamdaConverterHelper
+	{
+
+		/// <summary>
+		/// Provides common exception handling for LambdaConverter actions
+		/// </summary>
+		/// <param name="owningMember"></param>
+		/// <param name="converOrConvertBack"></param>
+		/// <param name="action"></param>
+		public static void TryConvertAction(MemberInfo owningMember, ConvertAction convertAction, Action action)
+		{
+			try
+			{
+				action();
+			}
+			catch (Exception ex)
+			{
+				if (owningMember == null)
+					throw; // unable to add any helpful information, so throw original exception
+
+				throw new ValueConverterException(
+					string.Format("An exception occurred in {0}.{1}.{2}. See inner exception for details",
+						owningMember.DeclaringType,
+						owningMember.Name,
+						convertAction),
+					ex);
+			}
+		}
+
+	}
+
+	public class LambdaConverter<TSource, TTarget, TParam> : ValueConverterBase<TSource, TTarget, TParam>
+	{
+		private readonly Func<TSource, TParam, CultureInfo, TTarget> _convert;
+		private readonly Func<TTarget, object, CultureInfo, TSource> _convertBack;
+		private readonly MemberInfo _owningMember;
+
+		public LambdaConverter(
+			Func<TSource, TParam, CultureInfo, TTarget> convert,
+			Func<TTarget, object, CultureInfo, TSource> convertBack,
+			CheckTargetType checkTargetType,
+			MemberInfo owningMember = null)
+		{
+			base.CheckTargetType = checkTargetType;
+			_convert = convert;
+			_convertBack = convertBack;
+			_owningMember = owningMember;
+		}
+
+
+		protected override TTarget Convert(TSource value, TParam parameter, CultureInfo culture)
+		{
+			if (_convert == null)
+				throw new NotImplementedException();
+			TTarget result = default(TTarget);
+			LamdaConverterHelper.TryConvertAction(_owningMember, ConvertAction.Convert, () =>
+			{
+				result = _convert(value, parameter, culture);
+			});
+			return result;
+		}
+
+		protected override TSource ConvertBack(TTarget value, object parameter, CultureInfo culture)
+		{
+			if (_convertBack == null)
+				throw new NotImplementedException();
+			TSource result = default(TSource);
+			LamdaConverterHelper.TryConvertAction(_owningMember, ConvertAction.ConvertBack, () =>
+			{
+				result = _convertBack(value, parameter, culture);
+			});
+			return result;
+		}
+
+		protected override void CheckArgumentsForConvert(object valueFrom, Type typeTo)
+		{
+			LamdaConverterHelper.TryConvertAction(_owningMember, ConvertAction.Convert, () =>
+			{
+				base.CheckArgumentsForConvert(valueFrom, typeTo);
+			});
+		}
+
+		protected override void CheckArgumentsForConvertBack(object valueTo, Type typeFrom)
+		{
+			LamdaConverterHelper.TryConvertAction(_owningMember, ConvertAction.ConvertBack, () =>
+			{
+				base.CheckArgumentsForConvertBack(valueTo, typeFrom);
+			});
+		}
+	}
+
+	public class LambdaConverter<TSource, TTarget> : ValueConverterBase<TSource, TTarget>
+	{
+		private readonly Func<TSource, CultureInfo, TTarget> _convert;
+		private readonly Func<TTarget, CultureInfo, TSource> _convertBack;
+		private readonly MemberInfo _owningMember;
+
+		public LambdaConverter(
+			Func<TSource, CultureInfo, TTarget> convert,
+			Func<TTarget, CultureInfo, TSource> convertBack,
+			CheckTargetType checkTargetType,
+			MemberInfo owningMember = null)
+		{
+			base.CheckTargetType = checkTargetType;
+			_convert = convert;
+			_convertBack = convertBack;
+			_owningMember = owningMember;
+		}
+
+		protected override void CheckArgumentsForConvert(object valueFrom, Type typeTo)
+		{
+			LamdaConverterHelper.TryConvertAction(_owningMember, ConvertAction.Convert, () =>
+			{
+				base.CheckArgumentsForConvert(valueFrom, typeTo);
+			});
+		}
+
+		protected override void CheckArgumentsForConvertBack(object valueTo, Type typeFrom)
+		{
+			LamdaConverterHelper.TryConvertAction(_owningMember, ConvertAction.ConvertBack, () =>
+			{
+				base.CheckArgumentsForConvertBack(valueTo, typeFrom);
+			});
+		}
+
+		protected override TTarget Convert(TSource value, CultureInfo culture)
+		{
+			if (_convert == null)
+				throw new NotImplementedException();
+
+			var result = default(TTarget);
+			LamdaConverterHelper.TryConvertAction(_owningMember, ConvertAction.Convert, () =>
+			{
+				result = _convert(value, culture);
+			});
+			return result;
+		}
+
+		protected override TSource ConvertBack(TTarget value, CultureInfo culture)
+		{
+			if (_convertBack == null)
+				throw new NotImplementedException();
+
+			var result = default(TSource);
+			LamdaConverterHelper.TryConvertAction(_owningMember, ConvertAction.ConvertBack, () =>
+			{
+				result = _convertBack(value, culture);
+			});
+			return result;
+		}
+	}
+}

UserProjects/sprucely/CSharp/Composer/Composer/UI/Converters/ObjectToTypeConverter.cs

 	{
 		public ObjectToTypeConverter()
 		{
-			CheckTRight = false;
+			this.CheckTargetType = CheckTargetType.No;
 		}
 
 		protected override Type Convert(object value, CultureInfo culture)

UserProjects/sprucely/CSharp/Composer/Composer/UI/Converters/ValueConverterBase.cs

 using System.Globalization;
 using System.Windows.Data;
 
+using Composer.Common.Collections;
+
 namespace Composer.UI.Converters
 {
+	public enum CheckTargetType
+	{
+		Yes = 0,
+		No = 1
+	}
+
+	public interface ICheckingValueConverter : IValueConverter
+	{
+		CheckTargetType CheckTargetType { get; set; }
+	}
+
 	/// <summary>
 	/// Generic base class that provides common implementation logic of <see cref="IValueConverter"/>
 	/// </summary>
-	public abstract class ValueConverterBase<TFrom, TTo, TParam> : IValueConverter
+	public abstract class ValueConverterBase<TSource, TTarget, TParam> : ICheckingValueConverter
 	{
+		#region < Constructors >
+
 		public ValueConverterBase()
 		{
-			CheckTRight = true;
+			CheckTargetType = CheckTargetType.Yes;
 		}
 
-		protected bool CheckTRight
+		#endregion < Constructors >
+
+		#region < Properties >
+
+		public CheckTargetType CheckTargetType
 		{
 			get;
 			set;
 		}
 
+		#endregion < Properties >
+
+		#region < Methods >
+
 		public object Convert(object valueFrom, Type typeTo,
 			object parameter, CultureInfo culture)
 		{
-			if (valueFrom != null && !typeof(TFrom).IsAssignableFrom(valueFrom.GetType()))
-				throw new ArgumentException(GetType().Name
-				  + ".Convert: value type not assignable to type " + typeof(TFrom).Name);
-			if (CheckTRight && !typeof(TTo).IsAssignableFrom(typeTo))
-				throw new ArgumentException(GetType().Name
-				  + ".Convert: target type not assignable to type " + typeof(TTo).Name);
-			return Convert((TFrom)valueFrom, (TParam)parameter, culture);
+			CheckArgumentsForConvert(valueFrom, typeTo);
+			return Convert((TSource)valueFrom, (TParam)parameter, culture);
+		}
+
+		protected virtual void CheckArgumentsForConvert(object valueFrom, Type typeTo)
+		{
+			if (valueFrom != null && !typeof(TSource).IsAssignableFrom(valueFrom.GetType()))
+				throw new ArgumentException(string.Format("{0}.Convert: expecting valueFrom type of [{1}]. Actual type is [{2}]",
+					GetType().Name, typeof(TSource).Name, valueFrom.GetType()));
+			if (CheckTargetType == CheckTargetType.Yes && !typeof(TTarget).IsAssignableFrom(typeTo))
+				throw new ArgumentException(string.Format("{0}.Convert: expecting typeTo of [{1}]. Actual type is [{2}].  This check can be disabled using CheckTargetType.No",
+					GetType().Name, typeof(TTarget).Name, typeTo));
 		}
 
 		public object ConvertBack(object valueTo, Type typeFrom,
 			object parameter, CultureInfo culture)
 		{
-			if (valueTo != null && !typeof(TTo).IsAssignableFrom(valueTo.GetType()))
-				throw new ArgumentException(GetType().Name
-				  + ".ConvertBack: value type not " + typeof(TTo).Name);
-			if (typeFrom != typeof(TFrom))
-				throw new ArgumentException(GetType().Name
-				  + ".ConvertBack: target type not " + typeof(TFrom).Name);
-			return ConvertBack((TTo)valueTo, parameter, culture);
+			CheckArgumentsForConvertBack(valueTo, typeFrom);
+			return ConvertBack((TTarget)valueTo, parameter, culture);
 		}
 
-		protected virtual TTo Convert(TFrom value, TParam parameter,
+		protected virtual void CheckArgumentsForConvertBack(object valueTo, Type typeFrom)
+		{
+			if (valueTo != null && !typeof(TTarget).IsAssignableFrom(valueTo.GetType()))
+				throw new ArgumentException(GetType().Name
+				  + ".ConvertBack: value type not " + typeof(TTarget).Name);
+			if (CheckTargetType == CheckTargetType.Yes && !typeFrom.IsAssignableFrom(typeof(TSource)))
+				throw new ArgumentException(GetType().Name
+				  + ".ConvertBack: target type not " + typeof(TSource).Name);
+		}
+
+		protected virtual TTarget Convert(TSource value, TParam parameter,
 			CultureInfo culture)
 		{
 			throw new NotImplementedException(
 			  GetType().Name + ".Convert not implemented");
 		}
 
-		protected virtual TFrom ConvertBack(TTo value,
+		protected virtual TSource ConvertBack(TTarget value,
 			object parameter, CultureInfo culture)
 		{
 			throw new NotImplementedException(
 			  GetType().Name + ".ConvertBack not implemented");
 		}
 
+		#endregion < Methods >
 	}
 
 	/// <summary>
 	/// Generic base class that provides common implementation logic of <see cref="IValueConverter"/>
 	/// </summary>
-	public abstract class ValueConverterBase<TFrom, TTo> : ValueConverterBase<TFrom, TTo, object>
+	public abstract class ValueConverterBase<TSource, TTarget> : ValueConverterBase<TSource, TTarget, object>
 	{
-		protected virtual TTo Convert(TFrom value, CultureInfo culture)
+		#region < Methods >
+
+		protected virtual TTarget Convert(TSource value, CultureInfo culture)
 		{
 			throw new NotImplementedException(
 			  GetType().Name + ".Convert not implemented");
 		}
 
-		protected override sealed TTo Convert(TFrom value,
+		protected override sealed TTarget Convert(TSource value,
 			object parameter, CultureInfo culture)
 		{
 			if (parameter != null)
 			return Convert(value, culture);
 		}
 
-		protected virtual TFrom ConvertBack(TTo value, CultureInfo culture)
+		protected virtual TSource ConvertBack(TTarget value, CultureInfo culture)
 		{
 			throw new NotImplementedException(
 			  GetType().Name + ".ConvertBack not implemented");
 		}
 
-		protected override sealed TFrom ConvertBack(TTo value,
+		protected override sealed TSource ConvertBack(TTarget value,
 			object parameter, CultureInfo culture)
 		{
 			if (parameter != null)
 			return ConvertBack(value, culture);
 		}
 
+		#endregion < Methods >
+	}
+
+
+
+	/// <summary>
+	/// Generic base class that provides common implementation logic of <see cref="IMultiValueConverter"/>
+	/// </summary>
+	public abstract class MultiValueConverterBase<TTo, TParam> : IMultiValueConverter
+	{
+		#region < Constructors >
+
+		public MultiValueConverterBase()
+		{
+			CheckTRight = true;
+		}
+
+		#endregion < Constructors >
+
+		#region < Properties >
+
+		protected bool CheckTRight
+		{
+			get;
+			set;
+		}
+
+		protected abstract Type[] FromTypes
+		{
+			get;
+		}
+
+		#endregion < Properties >
+
+		#region < Methods >
+
+		public object Convert(object[] fromValues, Type typeTo,
+			object parameter, CultureInfo culture)
+		{
+			if (fromValues != null)
+			{
+				if (fromValues.Length != this.FromTypes.Length)
+					throw new ArgumentException(GetType().Name
+						 + ".Convert: Number of input values does not match the expected amount.");
+				int i = -1;
+				List<int> invalidValues = null;
+				foreach (var pair in FromTypes.Zip(fromValues, (t, v) => new { Type = t, Value = v }))
+				{
+					i += 1;
+					if (pair.Value != null && !pair.Type.IsAssignableFrom(pair.Value.GetType()))
+					{
+						invalidValues = invalidValues ?? new List<int>();
+						invalidValues.Add(i);
+					}
+					if (invalidValues != null)
+						throw new ArgumentException(string.Format(
+							"{0}.Convert: value with the following indices were not assignable to their corresponding type: ({1}) ",
+							GetType().Name, invalidValues.Join(",")));
+				}
+			}
+			if (CheckTRight && !typeof(TTo).IsAssignableFrom(typeTo))
+				throw new ArgumentException(GetType().Name
+				  + ".Convert: target type not assignable to type " + typeof(TTo).Name);
+			return Convert(fromValues, (TParam)parameter, culture);
+		}
+
+		public object[] ConvertBack(object valueTo, Type[] typesFrom,
+			object parameter, CultureInfo culture)
+		{
+			if (valueTo != null && !typeof(TTo).IsAssignableFrom(valueTo.GetType()))
+				throw new ArgumentException(GetType().Name
+				  + ".ConvertBack: value type not " + typeof(TTo).Name);
+
+
+			int i = -1;
+			List<int> invalidTypes = null;
+			foreach (var pair in FromTypes.Zip(typesFrom, (localType, paramType) => new { LocalType = localType, ParamType = paramType }))
+			{
+				i += 1;
+				if (pair.LocalType != pair.ParamType)
+				{
+					invalidTypes = invalidTypes ?? new List<int>();
+					invalidTypes.Add(i);
+				}
+				if (invalidTypes != null)
+					throw new ArgumentException(string.Format(
+						"{0}.ConvertBack: Types with following indices weredo not match: ({1}) ",
+						GetType().Name, invalidTypes.Join(",")));
+			}
+
+
+			return ConvertBack((TTo)valueTo, parameter, culture);
+		}
+
+		protected virtual TTo Convert(object[] fromValues, TParam parameter,
+			CultureInfo culture)
+		{
+			throw new NotImplementedException(
+			  GetType().Name + ".Convert not implemented");
+		}
+
+		protected virtual object[] ConvertBack(TTo value,
+			object parameter, CultureInfo culture)
+		{
+			throw new NotImplementedException(
+			  GetType().Name + ".ConvertBack not implemented");
+		}
+
+		#endregion < Methods >
+	}
+
+
+	/// <summary>
+	/// Generic base class that provides common implementation logic of <see cref="IMultiValueConverter"/>
+	/// </summary>
+	public abstract class MultiValueConverterBase<TTo> : MultiValueConverterBase<TTo, object>
+	{
+		#region < Methods >
+
+		protected virtual TTo Convert(object[] fromValues, CultureInfo culture)
+		{
+			throw new NotImplementedException(
+			  GetType().Name + ".Convert not implemented");
+		}
+
+		protected override TTo Convert(object[] fromValues, object parameter, CultureInfo culture)
+		{
+			if (parameter != null)
+				throw new ArgumentException(GetType().Name
+				  + ".Convert: binding contains unexpected parameter");
+			return Convert(fromValues, culture);
+		}
+
+		protected virtual object[] ConvertBack(TTo value, CultureInfo culture)
+		{
+			throw new NotImplementedException(
+			  GetType().Name + ".ConvertBack not implemented");
+		}
+
+		protected override object[] ConvertBack(TTo value, object parameter, CultureInfo culture)
+		{
+			if (parameter != null)
+				throw new ArgumentException(GetType().Name
+				  + ".ConvertBack: binding contains unexpected parameter");
+			return ConvertBack(value, culture);
+		}
+
+		#endregion < Methods >
 	}
 
 }

UserProjects/sprucely/CSharp/Composer/Composer/UI/Converters/ValueConverterException.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Composer.UI.Converters
+{
+	public class ValueConverterException : Exception
+	{
+		public ValueConverterException(string message)
+			: base(message)
+		{ }
+
+		public ValueConverterException(string message, Exception innerException)
+			: base(message, innerException)
+		{ }
+	}
+}

UserProjects/sprucely/CSharp/Composer/Composer/UI/Elements/ButtonModel.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Composer.UI.Elements
+{
+	public class ButtonModel
+	{
+		#region < Constructors >
+
+		public ButtonModel()
+		{
+		}
+
+		public ButtonModel(string name)
+		{
+			Content = ToolTip = Action = name;
+		}
+
+		#endregion < Constructors >
+
+		#region < Properties >
+
+		/// <summary>
+		/// The name of the action to be performed.
+		/// </summary>
+		/// <remarks>
+		/// Used by MVVM message binding system.
+		/// </remarks>
+		public string Action
+		{
+			get;
+			set;
+		}
+
+		public object Content
+		{
+			get;
+			set;
+		}
+
+		public object ToolTip
+		{
+			get;
+			set;
+		}
+
+		public bool IsDefault
+		{
+			get;
+			set;
+		}
+
+		public bool IsCancel
+		{
+			get;
+			set;
+		}
+
+		#endregion < Properties >
+	}
+}

UserProjects/sprucely/CSharp/Composer/Composer/UI/Elements/ControlState.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;