diff --git a/src/MahApps.Metro.Demo_v2/App.xaml b/src/MahApps.Metro.Demo_v2/App.xaml
new file mode 100644
index 0000000000..e33cbaf11b
--- /dev/null
+++ b/src/MahApps.Metro.Demo_v2/App.xaml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/MahApps.Metro.Demo_v2/App.xaml.cs b/src/MahApps.Metro.Demo_v2/App.xaml.cs
new file mode 100644
index 0000000000..8dd65ea95a
--- /dev/null
+++ b/src/MahApps.Metro.Demo_v2/App.xaml.cs
@@ -0,0 +1,15 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Windows;
+
+namespace MahApps.Metro.Demo_v2
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : Application
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/MahApps.Metro.Demo_v2/AssemblyInfo.cs b/src/MahApps.Metro.Demo_v2/AssemblyInfo.cs
new file mode 100644
index 0000000000..815c616377
--- /dev/null
+++ b/src/MahApps.Metro.Demo_v2/AssemblyInfo.cs
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
\ No newline at end of file
diff --git a/src/MahApps.Metro.Demo_v2/Controls/DemoView.cs b/src/MahApps.Metro.Demo_v2/Controls/DemoView.cs
new file mode 100644
index 0000000000..51ac20e56a
--- /dev/null
+++ b/src/MahApps.Metro.Demo_v2/Controls/DemoView.cs
@@ -0,0 +1,103 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using ICSharpCode.AvalonEdit;
+using MahApps.Demo.Core;
+using System.Collections.ObjectModel;
+using System.Diagnostics;
+using System.Windows;
+using System.Windows.Controls;
+
+namespace MahApps.Demo.Controls
+{
+ [TemplatePart(Name = nameof(PART_AvalonEdit), Type = typeof(TextEditor))]
+ public class DemoView : HeaderedContentControl
+ {
+ private TextEditor PART_AvalonEdit;
+
+ /// Identifies the dependency property.
+ public static readonly DependencyProperty ExampleXamlProperty = DependencyProperty.Register(nameof(ExampleXaml), typeof(string), typeof(DemoView), new PropertyMetadata(null, OnExampleXamlChanged));
+
+ /// Identifies the dependency property.
+ public static readonly DependencyProperty HyperlinkOnlineDocsProperty = DependencyProperty.Register(nameof(HyperlinkOnlineDocs), typeof(string), typeof(DemoView), new PropertyMetadata(null));
+
+ public DemoView()
+ {
+ this.DemoProperties.CollectionChanged += this.DemoProperties_CollectionChanged;
+ }
+
+ public ObservableCollection DemoProperties { get; } = new ObservableCollection();
+
+ private void DemoProperties_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
+ {
+ if (e.NewItems != null)
+ {
+ foreach (DemoViewProperty item in e.NewItems)
+ {
+ item.PropertyChanged += this.DemoProperties_ItemPropertyChanged;
+ }
+ }
+
+ if (e.OldItems != null)
+ {
+ foreach (DemoViewProperty item in e.OldItems)
+ {
+ item.PropertyChanged -= this.DemoProperties_ItemPropertyChanged;
+ }
+ }
+ }
+
+ private void DemoProperties_ItemPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
+ {
+ this.SetExampleXaml();
+ }
+
+ public string ExampleXaml
+ {
+ get => (string)this.GetValue(ExampleXamlProperty);
+ set => this.SetValue(ExampleXamlProperty, value);
+ }
+
+ private void SetExampleXaml()
+ {
+ if (this.PART_AvalonEdit != null)
+ {
+ var exampleText = this.ExampleXaml;
+
+ foreach (var item in this.DemoProperties)
+ {
+ exampleText = exampleText.Replace($"[{item.PropertyName}]", item.GetExampleXamlContent());
+ }
+
+ this.PART_AvalonEdit.Text = exampleText;
+ }
+ }
+
+ private static void OnExampleXamlChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ if (d is DemoView demoView)
+ {
+ demoView.SetExampleXaml();
+ }
+ }
+
+ public string HyperlinkOnlineDocs
+ {
+ get => (string)this.GetValue(HyperlinkOnlineDocsProperty);
+ set => this.SetValue(HyperlinkOnlineDocsProperty, value);
+ }
+
+ public SimpleCommand NavigateToOnlineDocs { get; } = new SimpleCommand(
+ (param) => !string.IsNullOrEmpty(param?.ToString()),
+ (param) => Process.Start(new ProcessStartInfo(param?.ToString())));
+
+ public override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+
+ this.PART_AvalonEdit = this.GetTemplateChild(nameof(this.PART_AvalonEdit)) as TextEditor;
+ this.SetExampleXaml();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/MahApps.Metro.Demo_v2/Controls/DemoViewProperty.cs b/src/MahApps.Metro.Demo_v2/Controls/DemoViewProperty.cs
new file mode 100644
index 0000000000..9c3865562f
--- /dev/null
+++ b/src/MahApps.Metro.Demo_v2/Controls/DemoViewProperty.cs
@@ -0,0 +1,207 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using MahApps.Metro.Demo_v2;
+using System;
+using System.Collections;
+using System.ComponentModel;
+using System.Windows;
+using System.Windows.Data;
+
+namespace MahApps.Demo.Controls
+{
+ public class DemoViewProperty : DependencyObject, INotifyPropertyChanged
+ {
+ #region PropertyChanged
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ public void RaisePropertyChanged(string PropertyName)
+ {
+ this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
+ }
+
+ private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ if (d is DemoViewProperty demoViewProperty)
+ {
+ demoViewProperty.RaisePropertyChanged(e.Property.Name);
+ }
+ }
+
+ #endregion
+
+ #region Constructors
+
+ public DemoViewProperty()
+ {
+ this.GetExampleXamlContent = this.GetExampleXamlContent_Default;
+ }
+
+ public DemoViewProperty(DependencyProperty dependencyProperty, DependencyObject bindingTarget, string groupName = null, DataTemplate dataTemplate = null)
+ : this()
+ {
+ this.SetCurrentValue(PropertyNameProperty, this.GetDefaultPropertyName(dependencyProperty));
+
+ this.SetCurrentValue(GroupNameProperty, groupName ?? this.GetDefaultGroupName());
+
+ this.SetCurrentValue(DataTemplateProperty, dataTemplate ?? this.GetDefaultDataTemplate(dependencyProperty));
+
+ // Create Binding to the Control
+ var binding = new Binding()
+ {
+ Path = new PropertyPath(dependencyProperty),
+ Source = bindingTarget,
+ Mode = BindingMode.TwoWay
+ };
+ BindingOperations.SetBinding(this, ValueProperty, binding);
+ }
+
+ #endregion
+
+ /// Identifies the dependency property.
+ public static readonly DependencyProperty PropertyNameProperty = DependencyProperty.Register(nameof(PropertyName), typeof(string), typeof(DemoViewProperty), new PropertyMetadata(null));
+
+ /// Identifies the dependency property.
+ public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(nameof(Value), typeof(object), typeof(DemoViewProperty), new PropertyMetadata(null, OnValueChanged));
+
+ /// Identifies the dependency property.
+ public static readonly DependencyProperty DataTemplateProperty = DependencyProperty.Register(nameof(DataTemplate), typeof(DataTemplate), typeof(DemoViewProperty), new PropertyMetadata(null));
+
+ /// Identifies the dependency property.
+ public static readonly DependencyProperty ItemSourceProperty = DependencyProperty.Register(nameof(ItemSource), typeof(IEnumerable), typeof(DemoViewProperty), new PropertyMetadata(null));
+
+ /// Identifies the dependency property.
+ public static readonly DependencyProperty GroupNameProperty = DependencyProperty.Register(nameof(GroupName), typeof(string), typeof(DemoViewProperty), new PropertyMetadata(null));
+
+ ///
+ /// Gets or Sets the PropertyName
+ ///
+ public string PropertyName
+ {
+ get => (string)this.GetValue(PropertyNameProperty);
+ set => this.SetValue(PropertyNameProperty, value);
+ }
+
+ private string GetDefaultPropertyName(DependencyProperty dependencyProperty)
+ {
+ if (typeof(UIElement).IsAssignableFrom(dependencyProperty.OwnerType))
+ {
+ return dependencyProperty.Name;
+ }
+ else
+ {
+ return $"{dependencyProperty.OwnerType.Name}.{dependencyProperty.Name}";
+ }
+ }
+
+ ///
+ /// Gets or Sets the Value
+ ///
+ public object Value
+ {
+ get => (object)this.GetValue(ValueProperty);
+ set => this.SetValue(ValueProperty, value);
+ }
+
+ ///
+ /// Gets or Sets the GroupName
+ ///
+ public string GroupName
+ {
+ get => (string)this.GetValue(GroupNameProperty);
+ set => this.SetValue(GroupNameProperty, value);
+ }
+
+ private string GetDefaultGroupName()
+ {
+ if (string.IsNullOrWhiteSpace(this.PropertyName))
+ return null;
+
+ switch (this.PropertyName)
+ {
+ case string _ when this.PropertyName.EndsWith("Alignment"):
+ case string _ when this.PropertyName.EndsWith("Height"):
+ case string _ when this.PropertyName.EndsWith("Width"):
+ return "Layout";
+
+ default:
+ return "Misc";
+ }
+ }
+
+ ///
+ /// Gets or Sets the DataTemplate
+ ///
+ public DataTemplate DataTemplate
+ {
+ get => (DataTemplate)this.GetValue(DataTemplateProperty);
+ set => this.SetValue(DataTemplateProperty, value);
+ }
+
+ private DataTemplate GetDefaultDataTemplate(DependencyProperty dependencyProperty)
+ {
+ // Any Object
+ if (dependencyProperty.PropertyType == typeof(object)) return null;
+
+ // Numeric
+ if (dependencyProperty.PropertyType.IsAssignableFrom(typeof(sbyte)) ||
+ dependencyProperty.PropertyType.IsAssignableFrom(typeof(byte)) ||
+ dependencyProperty.PropertyType.IsAssignableFrom(typeof(short)) ||
+ dependencyProperty.PropertyType.IsAssignableFrom(typeof(ushort)) ||
+ dependencyProperty.PropertyType.IsAssignableFrom(typeof(int)) ||
+ dependencyProperty.PropertyType.IsAssignableFrom(typeof(uint)) ||
+ dependencyProperty.PropertyType.IsAssignableFrom(typeof(long)) ||
+ dependencyProperty.PropertyType.IsAssignableFrom(typeof(ulong)) ||
+ dependencyProperty.PropertyType.IsAssignableFrom(typeof(float)) ||
+ dependencyProperty.PropertyType.IsAssignableFrom(typeof(double)) ||
+ dependencyProperty.PropertyType.IsAssignableFrom(typeof(decimal)))
+ {
+ return Application.Current.Resources["MahDemo.DataTemplates.PropertyPresenter.Numeric"] as DataTemplate;
+ }
+
+ if (dependencyProperty.PropertyType.IsAssignableFrom(typeof(string)))
+ {
+ return Application.Current.Resources["MahDemo.DataTemplates.PropertyPresenter.String"] as DataTemplate;
+ }
+
+ if (dependencyProperty.PropertyType == typeof(bool))
+ {
+ return Application.Current.Resources["MahDemo.DataTemplates.PropertyPresenter.Boolean"] as DataTemplate;
+ }
+
+ if (dependencyProperty.PropertyType.IsEnum)
+ {
+ return Application.Current.Resources["MahDemo.DataTemplates.PropertyPresenter.Enum"] as DataTemplate;
+ }
+
+ if (dependencyProperty.PropertyType.IsAssignableFrom(typeof(Style)))
+ {
+ return Application.Current.Resources["MahDemo.DataTemplates.PropertyPresenter.Styles"] as DataTemplate;
+ }
+
+ return null;
+ }
+
+ ///
+ /// Gets or Sets the ItemSource used for selectable
+ ///
+ public IEnumerable ItemSource
+ {
+ get => (IEnumerable)this.GetValue(ItemSourceProperty);
+ set => this.SetValue(ItemSourceProperty, value);
+ }
+
+ #region XAML Replace Value
+
+ public Func GetExampleXamlContent { get; set; }
+
+ private string GetExampleXamlContent_Default()
+ {
+ return this.Value?.ToString();
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/src/MahApps.Metro.Demo_v2/Controls/DemoViewPropertyTemplateSelector.cs b/src/MahApps.Metro.Demo_v2/Controls/DemoViewPropertyTemplateSelector.cs
new file mode 100644
index 0000000000..f218bb31ce
--- /dev/null
+++ b/src/MahApps.Metro.Demo_v2/Controls/DemoViewPropertyTemplateSelector.cs
@@ -0,0 +1,26 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Windows;
+using System.Windows.Controls;
+
+namespace MahApps.Demo.Controls
+{
+ public class DemoViewPropertyTemplateSelector : DataTemplateSelector
+ {
+ public DataTemplate FallbackTemplate { get; set; }
+
+ public override DataTemplate SelectTemplate(object item, DependencyObject container)
+ {
+ if (item is DemoViewProperty demoProperty)
+ {
+ return demoProperty.DataTemplate ?? this.FallbackTemplate;
+ }
+ else
+ {
+ return this.FallbackTemplate;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/MahApps.Metro.Demo_v2/Converter/EnumToItemSourceConverter.cs b/src/MahApps.Metro.Demo_v2/Converter/EnumToItemSourceConverter.cs
new file mode 100644
index 0000000000..b5cc414428
--- /dev/null
+++ b/src/MahApps.Metro.Demo_v2/Converter/EnumToItemSourceConverter.cs
@@ -0,0 +1,28 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using MahApps.Metro.Converters;
+using System;
+using System.Globalization;
+
+namespace MahApps.Demo.Converter
+{
+ public class EnumToItemSourceConverter : MarkupConverter
+ {
+ protected override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is Enum)
+ {
+ return Enum.GetValues(value.GetType());
+ }
+
+ throw new ArgumentException("the provided value is not a valid Enum");
+ }
+
+ protected override object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotSupportedException();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/MahApps.Metro.Demo_v2/Core/BaseClass.cs b/src/MahApps.Metro.Demo_v2/Core/BaseClass.cs
new file mode 100644
index 0000000000..d102c2e533
--- /dev/null
+++ b/src/MahApps.Metro.Demo_v2/Core/BaseClass.cs
@@ -0,0 +1,80 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.ComponentModel;
+
+namespace MahApps.Demo.Core
+{
+ public class BaseClass : INotifyPropertyChanged, INotifyDataErrorInfo
+ {
+ #region INotifyPropertyChanged
+
+ // This event tells the UI to update
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ public void RaisePropertyChanged(string PropertyName)
+ {
+ this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
+ }
+
+ #endregion INotifyPropertyChanged
+
+ #region INotifyDataErrorInfo
+
+ public event EventHandler ErrorsChanged;
+
+ public bool HasErrors => this._errorsByPropertyName.Count > 0;
+
+ private readonly Dictionary> _errorsByPropertyName = new Dictionary>();
+
+ public IEnumerable GetErrors(string propertyName)
+ {
+ if (string.IsNullOrEmpty(propertyName))
+ {
+ return null;
+ }
+ else
+ {
+ return this._errorsByPropertyName.ContainsKey(propertyName)
+ ? this._errorsByPropertyName[propertyName]
+ : null;
+ }
+ }
+
+ public bool GetHasError(string PropertyName)
+ {
+ return this._errorsByPropertyName.ContainsKey(PropertyName);
+ }
+
+ public void AddError(string propertyName, string error)
+ {
+ if (!this._errorsByPropertyName.ContainsKey(propertyName)) this._errorsByPropertyName[propertyName] = new List();
+
+ if (!this._errorsByPropertyName[propertyName].Contains(error))
+ {
+ this._errorsByPropertyName[propertyName].Add(error);
+ this.OnErrorsChanged(propertyName);
+ }
+ }
+
+ public void ClearErrors(string propertyName)
+ {
+ if (this._errorsByPropertyName.ContainsKey(propertyName))
+ {
+ this._errorsByPropertyName.Remove(propertyName);
+ this.OnErrorsChanged(propertyName);
+ }
+ }
+
+ public void OnErrorsChanged(string propertyName)
+ {
+ this.ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName));
+ }
+
+ #endregion INotifyDataErrorInfo
+ }
+}
\ No newline at end of file
diff --git a/src/MahApps.Metro.Demo_v2/Core/SimpleCommand.cs b/src/MahApps.Metro.Demo_v2/Core/SimpleCommand.cs
new file mode 100644
index 0000000000..9a91c48f8d
--- /dev/null
+++ b/src/MahApps.Metro.Demo_v2/Core/SimpleCommand.cs
@@ -0,0 +1,39 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Windows.Input;
+
+namespace MahApps.Demo.Core
+{
+ public class SimpleCommand : ICommand
+ {
+ public SimpleCommand(Func