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 canExecute = null, Action execute = null) + { + this.CanExecuteDelegate = canExecute; + this.ExecuteDelegate = execute; + } + + public Func CanExecuteDelegate { get; set; } + + public Action ExecuteDelegate { get; set; } + + public bool CanExecute(object parameter) + { + var canExecute = this.CanExecuteDelegate; + return canExecute == null || canExecute(parameter); + } + + public event EventHandler CanExecuteChanged + { + add => CommandManager.RequerySuggested += value; + remove => CommandManager.RequerySuggested -= value; + } + + public void Execute(object parameter) + { + this.ExecuteDelegate?.Invoke(parameter); + } + } +} \ No newline at end of file diff --git a/src/MahApps.Metro.Demo_v2/ExampleViews/BadgedExample.xaml b/src/MahApps.Metro.Demo_v2/ExampleViews/BadgedExample.xaml new file mode 100644 index 0000000000..af417a7472 --- /dev/null +++ b/src/MahApps.Metro.Demo_v2/ExampleViews/BadgedExample.xaml @@ -0,0 +1,39 @@ + + + + + + + diff --git a/src/MahApps.Metro.Demo_v2/ExampleViews/BadgedExample.xaml.cs b/src/MahApps.Metro.Demo_v2/ExampleViews/BadgedExample.xaml.cs new file mode 100644 index 0000000000..d781836e95 --- /dev/null +++ b/src/MahApps.Metro.Demo_v2/ExampleViews/BadgedExample.xaml.cs @@ -0,0 +1,36 @@ +// 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.Demo.Controls; +using MahApps.Metro.Controls; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using ControlzEx; + +namespace MahApps.Demo.ExampleViews +{ + /// + /// Interaction logic for BadgedExample.xaml + /// + public partial class BadgedExample : UserControl + { + public BadgedExample() + { + this.InitializeComponent(); + + this.demoView.DemoProperties.Add(new DemoViewProperty(BadgedEx.BadgeProperty, this.badge, "Badged")); + } + + private void Button_Click(object sender, RoutedEventArgs e) + { + this.badge.SetCurrentValue(ControlzEx.BadgedEx.BadgeProperty, (this.badge.Badge as int? ?? 0) + 1); + } + + private void Button_MouseRightButtonUp(object sender, MouseButtonEventArgs e) + { + this.badge.SetCurrentValue(ControlzEx.BadgedEx.BadgeProperty, null); + } + } +} \ No newline at end of file diff --git a/src/MahApps.Metro.Demo_v2/ExampleViews/ButtonExample.xaml b/src/MahApps.Metro.Demo_v2/ExampleViews/ButtonExample.xaml new file mode 100644 index 0000000000..08292b9036 --- /dev/null +++ b/src/MahApps.Metro.Demo_v2/ExampleViews/ButtonExample.xaml @@ -0,0 +1,29 @@ + + + ]]> + +