From a7544f617184c58d0afa90ece9c3d6484c99197b Mon Sep 17 00:00:00 2001 From: NaBian <836904362@qq.com> Date: Sat, 4 May 2019 22:17:10 +0800 Subject: [PATCH] completed the branch --- README.md | 8 +- Resources/SideMenu.png | Bin 0 -> 12648 bytes .../HandyControlDemo_Core30.csproj | 24 ++++ .../HandyControlDemo_Net_40.csproj | 24 +++- .../HandyControlDemo_Shared.projitems | 1 + .../UserControl/Controls/SideMenuDemoCtl.xaml | 15 ++- .../UserControl/Main/LeftMainContent.xaml | 2 +- .../Controls/SideMenuDemoViewModel.cs | 20 ++++ .../ViewModel/ViewModelLocator.cs | 4 + .../Controls/SideMenu/SideMenu.cs | 110 +++++++++++++++++- .../Controls/SideMenu/SideMenuItem.cs | 54 ++++++++- .../Data/Enum/ExpandMode.cs | 12 +- .../Themes/Styles/Base/SideMenuBaseStyle.xaml | 83 ++++++++++++- .../Themes/Styles/SideMenu.xaml | 16 ++- .../Tools/Helper/ConfigHelper.cs | 13 ++- 15 files changed, 361 insertions(+), 25 deletions(-) create mode 100644 Resources/SideMenu.png create mode 100644 src/Shared/HandyControlDemo_Shared/ViewModel/Controls/SideMenuDemoViewModel.cs diff --git a/README.md b/README.md index 5a05eb07..402c5f19 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,10 @@ Step 3:enjoy coding ## Latest examples +### SideMenu + +![SideMenu](https://raw.githubusercontent.com/NaBian/HandyControl/master/Resources/SideMenu.png) + ### NotifyIcon ![NotifyIcon](https://raw.githubusercontent.com/NaBian/HandyControl/master/Resources/NotifyIcon.png) @@ -64,12 +68,12 @@ Step 3:enjoy coding ![Badge](https://raw.githubusercontent.com/NaBian/HandyControl/master/Resources/Badge.png) +## History publication + ### Gravatar ![Gravatar](https://raw.githubusercontent.com/NaBian/HandyControl/master/Resources/Gravatar.gif) -## History publication - ### GoToTop ![GoToTop](https://raw.githubusercontent.com/NaBian/HandyControl/master/Resources/GoToTop.gif) diff --git a/Resources/SideMenu.png b/Resources/SideMenu.png new file mode 100644 index 0000000000000000000000000000000000000000..fc0a655ac9a4fb433a7334ff66361e7d6ea5b6fa GIT binary patch literal 12648 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 literal 0 HcmV?d00001 diff --git a/src/Core_30/HandyControlDemo_Core_30/HandyControlDemo_Core30.csproj b/src/Core_30/HandyControlDemo_Core_30/HandyControlDemo_Core30.csproj index 62a7d384..2f019719 100644 --- a/src/Core_30/HandyControlDemo_Core_30/HandyControlDemo_Core30.csproj +++ b/src/Core_30/HandyControlDemo_Core_30/HandyControlDemo_Core30.csproj @@ -321,6 +321,30 @@ Resources\Img\under_construction.gif + + Resources\Img\DevOps\DevOps-Boards.png + + + Resources\Img\DevOps\DevOps-Overview.png + + + Resources\Img\DevOps\DevOps-Pipelines.png + + + Resources\Img\DevOps\DevOps-Repos.png + + + Resources\Img\DevOps\DevOps-TestPlans.png + + + Resources\Img\LeftMainContent\MainMenuControl_16x.png + + + Resources\fabric-icons.ttf + + + Resources\Img\LeftMainContent\MainMenuControl_16x.png + diff --git a/src/Net_40/HandyControlDemo_Net_40/HandyControlDemo_Net_40.csproj b/src/Net_40/HandyControlDemo_Net_40/HandyControlDemo_Net_40.csproj index bd927660..3324c055 100644 --- a/src/Net_40/HandyControlDemo_Net_40/HandyControlDemo_Net_40.csproj +++ b/src/Net_40/HandyControlDemo_Net_40/HandyControlDemo_Net_40.csproj @@ -381,13 +381,35 @@ Lang.Designer.cs - + + + Resources\fabric-icons.ttf + + Data\MessageToken.tt TextTemplatingFileGenerator MessageToken.cs + + Resources\Img\LeftMainContent\MainMenuControl_16x.png + + + Resources\Img\DevOps\DevOps-Boards.png + + + Resources\Img\DevOps\DevOps-Overview.png + + + Resources\Img\DevOps\DevOps-Pipelines.png + + + Resources\Img\DevOps\DevOps-Repos.png + + + Resources\Img\DevOps\DevOps-TestPlans.png + Resources\Img\LeftMainContent\RepeatButton_16x.png diff --git a/src/Shared/HandyControlDemo_Shared/HandyControlDemo_Shared.projitems b/src/Shared/HandyControlDemo_Shared/HandyControlDemo_Shared.projitems index e7fe4d04..c189a905 100644 --- a/src/Shared/HandyControlDemo_Shared/HandyControlDemo_Shared.projitems +++ b/src/Shared/HandyControlDemo_Shared/HandyControlDemo_Shared.projitems @@ -283,6 +283,7 @@ + diff --git a/src/Shared/HandyControlDemo_Shared/UserControl/Controls/SideMenuDemoCtl.xaml b/src/Shared/HandyControlDemo_Shared/UserControl/Controls/SideMenuDemoCtl.xaml index 3c8dbb3a..b056b8ce 100644 --- a/src/Shared/HandyControlDemo_Shared/UserControl/Controls/SideMenuDemoCtl.xaml +++ b/src/Shared/HandyControlDemo_Shared/UserControl/Controls/SideMenuDemoCtl.xaml @@ -2,11 +2,18 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:controls="clr-namespace:HandyControl.Controls;assembly=HandyControl" - xmlns:interactivity="clr-namespace:HandyControl.Interactivity;assembly=HandyControl" + xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" + xmlns:command="http://www.galasoft.ch/mvvmlight" + DataContext="{Binding SideMenuDemo,Source={StaticResource Locator}}" Background="{DynamicResource RegionBrush}"> - - + + + + + + + @@ -143,6 +150,6 @@ - + diff --git a/src/Shared/HandyControlDemo_Shared/UserControl/Main/LeftMainContent.xaml b/src/Shared/HandyControlDemo_Shared/UserControl/Main/LeftMainContent.xaml index d3b4c74d..53d8d6de 100644 --- a/src/Shared/HandyControlDemo_Shared/UserControl/Main/LeftMainContent.xaml +++ b/src/Shared/HandyControlDemo_Shared/UserControl/Main/LeftMainContent.xaml @@ -203,7 +203,7 @@ - + diff --git a/src/Shared/HandyControlDemo_Shared/ViewModel/Controls/SideMenuDemoViewModel.cs b/src/Shared/HandyControlDemo_Shared/ViewModel/Controls/SideMenuDemoViewModel.cs new file mode 100644 index 00000000..e8b4e32a --- /dev/null +++ b/src/Shared/HandyControlDemo_Shared/ViewModel/Controls/SideMenuDemoViewModel.cs @@ -0,0 +1,20 @@ +using System; +using GalaSoft.MvvmLight; +using HandyControl.Controls; +using HandyControl.Data; +#if netle40 +using GalaSoft.MvvmLight.Command; +#else +using GalaSoft.MvvmLight.CommandWpf; +# endif + +namespace HandyControlDemo.ViewModel +{ + public class SideMenuDemoViewModel : ViewModelBase + { + public RelayCommand> SwitchItemCmd => new Lazy>>(() => + new RelayCommand>(SwitchItem)).Value; + + private void SwitchItem(FunctionEventArgs info) => Growl.Info((info.Info as SideMenuItem)?.Header.ToString()); + } +} \ No newline at end of file diff --git a/src/Shared/HandyControlDemo_Shared/ViewModel/ViewModelLocator.cs b/src/Shared/HandyControlDemo_Shared/ViewModel/ViewModelLocator.cs index e7f59c12..c3c84631 100644 --- a/src/Shared/HandyControlDemo_Shared/ViewModel/ViewModelLocator.cs +++ b/src/Shared/HandyControlDemo_Shared/ViewModel/ViewModelLocator.cs @@ -33,6 +33,7 @@ namespace HandyControlDemo.ViewModel SimpleIoc.Default.Register(); SimpleIoc.Default.Register(); SimpleIoc.Default.Register(); + SimpleIoc.Default.Register(); } public static ViewModelLocator Instance => new Lazy(() => @@ -71,6 +72,9 @@ namespace HandyControlDemo.ViewModel public InteractiveDialogViewModel InteractiveDialog => ServiceLocator.Current.GetInstance(); public BadgeDemoViewModel BadgeDemo => ServiceLocator.Current.GetInstance(); + + public SideMenuDemoViewModel SideMenuDemo => ServiceLocator.Current.GetInstance(); + #endregion } } \ No newline at end of file diff --git a/src/Shared/HandyControl_Shared/Controls/SideMenu/SideMenu.cs b/src/Shared/HandyControl_Shared/Controls/SideMenu/SideMenu.cs index 9f5dff65..02656070 100644 --- a/src/Shared/HandyControl_Shared/Controls/SideMenu/SideMenu.cs +++ b/src/Shared/HandyControl_Shared/Controls/SideMenu/SideMenu.cs @@ -1,6 +1,7 @@ -using System.Windows; +using System; +using System.Linq; +using System.Windows; using HandyControl.Data; -using HandyControl.Data.Enum; namespace HandyControl.Controls { @@ -15,6 +16,22 @@ namespace HandyControl.Controls public SideMenu() { AddHandler(SideMenuItem.SelectedEvent, new RoutedEventHandler(SideMenuItemSelected)); + + Loaded += (s, e) => Init(); + } + + protected override void Refresh() + { + base.Refresh(); + + Init(); + } + + private void Init() + { + if (ItemsHost == null) return; + + OnExpandModeChanged(ExpandMode); } private void SideMenuItemSelected(object sender, RoutedEventArgs e) @@ -23,6 +40,8 @@ namespace HandyControl.Controls { if (item.Role == SideMenuItemRole.Item) { + if (Equals(item, _selectedItem)) return; + if (_selectedItem != null) { _selectedItem.IsSelected = false; @@ -30,6 +49,10 @@ namespace HandyControl.Controls _selectedItem = item; _selectedItem.IsSelected = true; + RaiseEvent(new FunctionEventArgs(SelectionChangedEvent, this) + { + Info = e.OriginalSource + }); _isItemSelected = true; } else @@ -38,20 +61,36 @@ namespace HandyControl.Controls { if (_selectedHeader != null) { + if (ExpandMode == ExpandMode.Freedom && item.ItemsHost.IsVisible && !_isItemSelected) + { + item.IsSelected = false; + SwitchPanelArea(item); + return; + } + _selectedHeader.IsSelected = false; - SwitchPanelArea(_selectedHeader); + if (ExpandMode != ExpandMode.Freedom) + { + SwitchPanelArea(_selectedHeader); + } } _selectedHeader = item; _selectedHeader.IsSelected = true; SwitchPanelArea(_selectedHeader); } + else if (ExpandMode == ExpandMode.Freedom && !_isItemSelected) + { + _selectedHeader.IsSelected = false; + SwitchPanelArea(_selectedHeader); + _selectedHeader = null; + } if (_isItemSelected) { _isItemSelected = false; } - else + else if(_selectedHeader != null) { _selectedHeader.SelectDefaultItem(); _isItemSelected = false; @@ -67,6 +106,7 @@ namespace HandyControl.Controls case ExpandMode.ShowAll: return; case ExpandMode.ShowOne: + case ExpandMode.Freedom: case ExpandMode.Accordion: oldItem.SwitchPanelArea(oldItem.IsSelected); break; @@ -78,12 +118,72 @@ namespace HandyControl.Controls protected override bool IsItemItsOwnContainerOverride(object item) => item is SideMenuItem; public static readonly DependencyProperty ExpandModeProperty = DependencyProperty.Register( - "ExpandMode", typeof(ExpandMode), typeof(SideMenu), new PropertyMetadata(default(ExpandMode))); + "ExpandMode", typeof(ExpandMode), typeof(SideMenu), new PropertyMetadata(default(ExpandMode), OnExpandModeChanged)); + + private static void OnExpandModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var ctl = (SideMenu) d; + var v = (ExpandMode) e.NewValue; + + if (ctl.ItemsHost == null) + { + return; + } + + ctl.OnExpandModeChanged(v); + } + + private void OnExpandModeChanged(ExpandMode mode) + { + if (mode == ExpandMode.ShowAll) + { + ShowAll(); + } + else if (mode == ExpandMode.ShowOne) + { + var sideMenuItem = ItemsHost.Children.OfType().FirstOrDefault(item => item.IsSelected); + ShowSelectedOne(sideMenuItem); + } + } public ExpandMode ExpandMode { get => (ExpandMode) GetValue(ExpandModeProperty); set => SetValue(ExpandModeProperty, value); } + + public static readonly DependencyProperty PanelAreaLengthProperty = DependencyProperty.Register( + "PanelAreaLength", typeof(double), typeof(SideMenu), new PropertyMetadata(double.NaN)); + + public double PanelAreaLength + { + get => (double) GetValue(PanelAreaLengthProperty); + set => SetValue(PanelAreaLengthProperty, value); + } + + private void ShowAll() + { + foreach (var sideMenuItem in ItemsHost.Children.OfType()) + { + sideMenuItem.SwitchPanelArea(true); + } + } + + private void ShowSelectedOne(SideMenuItem item) + { + foreach (var sideMenuItem in ItemsHost.Children.OfType()) + { + sideMenuItem.SwitchPanelArea(Equals(sideMenuItem, item)); + } + } + + public static readonly RoutedEvent SelectionChangedEvent = EventManager.RegisterRoutedEvent( + "SelectionChanged", RoutingStrategy.Bubble, typeof(EventHandler>), typeof(SideMenu)); + + public event EventHandler> SelectionChanged + { + add => AddHandler(SelectionChangedEvent, value); + remove => RemoveHandler(SelectionChangedEvent, value); + } } } diff --git a/src/Shared/HandyControl_Shared/Controls/SideMenu/SideMenuItem.cs b/src/Shared/HandyControl_Shared/Controls/SideMenu/SideMenuItem.cs index 937b3c52..26f43578 100644 --- a/src/Shared/HandyControl_Shared/Controls/SideMenu/SideMenuItem.cs +++ b/src/Shared/HandyControl_Shared/Controls/SideMenu/SideMenuItem.cs @@ -1,5 +1,6 @@ using System.Linq; using System.Windows; +using System.Windows.Data; using System.Windows.Input; using HandyControl.Data; using HandyControl.Tools.Extension; @@ -19,6 +20,54 @@ namespace HandyControl.Controls set => SetValue(IconProperty, value); } + public SideMenuItem() + { + SetBinding(ExpandModeProperty, new Binding(SideMenu.ExpandModeProperty.Name) + { + RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(SideMenu), 1) + }); + } + + internal static readonly DependencyProperty ExpandModeProperty = + SideMenu.ExpandModeProperty.AddOwner(typeof(SideMenuItem), new PropertyMetadata(default(ExpandMode))); + + internal ExpandMode ExpandMode + { + get => (ExpandMode) GetValue(ExpandModeProperty); + set => SetValue(ExpandModeProperty, value); + } + + protected override void Refresh() + { + if (ItemsHost == null) return; + + ItemsHost.Children.Clear(); + foreach (var item in Items) + { + DependencyObject container; + if (IsItemItsOwnContainerOverride(item)) + { + container = item as DependencyObject; + } + else + { + container = GetContainerForItemOverride(); + PrepareContainerForItemOverride(container, item); + } + + if (container is FrameworkElement element) + { + element.Style = ItemContainerStyle; + ItemsHost.Children.Add(element); + } + } + + if (IsLoaded) + { + SwitchPanelArea(ExpandMode == ExpandMode.ShowAll || IsSelected); + } + } + protected virtual void OnSelected(RoutedEventArgs e) => RaiseEvent(e); public static readonly RoutedEvent SelectedEvent = @@ -91,11 +140,12 @@ namespace HandyControl.Controls } } - internal void SwitchPanelArea(bool close) + internal void SwitchPanelArea(bool isShow) { + if (ItemsHost == null) return; if (Role == SideMenuItemRole.Header) { - ItemsHost.Show(close); + ItemsHost.Show(isShow); } } } diff --git a/src/Shared/HandyControl_Shared/Data/Enum/ExpandMode.cs b/src/Shared/HandyControl_Shared/Data/Enum/ExpandMode.cs index ab404a40..128c5aaf 100644 --- a/src/Shared/HandyControl_Shared/Data/Enum/ExpandMode.cs +++ b/src/Shared/HandyControl_Shared/Data/Enum/ExpandMode.cs @@ -1,17 +1,17 @@ -namespace HandyControl.Data.Enum +namespace HandyControl.Data { public enum ExpandMode { - /// - /// 显示所有项,且不可折叠 - /// - ShowAll, - /// /// 最多只能显示一项,且不可折叠 /// ShowOne, + /// + /// 显示所有项,且不可折叠 + /// + ShowAll, + /// /// 类似ShowOne,但是控件的尺寸不随项的数量而改变 /// diff --git a/src/Shared/HandyControl_Shared/Themes/Styles/Base/SideMenuBaseStyle.xaml b/src/Shared/HandyControl_Shared/Themes/Styles/Base/SideMenuBaseStyle.xaml index 64ff65c6..5bdde1c6 100644 --- a/src/Shared/HandyControl_Shared/Themes/Styles/Base/SideMenuBaseStyle.xaml +++ b/src/Shared/HandyControl_Shared/Themes/Styles/Base/SideMenuBaseStyle.xaml @@ -44,6 +44,44 @@ + + + + diff --git a/src/Shared/HandyControl_Shared/Tools/Helper/ConfigHelper.cs b/src/Shared/HandyControl_Shared/Tools/Helper/ConfigHelper.cs index ed1ab456..915f0869 100644 --- a/src/Shared/HandyControl_Shared/Tools/Helper/ConfigHelper.cs +++ b/src/Shared/HandyControl_Shared/Tools/Helper/ConfigHelper.cs @@ -1,7 +1,9 @@ using System; using System.ComponentModel; using System.Globalization; +#if !netle40 using System.Runtime.CompilerServices; +#endif using System.Windows; using System.Windows.Markup; using HandyControl.Controls; @@ -28,7 +30,7 @@ namespace HandyControl.Tools if (!_lang.IetfLanguageTag.Equals(value.IetfLanguageTag)) { _lang = value; - OnPropertyChanged(); + OnPropertyChanged(nameof(Lang)); } } } @@ -52,9 +54,16 @@ namespace HandyControl.Tools public event PropertyChangedEventHandler PropertyChanged; - protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) +#if netle40 + protected virtual void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } +#else + protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } +#endif } } \ No newline at end of file