├── DropDownButtonLibLogo.png ├── source ├── DropDownButtonLib_Source.txt ├── DropDownButtonTest │ ├── App.config │ ├── Properties │ │ ├── Settings.settings │ │ ├── Settings.Designer.cs │ │ ├── AssemblyInfo.cs │ │ ├── Resources.Designer.cs │ │ └── Resources.resx │ ├── App.xaml.cs │ ├── MainWindow.xaml.cs │ ├── MainWindow.xaml │ ├── Views │ │ ├── DemoView.xaml.cs │ │ ├── BindingProxy.cs │ │ ├── DemoViewModel.cs │ │ └── DemoView.xaml │ ├── ViewModels │ │ └── AppViewModel.cs │ ├── App.xaml │ └── DropDownButtonTest.csproj ├── DropDownButtonLib │ ├── Themes │ │ ├── MetroDark.xaml │ │ ├── MetroLight.xaml │ │ ├── Generic.xaml │ │ ├── LightBrushes.xaml │ │ ├── DarkBrushes.xaml │ │ └── ResourceKeys.cs │ ├── AssemblyInfo.cs │ ├── Readme.md │ ├── DropDownButtonLib.csproj.user │ ├── ViewModels │ │ ├── Items │ │ │ └── ItemsItemViewModel.cs │ │ ├── Base │ │ │ └── BaseViewModel.cs │ │ ├── DropDownItemsButtonViewModel.cs │ │ ├── SplitItemsButtonViewModel.cs │ │ ├── DropDownButtonViewModel.cs │ │ └── SplitButtonViewModel.cs │ ├── Utilities │ │ └── KeyboardUtilities.cs │ ├── Controls │ │ ├── SplitItemsButton.xaml.cs │ │ ├── SplitButton.xaml.cs │ │ ├── DropDownButton.xaml │ │ ├── Chromes │ │ │ ├── ButtonChrome_Metro.xaml │ │ │ ├── ButtonChrome.cs │ │ │ └── ButtonChrome.xaml │ │ ├── DropDownItemsButton.xaml │ │ ├── SplitButton.xaml │ │ ├── SplitItemsButton.xaml │ │ └── DropDownButton.xaml.cs │ ├── Converters │ │ └── InverseBoolConverter.cs │ ├── DropDownButtonLib.csproj │ └── Command │ │ └── RelayCommand.cs ├── CleanAll.bat └── DropDownButtonTest.sln ├── .gitignore ├── appveyor.yml ├── LICENSE.md └── README.md /DropDownButtonLibLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dirkster99/DropDownButtonLib/HEAD/DropDownButtonLibLogo.png -------------------------------------------------------------------------------- /source/DropDownButtonLib_Source.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | DropDownButtonLib 4 | ----------------- 5 | 6 | The source code and project description for the DropDownButtonLib.dll is here: 7 | https://github.com/Dirkster99/DropDownButtonLib 8 | -------------------------------------------------------------------------------- /source/DropDownButtonTest/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 00_Release/ 2 | 01_Nuget/ 3 | debug/ 4 | release/ 5 | source/.vs 6 | build/ 7 | bin/ 8 | obj/ 9 | cache/ 10 | log/ 11 | tmp/ 12 | 13 | *~ 14 | *.lock 15 | *.DS_Store 16 | *.swp 17 | *.out 18 | *.sou 19 | *.suo 20 | *.sqlite 21 | -------------------------------------------------------------------------------- /source/DropDownButtonTest/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /source/DropDownButtonTest/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Configuration; 4 | using System.Data; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | 9 | namespace DropDownButtonTest 10 | { 11 | /// 12 | /// Interaction logic for App.xaml 13 | /// 14 | public partial class App : Application 15 | { 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /source/DropDownButtonTest/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | namespace DropDownButtonTest 2 | { 3 | using System.Windows; 4 | using DropDownButtonTest.ViewModels; 5 | 6 | /// 7 | /// Interaction logic for MainWindow.xaml 8 | /// 9 | public partial class MainWindow : Window 10 | { 11 | public MainWindow() 12 | { 13 | this.InitializeComponent(); 14 | 15 | this.DataContext = new AppViewModel(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.1.{build} 2 | 3 | configuration: Release 4 | 5 | platform: Any CPU 6 | 7 | image: Visual Studio 2019 Preview 8 | 9 | install: 10 | - cmd: choco install dotnetcore-sdk --pre 11 | 12 | before_build: 13 | - cmd: nuget restore source/DropDownButtonTest.sln 14 | 15 | build: 16 | verbosity: minimal 17 | 18 | artifacts: 19 | 20 | - path: source\DropDownButtonTest\bin\Release 21 | name: DropDownButtonTest 22 | 23 | - path: source\DropDownButtonLib\bin\Release 24 | name: DropDownButtonLib 25 | -------------------------------------------------------------------------------- /source/DropDownButtonTest/MainWindow.xaml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /source/DropDownButtonLib/Themes/MetroDark.xaml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /source/DropDownButtonLib/Themes/MetroLight.xaml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /source/DropDownButtonLib/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | [assembly: ThemeInfo( 4 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 5 | //(used if a resource is not found in the page, 6 | // or application resource dictionaries) 7 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 8 | //(used if a resource is not found in the page, 9 | // app, or any theme specific resource dictionaries) 10 | )] 11 | -------------------------------------------------------------------------------- /source/DropDownButtonTest/Views/DemoView.xaml.cs: -------------------------------------------------------------------------------- 1 | namespace DropDownButtonTest.Views 2 | { 3 | using System.Windows; 4 | using System.Windows.Controls; 5 | 6 | /// 7 | /// Interaction logic for DemoView.xaml 8 | /// 9 | public partial class DemoView : UserControl 10 | { 11 | public DemoView() 12 | { 13 | this.InitializeComponent(); 14 | } 15 | 16 | private void DropDownButton_Click(object sender, System.Windows.RoutedEventArgs e) 17 | { 18 | } 19 | 20 | private void Button_Click(object sender, System.Windows.RoutedEventArgs e) 21 | { 22 | // this._dropDownButton.IsOpen = false; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /source/DropDownButtonLib/Themes/Generic.xaml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /source/CleanAll.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | pushd "%~dp0" 3 | ECHO. 4 | ECHO. 5 | ECHO This script deletes all temporary build files in their 6 | ECHO corresponding BIN and OBJ Folder contained in the following projects 7 | ECHO. 8 | ECHO DropDownButtonLib 9 | ECHO DropDownButtonTest 10 | ECHO. 11 | REM Ask the user if hes really sure to continue beyond this point XXXXXXXX 12 | set /p choice=Are you sure to continue (Y/N)? 13 | if not '%choice%'=='Y' Goto EndOfBatch 14 | REM Script does not continue unless user types 'Y' in upper case letter 15 | ECHO. 16 | ECHO XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 17 | ECHO. 18 | ECHO XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 19 | 20 | ECHO. 21 | ECHO Deleting .vs and BIN, OBJ Folders in project folder 22 | ECHO. 23 | RMDIR .vs /S /Q 24 | 25 | RMDIR /S /Q DropDownButtonLib\bin 26 | RMDIR /S /Q DropDownButtonLib\obj 27 | 28 | RMDIR /S /Q DropDownButtonTest\bin 29 | RMDIR /S /Q DropDownButtonTest\obj 30 | 31 | PAUSE 32 | 33 | :EndOfBatch 34 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2015-2017 Dirk Bahle 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /source/DropDownButtonLib/Readme.md: -------------------------------------------------------------------------------- 1 | 2 | #Overview# 3 | 4 | This project includes some bugfixes to the original implementation and extends the original controls: 5 | 6 | - DropDownButton, SplitButton 7 | 8 | with: 9 | 10 | - DropDownItemsButton, SplitItemsButton 11 | 12 | controls. The original implementation (DropDownButton, SplitButton) can be used to drop down and interact with 13 | single drop down items, while the extended controls (DropDownItemsButton, SplittItemsButton) are based on an 14 | an *ItemsControl* and can thus be used with multiple drop down items (in a similar fashion as a *ComboBox* or *ListBox*). 15 | 16 | #Contributors and Licenses# 17 | 18 | The DropDownButtonLib project is based on the Extended WPF Toolkit. 19 | 20 | Copyright (C) 2007-2013 Xceed Software Inc. 21 | 22 | This program is provided to you under the terms of the Microsoft Public 23 | License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license 24 | 25 | For more features, controls, and fast professional support, 26 | pick up the Plus Edition at http://xceed.com/wpf_toolkit 27 | 28 | Stay informed: follow @datagrid on Twitter or Like http://facebook.com/datagrids 29 | -------------------------------------------------------------------------------- /source/DropDownButtonTest/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.18408 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace DropDownButtonTest.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /source/DropDownButtonTest/Views/BindingProxy.cs: -------------------------------------------------------------------------------- 1 | namespace DropDownButtonTest.Views 2 | { 3 | using System.Windows; 4 | 5 | /// 6 | /// Implements an XAML proxy which can be used to bind items (TreeViewItem, ListViewItem etc) 7 | /// with a viewmodel that manages the collecrions. 8 | /// 9 | /// Source: http://www.thomaslevesque.com/2011/03/21/wpf-how-to-bind-to-data-when-the-datacontext-is-not-inherited/ 10 | /// Issue: http://stackoverflow.com/questions/9994241/mvvm-binding-command-to-contextmenu-item 11 | /// 12 | public class BindingProxy : Freezable 13 | { 14 | public static readonly DependencyProperty DataProperty = 15 | DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null)); 16 | 17 | /// 18 | /// Gets the data object this class is forwarding to everyone 19 | /// who has a reference to this object. 20 | /// 21 | public object Data 22 | { 23 | get { return (object)this.GetValue(DataProperty); } 24 | set { this.SetValue(DataProperty, value); } 25 | } 26 | 27 | /// 28 | /// Overrides of Freezable 29 | /// 30 | /// 31 | protected override Freezable CreateInstanceCore() 32 | { 33 | return new BindingProxy(); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /source/DropDownButtonTest/ViewModels/AppViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace DropDownButtonTest.ViewModels 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using DropDownButtonLib.ViewModels; 8 | using DropDownButtonTest.Views; 9 | 10 | /// 11 | /// Implements an application viewmodel that can be used 12 | /// to drive the entire behaviour of the main application view. 13 | /// 14 | public class AppViewModel : DropDownButtonLib.ViewModels.Base.BaseViewModel 15 | { 16 | #region fields 17 | private readonly DemoViewModel mDemo; 18 | #endregion fields 19 | 20 | #region constructor 21 | /// 22 | /// Class constructor 23 | /// 24 | public AppViewModel() 25 | { 26 | this.mDemo = new DemoViewModel(); 27 | } 28 | #endregion constructor 29 | 30 | #region properties 31 | /// 32 | /// Gets an instance of a 33 | /// which can be bound to the 34 | /// control to test functions implemented in the DropDownButtonLib. 35 | /// 36 | public DemoViewModel Demo 37 | { 38 | get 39 | { 40 | return this.mDemo; 41 | } 42 | } 43 | #endregion properties 44 | 45 | #region methods 46 | #endregion methods 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /source/DropDownButtonLib/DropDownButtonLib.csproj.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Designer 7 | 8 | 9 | Designer 10 | 11 | 12 | Designer 13 | 14 | 15 | Designer 16 | 17 | 18 | Designer 19 | 20 | 21 | Designer 22 | 23 | 24 | Designer 25 | 26 | 27 | Designer 28 | 29 | 30 | Designer 31 | 32 | 33 | Designer 34 | 35 | 36 | Designer 37 | 38 | 39 | -------------------------------------------------------------------------------- /source/DropDownButtonLib/ViewModels/Items/ItemsItemViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace DropDownButtonLib.ViewModels.Items 2 | { 3 | /// 4 | /// Implements a viewmodel that drives the 5 | /// the control. 6 | /// 7 | public class ItemsItemViewModel : DropDownButtonLib.ViewModels.Base.BaseViewModel 8 | { 9 | #region fields 10 | private string mDisplayItemName; 11 | #endregion fields 12 | 13 | #region constructor 14 | /// 15 | /// Parmeterized class constructor 16 | /// 17 | public ItemsItemViewModel(string displayName) 18 | : this() 19 | { 20 | this.mDisplayItemName = displayName; 21 | } 22 | 23 | /// 24 | /// Class constructor 25 | /// 26 | protected ItemsItemViewModel() 27 | { 28 | this.mDisplayItemName = null; 29 | } 30 | #endregion constructor 31 | 32 | #region properties 33 | /// 34 | /// Gets the string that represents a name for display purposes. 35 | /// 36 | public string DisplayItemName 37 | { 38 | get 39 | { 40 | return this.mDisplayItemName; 41 | } 42 | 43 | private set 44 | { 45 | if (this.mDisplayItemName != value) 46 | { 47 | this.mDisplayItemName = value; 48 | this.NotifyPropertyChanged(() => this.DisplayItemName); 49 | } 50 | } 51 | } 52 | #endregion properties 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /source/DropDownButtonLib/Utilities/KeyboardUtilities.cs: -------------------------------------------------------------------------------- 1 | /************************************************************************************* 2 | 3 | Extended WPF Toolkit 4 | 5 | Copyright (C) 2007-2013 Xceed Software Inc. 6 | 7 | This program is provided to you under the terms of the Microsoft Public 8 | License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license 9 | 10 | For more features, controls, and fast professional support, 11 | pick up the Plus Edition at http://xceed.com/wpf_toolkit 12 | 13 | Stay informed: follow @datagrid on Twitter or Like http://facebook.com/datagrids 14 | 15 | ***********************************************************************************/ 16 | namespace DropDownButtonLib.Utilities 17 | { 18 | using System.Windows.Input; 19 | 20 | internal class KeyboardUtilities 21 | { 22 | /// 23 | /// Determine whether the parameter represents a keyboard short-cut 24 | /// that should modify the pop-up state of the drop-down button and return 25 | /// true if so, otherwise false. 26 | /// 27 | /// 28 | /// true if keyboard short-cut should modify the pop-up state, otherwise false. 29 | internal static bool IsKeyModifyingPopupState(KeyEventArgs e) 30 | { 31 | return ((((Keyboard.Modifiers & ModifierKeys.Alt) == ModifierKeys.Alt) && 32 | ((e.SystemKey == Key.Down) || (e.SystemKey == Key.Up))) 33 | || (e.Key == Key.F4)); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /source/DropDownButtonTest/App.xaml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 | #ff3399ff 11 | 12 | 13 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /source/DropDownButtonLib/Controls/SplitItemsButton.xaml.cs: -------------------------------------------------------------------------------- 1 | namespace DropDownButtonLib.Controls 2 | { 3 | using System.Windows; 4 | using System.Windows.Controls; 5 | using System.Windows.Controls.Primitives; 6 | 7 | /// 8 | /// Implements a look-less WPF SplitItemsButton control. 9 | /// 10 | [TemplatePart(Name = PART_ActionButton, Type = typeof(Button))] 11 | [TemplatePart(Name = DropDownItemsButton.PART_DropDownButton, Type = typeof(ToggleButton))] 12 | [TemplatePart(Name = DropDownItemsButton.PART_ItemsControl, Type = typeof(ContentPresenter))] 13 | [TemplatePart(Name = DropDownItemsButton.PART_Popup, Type = typeof(Popup))] 14 | public class SplitItemsButton : DropDownItemsButton 15 | { 16 | #region fields 17 | /// 18 | /// Const string of the required additional button element in the control. 19 | /// 20 | public const string PART_ActionButton = "PART_ActionButton"; 21 | #endregion fields 22 | 23 | #region constructors 24 | /// 25 | /// Static class constructor 26 | /// 27 | static SplitItemsButton() 28 | { 29 | DefaultStyleKeyProperty.OverrideMetadata(typeof(SplitItemsButton), 30 | new FrameworkPropertyMetadata(typeof(SplitItemsButton))); 31 | } 32 | #endregion constructors 33 | 34 | #region methods 35 | /// 36 | /// 37 | /// 38 | public override void OnApplyTemplate() 39 | { 40 | base.OnApplyTemplate(); 41 | this.Button = GetTemplateChild(PART_ActionButton) as Button; 42 | } 43 | #endregion methods 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /source/DropDownButtonLib/Controls/SplitButton.xaml.cs: -------------------------------------------------------------------------------- 1 | /************************************************************************************* 2 | 3 | Extended WPF Toolkit 4 | 5 | Copyright (C) 2007-2013 Xceed Software Inc. 6 | 7 | This program is provided to you under the terms of the Microsoft Public 8 | License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license 9 | 10 | For more features, controls, and fast professional support, 11 | pick up the Plus Edition at http://xceed.com/wpf_toolkit 12 | 13 | Stay informed: follow @datagrid on Twitter or Like http://facebook.com/datagrids 14 | 15 | ***********************************************************************************/ 16 | namespace DropDownButtonLib.Controls 17 | { 18 | using System.Windows; 19 | using System.Windows.Controls; 20 | using System.Windows.Controls.Primitives; 21 | 22 | /// 23 | /// Implements a look-less WPF SplitButton control. 24 | /// 25 | [TemplatePart(Name = SplitButton.PART_ActionButton, Type = typeof(Button))] 26 | [TemplatePart(Name = SplitButton.PART_DropDownButton, Type = typeof(ToggleButton))] 27 | [TemplatePart(Name = SplitButton.PART_ContentPresenter, Type = typeof(ContentPresenter))] 28 | [TemplatePart(Name = SplitButton.PART_Popup, Type = typeof(Popup))] 29 | public class SplitButton : DropDownButton 30 | { 31 | #region fields 32 | /// 33 | /// Const string of the required additional button element in the control. 34 | /// 35 | public const string PART_ActionButton = "PART_ActionButton"; 36 | #endregion fields 37 | 38 | #region constructors 39 | /// 40 | /// Static class constructor 41 | /// 42 | static SplitButton() 43 | { 44 | DefaultStyleKeyProperty.OverrideMetadata(typeof(SplitButton), 45 | new FrameworkPropertyMetadata(typeof(SplitButton))); 46 | } 47 | #endregion constructors 48 | 49 | #region methods 50 | /// 51 | /// 52 | /// 53 | public override void OnApplyTemplate() 54 | { 55 | base.OnApplyTemplate(); 56 | this.Button = GetTemplateChild(PART_ActionButton) as Button; 57 | } 58 | #endregion methods 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /source/DropDownButtonTest/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Resources; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using System.Windows; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | [assembly: AssemblyTitle("DropDownButtonTest")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("")] 14 | [assembly: AssemblyProduct("DropDownButtonTest")] 15 | [assembly: AssemblyCopyright("Copyright © Open Source 2014")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | [assembly: ComVisible(false)] 23 | 24 | //In order to begin building localizable applications, set 25 | //CultureYouAreCodingWith in your .csproj file 26 | //inside a . For example, if you are using US english 27 | //in your source files, set the to en-US. Then uncomment 28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in 29 | //the line below to match the UICulture setting in the project file. 30 | 31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 32 | 33 | 34 | [assembly: ThemeInfo( 35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 36 | //(used if a resource is not found in the page, 37 | // or application resource dictionaries) 38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 39 | //(used if a resource is not found in the page, 40 | // app, or any theme specific resource dictionaries) 41 | )] 42 | 43 | 44 | // Version information for an assembly consists of the following four values: 45 | // 46 | // Major Version 47 | // Minor Version 48 | // Build Number 49 | // Revision 50 | // 51 | // You can specify all the values or you can default the Build and Revision Numbers 52 | // by using the '*' as shown below: 53 | // [assembly: AssemblyVersion("1.0.*")] 54 | [assembly: AssemblyVersion("1.0.0.0")] 55 | [assembly: AssemblyFileVersion("1.0.0.0")] 56 | -------------------------------------------------------------------------------- /source/DropDownButtonLib/Converters/InverseBoolConverter.cs: -------------------------------------------------------------------------------- 1 | /************************************************************************************* 2 | 3 | Extended WPF Toolkit 4 | 5 | Copyright (C) 2007-2013 Xceed Software Inc. 6 | 7 | This program is provided to you under the terms of the Microsoft Public 8 | License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license 9 | 10 | For more features, controls, and fast professional support, 11 | pick up the Plus Edition at http://xceed.com/wpf_toolkit 12 | 13 | Stay informed: follow @datagrid on Twitter or Like http://facebook.com/datagrids 14 | 15 | ***********************************************************************************/ 16 | namespace DropDownButtonLib.Converters 17 | { 18 | using System; 19 | using System.Windows.Data; 20 | 21 | /// 22 | /// Implements a WPF that converts an input 23 | /// boolean value into an inverted boolean value (true to false, and vice versa). 24 | /// 25 | [ValueConversion(typeof(bool), typeof(bool))] 26 | public class InverseBoolConverter : IValueConverter 27 | { 28 | #region IValueConverter Members 29 | /// 30 | /// Standard Convert method of the interface. 31 | /// 32 | /// 33 | /// 34 | /// 35 | /// 36 | /// 37 | public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 38 | { 39 | if (value == null) 40 | return Binding.DoNothing; 41 | 42 | if ((value is bool) == false) 43 | return Binding.DoNothing; 44 | 45 | return !(bool)value; 46 | } 47 | 48 | /// 49 | /// Standard ConvertBack method of the interface. 50 | /// 51 | /// 52 | /// 53 | /// 54 | /// 55 | /// 56 | public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 57 | { 58 | if (value == null) 59 | return Binding.DoNothing; 60 | 61 | if ((value is bool) == false) 62 | return Binding.DoNothing; 63 | 64 | return !(bool)value; 65 | } 66 | #endregion 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /source/DropDownButtonTest.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29209.152 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DropDownButtonTest", "DropDownButtonTest\DropDownButtonTest.csproj", "{E5F032E0-CC7A-40A0-B11D-98D1F5A67775}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{DE2DCFE5-1E33-4BBA-B76E-90B88BD3A5B3}" 9 | ProjectSection(SolutionItems) = preProject 10 | DropDownButtonLib_Source.txt = DropDownButtonLib_Source.txt 11 | EndProjectSection 12 | EndProject 13 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DropDownButtonLib", "DropDownButtonLib\DropDownButtonLib.csproj", "{C9B8C80D-60CB-44CE-8A22-06010C62FD74}" 14 | EndProject 15 | Global 16 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 17 | Debug|Any CPU = Debug|Any CPU 18 | Debug|x86 = Debug|x86 19 | Release|Any CPU = Release|Any CPU 20 | Release|x86 = Release|x86 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {E5F032E0-CC7A-40A0-B11D-98D1F5A67775}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {E5F032E0-CC7A-40A0-B11D-98D1F5A67775}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {E5F032E0-CC7A-40A0-B11D-98D1F5A67775}.Debug|x86.ActiveCfg = Debug|x86 26 | {E5F032E0-CC7A-40A0-B11D-98D1F5A67775}.Debug|x86.Build.0 = Debug|x86 27 | {E5F032E0-CC7A-40A0-B11D-98D1F5A67775}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {E5F032E0-CC7A-40A0-B11D-98D1F5A67775}.Release|Any CPU.Build.0 = Release|Any CPU 29 | {E5F032E0-CC7A-40A0-B11D-98D1F5A67775}.Release|x86.ActiveCfg = Release|x86 30 | {E5F032E0-CC7A-40A0-B11D-98D1F5A67775}.Release|x86.Build.0 = Release|x86 31 | {C9B8C80D-60CB-44CE-8A22-06010C62FD74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 32 | {C9B8C80D-60CB-44CE-8A22-06010C62FD74}.Debug|Any CPU.Build.0 = Debug|Any CPU 33 | {C9B8C80D-60CB-44CE-8A22-06010C62FD74}.Debug|x86.ActiveCfg = Debug|Any CPU 34 | {C9B8C80D-60CB-44CE-8A22-06010C62FD74}.Debug|x86.Build.0 = Debug|Any CPU 35 | {C9B8C80D-60CB-44CE-8A22-06010C62FD74}.Release|Any CPU.ActiveCfg = Release|Any CPU 36 | {C9B8C80D-60CB-44CE-8A22-06010C62FD74}.Release|Any CPU.Build.0 = Release|Any CPU 37 | {C9B8C80D-60CB-44CE-8A22-06010C62FD74}.Release|x86.ActiveCfg = Release|Any CPU 38 | {C9B8C80D-60CB-44CE-8A22-06010C62FD74}.Release|x86.Build.0 = Release|Any CPU 39 | EndGlobalSection 40 | GlobalSection(SolutionProperties) = preSolution 41 | HideSolutionNode = FALSE 42 | EndGlobalSection 43 | GlobalSection(ExtensibilityGlobals) = postSolution 44 | SolutionGuid = {D92C3282-83F7-4190-B9B4-44F86C16727A} 45 | EndGlobalSection 46 | EndGlobal 47 | -------------------------------------------------------------------------------- /source/DropDownButtonTest/Views/DemoViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace DropDownButtonTest.Views 2 | { 3 | using DropDownButtonLib.ViewModels; 4 | 5 | /// 6 | /// Implements an application viewmodel that can be used 7 | /// to drive the entire behaviour of the main application view. 8 | /// 9 | public class DemoViewModel : DropDownButtonLib.ViewModels.Base.BaseViewModel 10 | { 11 | #region fields 12 | private readonly SplitButtonViewModel mSplitButtonTest; 13 | private readonly DropDownItemsButtonViewModel mDropDownItemsButtonTest; 14 | private readonly DropDownButtonViewModel mDropDownButtonTest; 15 | private readonly SplitItemsButtonViewModel mSplitItemsButtonTest; 16 | #endregion fields 17 | 18 | #region constructor 19 | /// 20 | /// Class constructor 21 | /// 22 | public DemoViewModel() 23 | { 24 | this.mDropDownButtonTest = new DropDownButtonViewModel(); 25 | this.mSplitButtonTest = new SplitButtonViewModel(); 26 | this.mDropDownItemsButtonTest = new DropDownItemsButtonViewModel(); 27 | this.mSplitItemsButtonTest = new SplitItemsButtonViewModel(); 28 | } 29 | #endregion constructor 30 | 31 | #region properties 32 | /// 33 | /// Gets an instance of a 34 | /// which can be bound to a 35 | /// control to test it. 36 | /// 37 | public DropDownButtonViewModel DropDownButtonTest 38 | { 39 | get 40 | { 41 | return this.mDropDownButtonTest; 42 | } 43 | } 44 | 45 | /// 46 | /// Gets an instance of a 47 | /// which can be bound to a 48 | /// control to test it. 49 | /// 50 | public SplitButtonViewModel SplitButtonTest 51 | { 52 | get 53 | { 54 | return this.mSplitButtonTest; 55 | } 56 | } 57 | 58 | /// 59 | /// Gets an instance of a 60 | /// which can be bound to a 61 | /// control to test it. 62 | /// 63 | public DropDownItemsButtonViewModel DropDownItemsButtonTest 64 | { 65 | get 66 | { 67 | return this.mDropDownItemsButtonTest; 68 | } 69 | } 70 | 71 | public SplitItemsButtonViewModel SplitItemsButtonTest 72 | { 73 | get 74 | { 75 | return this.mSplitItemsButtonTest; 76 | } 77 | } 78 | #endregion properties 79 | 80 | #region methods 81 | #endregion methods 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /source/DropDownButtonTest/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.18408 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace DropDownButtonTest.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("DropDownButtonTest.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /source/DropDownButtonLib/ViewModels/Base/BaseViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace DropDownButtonLib.ViewModels.Base 2 | { 3 | using System; 4 | using System.ComponentModel; 5 | using System.Linq.Expressions; 6 | using System.Windows; 7 | 8 | /// 9 | /// Every ViewModel class is required to implement the INotifyPropertyChanged 10 | /// interface in order to tell WPF when a property changed (for instance, when 11 | /// a method or setter is executed). 12 | /// 13 | /// Therefore, the PropertyChanged methode has to be called when data changes, 14 | /// because the relevant properties may or may not be bound to GUI elements, 15 | /// which in turn have to refresh their display. 16 | /// 17 | /// The PropertyChanged method is to be called by the members and properties of 18 | /// the class that derives from this class. Each call contains the name of the 19 | /// property that has to be refreshed. 20 | /// 21 | /// The BaseViewModel is derived from from System.Windows.DependencyObject to allow 22 | /// resulting ViewModels the implemantion of dependency properties. Dependency properties 23 | /// in turn are useful when working with IValueConverter and ConverterParameters. 24 | /// 25 | public class BaseViewModel : INotifyPropertyChanged 26 | { 27 | /// 28 | /// Standard event handler of the interface 29 | /// 30 | public event PropertyChangedEventHandler PropertyChanged; 31 | 32 | /// 33 | /// Tell bound controls (via WPF binding) to refresh their display. 34 | /// 35 | /// Sample call: this.NotifyPropertyChanged(() => this.IsSelected); 36 | /// where 'this' is derived from 37 | /// and IsSelected is a property. 38 | /// 39 | /// 40 | /// 41 | public void NotifyPropertyChanged(Expression> property) 42 | { 43 | var lambda = (LambdaExpression)property; 44 | MemberExpression memberExpression; 45 | 46 | if (lambda.Body is UnaryExpression) 47 | { 48 | var unaryExpression = (UnaryExpression)lambda.Body; 49 | memberExpression = (MemberExpression)unaryExpression.Operand; 50 | } 51 | else 52 | memberExpression = (MemberExpression)lambda.Body; 53 | 54 | this.OnPropertyChanged(memberExpression.Member.Name); 55 | } 56 | 57 | /// 58 | /// Tell bound controls (via WPF binding) to refresh their display. 59 | /// 60 | /// Sample call: this.OnPropertyChanged("IsSelected"); 61 | /// where 'this' is derived from 62 | /// and IsSelected is a property. 63 | /// 64 | /// Name of property to refresh 65 | public void OnPropertyChanged(string propertyName) 66 | { 67 | try 68 | { 69 | if (this.PropertyChanged != null) 70 | this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 71 | } 72 | catch 73 | { 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /source/DropDownButtonLib/DropDownButtonLib.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net4;netcoreapp3.0 5 | true 6 | 7 | true 8 | 1.1.0.0 9 | 1.1.0.0 10 | 1.1.0.0 11 | Open Source 12 | DropDownButtonLib 13 | 2014-2019 14 | Provides a .Net implementation of a WPF drop down button control. 15 | https://github.com/Dirkster99/DropDownButtonLib 16 | https://github.com/Dirkster99/DropDownButtonLib 17 | Dirkster.DropDownButtonLib 18 | https://github.com/Dirkster99/DropDownButtonLib 19 | MIT 20 | wpf mvvm c# .net metro dark light themed dropdown button dropdownbutton control library 21 | This control is now supported in NetCore 3 (with Preview 8) and .Net 4. 22 | en 23 | https://raw.githubusercontent.com/Dirkster99/DropDownButtonLib/master/DropDownButtonLibLogo.png 24 | 25 | 26 | 27 | 28 | 29 | ButtonChrome.xaml 30 | 31 | 32 | DropDownButton.xaml 33 | 34 | 35 | DropDownItemsButton.xaml 36 | 37 | 38 | SplitButton.xaml 39 | 40 | 41 | SplitItemsButton.xaml 42 | 43 | 44 | 45 | 46 | 47 | Designer 48 | 49 | 50 | Designer 51 | 52 | 53 | Designer 54 | 55 | 56 | Designer 57 | 58 | 59 | Designer 60 | 61 | 62 | Designer 63 | 64 | 65 | Designer 66 | 67 | 68 | Designer 69 | MSBuild:Compile 70 | 71 | 72 | Designer 73 | 74 | 75 | Designer 76 | 77 | 78 | Designer 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build status](https://ci.appveyor.com/api/projects/status/ie3dx7fa4vujwhgs?svg=true)](https://ci.appveyor.com/project/Dirkster99/dropdownbuttonlib) 2 | [![Release](https://img.shields.io/github/release/Dirkster99/DropDownButtonLib.svg)](https://github.com/Dirkster99/DropDownButtonLib/releases/latest) 3 | [![NuGet](https://img.shields.io/nuget/dt/Dirkster.DropDownButtonLib.svg)](http://nuget.org/packages/Dirkster.DropDownButtonLib) 4 | 5 | ![Net4](https://badgen.net/badge/Framework/.Net 4/blue) ![NetCore3](https://badgen.net/badge/Framework/NetCore 3/blue) 6 | 7 |

 Overview

8 | The DropDownButtonLib project supplies MVVM/WPF drop down controls that are based on a button. 9 | 10 | There are sample screenshot on the Codeplex site from were this project is migrated from: 11 | https://dropdownbuttonlib.codeplex.com/ 12 | 13 | This project is based on the drop down control contained in the 14 | Extended WPF Toolkit™ Community Edition:https://wpftoolkit.codeplex.com/ from Xceed. 15 | It includes some bugfixes to the original implementation and extends the original controls with a: 16 | 17 | - DropDownButton, SplitButton 18 | with: 19 | - DropDownItemsButton, SplitItemsButton 20 | 21 | controls. The original implementation (DropDownButton, SplitButton) can be used to drop down and interact with single drop down items, while the extended controls (DropDownItemsButton, SplittItemsButton) are based on an ItemsControl and can thus be with multiple drop down items (in a similar fashion as a standard WPF ComboBox or ListBox control). 22 | 23 | ## DropDownButton 24 | 25 | The DropDown button shows a drop down element that gives users a way of editing something and confirming it with Cancel or OK (this works similar to a dialog but in a drop down scenario). 26 | 27 | ## SplitButton 28 | 29 | The Split button has a drop down section and a button. 30 | The drop down element gives you a way of editing/selecting in a similar scenario as in the DropDownButton shown above, while the button itself can be used like a shortcut that refers to the last selected element. 31 | 32 | ## DropDownItemsButton 33 | 34 | The DropDown button shows a drop down element which can be used to select one element out of many. This could also be implemented with the DropDownButton control but it is much easier with this control since it already contains an ItemsControl inside the drop down element. 35 | 36 | ## SplitItemsButton 37 | 38 | The Split button has a drop down section and a button. 39 | The drop down element gives you a way of selecting from among many elements while the button itself can be used like a shortcut that refers to the last selected element. 40 | 41 | ## Demo Application 42 | 43 | This is a screenshot of the MainWindow of the included Test Application. The complete implementation is MVVM compliant and all controls are fully themeable (look-less controls). 44 | 45 | ## Theming 46 | 47 | Load *Light* or *Dark* brush resources in you resource dictionary to take advantage of existing definitions. 48 | 49 | ```XAML 50 | 51 | 52 | 53 | ``` 54 | 55 | ```XAML 56 | 57 | 58 | 59 | ``` 60 | 61 | These definitions do not theme all controls used within this library. You should use a standard theming library, such as: 62 | - [MahApps.Metro](https://github.com/MahApps/MahApps.Metro), 63 | - [MLib](https://github.com/Dirkster99/MLib), or 64 | - [MUI](https://github.com/firstfloorsoftware/mui) 65 | 66 | to also theme standard elements, such as, button and textblock etc. 67 | -------------------------------------------------------------------------------- /source/DropDownButtonLib/Themes/LightBrushes.xaml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 8 | #FF2C628B 10 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /source/DropDownButtonLib/Themes/DarkBrushes.xaml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 9 | #FF2C628B 11 | 12 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 35 | 36 | 37 | 38 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /source/DropDownButtonLib/Controls/DropDownButton.xaml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /source/DropDownButtonLib/Command/RelayCommand.cs: -------------------------------------------------------------------------------- 1 | namespace DropDownButtonLib.Command 2 | { 3 | using System; 4 | using System.Diagnostics; 5 | using System.Windows.Input; 6 | 7 | /// 8 | /// A command whose sole purpose is to 9 | /// relay its functionality to other 10 | /// objects by invoking delegates. The 11 | /// default return value for the CanExecute 12 | /// method is 'true'. 13 | /// 14 | /// Source: http://www.codeproject.com/Articles/31837/Creating-an-Internationalized-Wizard-in-WPF 15 | /// 16 | internal class RelayCommand : ICommand 17 | { 18 | #region Fields 19 | private readonly Action mExecute = null; 20 | private readonly Predicate mCanExecute = null; 21 | #endregion // Fields 22 | 23 | #region Constructors 24 | /// 25 | /// Class constructor 26 | /// 27 | /// 28 | public RelayCommand(Action execute) 29 | : this(execute, null) 30 | { 31 | } 32 | 33 | /// 34 | /// Creates a new command. 35 | /// 36 | /// The execution logic. 37 | /// The execution status logic. 38 | public RelayCommand(Action execute, Predicate canExecute) 39 | { 40 | if (execute == null) 41 | throw new ArgumentNullException("execute"); 42 | 43 | this.mExecute = execute; 44 | this.mCanExecute = canExecute; 45 | } 46 | 47 | #endregion // Constructors 48 | 49 | #region events 50 | /// 51 | /// Eventhandler to re-evaluate whether this command can execute or not 52 | /// 53 | public event EventHandler CanExecuteChanged 54 | { 55 | add 56 | { 57 | if (this.mCanExecute != null) 58 | CommandManager.RequerySuggested += value; 59 | } 60 | 61 | remove 62 | { 63 | if (this.mCanExecute != null) 64 | CommandManager.RequerySuggested -= value; 65 | } 66 | } 67 | #endregion 68 | 69 | #region methods 70 | /// 71 | /// Determine whether this pre-requisites to execute this command are given or not. 72 | /// 73 | /// 74 | /// 75 | [DebuggerStepThrough] 76 | public bool CanExecute(object parameter) 77 | { 78 | return this.mCanExecute == null ? true : this.mCanExecute((T)parameter); 79 | } 80 | 81 | /// 82 | /// Execute the command method managed in this class. 83 | /// 84 | /// 85 | public void Execute(object parameter) 86 | { 87 | this.mExecute((T)parameter); 88 | } 89 | #endregion methods 90 | } 91 | 92 | /// 93 | /// A command whose sole purpose is to 94 | /// relay its functionality to other 95 | /// objects by invoking delegates. The 96 | /// default return value for the CanExecute 97 | /// method is 'true'. 98 | /// 99 | internal class RelayCommand : ICommand 100 | { 101 | #region Fields 102 | private readonly Action mExecute; 103 | private readonly Func mCanExecute; 104 | #endregion Fields 105 | 106 | #region Constructors 107 | 108 | /// 109 | /// Creates a new command that can always execute. 110 | /// 111 | /// The execution logic. 112 | public RelayCommand(Action execute) 113 | : this(execute, null) 114 | { 115 | } 116 | 117 | /// 118 | /// Copy constructor 119 | /// 120 | /// 121 | public RelayCommand(RelayCommand inputRC) 122 | : this(inputRC.mExecute, inputRC.mCanExecute) 123 | { 124 | } 125 | 126 | /// 127 | /// Creates a new command. 128 | /// 129 | /// The execution logic. 130 | /// The execution status logic. 131 | public RelayCommand(Action execute, Func canExecute) 132 | { 133 | if (execute == null) 134 | throw new ArgumentNullException("execute"); 135 | 136 | this.mExecute = execute; 137 | this.mCanExecute = canExecute; 138 | } 139 | 140 | #endregion Constructors 141 | 142 | #region Events 143 | /// 144 | /// Eventhandler to re-evaluate whether this command can execute or not 145 | /// 146 | public event EventHandler CanExecuteChanged 147 | { 148 | add 149 | { 150 | if (this.mCanExecute != null) 151 | CommandManager.RequerySuggested += value; 152 | } 153 | 154 | remove 155 | { 156 | if (this.mCanExecute != null) 157 | CommandManager.RequerySuggested -= value; 158 | } 159 | } 160 | #endregion Events 161 | 162 | #region Methods 163 | /// 164 | /// Execute the attached CanExecute methode delegate (or always return true) 165 | /// to determine whether the command managed in this object can execute or not. 166 | /// 167 | /// 168 | /// 169 | [DebuggerStepThrough] 170 | public bool CanExecute(object parameter) 171 | { 172 | return this.mCanExecute == null ? true : this.mCanExecute(); 173 | } 174 | 175 | /// 176 | /// Return the attached delegate method. 177 | /// 178 | /// 179 | public void Execute(object parameter) 180 | { 181 | this.mExecute(); 182 | } 183 | #endregion Methods 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /source/DropDownButtonLib/ViewModels/DropDownItemsButtonViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace DropDownButtonLib.ViewModels 2 | { 3 | using System; 4 | using System.Collections.ObjectModel; 5 | using System.Windows.Input; 6 | using DropDownButtonLib.Command; 7 | using DropDownButtonLib.ViewModels.Items; 8 | 9 | /// 10 | /// Implements a viewmodel that drives the 11 | /// the control. 12 | /// 13 | public class DropDownItemsButtonViewModel : Base.BaseViewModel 14 | { 15 | #region fields 16 | private readonly ObservableCollection mDropDownItems; 17 | 18 | private RelayCommand mDropDownButtonClickCommand; 19 | private RelayCommand mItemButtonClickCommand; 20 | 21 | private bool mIsOpen; 22 | private string mStatus; 23 | private bool mIsEnabled; 24 | #endregion fields 25 | 26 | #region constructor 27 | /// 28 | /// Class constructor 29 | /// 30 | public DropDownItemsButtonViewModel() 31 | { 32 | this.mIsOpen = false; 33 | this.mIsEnabled = true; 34 | this.mStatus = string.Empty; 35 | this.mDropDownItems = new ObservableCollection(); 36 | 37 | for (int i = 0; i < 255; i++) 38 | this.mDropDownItems.Add(new ItemsItemViewModel("Drop Down Items Demo " + i)); 39 | } 40 | #endregion constructor 41 | 42 | #region properties 43 | /// 44 | /// Command is invoked when user clicks on the DropDownButton 45 | /// (either on label part or DropDown arrow). 46 | /// 47 | public ICommand DropDownButtonClickCommand 48 | { 49 | get 50 | { 51 | if (this.mDropDownButtonClickCommand == null) 52 | this.mDropDownButtonClickCommand = new RelayCommand( 53 | (p) => this.DropDownButtonClickCommand_Executed(), 54 | (p) => this.IsEnabled); 55 | 56 | return this.mDropDownButtonClickCommand; 57 | } 58 | } 59 | 60 | /// 61 | /// Command is invoked when user clicks on the DropDownButton 62 | /// (user clicked a button or hyperlink in DropDown part). 63 | /// 64 | public ICommand ItemButtonClickCommand 65 | { 66 | get 67 | { 68 | if (this.mItemButtonClickCommand == null) 69 | this.mItemButtonClickCommand = new RelayCommand((p) => 70 | { 71 | var param = p as ItemsItemViewModel; 72 | 73 | if (param != null) 74 | this.ItemButtonClickCommand_Executed(param); 75 | }, 76 | (p) => this.IsEnabled); 77 | 78 | return this.mItemButtonClickCommand; 79 | } 80 | } 81 | 82 | /// 83 | /// Gets the list of drives and folders for display in treeview structure control. 84 | /// 85 | public ObservableCollection DropDownItems 86 | { 87 | get 88 | { 89 | return this.mDropDownItems; 90 | } 91 | } 92 | 93 | /// 94 | /// Gets the caption of the dropdown button. 95 | /// 96 | public string ButtonLabel 97 | { 98 | get 99 | { 100 | return "Select an item"; 101 | } 102 | } 103 | 104 | /// 105 | /// Gets/sets bound property to determine whether 106 | /// drop-down/pop-up element is open or not. 107 | /// 108 | public bool IsOpen 109 | { 110 | get 111 | { 112 | return this.mIsOpen; 113 | } 114 | 115 | set 116 | { 117 | if (this.mIsOpen != value) 118 | { 119 | this.mIsOpen = value; 120 | this.NotifyPropertyChanged(() => this.IsOpen); 121 | } 122 | } 123 | } 124 | 125 | /// 126 | /// Gets/sets Boolean value to determine whether attached dropdown view is enabled or not. 127 | /// 128 | public bool IsEnabled 129 | { 130 | get 131 | { 132 | return this.mIsEnabled; 133 | } 134 | 135 | set 136 | { 137 | if (this.mIsEnabled != value) 138 | { 139 | this.mIsEnabled = value; 140 | this.NotifyPropertyChanged(() => this.IsEnabled); 141 | } 142 | } 143 | } 144 | 145 | /// 146 | /// Gets the status description of this viewmodel 147 | /// (this property is present for testing/demo/debugging purposes only 148 | /// since it gives us the ability to give state feedback without using 149 | /// MessageBoxes, which steals the focus and thus influence the pop-up element) 150 | /// 151 | public string Status 152 | { 153 | get 154 | { 155 | return this.mStatus; 156 | } 157 | 158 | private set 159 | { 160 | if (this.mStatus != value) 161 | { 162 | this.mStatus = value; 163 | this.NotifyPropertyChanged(() => this.Status); 164 | } 165 | } 166 | } 167 | #endregion properties 168 | 169 | #region methods 170 | private void DropDownButtonClickCommand_Executed() 171 | { 172 | this.Status = "Thanks for clicking the DropDownItemsButton!"; 173 | } 174 | 175 | private void ItemButtonClickCommand_Executed(ItemsItemViewModel p) 176 | { 177 | if (p == null) 178 | return; 179 | 180 | // This is the only button specific logic there is -> the rest is demo 181 | this.IsOpen = false; 182 | 183 | string source = "DropDownItemsButton"; 184 | 185 | this.Status += Environment.NewLine + 186 | string.Format("Thanks for clicking: '{0} -> {1}'!", source, p.DisplayItemName); 187 | } 188 | #endregion methods 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /source/DropDownButtonTest/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /source/DropDownButtonTest/DropDownButtonTest.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {E5F032E0-CC7A-40A0-B11D-98D1F5A67775} 8 | WinExe 9 | Properties 10 | DropDownButtonTest 11 | DropDownButtonTest 12 | v4.0 13 | 512 14 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 15 | 4 16 | Client 17 | 18 | 19 | AnyCPU 20 | true 21 | full 22 | false 23 | bin\Debug\ 24 | DEBUG;TRACE 25 | prompt 26 | 4 27 | 28 | 29 | AnyCPU 30 | pdbonly 31 | true 32 | bin\Release\ 33 | TRACE 34 | prompt 35 | 4 36 | 37 | 38 | true 39 | bin\x86\Debug\ 40 | DEBUG;TRACE 41 | full 42 | x86 43 | prompt 44 | ManagedMinimumRules.ruleset 45 | 46 | 47 | bin\x86\Release\ 48 | TRACE 49 | true 50 | pdbonly 51 | x86 52 | prompt 53 | ManagedMinimumRules.ruleset 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 4.0 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | MSBuild:Compile 73 | Designer 74 | 75 | 76 | 77 | 78 | DemoView.xaml 79 | 80 | 81 | 82 | MSBuild:Compile 83 | Designer 84 | 85 | 86 | App.xaml 87 | Code 88 | 89 | 90 | MainWindow.xaml 91 | Code 92 | 93 | 94 | MSBuild:Compile 95 | Designer 96 | 97 | 98 | 99 | 100 | Code 101 | 102 | 103 | True 104 | True 105 | Resources.resx 106 | 107 | 108 | True 109 | Settings.settings 110 | True 111 | 112 | 113 | ResXFileCodeGenerator 114 | Resources.Designer.cs 115 | 116 | 117 | SettingsSingleFileGenerator 118 | Settings.Designer.cs 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | {c9b8c80d-60cb-44ce-8a22-06010c62fd74} 128 | DropDownButtonLib 129 | 130 | 131 | 132 | 139 | -------------------------------------------------------------------------------- /source/DropDownButtonLib/ViewModels/SplitItemsButtonViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace DropDownButtonLib.ViewModels 2 | { 3 | using System; 4 | using System.Collections.ObjectModel; 5 | using System.Windows.Input; 6 | using DropDownButtonLib.Command; 7 | using DropDownButtonLib.ViewModels.Items; 8 | 9 | /// 10 | /// Implements a viewmodel for a split button that can display multiple items. 11 | /// The user experience is similar to a combobox with the difference that a 12 | /// splitbutton allows a re-apply of the last selected item. 13 | /// 14 | public class SplitItemsButtonViewModel : Base.BaseViewModel 15 | { 16 | #region fields 17 | private readonly ObservableCollection mDropDownItems; 18 | 19 | private RelayCommand mDropDownButtonClickCommand; 20 | private RelayCommand mItemButtonClickCommand; 21 | 22 | private bool mIsOpen; 23 | private string mStatus; 24 | private bool mIsEnabled; 25 | private ItemsItemViewModel mSelectedItem; 26 | #endregion fields 27 | 28 | #region constructor 29 | /// 30 | /// Class constructor 31 | /// 32 | public SplitItemsButtonViewModel() 33 | { 34 | this.mIsOpen = false; 35 | this.mIsEnabled = true; 36 | this.mStatus = string.Empty; 37 | this.mDropDownItems = new ObservableCollection(); 38 | 39 | this.mSelectedItem = new ItemsItemViewModel("Drop Down Items Demo 1"); 40 | 41 | this.mDropDownItems.Add(this.mSelectedItem); 42 | 43 | for (int i = 2; i < 255; i++) 44 | this.mDropDownItems.Add(new ItemsItemViewModel("Drop Down Items Demo " + i)); 45 | } 46 | #endregion constructor 47 | 48 | #region properties 49 | /// 50 | /// Command is invoked when user clicks on the DropDownButton 51 | /// (either on label part or DropDown arrow). 52 | /// 53 | public ICommand DropDownButtonClickCommand 54 | { 55 | get 56 | { 57 | if (this.mDropDownButtonClickCommand == null) 58 | this.mDropDownButtonClickCommand = new RelayCommand( 59 | (p) => 60 | { 61 | var param = p as ItemsItemViewModel; 62 | 63 | if (param != null) 64 | this.DropDownButtonClickCommand_Executed(param); 65 | }, 66 | (p) => this.IsEnabled); 67 | 68 | return this.mDropDownButtonClickCommand; 69 | } 70 | } 71 | 72 | /// 73 | /// Command is invoked when user clicks on the DropDownButton 74 | /// (user clicked a button or hyperlink in DropDown part). 75 | /// 76 | public ICommand ItemButtonClickCommand 77 | { 78 | get 79 | { 80 | if (this.mItemButtonClickCommand == null) 81 | this.mItemButtonClickCommand = new RelayCommand((p) => 82 | { 83 | var param = p as ItemsItemViewModel; 84 | 85 | if (param != null) 86 | this.ItemButtonClickCommand_Executed(param); 87 | }, 88 | (p) => this.IsEnabled); 89 | 90 | return this.mItemButtonClickCommand; 91 | } 92 | } 93 | 94 | /// 95 | /// Gets the list of drives and folders for display in treeview structure control. 96 | /// 97 | public ObservableCollection DropDownItems 98 | { 99 | get 100 | { 101 | return this.mDropDownItems; 102 | } 103 | } 104 | 105 | /// 106 | /// Gets/sets the selected (current) item of this button viewmodel. 107 | /// 108 | public ItemsItemViewModel SelectedItem 109 | { 110 | get 111 | { 112 | return this.mSelectedItem; 113 | } 114 | 115 | set 116 | { 117 | if (this.mSelectedItem != value) 118 | { 119 | this.mSelectedItem = value; 120 | this.NotifyPropertyChanged(() => this.SelectedItem); 121 | } 122 | } 123 | } 124 | 125 | /// 126 | /// Gets/sets bound property to determine whether 127 | /// drop-down/pop-up element is open or not. 128 | /// 129 | public bool IsOpen 130 | { 131 | get 132 | { 133 | return this.mIsOpen; 134 | } 135 | 136 | set 137 | { 138 | if (this.mIsOpen != value) 139 | { 140 | this.mIsOpen = value; 141 | this.NotifyPropertyChanged(() => this.IsOpen); 142 | } 143 | } 144 | } 145 | 146 | /// 147 | /// Gets/sets Boolean value to determine whether attached dropdown view is enabled or not. 148 | /// 149 | public bool IsEnabled 150 | { 151 | get 152 | { 153 | return this.mIsEnabled; 154 | } 155 | 156 | set 157 | { 158 | if (this.mIsEnabled != value) 159 | { 160 | this.mIsEnabled = value; 161 | this.NotifyPropertyChanged(() => this.IsEnabled); 162 | } 163 | } 164 | } 165 | 166 | /// 167 | /// Gets the status description of this viewmodel 168 | /// (this property is present for testing/demo/debugging purposes only 169 | /// since it gives us the ability to give state feedback without using 170 | /// MessageBoxes, which steals the focus and thus influence the pop-up element) 171 | /// 172 | public string Status 173 | { 174 | get 175 | { 176 | return this.mStatus; 177 | } 178 | 179 | private set 180 | { 181 | if (this.mStatus != value) 182 | { 183 | this.mStatus = value; 184 | this.NotifyPropertyChanged(() => this.Status); 185 | } 186 | } 187 | } 188 | #endregion properties 189 | 190 | #region methods 191 | private void DropDownButtonClickCommand_Executed(ItemsItemViewModel p) 192 | { 193 | if (p == null) 194 | return; 195 | 196 | string source = "SplitItemsButton -> DropDownButtonClickCommand"; 197 | 198 | this.Status = string.Format("Thanks for clicking: '{0} -> {1}'!", source, p.DisplayItemName); 199 | } 200 | 201 | private void ItemButtonClickCommand_Executed(ItemsItemViewModel p) 202 | { 203 | if (p == null) 204 | return; 205 | 206 | this.IsOpen = false; 207 | this.SelectedItem = p; 208 | 209 | string source = "SplitItemsButton -> ItemButtonClickCommand"; 210 | 211 | this.Status += Environment.NewLine + 212 | string.Format("Thanks for clicking: '{0} -> {1}'!", source, p.DisplayItemName); 213 | } 214 | #endregion methods 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /source/DropDownButtonLib/ViewModels/DropDownButtonViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace DropDownButtonLib.ViewModels 2 | { 3 | using System.Windows.Input; 4 | using DropDownButtonLib.Command; 5 | 6 | /// 7 | /// Implements a viewmodel that drives the 8 | /// the control. 9 | /// 10 | public class DropDownButtonViewModel : Base.BaseViewModel 11 | { 12 | #region fields 13 | private RelayCommand mDropDownButtonClickCommand; 14 | private RelayCommand mItemButtonClickCommand; 15 | private RelayCommand mItemCancelCommand; 16 | 17 | private bool mIsOpen; 18 | private bool mIsEnabled; 19 | private int mSliderValue; 20 | private string mStatus; 21 | private int mBackupSliderValue; 22 | #endregion fields 23 | 24 | #region constructor 25 | /// 26 | /// Class constructor 27 | /// 28 | public DropDownButtonViewModel() 29 | { 30 | this.mStatus = string.Empty; 31 | this.mBackupSliderValue = this.mSliderValue = 50; 32 | 33 | this.mIsEnabled = true; 34 | this.mIsOpen = false; 35 | } 36 | #endregion constructor 37 | 38 | #region properties 39 | #region commands 40 | /// 41 | /// Command is invoked when user clicks on the DropDownButton 42 | /// (either on label part or DropDown arrow). 43 | /// 44 | public ICommand DropDownButtonClickCommand 45 | { 46 | get 47 | { 48 | if (this.mDropDownButtonClickCommand == null) 49 | this.mDropDownButtonClickCommand = new RelayCommand( 50 | (p) => 51 | { 52 | if ((p is int) == false) 53 | return; 54 | 55 | var param = (int)p; 56 | 57 | this.DropDownButtonClickCommand_Executed(param); 58 | }, 59 | (p) => 60 | { 61 | return (this.IsEnabled == true); 62 | }); 63 | 64 | return this.mDropDownButtonClickCommand; 65 | } 66 | } 67 | 68 | /// 69 | /// Command is invoked when user clicks on the DropDownButton 70 | /// (user clicked a button or hyperlink in DropDown part). 71 | /// 72 | public ICommand ItemButtonClickCommand 73 | { 74 | get 75 | { 76 | if (this.mItemButtonClickCommand == null) 77 | this.mItemButtonClickCommand = new RelayCommand( 78 | (p) => 79 | { 80 | if ((p is int) == false) 81 | return; 82 | 83 | var param = (int)p; 84 | 85 | this.ItemButtonClickCommand_Executed(param); 86 | }); 87 | 88 | return this.mItemButtonClickCommand; 89 | } 90 | } 91 | 92 | /// 93 | /// Gets a command that is executed to cancel a dropdown action. 94 | /// This command closes the drop down and rolls back edited values (if any). 95 | /// 96 | public ICommand ItemCancelCommand 97 | { 98 | get 99 | { 100 | if (this.mItemCancelCommand == null) 101 | this.mItemCancelCommand = new RelayCommand( 102 | () => 103 | { 104 | this.ItemCancelCommand_Executed(); 105 | }); 106 | 107 | return this.mItemCancelCommand; 108 | } 109 | } 110 | #endregion commands 111 | 112 | /// 113 | /// Gets the selected item of the DropDown viewmodel. 114 | /// 115 | public string ButtonLabel 116 | { 117 | get 118 | { 119 | return "Select a Value"; 120 | } 121 | } 122 | 123 | /// 124 | /// Gets/sets bound property to determine whether 125 | /// drop-down/pop-up element is open or not. 126 | /// 127 | public bool IsOpen 128 | { 129 | get 130 | { 131 | return this.mIsOpen; 132 | } 133 | 134 | set 135 | { 136 | if (this.mIsOpen != value) 137 | { 138 | this.mIsOpen = value; 139 | this.NotifyPropertyChanged(() => this.IsOpen); 140 | } 141 | } 142 | } 143 | 144 | /// 145 | /// Gets/sets Boolean value to determine whether attached dropdown view is enabled or not. 146 | /// 147 | public bool IsEnabled 148 | { 149 | get 150 | { 151 | return this.mIsEnabled; 152 | } 153 | 154 | set 155 | { 156 | if (this.mIsEnabled != value) 157 | { 158 | this.mIsEnabled = value; 159 | this.NotifyPropertyChanged(() => this.IsEnabled); 160 | } 161 | } 162 | } 163 | 164 | /// 165 | /// Gets/sets the current value displayed in the slider control. 166 | /// 167 | public int SliderValue 168 | { 169 | get 170 | { 171 | return this.mSliderValue; 172 | } 173 | 174 | set 175 | { 176 | if (this.mSliderValue != value) 177 | { 178 | this.mSliderValue = value; 179 | this.NotifyPropertyChanged(() => this.SliderValue); 180 | } 181 | } 182 | } 183 | 184 | /// 185 | /// Gets the status description of this viewmodel 186 | /// (this property is present for testing/demo/debugging purposes only 187 | /// since it gives us the ability to give state feedback without using 188 | /// MessageBoxes, which steals the focus and thus influence the pop-up element) 189 | /// 190 | public string Status 191 | { 192 | get 193 | { 194 | return this.mStatus; 195 | } 196 | 197 | private set 198 | { 199 | if (this.mStatus != value) 200 | { 201 | this.mStatus = value; 202 | this.NotifyPropertyChanged(() => this.Status); 203 | } 204 | } 205 | } 206 | #endregion properties 207 | 208 | #region methods 209 | private void DropDownButtonClickCommand_Executed(int value) 210 | { 211 | string source = "SplitButton"; 212 | 213 | this.Status = string.Format("Thanks for clicking me: '{0} -> {1}'!", source, value); 214 | } 215 | 216 | private void ItemButtonClickCommand_Executed(int value) 217 | { 218 | this.mBackupSliderValue = this.SliderValue; 219 | this.IsOpen = false; 220 | 221 | string source = "OK from SplitButtonItem"; 222 | 223 | this.Status = string.Format("Thanks for clicking me: '{0} -> {1}'!", source, value); 224 | } 225 | 226 | private void ItemCancelCommand_Executed() 227 | { 228 | this.IsOpen = false; 229 | 230 | string source = "Cancel from SplitButtonItem"; 231 | 232 | this.Status = string.Format("Cancel -> roll back from '{0} to {1} ({2})'!", this.SliderValue, this.mBackupSliderValue, source); 233 | 234 | this.SliderValue = this.mBackupSliderValue; 235 | } 236 | #endregion methods 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /source/DropDownButtonLib/ViewModels/SplitButtonViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace DropDownButtonLib.ViewModels 2 | { 3 | using System.Windows.Input; 4 | using DropDownButtonLib.Command; 5 | 6 | /// 7 | /// Implements a viewmodel that drives the 8 | /// the control. 9 | /// 10 | public class SplitButtonViewModel : Base.BaseViewModel 11 | { 12 | #region fields 13 | private RelayCommand mDropDownButtonClickCommand; 14 | private RelayCommand mItemButtonClickCommand; 15 | private RelayCommand mItemCancelCommand; 16 | 17 | private bool mIsOpen; 18 | private bool mIsEnabled; 19 | private int mSliderValue, mBackupSliderValue; 20 | private string mStatus; 21 | #endregion fields 22 | 23 | #region constructor 24 | /// 25 | /// Class constructor 26 | /// 27 | public SplitButtonViewModel() 28 | { 29 | this.mStatus = string.Empty; 30 | this.mBackupSliderValue = this.mSliderValue = 50; 31 | 32 | this.mIsEnabled = true; 33 | this.mIsOpen = false; 34 | } 35 | #endregion constructor 36 | 37 | #region properties 38 | #region commands 39 | /// 40 | /// Command is invoked when user clicks on the DropDownButton 41 | /// (either on label part or DropDown arrow). 42 | /// 43 | public ICommand DropDownButtonClickCommand 44 | { 45 | get 46 | { 47 | if (this.mDropDownButtonClickCommand == null) 48 | this.mDropDownButtonClickCommand = new RelayCommand( 49 | (p) => 50 | { 51 | if ((p is int) == false) 52 | return; 53 | 54 | var param = (int)p; 55 | 56 | this.DropDownButtonClickCommand_Executed(param); 57 | }, 58 | (p) => 59 | { 60 | return (this.IsEnabled == true); 61 | }); 62 | 63 | return this.mDropDownButtonClickCommand; 64 | } 65 | } 66 | 67 | /// 68 | /// Command is invoked when user clicks on the DropDownButton 69 | /// (user clicked a button or hyperlink in DropDown part). 70 | /// 71 | public ICommand ItemButtonClickCommand 72 | { 73 | get 74 | { 75 | if (this.mItemButtonClickCommand == null) 76 | this.mItemButtonClickCommand = new RelayCommand( 77 | (p) => 78 | { 79 | if ((p is int) == false) 80 | return; 81 | 82 | var param = (int)p; 83 | 84 | this.ItemButtonClickCommand_Executed(param); 85 | }); 86 | 87 | return this.mItemButtonClickCommand; 88 | } 89 | } 90 | 91 | /// 92 | /// Gets a command that is executed to cancel a dropdown action. 93 | /// This command closes the drop down and rolls back edited values (if any). 94 | /// 95 | public ICommand ItemCancelCommand 96 | { 97 | get 98 | { 99 | if (this.mItemCancelCommand == null) 100 | this.mItemCancelCommand = new RelayCommand( 101 | () => 102 | { 103 | this.ItemCancelCommand_Executed(); 104 | }); 105 | 106 | return this.mItemCancelCommand; 107 | } 108 | } 109 | #endregion commands 110 | 111 | /// 112 | /// Gets the selected item of the DropDown viewmodel. 113 | /// 114 | public string SelectedItem 115 | { 116 | get 117 | { 118 | return "Demo Value " + this.mSliderValue; 119 | } 120 | } 121 | 122 | /// 123 | /// Gets/sets bound property to determine whether 124 | /// drop-down/pop-up element is open or not. 125 | /// 126 | public bool IsOpen 127 | { 128 | get 129 | { 130 | return this.mIsOpen; 131 | } 132 | 133 | set 134 | { 135 | if (this.mIsOpen != value) 136 | { 137 | this.mIsOpen = value; 138 | this.NotifyPropertyChanged(() => this.IsOpen); 139 | } 140 | } 141 | } 142 | 143 | /// 144 | /// Gets/sets Boolean value to determine whether attached dropdown view is enabled or not. 145 | /// 146 | public bool IsEnabled 147 | { 148 | get 149 | { 150 | return this.mIsEnabled; 151 | } 152 | 153 | set 154 | { 155 | if (this.mIsEnabled != value) 156 | { 157 | this.mIsEnabled = value; 158 | this.NotifyPropertyChanged(() => this.IsEnabled); 159 | } 160 | } 161 | } 162 | 163 | /// 164 | /// Gets/sets the current value displayed in the slider control. 165 | /// 166 | public int SliderValue 167 | { 168 | get 169 | { 170 | return this.mSliderValue; 171 | } 172 | 173 | set 174 | { 175 | if (this.mSliderValue != value) 176 | { 177 | this.mSliderValue = value; 178 | this.NotifyPropertyChanged(() => this.SliderValue); 179 | this.NotifyPropertyChanged(() => this.SelectedItem); 180 | } 181 | } 182 | } 183 | 184 | /// 185 | /// Gets the status description of this viewmodel 186 | /// (this property is present for testing/demo/debugging purposes only 187 | /// since it gives us the ability to give state feedback without using 188 | /// MessageBoxes, which steals the focus and thus influence the pop-up element) 189 | /// 190 | public string Status 191 | { 192 | get 193 | { 194 | return this.mStatus; 195 | } 196 | 197 | private set 198 | { 199 | if (this.mStatus != value) 200 | { 201 | this.mStatus = value; 202 | this.NotifyPropertyChanged(() => this.Status); 203 | } 204 | } 205 | } 206 | #endregion properties 207 | 208 | #region methods 209 | private void DropDownButtonClickCommand_Executed(int value) 210 | { 211 | string source = "SplitButton"; 212 | 213 | this.Status = string.Format("Thanks for clicking me: '{0} -> {1}'!", source, value); 214 | } 215 | 216 | private void ItemButtonClickCommand_Executed(int value) 217 | { 218 | this.mBackupSliderValue = this.mSliderValue; 219 | this.IsOpen = false; 220 | 221 | string source = "SplitButtonItem"; 222 | 223 | this.Status = string.Format("Thanks for clicking me: '{0} -> {1}'!", source, value); 224 | } 225 | 226 | private void ItemCancelCommand_Executed() 227 | { 228 | this.IsOpen = false; 229 | 230 | string source = "Cancel from SplitButtonItem"; 231 | 232 | this.Status = string.Format("Cancel -> roll back from '{0} to {1} ({2})'!", this.SliderValue, this.mBackupSliderValue, source); 233 | 234 | this.SliderValue = this.mBackupSliderValue; 235 | } 236 | #endregion methods 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /source/DropDownButtonLib/Controls/Chromes/ButtonChrome_Metro.xaml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /source/DropDownButtonLib/Controls/DropDownItemsButton.xaml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /source/DropDownButtonLib/Controls/SplitButton.xaml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /source/DropDownButtonLib/Themes/ResourceKeys.cs: -------------------------------------------------------------------------------- 1 | namespace DropDownButtonLib.Themes 2 | { 3 | using System.Windows; 4 | 5 | /// 6 | /// Resource key management class to keep track of all resources 7 | /// that can be re-styled in applications that make use of the implemented controls. 8 | /// 9 | public static class ResourceKeys 10 | { 11 | #region Accent Keys 12 | /// 13 | /// Accent Color Key - This Color key is used to accent elements in the UI 14 | /// (e.g.: Color of Activated Normal Window Frame, ResizeGrip, Focus or MouseOver input elements) 15 | /// 16 | public static readonly ComponentResourceKey ControlAccentColorKey = new ComponentResourceKey(typeof(ResourceKeys), "ControlAccentColorKey"); 17 | 18 | /// 19 | /// Accent Brush Key - This Brush key is used to accent elements in the UI 20 | /// (e.g.: Color of Activated Normal Window Frame, ResizeGrip, Focus or MouseOver input elements) 21 | /// 22 | public static readonly ComponentResourceKey ControlAccentBrushKey = new ComponentResourceKey(typeof(ResourceKeys), "ControlAccentBrushKey"); 23 | #endregion Accent Keys 24 | 25 | #region Brush Keys 26 | /// 27 | /// Resource key of the controls normal background brush key. 28 | /// 29 | public static readonly ComponentResourceKey ControlNormalBackgroundKey = new ComponentResourceKey(typeof(ResourceKeys), "ControlNormalBackgroundKey"); 30 | 31 | /// 32 | /// Resource key of the controls disabled background brush key. 33 | /// 34 | public static readonly ComponentResourceKey ControlDisabledBackgroundKey = new ComponentResourceKey(typeof(ResourceKeys), "ControlDisabledBackgroundKey"); 35 | 36 | /// 37 | /// Resource key of the controls normal background brush key. 38 | /// 39 | public static readonly ComponentResourceKey ControlNormalBorderKey = new ComponentResourceKey(typeof(ResourceKeys), "ControlNormalBorderKey"); 40 | 41 | /// 42 | /// Resource key of the controls mouse over border brush key. 43 | /// 44 | public static readonly ComponentResourceKey ControlMouseOverBorderKey = new ComponentResourceKey(typeof(ResourceKeys), "ControlMouseOverBorderKey"); 45 | 46 | /// 47 | /// Resource key of the controls selected border brush key. 48 | /// 49 | public static readonly ComponentResourceKey ControlSelectedBorderKey = new ComponentResourceKey(typeof(ResourceKeys), "ControlSelectedBorderKey"); 50 | 51 | /// 52 | /// Resource key of the controls focused border brush key. 53 | /// 54 | public static readonly ComponentResourceKey ControlFocusedBorderKey = new ComponentResourceKey(typeof(ResourceKeys), "ControlFocusedBorderKey"); 55 | 56 | /// 57 | /// Resource key of the button's normal outer border brush key. 58 | /// 59 | public static readonly ComponentResourceKey ButtonNormalOuterBorderKey = new ComponentResourceKey(typeof(ResourceKeys), "ButtonNormalOuterBorderKey"); 60 | 61 | /// 62 | /// Resource key of the button's normal inner border brush key. 63 | /// 64 | public static readonly ComponentResourceKey ButtonNormalInnerBorderKey = new ComponentResourceKey(typeof(ResourceKeys), "ButtonNormalInnerBorderKey"); 65 | 66 | /// 67 | /// Resource key of the button's normal background brush key. 68 | /// 69 | public static readonly ComponentResourceKey ButtonNormalBackgroundKey = new ComponentResourceKey(typeof(ResourceKeys), "ButtonNormalBackgroundKey"); 70 | 71 | /// 72 | /// Resource key of the button's mouse over background brush key. 73 | /// 74 | public static readonly ComponentResourceKey ButtonMouseOverBackgroundKey = new ComponentResourceKey(typeof(ResourceKeys), "ButtonMouseOverBackgroundKey"); 75 | 76 | /// 77 | /// Resource key of the button's mouse over outer border brush key. 78 | /// 79 | public static readonly ComponentResourceKey ButtonMouseOverOuterBorderKey = new ComponentResourceKey(typeof(ResourceKeys), "ButtonMouseOverOuterBorderKey"); 80 | 81 | /// 82 | /// Resource key of the button's mouse over inner border brush key. 83 | /// 84 | public static readonly ComponentResourceKey ButtonMouseOverInnerBorderKey = new ComponentResourceKey(typeof(ResourceKeys), "ButtonMouseOverInnerBorderKey"); 85 | 86 | /// 87 | /// Resource key of the button's pressed outer border brush key. 88 | /// 89 | public static readonly ComponentResourceKey ButtonPressedOuterBorderKey = new ComponentResourceKey(typeof(ResourceKeys), "ButtonPressedOuterBorderKey"); 90 | 91 | /// 92 | /// Resource key of the button's pressed inner border brush key. 93 | /// 94 | public static readonly ComponentResourceKey ButtonPressedInnerBorderKey = new ComponentResourceKey(typeof(ResourceKeys), "ButtonPressedInnerBorderKey"); 95 | 96 | /// 97 | /// Resource key of the button's pressed background brush key. 98 | /// 99 | public static readonly ComponentResourceKey ButtonPressedBackgroundKey = new ComponentResourceKey(typeof(ResourceKeys), "ButtonPressedBackgroundKey"); 100 | 101 | /// 102 | /// Resource key of the button's focused outer border brush key. 103 | /// 104 | public static readonly ComponentResourceKey ButtonFocusedOuterBorderKey = new ComponentResourceKey(typeof(ResourceKeys), "ButtonFocusedOuterBorderKey"); 105 | 106 | /// 107 | /// Resource key of the button's focused inner border brush key. 108 | /// 109 | public static readonly ComponentResourceKey ButtonFocusedInnerBorderKey = new ComponentResourceKey(typeof(ResourceKeys), "ButtonFocusedInnerBorderKey"); 110 | 111 | /// 112 | /// Resource key of the button's focused background brush key. 113 | /// 114 | public static readonly ComponentResourceKey ButtonFocusedBackgroundKey = new ComponentResourceKey(typeof(ResourceKeys), "ButtonFocusedBackgroundKey"); 115 | 116 | /// 117 | /// Resource key of the button's disbled outer border brush key. 118 | /// 119 | public static readonly ComponentResourceKey ButtonDisabledOuterBorderKey = new ComponentResourceKey(typeof(ResourceKeys), "ButtonDisabledOuterBorderKey"); 120 | 121 | /// 122 | /// Resource key of the button's disabled inner border brush key. 123 | /// 124 | public static readonly ComponentResourceKey ButtonInnerBorderDisabledKey = new ComponentResourceKey(typeof(ResourceKeys), "ButtonInnerBorderDisabledKey"); 125 | 126 | /// 127 | /// Resource key of the drop down list item background key. 128 | /// 129 | public static readonly ComponentResourceKey DropDownList_MouseOverBackgroundKey = new ComponentResourceKey(typeof(ResourceKeys), "DropDownList_MouseOverBackgroundKey"); 130 | 131 | /// 132 | /// Resource key of the drop down background key. 133 | /// 134 | public static readonly ComponentResourceKey DropDownList_BackgroundKey = new ComponentResourceKey(typeof(ResourceKeys), "DropDownList_BackgroundKey"); 135 | 136 | /// 137 | /// Resource key of the drop down border key. 138 | /// 139 | public static readonly ComponentResourceKey DropDownList_BorderForegroundKey = new ComponentResourceKey(typeof(ResourceKeys), "DropDownList_BorderForegroundKey"); 140 | #endregion Brush Keys 141 | 142 | /// 143 | /// Resource key object of the normal glyph color. 144 | /// 145 | public static readonly ComponentResourceKey GlyphNormalForegroundKey = new ComponentResourceKey(typeof(ResourceKeys), "GlyphNormalForegroundKey"); 146 | 147 | /// 148 | /// Resource key object of the mouseover foreground glyph color. 149 | /// 150 | public static readonly ComponentResourceKey GlyphMouseOverForegroundKey = new ComponentResourceKey(typeof(ResourceKeys), "GlyphMouseOverForegroundKey"); 151 | 152 | /// 153 | /// Resource key object of the disabled foreground glyph color. 154 | /// 155 | public static readonly ComponentResourceKey GlyphDisabledForegroundKey = new ComponentResourceKey(typeof(ResourceKeys), "GlyphDisabledForegroundKey"); 156 | 157 | /// 158 | /// Resource key object of the corner radius property assigned in different UI elements (not just SpinButton). 159 | /// 160 | public static readonly ComponentResourceKey SpinButtonCornerRadiusKey = new ComponentResourceKey(typeof(ResourceKeys), "SpinButtonCornerRadiusKey"); 161 | 162 | /// 163 | /// Resource style key of the repeat button. 164 | /// 165 | public static readonly ComponentResourceKey SpinnerButtonStyleKey = new ComponentResourceKey(typeof(ResourceKeys), "SpinnerButtonStyleKey"); 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /source/DropDownButtonLib/Controls/SplitItemsButton.xaml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 164 | 165 | 166 | -------------------------------------------------------------------------------- /source/DropDownButtonLib/Controls/Chromes/ButtonChrome.cs: -------------------------------------------------------------------------------- 1 | /************************************************************************************* 2 | 3 | Extended WPF Toolkit 4 | 5 | Copyright (C) 2007-2013 Xceed Software Inc. 6 | 7 | This program is provided to you under the terms of the Microsoft Public 8 | License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license 9 | 10 | For more features, controls, and fast professional support, 11 | pick up the Plus Edition at http://xceed.com/wpf_toolkit 12 | 13 | Stay informed: follow @datagrid on Twitter or Like http://facebook.com/datagrids 14 | 15 | ***********************************************************************************/ 16 | namespace DropDownButtonLib.Controls.Chromes 17 | { 18 | using System; 19 | using System.Windows; 20 | using System.Windows.Controls; 21 | 22 | /// 23 | /// Implements a ButtonChrome which is a kind of frame that 24 | /// determines the look and feel of a button. 25 | /// 26 | public class ButtonChrome : ContentControl 27 | { 28 | #region fields 29 | /// 30 | /// Backing store for corner radius dependency property of button chrome. 31 | /// 32 | public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.Register("CornerRadius", typeof(CornerRadius), typeof(ButtonChrome), new UIPropertyMetadata(default(CornerRadius), new PropertyChangedCallback(OnCornerRadiusChanged))); 33 | 34 | /// 35 | /// Backing store for inner corner radius dependency property of button chrome. 36 | /// 37 | public static readonly DependencyProperty InnerCornerRadiusProperty = DependencyProperty.Register("InnerCornerRadius", typeof(CornerRadius), typeof(ButtonChrome), new UIPropertyMetadata(default(CornerRadius), new PropertyChangedCallback(OnInnerCornerRadiusChanged))); 38 | 39 | /// 40 | /// Backing store for render checked dependency property of button chrome. 41 | /// 42 | public static readonly DependencyProperty RenderCheckedProperty = DependencyProperty.Register("RenderChecked", typeof(bool), typeof(ButtonChrome), new UIPropertyMetadata(false, OnRenderCheckedChanged)); 43 | 44 | /// 45 | /// Backing store for render enabled dependency property of button chrome. 46 | /// 47 | public static readonly DependencyProperty RenderEnabledProperty = DependencyProperty.Register("RenderEnabled", typeof(bool), typeof(ButtonChrome), new UIPropertyMetadata(true, OnRenderEnabledChanged)); 48 | 49 | /// 50 | /// Backing store for render focused dependency property of button chrome. 51 | /// 52 | public static readonly DependencyProperty RenderFocusedProperty = DependencyProperty.Register("RenderFocused", typeof(bool), typeof(ButtonChrome), new UIPropertyMetadata(false, OnRenderFocusedChanged)); 53 | 54 | /// 55 | /// Backing store for render mouse over dependency property of button chrome. 56 | /// 57 | public static readonly DependencyProperty RenderMouseOverProperty = DependencyProperty.Register("RenderMouseOver", typeof(bool), typeof(ButtonChrome), new UIPropertyMetadata(false, OnRenderMouseOverChanged)); 58 | 59 | /// 60 | /// Backing store for render normal dependency property of button chrome. 61 | /// 62 | public static readonly DependencyProperty RenderNormalProperty = DependencyProperty.Register("RenderNormal", typeof(bool), typeof(ButtonChrome), new UIPropertyMetadata(true, OnRenderNormalChanged)); 63 | 64 | /// 65 | /// Backing store for render pressed dependency property of button chrome. 66 | /// 67 | public static readonly DependencyProperty RenderPressedProperty = DependencyProperty.Register("RenderPressed", typeof(bool), typeof(ButtonChrome), new UIPropertyMetadata(false, OnRenderPressedChanged)); 68 | #endregion fields 69 | 70 | #region constructors 71 | /// 72 | /// Static class constructor 73 | /// 74 | static ButtonChrome() 75 | { 76 | DefaultStyleKeyProperty.OverrideMetadata(typeof(ButtonChrome), new FrameworkPropertyMetadata(typeof(ButtonChrome))); 77 | } 78 | #endregion constructors 79 | 80 | #region properties 81 | #region dependency properties 82 | /// 83 | /// Gets/sets corner radius dependency property of button chrome. 84 | /// 85 | public CornerRadius CornerRadius 86 | { 87 | get 88 | { 89 | return (CornerRadius)GetValue(CornerRadiusProperty); 90 | } 91 | 92 | set 93 | { 94 | this.SetValue(CornerRadiusProperty, value); 95 | } 96 | } 97 | 98 | /// 99 | /// Gets/sets inner corner radius dependency property of button chrome. 100 | /// 101 | public CornerRadius InnerCornerRadius 102 | { 103 | get 104 | { 105 | return (CornerRadius)GetValue(InnerCornerRadiusProperty); 106 | } 107 | 108 | set 109 | { 110 | this.SetValue(InnerCornerRadiusProperty, value); 111 | } 112 | } 113 | 114 | /// 115 | /// Gets/sets render checked dependency property of button chrome. 116 | /// 117 | public bool RenderChecked 118 | { 119 | get 120 | { 121 | return (bool)GetValue(RenderCheckedProperty); 122 | } 123 | 124 | set 125 | { 126 | this.SetValue(RenderCheckedProperty, value); 127 | } 128 | } 129 | 130 | /// 131 | /// Gets/sets render enabled dependency property of button chrome. 132 | /// 133 | public bool RenderEnabled 134 | { 135 | get 136 | { 137 | return (bool)GetValue(RenderEnabledProperty); 138 | } 139 | 140 | set 141 | { 142 | this.SetValue(RenderEnabledProperty, value); 143 | } 144 | } 145 | 146 | /// 147 | /// Gets/sets render focused dependency property of button chrome. 148 | /// 149 | public bool RenderFocused 150 | { 151 | get 152 | { 153 | return (bool)GetValue(RenderFocusedProperty); 154 | } 155 | 156 | set 157 | { 158 | this.SetValue(RenderFocusedProperty, value); 159 | } 160 | } 161 | 162 | /// 163 | /// Gets/sets render mouseover dependency property of button chrome. 164 | /// 165 | public bool RenderMouseOver 166 | { 167 | get 168 | { 169 | return (bool)GetValue(RenderMouseOverProperty); 170 | } 171 | 172 | set 173 | { 174 | this.SetValue(RenderMouseOverProperty, value); 175 | } 176 | } 177 | 178 | /// 179 | /// Gets/sets render normal dependency property of button chrome. 180 | /// 181 | public bool RenderNormal 182 | { 183 | get 184 | { 185 | return (bool)GetValue(RenderNormalProperty); 186 | } 187 | 188 | set 189 | { 190 | this.SetValue(RenderNormalProperty, value); 191 | } 192 | } 193 | 194 | /// 195 | /// Gets/sets render pressed dependency property of button chrome. 196 | /// 197 | public bool RenderPressed 198 | { 199 | get 200 | { 201 | return (bool)GetValue(RenderPressedProperty); 202 | } 203 | 204 | set 205 | { 206 | this.SetValue(RenderPressedProperty, value); 207 | } 208 | } 209 | #endregion dependency properties 210 | #endregion properties 211 | 212 | #region methods 213 | /// 214 | /// Method executes when the corner radius dependency property is changed. 215 | /// It re-computes the inner corner radius since it always needs to be 1 216 | /// less than the outer radius. 217 | /// 218 | /// 219 | /// 220 | protected virtual void OnCornerRadiusChanged(CornerRadius oldValue, CornerRadius newValue) 221 | { 222 | // we always want the InnerBorderRadius to be one less than the CornerRadius 223 | CornerRadius newInnerCornerRadius = new CornerRadius(Math.Max(0, newValue.TopLeft - 1), 224 | Math.Max(0, newValue.TopRight - 1), 225 | Math.Max(0, newValue.BottomRight - 1), 226 | Math.Max(0, newValue.BottomLeft - 1)); 227 | 228 | this.InnerCornerRadius = newInnerCornerRadius; 229 | } 230 | 231 | /// 232 | /// Method executes when the inner corner radius dependency property is changed. 233 | /// 234 | /// TODO: Add your property changed side-effects. Descendants can override as well. 235 | /// 236 | /// 237 | /// 238 | protected virtual void OnInnerCornerRadiusChanged(CornerRadius oldValue, CornerRadius newValue) 239 | { 240 | 241 | } 242 | 243 | /// 244 | /// Method executes when the render checked dependency property is changed. 245 | /// 246 | /// TODO: Add your property changed side-effects. Descendants can override as well. 247 | /// 248 | /// 249 | /// 250 | protected virtual void OnRenderCheckedChanged(bool oldValue, bool newValue) 251 | { 252 | } 253 | 254 | /// 255 | /// Method executes when the render enabled dependency property is changed. 256 | /// 257 | /// TODO: Add your property changed side-effects. Descendants can override as well. 258 | /// 259 | /// 260 | /// 261 | protected virtual void OnRenderEnabledChanged(bool oldValue, bool newValue) 262 | { 263 | } 264 | 265 | /// 266 | /// Method executes when the render focused dependency property is changed. 267 | /// 268 | /// TODO: Add your property changed side-effects. Descendants can override as well. 269 | /// 270 | /// 271 | /// 272 | protected virtual void OnRenderFocusedChanged(bool oldValue, bool newValue) 273 | { 274 | } 275 | 276 | /// 277 | /// Method executes when the render mouseover dependency property is changed. 278 | /// 279 | /// TODO: Add your property changed side-effects. Descendants can override as well. 280 | /// 281 | /// 282 | /// 283 | protected virtual void OnRenderMouseOverChanged(bool oldValue, bool newValue) 284 | { 285 | } 286 | 287 | /// 288 | /// Method executes when the render normal dependency property is changed. 289 | /// 290 | /// TODO: Add your property changed side-effects. Descendants can override as well. 291 | /// 292 | /// 293 | /// 294 | protected virtual void OnRenderNormalChanged(bool oldValue, bool newValue) 295 | { 296 | } 297 | 298 | /// 299 | /// Method executes when the render pressed dependency property is changed. 300 | /// 301 | /// TODO: Add your property changed side-effects. Descendants can override as well. 302 | /// 303 | /// 304 | /// 305 | protected virtual void OnRenderPressedChanged(bool oldValue, bool newValue) 306 | { 307 | } 308 | 309 | /// 310 | /// Method executes when the corner radius dependency property is changed. 311 | /// 312 | /// 313 | /// 314 | private static void OnCornerRadiusChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) 315 | { 316 | ButtonChrome buttonChrome = o as ButtonChrome; 317 | 318 | if (buttonChrome != null) 319 | buttonChrome.OnCornerRadiusChanged((CornerRadius)e.OldValue, (CornerRadius)e.NewValue); 320 | } 321 | 322 | /// 323 | /// Method executes when the inner corner radius dependency property is changed. 324 | /// 325 | /// 326 | /// 327 | private static void OnInnerCornerRadiusChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) 328 | { 329 | ButtonChrome buttonChrome = o as ButtonChrome; 330 | if (buttonChrome != null) 331 | buttonChrome.OnInnerCornerRadiusChanged((CornerRadius)e.OldValue, (CornerRadius)e.NewValue); 332 | } 333 | 334 | /// 335 | /// Method executes when the render checked dependency property is changed. 336 | /// 337 | /// 338 | /// 339 | private static void OnRenderCheckedChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) 340 | { 341 | ButtonChrome buttonChrome = o as ButtonChrome; 342 | if (buttonChrome != null) 343 | buttonChrome.OnRenderCheckedChanged((bool)e.OldValue, (bool)e.NewValue); 344 | } 345 | 346 | /// 347 | /// Method executes when the render enabled dependency property is changed. 348 | /// 349 | /// 350 | /// 351 | private static void OnRenderEnabledChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) 352 | { 353 | ButtonChrome buttonChrome = o as ButtonChrome; 354 | if (buttonChrome != null) 355 | buttonChrome.OnRenderEnabledChanged((bool)e.OldValue, (bool)e.NewValue); 356 | } 357 | 358 | /// 359 | /// Method executes when the render focused dependency property is changed. 360 | /// 361 | /// 362 | /// 363 | private static void OnRenderFocusedChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) 364 | { 365 | ButtonChrome buttonChrome = o as ButtonChrome; 366 | if (buttonChrome != null) 367 | buttonChrome.OnRenderFocusedChanged((bool)e.OldValue, (bool)e.NewValue); 368 | } 369 | 370 | /// 371 | /// Method executes when the render mouseover dependency property is changed. 372 | /// 373 | /// 374 | /// 375 | private static void OnRenderMouseOverChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) 376 | { 377 | ButtonChrome buttonChrome = o as ButtonChrome; 378 | if (buttonChrome != null) 379 | buttonChrome.OnRenderMouseOverChanged((bool)e.OldValue, (bool)e.NewValue); 380 | } 381 | 382 | /// 383 | /// Method executes when the render normal dependency property is changed. 384 | /// 385 | /// 386 | /// 387 | private static void OnRenderNormalChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) 388 | { 389 | ButtonChrome buttonChrome = o as ButtonChrome; 390 | if (buttonChrome != null) 391 | buttonChrome.OnRenderNormalChanged((bool)e.OldValue, (bool)e.NewValue); 392 | } 393 | 394 | /// 395 | /// Method executes when the render pressed dependency property is changed. 396 | /// 397 | /// 398 | /// 399 | private static void OnRenderPressedChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) 400 | { 401 | ButtonChrome buttonChrome = o as ButtonChrome; 402 | if (buttonChrome != null) 403 | buttonChrome.OnRenderPressedChanged((bool)e.OldValue, (bool)e.NewValue); 404 | } 405 | #endregion methods 406 | } 407 | } 408 | -------------------------------------------------------------------------------- /source/DropDownButtonTest/Views/DemoView.xaml: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | Click the DropDown and SplitButton below to see how these work. 22 | 23 | Insert a ResourceDictionary reference in App.xaml to activate a supported theme: 24 | DropDownButtonLib/Themes/Generic.xaml and 25 | DropDownButtonLib/Themes/MetroDark.xaml 26 | DropDownButtonLib/Themes/MetroLight.xaml 27 | 28 | Note: The Generic.xaml theme is the same as the MetroLight.xaml theme. 29 | 30 | Note: The Generic.xaml theme does not need explicit activation 31 | because WPF loads it by default when nothings else was defined. 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 |