├── .gitignore ├── LICENSE ├── README.md ├── RadialMenu.sln ├── RadialMenuControl ├── Components │ ├── CenterButton.xaml │ ├── CenterButton.xaml.cs │ ├── RadialMenuButton.xaml │ └── RadialMenuButton.xaml.cs ├── Extensions │ ├── ColorExtensions.cs │ └── StoryboardExtension.cs ├── InnerPieSlicePath.cs ├── Properties │ └── AssemblyInfo.cs ├── RadialMenuControl.csproj ├── RadialMenuControl.nuspec ├── Shims │ └── Shims.cs ├── Themes │ ├── Colors.cs │ ├── Generic.xaml │ └── Styles.xaml ├── UserControl │ ├── Floating.cs │ ├── ListSubMenu.xaml │ ├── ListSubMenu.xaml.cs │ ├── MenuBase.cs │ ├── MeterSubMenu.xaml │ ├── MeterSubmenu.xaml.cs │ ├── MeterSubmenuPath.cs │ ├── OuterPieSlicePath.cs │ ├── PathBase.cs │ ├── Pie.xaml │ ├── Pie.xaml.cs │ ├── PieSlice.xaml │ ├── PieSlice.xaml.cs │ ├── RadialMenu.xaml │ └── RadialMenu.xaml.cs ├── nuget │ └── RadialMenuControl.targets ├── project.json └── project.lock.json ├── RadialMenuDemo ├── App.xaml ├── App.xaml.cs ├── Assets │ ├── LockScreenLogo.scale-200.png │ ├── SplashScreen.scale-200.png │ ├── Square150x150Logo.scale-200.png │ ├── Square44x44Logo.scale-200.png │ ├── Square44x44Logo.targetsize-24_altform-unplated.png │ ├── StoreLogo.png │ ├── Wide310x150Logo.scale-200.png │ ├── background.jpg │ ├── background2.jpg │ └── button_blue_stop.png ├── Icons │ ├── Chisel Tip Marker-50.png │ ├── Cursor-50.png │ ├── Delete-50.png │ ├── Hand Cursor-50.png │ ├── Icons License.txt │ ├── Polygon-50.png │ ├── Strikethrough-50.png │ ├── Text Cursor-50.png │ ├── Underline-50.png │ ├── opacity.png │ └── stroke.png ├── MainPage.xaml ├── MainPage.xaml.cs ├── Melbourne.xaml ├── Melbourne.xaml.cs ├── Package.appxmanifest ├── Properties │ ├── AssemblyInfo.cs │ └── Default.rd.xml ├── RadialMenuDemo.csproj ├── project.json └── project.lock.json └── RadialMenuUITests ├── CodedUITest1.cs ├── Properties └── AssemblyInfo.cs └── RadialMenuUITests.csproj /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | build/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studo 2015 cache/options directory 26 | .vs/ 27 | 28 | # MSTest test Results 29 | [Tt]est[Rr]esult*/ 30 | [Bb]uild[Ll]og.* 31 | 32 | # NUNIT 33 | *.VisualState.xml 34 | TestResult.xml 35 | 36 | # Build Results of an ATL Project 37 | [Dd]ebugPS/ 38 | [Rr]eleasePS/ 39 | dlldata.c 40 | 41 | *_i.c 42 | *_p.c 43 | *_i.h 44 | *.ilk 45 | *.meta 46 | *.obj 47 | *.pch 48 | *.pdb 49 | *.pgc 50 | *.pgd 51 | *.rsp 52 | *.sbr 53 | *.tlb 54 | *.tli 55 | *.tlh 56 | *.tmp 57 | *.tmp_proj 58 | *.log 59 | *.vspscc 60 | *.vssscc 61 | .builds 62 | *.pidb 63 | *.svclog 64 | *.scc 65 | 66 | # Chutzpah Test files 67 | _Chutzpah* 68 | 69 | # Visual C++ cache files 70 | ipch/ 71 | *.aps 72 | *.ncb 73 | *.opensdf 74 | *.sdf 75 | *.cachefile 76 | 77 | # Visual Studio profiler 78 | *.psess 79 | *.vsp 80 | *.vspx 81 | 82 | # TFS 2012 Local Workspace 83 | $tf/ 84 | 85 | # Guidance Automation Toolkit 86 | *.gpState 87 | 88 | # ReSharper is a .NET coding add-in 89 | _ReSharper*/ 90 | *.[Rr]e[Ss]harper 91 | *.DotSettings.user 92 | 93 | # JustCode is a .NET coding addin-in 94 | .JustCode 95 | 96 | # TeamCity is a build add-in 97 | _TeamCity* 98 | 99 | # DotCover is a Code Coverage Tool 100 | *.dotCover 101 | 102 | # NCrunch 103 | _NCrunch_* 104 | .*crunch*.local.xml 105 | 106 | # MightyMoose 107 | *.mm.* 108 | AutoTest.Net/ 109 | 110 | # Web workbench (sass) 111 | .sass-cache/ 112 | 113 | # Installshield output folder 114 | [Ee]xpress/ 115 | 116 | # DocProject is a documentation generator add-in 117 | DocProject/buildhelp/ 118 | DocProject/Help/*.HxT 119 | DocProject/Help/*.HxC 120 | DocProject/Help/*.hhc 121 | DocProject/Help/*.hhk 122 | DocProject/Help/*.hhp 123 | DocProject/Help/Html2 124 | DocProject/Help/html 125 | 126 | # Click-Once directory 127 | publish/ 128 | 129 | # Publish Web Output 130 | *.[Pp]ublish.xml 131 | *.azurePubxml 132 | # TODO: Comment the next line if you want to checkin your web deploy settings 133 | # but database connection strings (with potential passwords) will be unencrypted 134 | *.pubxml 135 | *.publishproj 136 | 137 | # NuGet Packages 138 | *.nupkg 139 | # The packages folder can be ignored because of Package Restore 140 | **/packages/* 141 | # except build/, which is used as an MSBuild target. 142 | !**/packages/build/ 143 | # Uncomment if necessary however generally it will be regenerated when needed 144 | #!**/packages/repositories.config 145 | 146 | # Windows Azure Build Output 147 | csx/ 148 | *.build.csdef 149 | 150 | # Windows Store app package directory 151 | AppPackages/ 152 | 153 | # Others 154 | *.[Cc]ache 155 | ClientBin/ 156 | [Ss]tyle[Cc]op.* 157 | ~$* 158 | *~ 159 | *.dbmdl 160 | *.dbproj.schemaview 161 | *.pfx 162 | *.publishsettings 163 | node_modules/ 164 | bower_components/ 165 | 166 | # RIA/Silverlight projects 167 | Generated_Code/ 168 | 169 | # Backup & report files from converting an old project file 170 | # to a newer Visual Studio version. Backup files are not needed, 171 | # because we have git ;-) 172 | _UpgradeReport_Files/ 173 | Backup*/ 174 | UpgradeLog*.XML 175 | UpgradeLog*.htm 176 | 177 | # SQL Server files 178 | *.mdf 179 | *.ldf 180 | 181 | # Business Intelligence projects 182 | *.rdl.data 183 | *.bim.layout 184 | *.bim_*.settings 185 | 186 | # Microsoft Fakes 187 | FakesAssemblies/ 188 | 189 | # Node.js Tools for Visual Studio 190 | .ntvs_analysis.dat 191 | 192 | # Visual Studio 6 build log 193 | *.plg 194 | 195 | # Visual Studio 6 workspace options file 196 | *.opt 197 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Catalyst Code 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 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Radial Menu 2 | A Radial Menu for Windows UWP Applications, as made popular by the first versions of the modern OneNote App for Windows. Create radial menus floating op top of your application. The control supports variable numbers of buttons, toggle & radio buttons, a selector for long lists, and a fancy metered 3 | menu for intuitive selection of numbers. 4 | 5 | !['Fancy GIF showing the control'](https://github.com/CatalystCode/radial-menu/blob/CaseStudyDemo/gif.gif) 6 | 7 | ## Installation 8 | 9 | Install this component via nuget by doing: 10 | 11 | ``` 12 | PM> Install-Package RadialMenuControl 13 | ``` 14 | 15 | Or add it to your project directly by searching for `RadialMenuControl` in Nuget Package Manager for Visual Studio. 16 | 17 | ## Documentation 18 | 19 | Want to learn how to place this control into your application? It's pretty easy to do - just head over to our [documentation wiki](https://github.com/CatalystCode/radial-menu/wiki) to find out how! 20 | 21 | ## Special Thanks 22 | A bunch of thanks to [Jason Poon](https://github.com/jpoon), who wrote an early version of this code (and helpes us all a lot). 23 | 24 | ## License 25 | 26 | Copyright (C) 2015 Microsoft, licensed MIT. Please check `LICENSE` for details. 27 | -------------------------------------------------------------------------------- /RadialMenu.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.23107.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RadialMenuDemo", "RadialMenuDemo\RadialMenuDemo.csproj", "{F2169718-9CB4-4A32-BCAB-92E254B74213}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RadialMenuControl", "RadialMenuControl\RadialMenuControl.csproj", "{A10EE2D6-D98A-4074-8B29-19A7702F9493}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Debug|ARM = Debug|ARM 14 | Debug|x64 = Debug|x64 15 | Debug|x86 = Debug|x86 16 | Release|Any CPU = Release|Any CPU 17 | Release|ARM = Release|ARM 18 | Release|x64 = Release|x64 19 | Release|x86 = Release|x86 20 | EndGlobalSection 21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 22 | {F2169718-9CB4-4A32-BCAB-92E254B74213}.Debug|Any CPU.ActiveCfg = Debug|x86 23 | {F2169718-9CB4-4A32-BCAB-92E254B74213}.Debug|Any CPU.Build.0 = Debug|x86 24 | {F2169718-9CB4-4A32-BCAB-92E254B74213}.Debug|Any CPU.Deploy.0 = Debug|x86 25 | {F2169718-9CB4-4A32-BCAB-92E254B74213}.Debug|ARM.ActiveCfg = Debug|ARM 26 | {F2169718-9CB4-4A32-BCAB-92E254B74213}.Debug|ARM.Build.0 = Debug|ARM 27 | {F2169718-9CB4-4A32-BCAB-92E254B74213}.Debug|ARM.Deploy.0 = Debug|ARM 28 | {F2169718-9CB4-4A32-BCAB-92E254B74213}.Debug|x64.ActiveCfg = Debug|x64 29 | {F2169718-9CB4-4A32-BCAB-92E254B74213}.Debug|x64.Build.0 = Debug|x64 30 | {F2169718-9CB4-4A32-BCAB-92E254B74213}.Debug|x64.Deploy.0 = Debug|x64 31 | {F2169718-9CB4-4A32-BCAB-92E254B74213}.Debug|x86.ActiveCfg = Debug|x86 32 | {F2169718-9CB4-4A32-BCAB-92E254B74213}.Debug|x86.Build.0 = Debug|x86 33 | {F2169718-9CB4-4A32-BCAB-92E254B74213}.Debug|x86.Deploy.0 = Debug|x86 34 | {F2169718-9CB4-4A32-BCAB-92E254B74213}.Release|Any CPU.ActiveCfg = Release|x86 35 | {F2169718-9CB4-4A32-BCAB-92E254B74213}.Release|ARM.ActiveCfg = Release|ARM 36 | {F2169718-9CB4-4A32-BCAB-92E254B74213}.Release|ARM.Build.0 = Release|ARM 37 | {F2169718-9CB4-4A32-BCAB-92E254B74213}.Release|ARM.Deploy.0 = Release|ARM 38 | {F2169718-9CB4-4A32-BCAB-92E254B74213}.Release|x64.ActiveCfg = Release|x64 39 | {F2169718-9CB4-4A32-BCAB-92E254B74213}.Release|x64.Build.0 = Release|x64 40 | {F2169718-9CB4-4A32-BCAB-92E254B74213}.Release|x64.Deploy.0 = Release|x64 41 | {F2169718-9CB4-4A32-BCAB-92E254B74213}.Release|x86.ActiveCfg = Release|x86 42 | {F2169718-9CB4-4A32-BCAB-92E254B74213}.Release|x86.Build.0 = Release|x86 43 | {F2169718-9CB4-4A32-BCAB-92E254B74213}.Release|x86.Deploy.0 = Release|x86 44 | {A10EE2D6-D98A-4074-8B29-19A7702F9493}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 45 | {A10EE2D6-D98A-4074-8B29-19A7702F9493}.Debug|Any CPU.Build.0 = Debug|Any CPU 46 | {A10EE2D6-D98A-4074-8B29-19A7702F9493}.Debug|Any CPU.Deploy.0 = Debug|Any CPU 47 | {A10EE2D6-D98A-4074-8B29-19A7702F9493}.Debug|ARM.ActiveCfg = Debug|ARM 48 | {A10EE2D6-D98A-4074-8B29-19A7702F9493}.Debug|ARM.Build.0 = Debug|ARM 49 | {A10EE2D6-D98A-4074-8B29-19A7702F9493}.Debug|x64.ActiveCfg = Debug|x64 50 | {A10EE2D6-D98A-4074-8B29-19A7702F9493}.Debug|x64.Build.0 = Debug|x64 51 | {A10EE2D6-D98A-4074-8B29-19A7702F9493}.Debug|x86.ActiveCfg = Debug|x86 52 | {A10EE2D6-D98A-4074-8B29-19A7702F9493}.Debug|x86.Build.0 = Debug|x86 53 | {A10EE2D6-D98A-4074-8B29-19A7702F9493}.Release|Any CPU.ActiveCfg = Release|Any CPU 54 | {A10EE2D6-D98A-4074-8B29-19A7702F9493}.Release|Any CPU.Build.0 = Release|Any CPU 55 | {A10EE2D6-D98A-4074-8B29-19A7702F9493}.Release|ARM.ActiveCfg = Release|ARM 56 | {A10EE2D6-D98A-4074-8B29-19A7702F9493}.Release|ARM.Build.0 = Release|ARM 57 | {A10EE2D6-D98A-4074-8B29-19A7702F9493}.Release|x64.ActiveCfg = Release|x64 58 | {A10EE2D6-D98A-4074-8B29-19A7702F9493}.Release|x64.Build.0 = Release|x64 59 | {A10EE2D6-D98A-4074-8B29-19A7702F9493}.Release|x86.ActiveCfg = Release|x86 60 | {A10EE2D6-D98A-4074-8B29-19A7702F9493}.Release|x86.Build.0 = Release|x86 61 | EndGlobalSection 62 | GlobalSection(SolutionProperties) = preSolution 63 | HideSolutionNode = FALSE 64 | EndGlobalSection 65 | EndGlobal 66 | -------------------------------------------------------------------------------- /RadialMenuControl/Components/CenterButton.xaml: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /RadialMenuControl/Components/CenterButton.xaml.cs: -------------------------------------------------------------------------------- 1 | using Windows.UI.Xaml.Controls; 2 | 3 | namespace RadialMenuControl.Components 4 | { 5 | public partial class CenterButton : Button 6 | { 7 | /// 8 | /// The Canvas Top of the center button 9 | /// 10 | public double? Top { get; set; } 11 | /// 12 | /// The Canvas Left of the center button 13 | /// 14 | public double? Left { get; set; } 15 | 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /RadialMenuControl/Components/RadialMenuButton.xaml: -------------------------------------------------------------------------------- 1 |  10 | -------------------------------------------------------------------------------- /RadialMenuControl/Components/RadialMenuButton.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using Windows.UI; 5 | using Windows.UI.Xaml; 6 | using Windows.UI.Xaml.Controls; 7 | using Windows.UI.Xaml.Input; 8 | using Windows.UI.Xaml.Media; 9 | using RadialMenuControl.UserControl; 10 | 11 | namespace RadialMenuControl.Components 12 | { 13 | public partial class RadialMenuButton : Button 14 | { 15 | public enum ButtonType 16 | { 17 | Simple = 0, 18 | Radio, 19 | Toggle, 20 | Custom 21 | }; 22 | 23 | // SubMenu 24 | public static readonly DependencyProperty SubmenuProperty = 25 | DependencyProperty.Register("Submenu", typeof (RadialMenu), typeof (RadialMenuButton), null); 26 | 27 | public RadialMenuButton() 28 | { 29 | InitializeComponent(); 30 | } 31 | 32 | /// 33 | /// A RadialMenu that is opened when the user presses the button 34 | /// 35 | public RadialMenu Submenu 36 | { 37 | get { return (RadialMenu) GetValue(SubmenuProperty); } 38 | set { SetValue(SubmenuProperty, value); } 39 | } 40 | 41 | /// 42 | /// Allows the use of key/value pairs to set the colors 43 | /// 44 | /// Dictionary containing colors in a key/value pair 45 | public void SetColors(Dictionary colors) 46 | { 47 | foreach (var colorVariable in colors) 48 | { 49 | if (GetType().GetProperty(colorVariable.Key) != null) 50 | { 51 | var prop = GetType().GetProperty(colorVariable.Key); 52 | prop.SetValue(this, colorVariable.Value); 53 | } 54 | } 55 | } 56 | 57 | # region properties 58 | 59 | // Label 60 | public static readonly DependencyProperty LabelProperty = 61 | DependencyProperty.Register("Label", typeof (string), typeof (RadialMenuButton), new PropertyMetadata("")); 62 | 63 | public static readonly DependencyProperty LabelSizeProperty = 64 | DependencyProperty.Register("LabelSize", typeof (int), typeof (RadialMenuButton), new PropertyMetadata(10)); 65 | 66 | public static readonly DependencyProperty IsLabelHiddenProperty = 67 | DependencyProperty.Register("IsLabelHidden", typeof (bool), typeof (RadialMenuButton), null); 68 | 69 | public static readonly DependencyProperty IconProperty = 70 | DependencyProperty.Register("Icon", typeof (string), typeof (RadialMenuButton), new PropertyMetadata("")); 71 | 72 | public static readonly DependencyProperty IconFontFamilyProperty = 73 | DependencyProperty.Register("IconFontFamily", typeof (FontFamily), typeof (RadialMenuButton), 74 | new PropertyMetadata(new FontFamily("Segoe UI"))); 75 | 76 | public static readonly DependencyProperty IconForegroundBrushProperty = 77 | DependencyProperty.Register("IconForegroundBrush", typeof (Brush), typeof (RadialMenuButton), 78 | new PropertyMetadata(new SolidColorBrush(Colors.Black))); 79 | 80 | public static readonly DependencyProperty IconSizeProperty = 81 | DependencyProperty.Register("IconSize", typeof (int), typeof (RadialMenuButton), new PropertyMetadata(26)); 82 | 83 | public static readonly DependencyProperty IconImageProperty = 84 | DependencyProperty.Register("IconImage", typeof (ImageSource), typeof (RadialMenuButton), null); 85 | 86 | public static readonly DependencyProperty MenuSeletedProperty = 87 | DependencyProperty.Register("MenuSelected", typeof (bool), typeof (RadialMenuButton), null); 88 | 89 | public static readonly DependencyProperty ButtonTypeProperty = 90 | DependencyProperty.Register("ButtonType", typeof (ButtonType), typeof (RadialMenuButton), 91 | new PropertyMetadata(ButtonType.Simple)); 92 | 93 | public static readonly DependencyProperty ValueProperty = 94 | DependencyProperty.Register("Value", typeof (object), typeof (RadialMenuButton), null); 95 | 96 | /// 97 | /// Label, displayed in the inner portion of the button 98 | /// 99 | public string Label 100 | { 101 | get { return (string) GetValue(LabelProperty) ?? ""; } 102 | set { SetValue(LabelProperty, value); } 103 | } 104 | 105 | /// 106 | /// Font Size for the label 107 | /// 108 | public int LabelSize 109 | { 110 | get { return (int) GetValue(LabelSizeProperty); } 111 | set { SetValue(LabelProperty, value); } 112 | } 113 | 114 | /// 115 | /// Should the label be hidden? 116 | /// 117 | public bool IsLabelHidden 118 | { 119 | get { return (bool) GetValue(IsLabelHiddenProperty); } 120 | set { SetValue(IsLabelHiddenProperty, value); } 121 | } 122 | 123 | /// 124 | /// Text-based icon, displayed in a TextBox (usually used with icon fonts) 125 | /// 126 | public string Icon 127 | { 128 | get { return (string) GetValue(IconProperty); } 129 | set { SetValue(IconProperty, value); } 130 | } 131 | 132 | /// 133 | /// Font for the text-based icon 134 | /// 135 | public FontFamily IconFontFamily 136 | { 137 | get { return (FontFamily) GetValue(IconFontFamilyProperty); } 138 | set { SetValue(IconFontFamilyProperty, value); } 139 | } 140 | 141 | /// 142 | /// Font size for the text-based icon 143 | /// 144 | public int IconSize 145 | { 146 | get { return (int) GetValue(IconSizeProperty); } 147 | set { SetValue(IconProperty, value); } 148 | } 149 | 150 | /// 151 | /// ForegroundBrush for the text-based icon 152 | /// 153 | public Brush IconForegroundBrush 154 | { 155 | get { return (Brush) GetValue(IconForegroundBrushProperty); } 156 | set { SetValue(IconForegroundBrushProperty, value); } 157 | } 158 | 159 | /// 160 | /// A ImageSource for the icon. If set, the text-based icon will be hidden. 161 | /// 162 | public ImageSource IconImage 163 | { 164 | get { return (ImageSource) GetValue(IconImageProperty); } 165 | set { SetValue(IconImageProperty, value); } 166 | } 167 | 168 | // Values & Button Type 169 | /// 170 | /// If the button is a radio button and selected on behalf of the whole RadialMenu, this value will be true (false 171 | /// otherwise) 172 | /// 173 | public bool MenuSelected 174 | { 175 | get { return (bool) GetValue(MenuSeletedProperty); } 176 | set { SetValue(MenuSeletedProperty, value); } 177 | } 178 | 179 | /// 180 | /// Value of this button 181 | /// 182 | public object Value 183 | { 184 | get { return GetValue(ValueProperty); } 185 | set 186 | { 187 | if (Type == ButtonType.Simple) 188 | { 189 | throw new Exception("A button of type SIMPLE should not have any value."); 190 | } 191 | SetValue(ValueProperty, value); 192 | } 193 | } 194 | 195 | /// 196 | /// Button type, indicating the way users can interact with this button 197 | /// 198 | public ButtonType Type 199 | { 200 | get { return (ButtonType) GetValue(ButtonTypeProperty); } 201 | set { SetValue(ButtonTypeProperty, value); } 202 | } 203 | 204 | // Inner Arc Colors 205 | public static readonly DependencyProperty InnerNormalColorProperty = 206 | DependencyProperty.Register("InnerNormalColor", typeof (Color?), typeof (RadialMenuButton), null); 207 | 208 | public static readonly DependencyProperty InnerHoverColorProperty = 209 | DependencyProperty.Register("InnerHoverColor", typeof (Color?), typeof (RadialMenuButton), null); 210 | 211 | public static readonly DependencyProperty InnerTappedColorProperty = 212 | DependencyProperty.Register("InnerTappedColor", typeof (Color?), typeof (RadialMenuButton), null); 213 | 214 | public static readonly DependencyProperty InnerReleasedColorProperty = 215 | DependencyProperty.Register("InnerReleasedColor", typeof (Color?), typeof (RadialMenuButton), null); 216 | 217 | /// 218 | /// Hover color for the inner portion of the button 219 | /// 220 | public Color? InnerHoverColor 221 | { 222 | get { return (Color?) GetValue(InnerHoverColorProperty); } 223 | set { SetValue(InnerHoverColorProperty, value); } 224 | } 225 | 226 | /// 227 | /// Normal color for the inner portion of the button 228 | /// 229 | public Color? InnerNormalColor 230 | { 231 | get { return (Color?) GetValue(InnerNormalColorProperty); } 232 | set { SetValue(InnerNormalColorProperty, value); } 233 | } 234 | 235 | /// 236 | /// Tapped color for the inner portion of the button 237 | /// 238 | public Color? InnerTappedColor 239 | { 240 | get { return (Color?) GetValue(InnerTappedColorProperty); } 241 | set { SetValue(InnerTappedColorProperty, value); } 242 | } 243 | 244 | /// 245 | /// Released color for the inner portion of the button 246 | /// 247 | public Color? InnerReleasedColor 248 | { 249 | get { return (Color?) GetValue(InnerReleasedColorProperty); } 250 | set { SetValue(InnerReleasedColorProperty, value); } 251 | } 252 | 253 | // Outer Arc Colors 254 | public static readonly DependencyProperty OuterNormalColorProperty = 255 | DependencyProperty.Register("OuterNormalColor", typeof (Color?), typeof (RadialMenuButton), null); 256 | 257 | public static readonly DependencyProperty OuterDisabledColorProperty = 258 | DependencyProperty.Register("OuterDisabledColor", typeof (Color?), typeof (RadialMenuButton), null); 259 | 260 | public static readonly DependencyProperty OuterHoverColorProperty = 261 | DependencyProperty.Register("OuterHoverColor", typeof (Color?), typeof (RadialMenuButton), null); 262 | 263 | public static readonly DependencyProperty OuterTappedColorProperty = 264 | DependencyProperty.Register("OuterTappedColor", typeof (Color?), typeof (RadialMenuButton), null); 265 | 266 | public static readonly DependencyProperty OuterThicknessProperty = 267 | DependencyProperty.Register("OuterThickness", typeof (double?), typeof (RadialMenuButton), null); 268 | 269 | public static readonly DependencyProperty StrokeColorProperty = 270 | DependencyProperty.Register("StrokeColor", typeof (Color?), typeof (RadialMenuButton), null); 271 | 272 | public static readonly DependencyProperty StrokeThicknessProperty = 273 | DependencyProperty.Register("StrokeThickness", typeof (double?), typeof (RadialMenuButton), null); 274 | 275 | // Indication Arc 276 | public static readonly DependencyProperty UseIndicationArcProperty = 277 | DependencyProperty.Register("UseIndicationArcProperty", typeof (bool?), typeof (RadialMenuButton), null); 278 | 279 | public static readonly DependencyProperty IndicationArcColorProperty = 280 | DependencyProperty.Register("IndicationArcColor", typeof (Color?), typeof (RadialMenuButton), null); 281 | 282 | public static readonly DependencyProperty IndicationArcStrokeThicknessProperty = 283 | DependencyProperty.Register("IndicationArcStrokeThickness", typeof (double?), typeof (RadialMenuButton), 284 | null); 285 | 286 | public static readonly DependencyProperty IndicationArcDistanceFromEdgeProperty = 287 | DependencyProperty.Register("IndicationArcDistanceFromEdge", typeof (double?), typeof (RadialMenuButton), 288 | null); 289 | 290 | /// 291 | /// Distance from the inner part of the outer band to the indication arc 292 | /// 293 | public double? IndicationArcDistanceFromEdge 294 | { 295 | get { return (double?) GetValue(IndicationArcDistanceFromEdgeProperty); } 296 | set { SetValue(IndicationArcDistanceFromEdgeProperty, value); } 297 | } 298 | 299 | /// 300 | /// When set to true, an indication arc will be placed on this button 301 | /// 302 | public bool? UseIndicationArc 303 | { 304 | get { return (bool?) GetValue(UseIndicationArcProperty); } 305 | set { SetValue(UseIndicationArcProperty, value); } 306 | } 307 | 308 | public Color? IndicationArcColor 309 | { 310 | get { return (Color?) GetValue(IndicationArcColorProperty); } 311 | set { SetValue(IndicationArcColorProperty, value); } 312 | } 313 | 314 | /// 315 | /// The Stroke thickness of the indication arc 316 | /// 317 | public double? IndicationArcStrokeThickness 318 | { 319 | get { return (double?) GetValue(IndicationArcStrokeThicknessProperty); } 320 | set { SetValue(IndicationArcStrokeThicknessProperty, value); } 321 | } 322 | 323 | /// 324 | /// Hover color for the outer portion of the button 325 | /// 326 | public Color? OuterHoverColor 327 | { 328 | get { return (Color?) GetValue(OuterHoverColorProperty); } 329 | set { SetValue(OuterHoverColorProperty, value); } 330 | } 331 | 332 | /// 333 | /// Normal color for the outer portion of the button 334 | /// 335 | public Color? OuterNormalColor 336 | { 337 | get { return (Color?) GetValue(OuterNormalColorProperty); } 338 | set { SetValue(OuterNormalColorProperty, value); } 339 | } 340 | 341 | /// 342 | /// Disabled color for the outer portion of the button 343 | /// 344 | public Color? OuterDisabledColor 345 | { 346 | get { return (Color?) GetValue(OuterDisabledColorProperty); } 347 | set { SetValue(OuterDisabledColorProperty, value); } 348 | } 349 | 350 | /// 351 | /// Tapped color for the outer portion of the button 352 | /// 353 | public Color? OuterTappedColor 354 | { 355 | get { return (Color?) GetValue(OuterTappedColorProperty); } 356 | set { SetValue(OuterTappedColorProperty, value); } 357 | } 358 | 359 | // Stroke 360 | /// 361 | /// Color of the stroke around the PieSLice 362 | /// 363 | public Color? StrokeColor 364 | { 365 | get { return (Color?) GetValue(StrokeColorProperty); } 366 | set { SetValue(StrokeColorProperty, value); } 367 | } 368 | 369 | /// 370 | /// Thickness of the stroke around the PieSlice 371 | /// 372 | public double? StrokeThickness 373 | { 374 | get { return (double?) GetValue(StrokeThicknessProperty); } 375 | set { SetValue(StrokeThicknessProperty, value); } 376 | } 377 | 378 | /// 379 | /// Thickness of the outer arc, on the outer side of the button 380 | /// 381 | public double? OuterThickness 382 | { 383 | get { return (double?) GetValue(OuterThicknessProperty); } 384 | set { SetValue(OuterThicknessProperty, value); } 385 | } 386 | 387 | // CustomMenu 388 | public static readonly DependencyProperty CustomMenuProperty = 389 | DependencyProperty.Register("Submenu", typeof (MenuBase), typeof (RadialMenuButton), null); 390 | 391 | /// 392 | /// CustomMenu behind the button 393 | /// 394 | public MenuBase CustomMenu 395 | { 396 | get { return (MenuBase) GetValue(CustomMenuProperty); } 397 | set { SetValue(CustomMenuProperty, value); } 398 | } 399 | 400 | // Access Keys 401 | /// 402 | /// Outer slice path access key 403 | /// 404 | public string OuterAccessKey 405 | { 406 | get { return (string)GetValue(OuterAccessKeyProperty); } 407 | set { SetValue(OuterAccessKeyProperty, value); } 408 | } 409 | 410 | /// 411 | /// Inner slice path access key 412 | /// 413 | public string InnerAccessKey 414 | { 415 | get { return (string)GetValue(InnerAccessKeyProperty); } 416 | set { SetValue(InnerAccessKeyProperty, value); } 417 | } 418 | 419 | #endregion properties 420 | 421 | #region events 422 | 423 | /// 424 | /// Delegate for the ValueChangedEvent, fired whenever the value of this button is changed 425 | /// 426 | /// 427 | /// 428 | public delegate void ValueChangedHandler(object sender, RoutedEventArgs args); 429 | 430 | public event ValueChangedHandler ValueChanged; 431 | 432 | public void OnValueChanged(RoutedEventArgs e) 433 | { 434 | ValueChanged?.Invoke(this, e); 435 | } 436 | 437 | public static readonly DependencyProperty InnerAccessKeyProperty = 438 | DependencyProperty.Register("InnerAccessKey", typeof (string), typeof (RadialMenuButton), null); 439 | 440 | public static readonly DependencyProperty OuterAccessKeyProperty = 441 | DependencyProperty.Register("OuterAccessKey", typeof (string), typeof (RadialMenuButton), null); 442 | 443 | /// 444 | /// Does this radial menu button have events on the outer arc? 445 | /// 446 | /// 447 | public bool HasOuterArcEvents => (OuterArcPressed != null || OuterArcReleased != null); 448 | 449 | /// 450 | /// Does this radial menu button have actions on the outer arc? 451 | /// 452 | /// 453 | public bool HasOuterArcAction => (Submenu != null || CustomMenu != null || HasOuterArcEvents); 454 | 455 | public delegate void InnerArcPressedEventHandler(object sender, PointerRoutedEventArgs e); 456 | 457 | /// 458 | /// Invoked when the inner arc of the button has been pressed (mouse, touch, stylus) 459 | /// 460 | public event InnerArcPressedEventHandler InnerArcPressed; 461 | 462 | public void OnInnerArcPressed(PointerRoutedEventArgs e) 463 | { 464 | InnerArcPressed?.Invoke(this, e); 465 | 466 | if (Type != ButtonType.Simple) 467 | { 468 | MenuSelected = true; 469 | } 470 | if (Type == ButtonType.Toggle) 471 | { 472 | Value = (Value == null || !(bool) Value); 473 | } 474 | } 475 | 476 | public delegate void OuterArcPressedEventHandler(object sender, PointerRoutedEventArgs e); 477 | 478 | /// 479 | /// Invoked when the outer arc of the button has been pressed (mouse, touch, stylus) 480 | /// 481 | public event OuterArcPressedEventHandler OuterArcPressed; 482 | 483 | public void OnOuterArcPressed(PointerRoutedEventArgs e) 484 | { 485 | OuterArcPressed?.Invoke(this, e); 486 | } 487 | 488 | public delegate void InnerArcReleasedEventHandler(object sender, PointerRoutedEventArgs e); 489 | 490 | /// 491 | /// Invoked when the inner arc of the button has been released (mouse, touch, stylus) 492 | /// 493 | public event InnerArcReleasedEventHandler InnerArcReleased; 494 | 495 | public void OnInnerArcReleased(PointerRoutedEventArgs e) 496 | { 497 | InnerArcReleased?.Invoke(this, e); 498 | } 499 | 500 | public delegate void OuterArcReleasedEventHandler(object sender, PointerRoutedEventArgs e); 501 | 502 | /// 503 | /// Invoked when the outer arc of the button has been pressed (mouse, touch, stylus) 504 | /// 505 | public event OuterArcReleasedEventHandler OuterArcReleased; 506 | 507 | public void OnOuterArcReleased(PointerRoutedEventArgs e) 508 | { 509 | OuterArcReleased?.Invoke(this, e); 510 | } 511 | 512 | #endregion 513 | } 514 | } -------------------------------------------------------------------------------- /RadialMenuControl/Extensions/ColorExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace RadialMenuControl.Extensions 2 | { 3 | using Windows.UI; 4 | 5 | public static class ColorExtensions 6 | { 7 | private const float DefaultCorrectionFactor = 0.1f; 8 | 9 | /// 10 | /// Lightens a given color 11 | /// 12 | /// The source Color 13 | /// The correction factor to use when ligtening 14 | /// 15 | public static Color Lighten(this Color source, float correctionFactor = DefaultCorrectionFactor) 16 | { 17 | var red = (255 - source.R) * correctionFactor + source.R; 18 | var green = (255 - source.G) * correctionFactor + source.G; 19 | var blue = (255 - source.B) * correctionFactor + source.B; 20 | return Color.FromArgb(source.A, (byte)red, (byte)green, (byte)blue); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /RadialMenuControl/Extensions/StoryboardExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Windows.UI.Xaml.Media.Animation; 4 | 5 | namespace RadialMenuControl.Extensions 6 | { 7 | public static class StoryboardExtension 8 | { 9 | /// 10 | /// Plays the storyboard and returns an awaitable Task 11 | /// 12 | /// 13 | /// 14 | public static Task PlayAsync(this Storyboard storyboard) 15 | { 16 | var tcs = new TaskCompletionSource(); 17 | if (storyboard == null) 18 | tcs.SetException(new ArgumentNullException()); 19 | else 20 | { 21 | EventHandler onComplete = null; 22 | onComplete = (s, e) => { 23 | storyboard.Completed -= onComplete; 24 | tcs.SetResult(true); 25 | }; 26 | storyboard.Completed += onComplete; 27 | storyboard.Begin(); 28 | } 29 | return tcs.Task; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /RadialMenuControl/InnerPieSlicePath.cs: -------------------------------------------------------------------------------- 1 | namespace RadialMenuControl.UserControl 2 | { 3 | using System; 4 | using System.Diagnostics; 5 | using Windows.Foundation; 6 | using Windows.UI.Xaml; 7 | using Windows.UI.Xaml.Media; 8 | 9 | public class InnerPieSlicePath : PathBase 10 | { 11 | public static readonly DependencyProperty AngleProperty = 12 | DependencyProperty.Register("Angle", typeof(double), typeof(PieSlice), 13 | new PropertyMetadata(DependencyProperty.UnsetValue, (s, e) => { Changed(s as PathBase); })); 14 | 15 | /// 16 | /// Angle for the path 17 | /// 18 | public double Angle 19 | { 20 | get { return (double)GetValue(AngleProperty); } 21 | set { SetValue(AngleProperty, value); } 22 | } 23 | 24 | /// 25 | /// Override for the path's redraw mehtod, controlling how the path is drawns 26 | /// 27 | protected override void Redraw() 28 | { 29 | Debug.Assert(GetValue(StartAngleProperty) != DependencyProperty.UnsetValue); 30 | Debug.Assert(GetValue(RadiusProperty) != DependencyProperty.UnsetValue); 31 | Debug.Assert(GetValue(AngleProperty) != DependencyProperty.UnsetValue); 32 | 33 | Width = Height = 2 * (Radius); 34 | var endAngle = StartAngle + Angle; 35 | 36 | // path container 37 | var figure = new PathFigure 38 | { 39 | StartPoint = new Point(Radius, Radius), 40 | IsClosed = true, 41 | IsFilled = true 42 | }; 43 | 44 | // start angle line 45 | var lineX = Radius + Math.Sin(StartAngle * Math.PI / 180) * Radius; 46 | var lineY = Radius - Math.Cos(StartAngle * Math.PI / 180) * Radius; 47 | var line = new LineSegment { Point = new Point(lineX, lineY) }; 48 | figure.Segments.Add(line); 49 | 50 | // outer arc 51 | var arcX = Radius + Math.Sin(endAngle * Math.PI / 180) * Radius; 52 | var arcY = Radius - Math.Cos(endAngle * Math.PI / 180) * Radius; 53 | var arc = new ArcSegment 54 | { 55 | IsLargeArc = Angle >= 180.0, 56 | Point = new Point(arcX, arcY), 57 | Size = new Size(Radius, Radius), 58 | SweepDirection = SweepDirection.Clockwise, 59 | }; 60 | 61 | figure.Segments.Add(arc); 62 | 63 | Data = new PathGeometry { Figures = { figure } }; 64 | InvalidateArrange(); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /RadialMenuControl/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("RadialMenuControl")] 8 | [assembly: AssemblyDescription("A Radial Menu Control (OneNote Style)")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("Microsoft")] 11 | [assembly: AssemblyProduct("RadialMenuControl")] 12 | [assembly: AssemblyCopyright("Copyright © Microsoft 2015")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Version information for an assembly consists of the following four values: 17 | // 18 | // Major Version 19 | // Minor Version 20 | // Build Number 21 | // Revision 22 | // 23 | // You can specify all the values or you can default the Build and Revision Numbers 24 | // by using the '*' as shown below: 25 | // [assembly: AssemblyVersion("1.0.*")] 26 | [assembly: AssemblyVersion("1.0.0.0")] 27 | [assembly: AssemblyFileVersion("1.0.0.0")] 28 | [assembly: ComVisible(false)] -------------------------------------------------------------------------------- /RadialMenuControl/RadialMenuControl.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {A10EE2D6-D98A-4074-8B29-19A7702F9493} 8 | Library 9 | Properties 10 | RadialMenuControl 11 | RadialMenuControl 12 | en-US 13 | UAP 14 | 10.0.10240.0 15 | 10.0.10240.0 16 | 14 17 | 512 18 | {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 19 | false 20 | 21 | 22 | AnyCPU 23 | true 24 | full 25 | false 26 | bin\Debug\ 27 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 28 | prompt 29 | 4 30 | 31 | 32 | AnyCPU 33 | pdbonly 34 | true 35 | bin\Release\ 36 | TRACE;NETFX_CORE;WINDOWS_UWP 37 | prompt 38 | 4 39 | 40 | 41 | ARM 42 | true 43 | bin\ARM\Debug\ 44 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 45 | ;2008 46 | full 47 | ARM 48 | false 49 | prompt 50 | 51 | 52 | ARM 53 | bin\ARM\Release\ 54 | TRACE;NETFX_CORE;WINDOWS_UWP 55 | true 56 | ;2008 57 | pdbonly 58 | ARM 59 | false 60 | prompt 61 | 62 | 63 | x64 64 | true 65 | bin\x64\Debug\ 66 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 67 | ;2008 68 | full 69 | x64 70 | false 71 | prompt 72 | 73 | 74 | x64 75 | bin\x64\Release\ 76 | TRACE;NETFX_CORE;WINDOWS_UWP 77 | true 78 | ;2008 79 | pdbonly 80 | x64 81 | false 82 | prompt 83 | 84 | 85 | x86 86 | true 87 | bin\x86\Debug\ 88 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 89 | ;2008 90 | full 91 | x86 92 | false 93 | prompt 94 | 95 | 96 | x86 97 | bin\x86\Release\ 98 | TRACE;NETFX_CORE;WINDOWS_UWP 99 | true 100 | ;2008 101 | pdbonly 102 | x86 103 | false 104 | prompt 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | CenterButton.xaml 113 | 114 | 115 | RadialMenuButton.xaml 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | ListSubMenu.xaml 126 | 127 | 128 | 129 | MeterSubMenu.xaml 130 | 131 | 132 | 133 | PieSlice.xaml 134 | 135 | 136 | 137 | Pie.xaml 138 | 139 | 140 | RadialMenu.xaml 141 | 142 | 143 | 144 | 145 | 146 | Designer 147 | MSBuild:Compile 148 | 149 | 150 | MSBuild:Compile 151 | Designer 152 | 153 | 154 | Designer 155 | MSBuild:Compile 156 | 157 | 158 | Designer 159 | MSBuild:Compile 160 | 161 | 162 | MSBuild:Compile 163 | Designer 164 | 165 | 166 | Designer 167 | MSBuild:Compile 168 | 169 | 170 | Designer 171 | MSBuild:Compile 172 | 173 | 174 | Designer 175 | MSBuild:Compile 176 | 177 | 178 | MSBuild:Compile 179 | Designer 180 | 181 | 182 | 183 | 184 | 14.0 185 | 186 | 187 | 188 | 189 | 190 | 197 | -------------------------------------------------------------------------------- /RadialMenuControl/RadialMenuControl.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | RadialMenuControl 5 | 0.2.1 6 | Radial Menu Control 7 | Felix Rieseberg;Steven Edouard;Rita Zhang 8 | Felix Rieseberg;Steven Edouard;Rita Zhang 9 | https://github.com/CatalystCode/radial-menu/blob/master/LICENSE 10 | https://github.com/CatalystCode/radial-menu 11 | false 12 | A Radial Menu for Windows UWP Applications 13 | A Radial Menu for Windows UWP Applications, as made popular by the first versions of the modern OneNote App for Windows. Create radial menus floating op top of your application. The control supports variable numbers of buttons, toggle and radio buttons, a selector for long lists, and a fancy metered menu for intuitive selection of numbers. 14 | Initial Release! Checkout our wiki page for more details - https://github.com/CatalystCode/radial-menu/wiki. Spot a bug? Give us a hand by filing issues here: https://github.com/CatalystCode/radial-menu/issues 15 | Copyright 2015 Microsoft 16 | UWP RadialMenu Windows10 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /RadialMenuControl/Shims/Shims.cs: -------------------------------------------------------------------------------- 1 | using Windows.UI.Xaml.Media; 2 | using RadialMenuControl.Components; 3 | using RadialMenuControl.UserControl; 4 | 5 | namespace RadialMenuControl.Shims 6 | { 7 | /// 8 | /// A shim for the Center Button. Retains most things we care about 9 | /// a center button without using the actual button. 10 | /// 11 | public class CenterButtonShim 12 | { 13 | /// 14 | /// The border brush for the center button 15 | /// 16 | public Brush BorderBrush { get; set; } 17 | /// 18 | /// The background brush for the center button 19 | /// 20 | public Brush Background { get; set; } 21 | /// 22 | /// The Content for this center button 23 | /// 24 | public object Content { get; set; } 25 | /// 26 | /// The font size for this center button 27 | /// 28 | public double FontSize { get; set; } 29 | /// 30 | /// The Cavas Top value 31 | /// 32 | public double? Top { get; set; } 33 | /// 34 | /// The Canvas Left value 35 | /// 36 | public double? Left { get; set; } 37 | /// 38 | /// The Tapped handler for this center button 39 | /// 40 | public RadialMenu.CenterButtonTappedHandler CenterButtonTappedHandler { get; set; } 41 | } 42 | 43 | public class Helpers 44 | { 45 | /// 46 | /// Creates a Shim from a button 47 | /// 48 | /// The actual CenterButton 49 | /// The tapped handler for this button 50 | /// 51 | public static CenterButtonShim ButtonToShim(CenterButton input, RadialMenu.CenterButtonTappedHandler tappedHandler) 52 | { 53 | return new CenterButtonShim 54 | { 55 | BorderBrush = input.BorderBrush, 56 | Background = input.Background, 57 | Content = input.Content, 58 | FontSize = input.FontSize, 59 | Top = input.Top, 60 | Left = input.Left, 61 | CenterButtonTappedHandler = tappedHandler 62 | }; 63 | } 64 | 65 | /// 66 | /// Given a Center Button returns a CenterButtonShim of that button 67 | /// 68 | /// The Actual CenterButton 69 | /// 70 | public static CenterButtonShim ButtonToShim(CenterButton input) 71 | { 72 | return ButtonToShim(input, null); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /RadialMenuControl/Themes/Colors.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Windows.UI; 3 | 4 | namespace RadialMenuControl.Themes 5 | { 6 | /// 7 | /// Common colors to use by default across control components 8 | /// 9 | public static class DefaultColors 10 | { 11 | public static Color InnerNormalColor = Color.FromArgb(255, 255, 255, 255), 12 | InnerHoverColor = Color.FromArgb(255, 245, 236, 243), 13 | InnerTappedColor = Color.FromArgb(255, 237, 234, 236), 14 | InnerReleasedColor = Color.FromArgb(255, 192, 157, 190), 15 | OuterNormalColor = Color.FromArgb(255, 128, 57, 123), 16 | OuterDisabledColor = Color.FromArgb(255, 237, 211, 236), 17 | OuterHoverColor = Color.FromArgb(255, 155, 79, 150), 18 | OuterTappedColor = Color.FromArgb(255, 104, 41, 100), 19 | BackgroundHighlightColor = Color.FromArgb(255, 235, 235, 235), 20 | ForegroundColor = Color.FromArgb(255, 241, 218, 234), 21 | HighlightColor = Color.FromArgb(255, 128, 57, 123), 22 | MeterSelectorColor = Colors.Green, 23 | MeterLineColor = Colors.Black; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /RadialMenuControl/Themes/Generic.xaml: -------------------------------------------------------------------------------- 1 |  4 | 5 | 6 | 25 | -------------------------------------------------------------------------------- /RadialMenuControl/Themes/Styles.xaml: -------------------------------------------------------------------------------- 1 |  8 | 9 | 50 | 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 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /RadialMenuControl/UserControl/Floating.cs: -------------------------------------------------------------------------------- 1 | namespace RadialMenuControl.UserControl 2 | { 3 | using System; 4 | using Windows.Foundation; 5 | using Windows.UI.Xaml; 6 | using Windows.UI.Xaml.Controls; 7 | using Windows.UI.Xaml.Input; 8 | 9 | /// 10 | /// A Content Control that can be dragged around. Huge thanks to Diederik Kols for the smartness behind this. 11 | /// 12 | [TemplatePart(Name = BorderPartName, Type = typeof(Border))] 13 | public class Floating : ContentControl 14 | { 15 | private const string BorderPartName = "DraggingBorder"; 16 | 17 | public static readonly DependencyProperty IsBoundByParentProperty = 18 | DependencyProperty.Register("IsBoundByParent", typeof(bool), typeof(Floating), new PropertyMetadata(false)); 19 | 20 | public static readonly DependencyProperty IsBoundByScreenProperty = 21 | DependencyProperty.Register("IsBoundByScreen", typeof(bool), typeof(Floating), new PropertyMetadata(false)); 22 | 23 | public static readonly DependencyProperty ShouldManiuplateChildProperty = 24 | DependencyProperty.Register("ShouldManipulateChild", typeof(bool), typeof(Floating), new PropertyMetadata(false)); 25 | private Border _border; 26 | 27 | /// 28 | /// Prevents the Floating control from manipulating it's content 29 | /// 30 | public bool ShouldManipulateChild 31 | { 32 | get { return (bool)GetValue(ShouldManiuplateChildProperty); } 33 | set { SetValue(ShouldManiuplateChildProperty, value); } 34 | } 35 | /// 36 | /// Initializes a new instance of the class. 37 | /// 38 | public Floating() 39 | { 40 | DefaultStyleKey = typeof(Floating); 41 | ShouldManipulateChild = true; 42 | } 43 | 44 | /// 45 | /// Gets or sets a value indicating whether the control is bound by its parent size. 46 | /// 47 | public bool IsBoundByParent 48 | { 49 | get { return (bool)GetValue(IsBoundByParentProperty); } 50 | set { SetValue(IsBoundByParentProperty, value); } 51 | } 52 | 53 | /// 54 | /// Gets or sets a value indicating whether the control is bound by the screen size. 55 | /// 56 | public bool IsBoundByScreen 57 | { 58 | get { return (bool)GetValue(IsBoundByScreenProperty); } 59 | set { SetValue(IsBoundByScreenProperty, value); } 60 | } 61 | 62 | /// 63 | /// Invoked whenever application code or internal processes (such as a rebuilding layout pass) call ApplyTemplate. 64 | /// In simplest terms, this means the method is called just before a UI element displays in your app. 65 | /// Override this method to influence the default post-template logic of a class. 66 | /// 67 | protected override void OnApplyTemplate() 68 | { 69 | // Border 70 | _border = GetTemplateChild(BorderPartName) as Border; 71 | if (_border != null) 72 | { 73 | //this.border.ManipulationMode = ManipulationModes.TranslateX | ManipulationModes.TranslateY | ManipulationModes.TranslateInertia; 74 | _border.ManipulationDelta += Border_ManipulationDelta; 75 | 76 | // Move Canvas properties from control to border. 77 | Canvas.SetLeft(_border, Canvas.GetLeft(this)); 78 | Canvas.SetLeft(this, 0); 79 | Canvas.SetTop(_border, Canvas.GetTop(this)); 80 | Canvas.SetTop(this, 0); 81 | 82 | // Move Margin to border. 83 | _border.Padding = Margin; 84 | Margin = new Thickness(0); 85 | } 86 | else 87 | { 88 | // Exception 89 | throw new Exception("Floating Control Style has no Border."); 90 | } 91 | 92 | Loaded += Floating_Loaded; 93 | } 94 | 95 | /// 96 | /// Loaded handler which registers for the SizeChanged event. 97 | /// 98 | /// 99 | /// 100 | private void Floating_Loaded(object sender, RoutedEventArgs e) 101 | { 102 | FrameworkElement el = GetClosestParentWithSize(this); 103 | if (el == null) 104 | { 105 | return; 106 | } 107 | 108 | el.SizeChanged += Floating_SizeChanged; 109 | } 110 | 111 | /// 112 | /// Size Changed Handler for Control 113 | /// 114 | /// 115 | /// 116 | private void Floating_SizeChanged(object sender, SizeChangedEventArgs e) 117 | { 118 | var left = Canvas.GetLeft(_border); 119 | var top = Canvas.GetTop(_border); 120 | var rect = new Rect(left, top, _border.ActualWidth, _border.ActualHeight); 121 | 122 | AdjustCanvasPosition(rect); 123 | } 124 | 125 | /// 126 | /// Handler for ManuplationDelta event 127 | /// 128 | /// 129 | /// 130 | private void Border_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e) 131 | { 132 | if (ShouldManipulateChild) 133 | { 134 | ManipulateControlPosition(e.Delta.Translation.X, e.Delta.Translation.Y); 135 | } 136 | } 137 | 138 | /// 139 | /// Manipulate the control's positon! 140 | /// 141 | /// Delta on the X axis 142 | /// Delta on the Y axis 143 | public void ManipulateControlPosition(double x, double y) 144 | { 145 | ManipulateControlPosition(x, y, _border.ActualHeight, _border.ActualWidth); 146 | } 147 | 148 | /// 149 | /// Manipulate the control's positon! 150 | /// 151 | /// Delta on the X axis 152 | /// Delta on the Y axis 153 | /// Expected heigth of the floating control 154 | /// Expected width of the floating control 155 | public void ManipulateControlPosition(double x, double y, double exptectedHeight, double expectedWidth) 156 | { 157 | var left = Canvas.GetLeft(_border) + x; 158 | var top = Canvas.GetTop(_border) + y; 159 | 160 | Rect rect = new Rect(left, top, exptectedHeight, expectedWidth); 161 | AdjustCanvasPosition(rect); 162 | } 163 | 164 | /// 165 | /// Adjusts the canvas position according to the IsBoundBy* properties. 166 | /// 167 | private void AdjustCanvasPosition(Rect rect) 168 | { 169 | // No boundaries 170 | if (!IsBoundByParent && !IsBoundByScreen) 171 | { 172 | Canvas.SetLeft(_border, rect.Left); 173 | Canvas.SetTop(_border, rect.Top); 174 | 175 | return; 176 | } 177 | 178 | FrameworkElement el = GetClosestParentWithSize(this); 179 | 180 | // No parent 181 | if (el == null) 182 | { 183 | // We probably never get here. 184 | return; 185 | } 186 | 187 | var position = new Point(rect.Left, rect.Top); 188 | 189 | if (IsBoundByParent) 190 | { 191 | Rect parentRect = new Rect(0, 0, el.ActualWidth, el.ActualHeight); 192 | position = AdjustedPosition(rect, parentRect); 193 | } 194 | 195 | if (IsBoundByScreen) 196 | { 197 | var ttv = el.TransformToVisual(Window.Current.Content); 198 | var topLeft = ttv.TransformPoint(new Point(0, 0)); 199 | Rect parentRect = new Rect(topLeft.X, topLeft.Y, Window.Current.Bounds.Width - topLeft.X, Window.Current.Bounds.Height - topLeft.Y); 200 | position = AdjustedPosition(rect, parentRect); 201 | } 202 | 203 | // Set new position 204 | Canvas.SetLeft(_border, position.X); 205 | Canvas.SetTop(_border, position.Y); 206 | } 207 | 208 | /// 209 | /// Returns the adjusted the topleft position of a rectangle so that is stays within a parent rectangle. 210 | /// 211 | /// The rectangle. 212 | /// The parent rectangle. 213 | /// 214 | private Point AdjustedPosition(Rect rect, Rect parentRect) 215 | { 216 | var left = rect.Left; 217 | var top = rect.Top; 218 | 219 | if (left < -parentRect.Left) 220 | { 221 | left = -parentRect.Left; 222 | } 223 | else if (left + rect.Width > parentRect.Width) 224 | { 225 | left = parentRect.Width - rect.Width; 226 | } 227 | 228 | if (top < -parentRect.Top) 229 | { 230 | top = -parentRect.Top; 231 | } 232 | else if (top + rect.Height > parentRect.Height) 233 | { 234 | top = parentRect.Height - rect.Height; 235 | } 236 | 237 | return new Point(left, top); 238 | } 239 | 240 | /// 241 | /// Gets the closest parent with a real size. 242 | /// 243 | private FrameworkElement GetClosestParentWithSize(FrameworkElement element) 244 | { 245 | while (element != null && (element.ActualHeight == 0 || element.ActualWidth == 0)) 246 | { 247 | element = element.Parent as FrameworkElement; 248 | } 249 | 250 | return element; 251 | } 252 | } 253 | } 254 | -------------------------------------------------------------------------------- /RadialMenuControl/UserControl/ListSubMenu.xaml: -------------------------------------------------------------------------------- 1 |  13 | 14 | 15 | 16 | 17 | 18 | 31 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /RadialMenuControl/UserControl/ListSubMenu.xaml.cs: -------------------------------------------------------------------------------- 1 | using RadialMenuControl.Components; 2 | 3 | namespace RadialMenuControl.UserControl 4 | { 5 | using System.Collections.Generic; 6 | using Themes; 7 | using Windows.UI; 8 | using Windows.UI.Xaml.Controls; 9 | using Windows.UI.Xaml.Input; 10 | using Windows.UI.Xaml.Media; 11 | 12 | public partial class ListSubMenu : MenuBase 13 | { 14 | 15 | #region properties 16 | private object _selectedValue; 17 | 18 | /// 19 | /// The currently selected element on this list 20 | /// 21 | public object SelectedValue 22 | { 23 | set 24 | { 25 | SetField(ref _selectedValue, value); 26 | } 27 | get 28 | { 29 | return _selectedValue; 30 | } 31 | } 32 | private Brush _backgroundFillBrush = new SolidColorBrush(Colors.WhiteSmoke); 33 | /// 34 | /// The selected value for the 35 | /// 36 | public Brush BackgroundFillBrush 37 | { 38 | set 39 | { 40 | SetField(ref _backgroundFillBrush, value); 41 | } 42 | get 43 | { 44 | return _backgroundFillBrush; 45 | } 46 | } 47 | 48 | private Brush _selectedValueBrush = new SolidColorBrush(DefaultColors.HighlightColor); 49 | /// 50 | /// The brush to use for the selected element on the list 51 | /// 52 | public Brush SelectedValueBrush 53 | { 54 | set 55 | { 56 | SetField(ref _selectedValueBrush, value); 57 | } 58 | get 59 | { 60 | return _selectedValueBrush; 61 | } 62 | } 63 | 64 | private List _listMenuItems = new List(); 65 | public List ListMenuItems 66 | { 67 | set 68 | { 69 | SetField(ref _listMenuItems, value); 70 | } 71 | get 72 | { 73 | return _listMenuItems; 74 | } 75 | } 76 | 77 | private Brush _hoverValueBrush = new SolidColorBrush(DefaultColors.MeterSelectorColor); 78 | 79 | /// 80 | /// The brush to use when the pointer hovers over an element 81 | /// 82 | public Brush HoverValueBrush 83 | { 84 | set 85 | { 86 | SetField(ref _hoverValueBrush, value); 87 | } 88 | get 89 | { 90 | return _hoverValueBrush; 91 | } 92 | } 93 | 94 | /// 95 | /// The CenterButton of this control for back navigation 96 | /// 97 | public override CenterButton CenterButton 98 | { 99 | get { return SubMenuCenterButton; } 100 | set { SetField(ref SubMenuCenterButton, value); } 101 | } 102 | 103 | #endregion 104 | 105 | /// 106 | /// Draws the list menu 107 | /// 108 | public void Draw() 109 | { 110 | 111 | SubMenuListView.ItemsSource = null; 112 | 113 | List items = new List(); 114 | int selectedIndex = 0; 115 | int count = 0; 116 | foreach(RadialMenuButton item in ListMenuItems) 117 | { 118 | items.Add(item.Label); 119 | if (item.MenuSelected) 120 | { 121 | selectedIndex = count; 122 | } 123 | count++; 124 | } 125 | 126 | SubMenuListView.ItemsSource = items; 127 | SubMenuListView.SelectedIndex = selectedIndex; 128 | //SubMenuListView.SelectionChanged += SubMenuListView_SelectionChanged; 129 | } 130 | 131 | /// 132 | /// Event Handler for selection changed event 133 | /// 134 | /// 135 | /// 136 | private void SubMenuListView_SelectionChanged(object sender, SelectionChangedEventArgs e) 137 | { 138 | string selectedItem = (sender as ListView).SelectedValue as string; 139 | foreach (RadialMenuButton item in ListMenuItems) 140 | { 141 | if (item.Label == selectedItem) 142 | { 143 | item.MenuSelected = true; 144 | SelectedValue = item.Value; 145 | break; 146 | } 147 | } 148 | 149 | } 150 | 151 | public delegate void ValueSelectedHandler(object sender, TappedRoutedEventArgs args); 152 | public event ValueSelectedHandler ValueSelected; 153 | /// 154 | /// Creates a ListSubMenu instance 155 | /// 156 | public ListSubMenu() 157 | { 158 | 159 | InitializeComponent(); 160 | DataContext = this; 161 | BackgroundFillBrush = new SolidColorBrush(DefaultColors.InnerNormalColor); 162 | 163 | Tapped += (sender, args) => 164 | { 165 | string selectedItem = SubMenuListView.SelectedValue as string; 166 | foreach (RadialMenuButton item in ListMenuItems) 167 | { 168 | if (item.Label == selectedItem) 169 | { 170 | item.MenuSelected = true; 171 | SelectedValue = item.Value; 172 | } 173 | else 174 | { 175 | item.MenuSelected = false; 176 | } 177 | } 178 | ValueSelected?.Invoke(this, args); 179 | }; 180 | 181 | PropertyChanged += (sender, args) => 182 | { 183 | if (args.PropertyName == "Diameter") 184 | { 185 | 186 | CenterButton.Top = Diameter / 2 - CenterButton.Width / 2; 187 | CenterButton.Left = Diameter / 2 - CenterButton.Width / 2; 188 | } 189 | 190 | 191 | }; 192 | Loaded += (sender, e) => 193 | { 194 | 195 | Draw(); 196 | 197 | }; 198 | 199 | } 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /RadialMenuControl/UserControl/MenuBase.cs: -------------------------------------------------------------------------------- 1 | namespace RadialMenuControl.UserControl 2 | { 3 | using Components; 4 | using Shims; 5 | using System.Collections.Generic; 6 | using System.ComponentModel; 7 | using System.Runtime.CompilerServices; 8 | using Windows.UI; 9 | using Windows.UI.Xaml.Controls; 10 | using Windows.UI.Xaml.Media; 11 | 12 | /// 13 | /// The base class for any custom menus in the radial menu control 14 | /// 15 | public class MenuBase : UserControl, INotifyPropertyChanged 16 | { 17 | /// 18 | /// The property changed event for this menu 19 | /// 20 | public event PropertyChangedEventHandler PropertyChanged; 21 | 22 | private Brush _centerButtonForeground = new SolidColorBrush(Colors.Black); 23 | /// 24 | /// Background Fill for the Center Button 25 | /// 26 | public Brush CenterButtonForeground 27 | { 28 | get { return _centerButtonForeground; } 29 | set { SetField(ref _centerButtonForeground, value); } 30 | } 31 | 32 | private Brush _centerButtonBackgroundFill = new SolidColorBrush(Colors.WhiteSmoke); 33 | /// 34 | /// Background Fill for the Center Button 35 | /// 36 | public Brush CenterButtonBackgroundFill 37 | { 38 | get { return _centerButtonBackgroundFill; } 39 | set { SetField(ref _centerButtonBackgroundFill, value); } 40 | } 41 | 42 | private Brush _centerButtonBorder = new SolidColorBrush(Colors.Transparent); 43 | /// 44 | /// Border Brush for the Center Button 45 | /// 46 | public Brush CenterButtonBorder 47 | { 48 | get { return _centerButtonBorder; } 49 | set { SetField(ref _centerButtonBorder, value); } 50 | } 51 | 52 | private bool _isDraggable = true; 53 | /// 54 | /// Indicates if this Menu should be draggable 55 | /// 56 | public bool IsDraggable 57 | { 58 | get { return _isDraggable; } 59 | set { SetField(ref _isDraggable, value); } 60 | } 61 | 62 | private double _centerButtonBorderThickness = 2; 63 | /// 64 | /// Border Brush for the Center Button 65 | /// 66 | public double CenterButtonBorderThickness 67 | { 68 | get { return _centerButtonBorderThickness; } 69 | set { SetField(ref _centerButtonBorderThickness, value); } 70 | } 71 | 72 | /// 73 | /// Content for the Center Button (using Segoe UI Symbol) 74 | /// 75 | private string _centerButtonIcon = ""; 76 | public string CenterButtonIcon 77 | { 78 | get { return _centerButtonIcon; } 79 | set { SetField(ref _centerButtonIcon, value); } 80 | } 81 | 82 | /// 83 | /// Width/Height for the Center Button 84 | /// 85 | private int _centerButtonSize = 60; 86 | public int CenterButtonSize 87 | { 88 | get { return _centerButtonSize; } 89 | set { SetField(ref _centerButtonSize, value); } 90 | } 91 | 92 | /// 93 | /// Font Size for the Center Button 94 | /// 95 | private double _centerButtonFontSize = 19; 96 | public double CenterButtonFontSize 97 | { 98 | get { return _centerButtonFontSize; } 99 | set { SetField(ref _centerButtonFontSize, value); } 100 | } 101 | 102 | /// 103 | /// Font Size for the Center Button 104 | /// 105 | private double _centerButtonTop = 19; 106 | public double CenterButtonTop 107 | { 108 | get { return _centerButtonTop; } 109 | set { SetField(ref _centerButtonTop, value); } 110 | } 111 | /// 112 | /// Font Size for the Center Button 113 | /// 114 | private double _centerButtonLeft = 19; 115 | public double CenterButtonLeft 116 | { 117 | get { return _centerButtonLeft; } 118 | set { SetField(ref _centerButtonLeft, value); } 119 | } 120 | 121 | public virtual CenterButton CenterButton { get; set; } 122 | 123 | private bool _isCenterButtonNavigationEnabled = true; 124 | 125 | protected Stack PreviousCenterButtons = new Stack(); 126 | 127 | /// 128 | /// If disabled, the center button won't automatically allow "back" navigation between submenue 129 | /// 130 | public bool IsCenterButtonNavigationEnabled 131 | { 132 | get { return _isCenterButtonNavigationEnabled; } 133 | set { SetField(ref _isCenterButtonNavigationEnabled, value); } 134 | } 135 | 136 | private double _diameter; 137 | /// 138 | /// Diameter of the whole control 139 | /// 140 | public double Diameter 141 | { 142 | get { return _diameter; } 143 | set 144 | { 145 | SetField(ref _diameter, value); 146 | 147 | Height = _diameter; 148 | Width = _diameter; 149 | } 150 | } 151 | 152 | /// 153 | /// Helper function ensuring that we're not wasting resources when updating a field 154 | /// 155 | /// 156 | /// Field to use 157 | /// Value to use 158 | /// Name of the property 159 | protected void SetField(ref T field, T value, [CallerMemberName] string propertyName = null) 160 | { 161 | if (!value.Equals(field)) 162 | { 163 | field = value; 164 | var eventHandler = PropertyChanged; 165 | eventHandler?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 166 | } 167 | } 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /RadialMenuControl/UserControl/MeterSubMenu.xaml: -------------------------------------------------------------------------------- 1 |  14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /RadialMenuControl/UserControl/MeterSubmenuPath.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Windows.Foundation; 4 | using Windows.UI.Xaml.Media; 5 | 6 | namespace RadialMenuControl.UserControl 7 | { 8 | /// 9 | /// A helper class for describing a tick point on the meter 10 | /// 11 | public class TickPoint 12 | { 13 | public Point Point { get; set; } 14 | public Point LabelPoint { get; set; } 15 | public double Value { get; set; } 16 | } 17 | 18 | /// 19 | /// The Arc which represents the meter 20 | /// 21 | class MeterSubmenuPath : PathBase 22 | { 23 | /// 24 | /// Draws the Meter arc 25 | /// 26 | public void Draw() 27 | { 28 | Redraw(); 29 | } 30 | #region properties 31 | /// 32 | /// Starting value for the meter. If set to 5, the first selectable value will be 5. 33 | /// 34 | public double MeterStartValue 35 | { 36 | get; set; 37 | } 38 | 39 | /// 40 | /// Ending value for the meter. If set to 10, the last selectable value will be 10. 41 | /// 42 | public double MeterEndValue 43 | { 44 | get; set; 45 | } 46 | 47 | /// 48 | /// Radius of the inner meter. If bigger, the user will have an easier time making fine-grained selections. 49 | /// 50 | public double MeterRadius 51 | { 52 | get; set; 53 | } 54 | 55 | /// 56 | /// Offset for the labe; 57 | /// 58 | public double LabelOffset { get; set; } 59 | 60 | /// 61 | /// List of all TickPoints for this Meter 62 | /// 63 | public IList MeterTickPoints 64 | { 65 | get; set; 66 | } 67 | 68 | /// 69 | /// Length of a tick 70 | /// 71 | public double? TickLength { get; set; } 72 | 73 | /// 74 | /// A list containing defined intervals, allowing you to set custom intervals for the meter. The upper half of the meter could contain 75 | /// values between 0 and 10, while the lower half could contain values between 10 and 50. 76 | /// 77 | public IList Intervals { get; set; } 78 | #endregion 79 | 80 | /// 81 | /// Constructs a new MeterSubmenuPath 82 | /// 83 | public MeterSubmenuPath() : base() 84 | { 85 | MeterTickPoints = new List(); 86 | } 87 | 88 | /// 89 | /// Draws the scale ticks, with an invisible circle with r=MeterRadius bisecting each tick 90 | /// 91 | /// The length of each tick 92 | /// The geometry group to add each tick to 93 | /// The angle to start drawing ticks at, relative to the negative Y axis 94 | private void DrawScale(double tickLength, GeometryGroup group, double startAngle = 0.0) 95 | { 96 | MeterTickPoints?.Clear(); 97 | if (Intervals == null) 98 | { 99 | return; 100 | } 101 | foreach (var interval in Intervals) 102 | { 103 | DrawInterval(interval, tickLength, group, startAngle); 104 | startAngle += (interval.EndDegree - interval.StartDegree)*(Math.PI/180); 105 | } 106 | 107 | 108 | } 109 | /// 110 | /// Draws an interval for the meter 111 | /// 112 | /// The MeterRangeInteraval which represents this range of the meter 113 | /// The length of the ticks for this range 114 | /// The geometry group to add this to add this to 115 | /// 116 | private void DrawInterval(MeterRangeInterval interval, double tickLength, GeometryGroup group, double startAngle = 0.0) 117 | { 118 | double startRad = interval.StartDegree*(Math.PI/180), endRad = interval.EndDegree*(Math.PI/180); 119 | double radianInterval = (endRad - startRad) * (interval.TickInterval / (interval.EndValue - interval.StartValue)); 120 | var tickCount = (uint)((endRad - startRad)/ radianInterval); 121 | 122 | 123 | for (var i = 0; i <= tickCount; i++) 124 | { 125 | var pathGeometry = new PathGeometry(); 126 | var figure = new PathFigure(); 127 | 128 | // draw tick line 129 | double x1 = MeterRadius * Math.Sin(startAngle), 130 | y1 = MeterRadius * Math.Cos(startAngle), 131 | x2 = (MeterRadius + tickLength) * Math.Sin(startAngle), 132 | y2 = (MeterRadius + tickLength) * Math.Cos(startAngle), 133 | labelX = (MeterRadius + LabelOffset + (tickLength / 2)) * Math.Sin(startAngle), 134 | labelY = (MeterRadius + LabelOffset + (tickLength / 2)) * Math.Cos(startAngle); 135 | 136 | figure.StartPoint = new Point(Radius + x1, Radius - y1); 137 | 138 | var line = new LineSegment 139 | { 140 | Point = new Point(Radius + x2, Radius - y2) 141 | }; 142 | 143 | MeterTickPoints?.Add(new TickPoint() { 144 | // midway point in the tick - the point the tick crosses the meter circle 145 | Point = new Point(Radius + (MeterRadius * Math.Sin(startAngle)), Radius - (MeterRadius * Math.Cos(startAngle))), 146 | LabelPoint = new Point(Radius + labelX, Radius - labelY), 147 | Value = i * interval.TickInterval + interval.StartValue 148 | }); 149 | 150 | figure.Segments.Add(line); 151 | pathGeometry.Figures.Add(figure); 152 | group.Children.Add(pathGeometry); 153 | startAngle += radianInterval; 154 | } 155 | } 156 | /// 157 | /// Redraws this meter 158 | /// 159 | protected override void Redraw() 160 | { 161 | var group = new GeometryGroup(); 162 | if (TickLength == null) 163 | { 164 | TickLength = 5; 165 | } 166 | DrawScale((double)TickLength, group, StartAngle); 167 | 168 | Data = group; 169 | InvalidateArrange(); 170 | } 171 | 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /RadialMenuControl/UserControl/OuterPieSlicePath.cs: -------------------------------------------------------------------------------- 1 | namespace RadialMenuControl.UserControl 2 | { 3 | using System; 4 | using System.Diagnostics; 5 | using Windows.Foundation; 6 | using Windows.UI.Xaml; 7 | using Windows.UI.Xaml.Media; 8 | 9 | public class OuterPieSlicePath : PathBase 10 | { 11 | public static readonly DependencyProperty AngleProperty = 12 | DependencyProperty.Register("Angle", typeof(double), typeof(PieSlice), 13 | new PropertyMetadata(DependencyProperty.UnsetValue, (s, e) => { Changed(s as PathBase); })); 14 | 15 | public static readonly DependencyProperty ThicknessProperty = 16 | DependencyProperty.Register("Thickness", typeof(double), typeof(PieSlice), 17 | new PropertyMetadata(DependencyProperty.UnsetValue, (s, e) => { Changed(s as PathBase); })); 18 | 19 | /// 20 | /// Angle for the path 21 | /// 22 | public double Angle 23 | { 24 | get { return (double)GetValue(AngleProperty); } 25 | set { SetValue(AngleProperty, value); } 26 | } 27 | 28 | /// 29 | /// Thickness for the path 30 | /// 31 | public double Thickness 32 | { 33 | get { return (double)GetValue(ThicknessProperty); } 34 | set { SetValue(ThicknessProperty, value); } 35 | } 36 | 37 | /// 38 | /// Override for the path's redraw mehtod, controlling how the path is drawns 39 | /// 40 | protected override void Redraw() 41 | { 42 | Debug.Assert(GetValue(StartAngleProperty) != DependencyProperty.UnsetValue); 43 | Debug.Assert(GetValue(RadiusProperty) != DependencyProperty.UnsetValue); 44 | Debug.Assert(GetValue(AngleProperty) != DependencyProperty.UnsetValue); 45 | 46 | if (Radius == 0 || !(Thickness > 0)) return; 47 | 48 | Width = Height = 2 * (Radius); 49 | var endAngle = StartAngle + Angle; 50 | var smallRadius = Radius - Thickness; 51 | 52 | var startX = Radius + Math.Sin(StartAngle * Math.PI / 180) * Radius; 53 | var startY = Radius - Math.Cos(StartAngle * Math.PI / 180) * Radius; 54 | 55 | // path container 56 | var figure = new PathFigure 57 | { 58 | StartPoint = new Point(startX, startY), 59 | IsClosed = true, 60 | IsFilled = true 61 | }; 62 | 63 | // outer arc 64 | var outerArcX = Radius + Math.Sin(endAngle * Math.PI / 180) * Radius; 65 | var outerArcY = Radius - Math.Cos(endAngle * Math.PI / 180) * Radius; 66 | var outerArc = new ArcSegment 67 | { 68 | IsLargeArc = Angle >= 180.0, 69 | Point = new Point(outerArcX, outerArcY), 70 | Size = new Size(Radius, Radius), 71 | SweepDirection = SweepDirection.Clockwise, 72 | }; 73 | figure.Segments.Add(outerArc); 74 | 75 | // start angle line 76 | var lineX = smallRadius + Math.Sin(endAngle * Math.PI / 180) * smallRadius; 77 | var lineY = smallRadius - Math.Cos(endAngle * Math.PI / 180) * smallRadius; 78 | var line = new LineSegment { Point = new Point(lineX + Thickness, lineY + Thickness) }; 79 | figure.Segments.Add(line); 80 | 81 | // inner arc 82 | var innerArcX = smallRadius + Math.Sin(StartAngle * Math.PI / 180) * smallRadius ; 83 | var innerArcY = smallRadius - Math.Cos(StartAngle * Math.PI / 180) * smallRadius; 84 | var innerArc = new ArcSegment 85 | { 86 | IsLargeArc = Angle >= 180.0, 87 | Point = new Point(innerArcX + Thickness, innerArcY + Thickness), 88 | Size = new Size(smallRadius, smallRadius), 89 | SweepDirection = SweepDirection.Counterclockwise, 90 | }; 91 | figure.Segments.Add(innerArc); 92 | 93 | Data = new PathGeometry { Figures = { figure } }; 94 | InvalidateArrange(); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /RadialMenuControl/UserControl/PathBase.cs: -------------------------------------------------------------------------------- 1 | using Windows.UI.Xaml; 2 | using Windows.UI.Xaml.Shapes; 3 | namespace RadialMenuControl.UserControl 4 | { 5 | /// 6 | /// Baseclass for Custom Paths drawn within a Menu. Conatains basic tings like angles and radius 7 | /// 8 | public abstract class PathBase : Path 9 | { 10 | #region dependency_properties 11 | public static readonly DependencyProperty StartAngleProperty = 12 | DependencyProperty.Register("StartAngle", typeof(double), typeof(PieSlice), 13 | new PropertyMetadata(default(double), (s, e) => { Changed(s as PathBase); })); 14 | 15 | public static readonly DependencyProperty RadiusProperty = 16 | DependencyProperty.Register("Radius", typeof(double), typeof(PieSlice), 17 | new PropertyMetadata(DependencyProperty.UnsetValue, (s, e) => { Changed(s as PathBase); })); 18 | #endregion 19 | 20 | /// 21 | /// Starting angle for this path object 22 | /// 23 | public double StartAngle 24 | { 25 | get { return (double)GetValue(StartAngleProperty); } 26 | set { SetValue(StartAngleProperty, value); } 27 | } 28 | 29 | /// 30 | /// Radius for this path object 31 | /// 32 | public double Radius 33 | { 34 | get { return (double)GetValue(RadiusProperty); } 35 | set { SetValue(RadiusProperty, value); } 36 | } 37 | 38 | /// 39 | /// Helper method, called when a path has changed and needs to be redrawn 40 | /// 41 | /// 42 | protected static void Changed(PathBase path) 43 | { 44 | if (path.IsLoaded) 45 | { 46 | path.Redraw(); 47 | } 48 | } 49 | 50 | /// 51 | /// Has the path already been loaded? 52 | /// 53 | protected bool IsLoaded; 54 | 55 | /// 56 | /// Specifies how to draw this path 57 | /// 58 | protected abstract void Redraw(); 59 | 60 | /// 61 | /// Constructs a new PathBase instance 62 | /// 63 | protected PathBase() 64 | { 65 | Loaded += (s, e) => 66 | { 67 | IsLoaded = true; 68 | Redraw(); 69 | }; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /RadialMenuControl/UserControl/Pie.xaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /RadialMenuControl/UserControl/Pie.xaml.cs: -------------------------------------------------------------------------------- 1 | using Windows.UI.Xaml.Media; 2 | 3 | namespace RadialMenuControl.UserControl 4 | { 5 | using Components; 6 | using System.Collections.Generic; 7 | using System.Collections.ObjectModel; 8 | using System.Collections.Specialized; 9 | using System.ComponentModel; 10 | using System.Linq; 11 | using System.Runtime.CompilerServices; 12 | using Windows.UI; 13 | using Windows.UI.Xaml; 14 | using Windows.UI.Xaml.Controls; 15 | 16 | /// 17 | /// The Pie is the circle making a RadialMenuControl so radial - it generates PieSlices form RadialMenuButtons and manages interaction 18 | /// between those PieSlices, surfacing only useful information up to the parent RadialMenuControl. 19 | /// 20 | public partial class Pie : UserControl, INotifyPropertyChanged 21 | { 22 | public readonly ObservableCollection PieSlices = new ObservableCollection(); 23 | public event PropertyChangedEventHandler PropertyChanged; 24 | 25 | public static readonly DependencyProperty SourceRadialMenuProperty = 26 | DependencyProperty.Register("SourceRadialMenu", typeof(RadialMenu), typeof(Pie), null); 27 | 28 | /// 29 | /// A Pie is generated by a RadialMenu - this is a reference to the RadialMenu that generated this pie. 30 | /// 31 | public RadialMenu SourceRadialMenu; 32 | 33 | private double _startAngle; 34 | /// 35 | /// Starting angle for the pie, with 0 being "north top". If set to 90, the first RadialMenuButton will be generated as 36 | /// as PieSlice on "east right". 37 | /// 38 | public double StartAngle 39 | { 40 | get { return _startAngle; } 41 | set { 42 | SetField(ref _startAngle, value); 43 | Draw(); 44 | } 45 | } 46 | 47 | /// 48 | /// Angle of the Pie 49 | /// 50 | private double _angle; 51 | public double Angle 52 | { 53 | get { return _angle; } 54 | set { SetField(ref _angle, value); } 55 | } 56 | 57 | /// 58 | /// Size (aka diameter) of the pie 59 | /// 60 | private double _size; 61 | public double Size 62 | { 63 | get { return _size; } 64 | set 65 | { 66 | SetField(ref _size, value); 67 | 68 | Height = _size; 69 | Width = _size; 70 | } 71 | } 72 | 73 | private IList _slices = new List(); 74 | /// 75 | /// A collection of RadialMenuButtons creating the slices on this pie 76 | /// 77 | public IList Slices 78 | { 79 | get { return _slices; } 80 | set 81 | { 82 | SetField(ref _slices, value); 83 | Draw(); 84 | } 85 | } 86 | 87 | /// 88 | /// A shortcut to the label of the currently selected item (if an item is selected). 89 | /// A selected item is the last selected radio button. 90 | /// 91 | public string SelectedItemValue => _selectedItem?.Label; 92 | 93 | private RadialMenuButton _selectedItem; 94 | /// 95 | /// The last selected item on this pie. A selected item is the currently active radio button, 96 | /// if a radio button is part of this pie. Null if no radio buttons are present, or a selection 97 | /// hasn't been made yet. 98 | /// 99 | public RadialMenuButton SelectedItem 100 | { 101 | get { return _selectedItem; } 102 | set 103 | { 104 | if (value != null) 105 | { 106 | SetField(ref _selectedItem, value); 107 | 108 | var eventHandler = PropertyChanged; 109 | eventHandler?.Invoke(this, new PropertyChangedEventArgs("SelectedItemValue")); 110 | } 111 | } 112 | } 113 | 114 | public Pie() 115 | { 116 | InitializeComponent(); 117 | DataContext = this; 118 | 119 | Loaded += (sender, args) => 120 | { 121 | Draw(); 122 | 123 | if (PieSlices.FirstOrDefault() != null) 124 | { 125 | Angle = 360 - PieSlices.FirstOrDefault().Angle / 4; 126 | } 127 | }; 128 | 129 | 130 | PieSlices.CollectionChanged += (sender, args) => 131 | { 132 | switch (args.Action) 133 | { 134 | case NotifyCollectionChangedAction.Add: 135 | foreach (PieSlice item in args.NewItems) 136 | { 137 | LayoutContent.Children.Add(item); 138 | } 139 | break; 140 | 141 | case NotifyCollectionChangedAction.Remove: 142 | foreach (PieSlice item in args.OldItems) 143 | { 144 | LayoutContent.Children.Remove(item); 145 | } 146 | break; 147 | 148 | case NotifyCollectionChangedAction.Reset: 149 | LayoutContent.Children.Clear(); 150 | break; 151 | } 152 | }; 153 | } 154 | 155 | /// 156 | /// Draw or redraw the pie by generating PieSlices from passed RadialMenuButtons. 157 | /// Previously created PieSlices on this Pie will be thrown away. 158 | /// 159 | public void Draw() 160 | { 161 | foreach (var pieSlice in PieSlices) 162 | { 163 | pieSlice.ChangeMenuRequestEvent -= SourceRadialMenu.ChangeMenu; 164 | pieSlice.ChangeSelectedEvent -= PieSlice_ChangeSelectedEvent; 165 | } 166 | 167 | PieSlices.Clear(); 168 | var startAngle = StartAngle; 169 | 170 | // Setup Background Ellipse 171 | if (SourceRadialMenu.HasBackgroundEllipse) 172 | { 173 | BackgroundEllipse.Height = Height; 174 | BackgroundEllipse.Width = Width; 175 | BackgroundEllipse.StrokeThickness = SourceRadialMenu.OuterThickness; 176 | BackgroundEllipse.Stroke = new SolidColorBrush(SourceRadialMenu.OuterDisabledColor); 177 | BackgroundEllipse.Fill = new SolidColorBrush(SourceRadialMenu.BackgroundEllipseFill); 178 | } 179 | 180 | // Draw PieSlices for each Slice Object 181 | foreach (var slice in Slices) 182 | { 183 | var sliceSize = 360.00 / Slices.Count; 184 | var pieSlice = new PieSlice 185 | { 186 | StartAngle = startAngle, 187 | Angle = sliceSize, 188 | Radius = Size / 2, 189 | Height = Height, 190 | Width = Width, 191 | // Arc & Stroke 192 | StrokeColor = slice.StrokeColor ?? SourceRadialMenu.InnerNormalColor, 193 | StrokeThickness = slice.StrokeThickness ?? 0, 194 | OuterArcThickness = slice.OuterThickness ?? SourceRadialMenu.OuterThickness, 195 | // The defaults below use OneNote-like purple colors 196 | InnerNormalColor = slice.InnerNormalColor ?? SourceRadialMenu.InnerNormalColor, 197 | InnerHoverColor = slice.InnerHoverColor ?? SourceRadialMenu.InnerHoverColor, 198 | InnerTappedColor = slice.InnerTappedColor ?? SourceRadialMenu.InnerTappedColor, 199 | InnerReleasedColor = slice.InnerReleasedColor ?? SourceRadialMenu.InnerReleasedColor, 200 | OuterNormalColor = slice.OuterNormalColor ?? SourceRadialMenu.OuterNormalColor, 201 | OuterDisabledColor = slice.OuterDisabledColor ?? SourceRadialMenu.OuterDisabledColor, 202 | OuterHoverColor = slice.OuterHoverColor ?? SourceRadialMenu.OuterHoverColor, 203 | OuterTappedColor = slice.OuterTappedColor ?? SourceRadialMenu.OuterTappedColor, 204 | IndicationArcStrokeThickness = slice.IndicationArcStrokeThickness ?? SourceRadialMenu.IndicationArcStrokeThickness, 205 | IndicationArcColor = slice.IndicationArcColor ?? SourceRadialMenu.IndicationArcColor, 206 | IndicationArcDistanceFromEdge = slice.IndicationArcDistanceFromEdge ?? SourceRadialMenu.IndicationArcDistanceFromEdge, 207 | UseIndicationArc = slice.UseIndicationArc ?? SourceRadialMenu.UseIndicationArcs, 208 | // Label 209 | IconSize = slice.IconSize, 210 | Icon = slice.Icon, 211 | IconFontFamily = slice.IconFontFamily, 212 | IconForegroundBrush = slice.IconForegroundBrush, 213 | IconImage = slice.IconImage, 214 | IconImageSideLength = (Size / 2) * .25, 215 | IsLabelHidden = slice.IsLabelHidden, 216 | Label = slice.Label, 217 | LabelSize = slice.LabelSize, 218 | // Original Button 219 | OriginalRadialMenuButton = slice, 220 | // Access Keys 221 | OuterAccessKey = slice.OuterAccessKey, 222 | InnerAccessKey = slice.InnerAccessKey 223 | }; 224 | 225 | if (slice.Type == RadialMenuButton.ButtonType.Custom) 226 | { 227 | pieSlice.CustomValue = (string)slice.Value; 228 | } 229 | 230 | // Allow slice to call the change request method on the radial menu 231 | pieSlice.ChangeMenuRequestEvent += SourceRadialMenu.ChangeMenu; 232 | // Allow slice to call the change selected request to clear all other radio buttons 233 | pieSlice.ChangeSelectedEvent += PieSlice_ChangeSelectedEvent; 234 | PieSlices.Add(pieSlice); 235 | startAngle += sliceSize; 236 | } 237 | 238 | FindSelectedPieSlice(); 239 | } 240 | 241 | /// 242 | /// Helper method performing a manual lookup of the currently selected item (a radio button) 243 | /// by checking all PieSices. 244 | /// 245 | public void FindSelectedPieSlice() 246 | { 247 | foreach (var ps in PieSlices) 248 | { 249 | var ormb = ps.OriginalRadialMenuButton; 250 | if (ormb.Type == RadialMenuButton.ButtonType.Radio && ormb.MenuSelected) 251 | { 252 | SelectedItem = ps.OriginalRadialMenuButton; 253 | } 254 | } 255 | } 256 | 257 | /// 258 | /// Helper method manually updating the visual state of all PieSlices generated from a RadialMenuButton of type "radio" or "toggle". 259 | /// 260 | public void UpdatePieSlicesVisualState() 261 | { 262 | foreach (var ps in PieSlices) 263 | { 264 | if (ps.OriginalRadialMenuButton.Type == RadialMenuButton.ButtonType.Radio) ps.UpdateSliceForRadio(); 265 | if (ps.OriginalRadialMenuButton.Type == RadialMenuButton.ButtonType.Toggle) ps.UpdateSliceForToggle(); 266 | } 267 | } 268 | 269 | /// 270 | /// Event handler for a value change on a radio button on this pie. 271 | /// 272 | /// 273 | /// 274 | private void PieSlice_ChangeSelectedEvent(object sender, PieSlice slice) 275 | { 276 | if (slice != null && slice.OriginalRadialMenuButton.Type == RadialMenuButton.ButtonType.Radio) 277 | { 278 | SelectedItem = slice.OriginalRadialMenuButton; 279 | } 280 | 281 | foreach (var ps in PieSlices.Where(ps => ps.OriginalRadialMenuButton.Type == RadialMenuButton.ButtonType.Radio 282 | && ps.OriginalRadialMenuButton.MenuSelected && ps.StartAngle != slice.StartAngle)) 283 | { 284 | ps.OriginalRadialMenuButton.MenuSelected = false; 285 | ps.UpdateSliceForRadio(); 286 | } 287 | } 288 | 289 | /// 290 | /// Helper method for setting fields on properties 291 | /// 292 | /// 293 | /// 294 | /// 295 | /// 296 | private void SetField(ref T field, T value, [CallerMemberName] string propertyName = null) 297 | { 298 | if (!value.Equals(field)) 299 | { 300 | field = value; 301 | var eventHandler = PropertyChanged; 302 | eventHandler?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 303 | } 304 | } 305 | } 306 | } 307 | 308 | -------------------------------------------------------------------------------- /RadialMenuControl/UserControl/PieSlice.xaml: -------------------------------------------------------------------------------- 1 |  11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 123 | 124 | 133 | 134 | 143 | 144 | 145 | 146 | 147 | 148 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /RadialMenuControl/UserControl/RadialMenu.xaml: -------------------------------------------------------------------------------- 1 |  13 | 14 | 15 | 16 | 17 | 18 | 19 | 22 | 23 | 24 | 25 | 26 | 29 | 30 | 31 | 32 | 33 | 36 | 37 | 38 | 39 | 40 | 44 | 45 | 46 | 50 | 51 | 52 | 53 | 54 | 58 | 59 | 60 | 61 | 62 | 66 | 67 | 68 | 69 | 70 | 75 | 76 | 77 | 82 | 83 | 84 | 85 | 86 | 91 | 92 | 93 | 94 | 95 | 100 | 101 | 102 | 107 | 108 | 109 | 110 | 111 | 116 | 117 | 118 | 119 | 120 | 125 | 126 | 127 | 132 | 133 | 134 | 135 | 136 | 141 | 142 | 143 | 144 | 145 | 151 | 152 | 153 | 158 | 159 | 160 | 161 | 162 | 167 | 168 | 169 | 170 | 171 | 177 | 178 | 179 | 180 | 186 | 187 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | -------------------------------------------------------------------------------- /RadialMenuControl/nuget/RadialMenuControl.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | $(MSBuildThisFileDirectory)..\..\lib\uap10.0 6 | $(TargetDir.Replace("\bin\", "\obj\")) 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /RadialMenuControl/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "Microsoft.NETCore.UniversalWindowsPlatform": "5.0.0" 4 | }, 5 | "frameworks": { 6 | "uap10.0": {} 7 | }, 8 | "runtimes": { 9 | "win10-arm": {}, 10 | "win10-arm-aot": {}, 11 | "win10-x86": {}, 12 | "win10-x86-aot": {}, 13 | "win10-x64": {}, 14 | "win10-x64-aot": {} 15 | } 16 | } -------------------------------------------------------------------------------- /RadialMenuDemo/App.xaml: -------------------------------------------------------------------------------- 1 |  7 | 8 | 9 | -------------------------------------------------------------------------------- /RadialMenuDemo/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Windows.ApplicationModel; 3 | using Windows.ApplicationModel.Activation; 4 | using Windows.UI.Xaml; 5 | using Windows.UI.Xaml.Controls; 6 | using Windows.UI.Xaml.Navigation; 7 | 8 | namespace RadialMenuDemo 9 | { 10 | /// 11 | /// Provides application-specific behavior to supplement the default Application class. 12 | /// 13 | sealed partial class App : Application 14 | { 15 | /// 16 | /// Initializes the singleton application object. This is the first line of authored code 17 | /// executed, and as such is the logical equivalent of main() or WinMain(). 18 | /// 19 | public App() 20 | { 21 | InitializeComponent(); 22 | Suspending += OnSuspending; 23 | } 24 | 25 | /// 26 | /// Invoked when the application is launched normally by the end user. Other entry points 27 | /// will be used such as when the application is launched to open a specific file. 28 | /// 29 | /// Details about the launch request and process. 30 | protected override void OnLaunched(LaunchActivatedEventArgs e) 31 | { 32 | 33 | #if DEBUG 34 | if (System.Diagnostics.Debugger.IsAttached) 35 | { 36 | DebugSettings.EnableFrameRateCounter = true; 37 | } 38 | #endif 39 | 40 | Frame rootFrame = Window.Current.Content as Frame; 41 | 42 | // Do not repeat app initialization when the Window already has content, 43 | // just ensure that the window is active 44 | if (rootFrame == null) 45 | { 46 | // Create a Frame to act as the navigation context and navigate to the first page 47 | rootFrame = new Frame(); 48 | 49 | rootFrame.NavigationFailed += OnNavigationFailed; 50 | 51 | if (e.PreviousExecutionState == ApplicationExecutionState.Terminated) 52 | { 53 | //TODO: Load state from previously suspended application 54 | } 55 | 56 | // Place the frame in the current Window 57 | Window.Current.Content = rootFrame; 58 | } 59 | 60 | if (rootFrame.Content == null) 61 | { 62 | // When the navigation stack isn't restored navigate to the first page, 63 | // configuring the new page by passing required information as a navigation 64 | // parameter 65 | rootFrame.Navigate(typeof(MainPage), e.Arguments); 66 | } 67 | // Ensure the current window is active 68 | Window.Current.Activate(); 69 | } 70 | 71 | /// 72 | /// Invoked when Navigation to a certain page fails 73 | /// 74 | /// The Frame which failed navigation 75 | /// Details about the navigation failure 76 | void OnNavigationFailed(object sender, NavigationFailedEventArgs e) 77 | { 78 | throw new Exception("Failed to load Page " + e.SourcePageType.FullName); 79 | } 80 | 81 | /// 82 | /// Invoked when application execution is being suspended. Application state is saved 83 | /// without knowing whether the application will be terminated or resumed with the contents 84 | /// of memory still intact. 85 | /// 86 | /// The source of the suspend request. 87 | /// Details about the suspend request. 88 | private void OnSuspending(object sender, SuspendingEventArgs e) 89 | { 90 | var deferral = e.SuspendingOperation.GetDeferral(); 91 | //TODO: Save application state and stop any background activity 92 | deferral.Complete(); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /RadialMenuDemo/Assets/LockScreenLogo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatalystCode/radial-menu/134efb249bf6fa823d87b767192074ab11556ad1/RadialMenuDemo/Assets/LockScreenLogo.scale-200.png -------------------------------------------------------------------------------- /RadialMenuDemo/Assets/SplashScreen.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatalystCode/radial-menu/134efb249bf6fa823d87b767192074ab11556ad1/RadialMenuDemo/Assets/SplashScreen.scale-200.png -------------------------------------------------------------------------------- /RadialMenuDemo/Assets/Square150x150Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatalystCode/radial-menu/134efb249bf6fa823d87b767192074ab11556ad1/RadialMenuDemo/Assets/Square150x150Logo.scale-200.png -------------------------------------------------------------------------------- /RadialMenuDemo/Assets/Square44x44Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatalystCode/radial-menu/134efb249bf6fa823d87b767192074ab11556ad1/RadialMenuDemo/Assets/Square44x44Logo.scale-200.png -------------------------------------------------------------------------------- /RadialMenuDemo/Assets/Square44x44Logo.targetsize-24_altform-unplated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatalystCode/radial-menu/134efb249bf6fa823d87b767192074ab11556ad1/RadialMenuDemo/Assets/Square44x44Logo.targetsize-24_altform-unplated.png -------------------------------------------------------------------------------- /RadialMenuDemo/Assets/StoreLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatalystCode/radial-menu/134efb249bf6fa823d87b767192074ab11556ad1/RadialMenuDemo/Assets/StoreLogo.png -------------------------------------------------------------------------------- /RadialMenuDemo/Assets/Wide310x150Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatalystCode/radial-menu/134efb249bf6fa823d87b767192074ab11556ad1/RadialMenuDemo/Assets/Wide310x150Logo.scale-200.png -------------------------------------------------------------------------------- /RadialMenuDemo/Assets/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatalystCode/radial-menu/134efb249bf6fa823d87b767192074ab11556ad1/RadialMenuDemo/Assets/background.jpg -------------------------------------------------------------------------------- /RadialMenuDemo/Assets/background2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatalystCode/radial-menu/134efb249bf6fa823d87b767192074ab11556ad1/RadialMenuDemo/Assets/background2.jpg -------------------------------------------------------------------------------- /RadialMenuDemo/Assets/button_blue_stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatalystCode/radial-menu/134efb249bf6fa823d87b767192074ab11556ad1/RadialMenuDemo/Assets/button_blue_stop.png -------------------------------------------------------------------------------- /RadialMenuDemo/Icons/Chisel Tip Marker-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatalystCode/radial-menu/134efb249bf6fa823d87b767192074ab11556ad1/RadialMenuDemo/Icons/Chisel Tip Marker-50.png -------------------------------------------------------------------------------- /RadialMenuDemo/Icons/Cursor-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatalystCode/radial-menu/134efb249bf6fa823d87b767192074ab11556ad1/RadialMenuDemo/Icons/Cursor-50.png -------------------------------------------------------------------------------- /RadialMenuDemo/Icons/Delete-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatalystCode/radial-menu/134efb249bf6fa823d87b767192074ab11556ad1/RadialMenuDemo/Icons/Delete-50.png -------------------------------------------------------------------------------- /RadialMenuDemo/Icons/Hand Cursor-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatalystCode/radial-menu/134efb249bf6fa823d87b767192074ab11556ad1/RadialMenuDemo/Icons/Hand Cursor-50.png -------------------------------------------------------------------------------- /RadialMenuDemo/Icons/Icons License.txt: -------------------------------------------------------------------------------- 1 | Icons provided by https://icons8.com - free for both personal and commercial use, but a link has to be provided. For more information and the actual license, please see https://icons8.com/license/. -------------------------------------------------------------------------------- /RadialMenuDemo/Icons/Polygon-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatalystCode/radial-menu/134efb249bf6fa823d87b767192074ab11556ad1/RadialMenuDemo/Icons/Polygon-50.png -------------------------------------------------------------------------------- /RadialMenuDemo/Icons/Strikethrough-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatalystCode/radial-menu/134efb249bf6fa823d87b767192074ab11556ad1/RadialMenuDemo/Icons/Strikethrough-50.png -------------------------------------------------------------------------------- /RadialMenuDemo/Icons/Text Cursor-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatalystCode/radial-menu/134efb249bf6fa823d87b767192074ab11556ad1/RadialMenuDemo/Icons/Text Cursor-50.png -------------------------------------------------------------------------------- /RadialMenuDemo/Icons/Underline-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatalystCode/radial-menu/134efb249bf6fa823d87b767192074ab11556ad1/RadialMenuDemo/Icons/Underline-50.png -------------------------------------------------------------------------------- /RadialMenuDemo/Icons/opacity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatalystCode/radial-menu/134efb249bf6fa823d87b767192074ab11556ad1/RadialMenuDemo/Icons/opacity.png -------------------------------------------------------------------------------- /RadialMenuDemo/Icons/stroke.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CatalystCode/radial-menu/134efb249bf6fa823d87b767192074ab11556ad1/RadialMenuDemo/Icons/stroke.png -------------------------------------------------------------------------------- /RadialMenuDemo/MainPage.xaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /RadialMenuDemo/MainPage.xaml.cs: -------------------------------------------------------------------------------- 1 | using Windows.UI.Xaml.Controls; 2 | using RadialMenuControl.UserControl; 3 | using RadialMenuControl.Components; 4 | using System.Diagnostics; 5 | using Windows.UI.Xaml.Media.Imaging; 6 | using System; 7 | using System.Collections.Generic; 8 | using Windows.UI; 9 | using Windows.UI.Xaml.Input; 10 | using Windows.UI.Xaml.Media; 11 | using Windows.UI.Xaml; 12 | 13 | namespace RadialMenuDemo 14 | { 15 | public sealed partial class MainPage : Page 16 | { 17 | public MainPage() 18 | { 19 | 20 | var button1 = new RadialMenuButton 21 | { 22 | Label = "Melbourne", 23 | Icon = "🌏", 24 | Type = RadialMenuButton.ButtonType.Simple 25 | 26 | }; 27 | 28 | button1.InnerArcPressed += Button1InnerArcPressed; 29 | 30 | button1.Submenu = new RadialMenu {CenterButtonIcon = ""}; 31 | 32 | var button11 = new RadialMenuButton 33 | { 34 | Label = "Radio #1", 35 | Icon = "🌍", 36 | Type = RadialMenuButton.ButtonType.Radio 37 | }; 38 | var button12 = new RadialMenuButton 39 | { 40 | Label = "Radio #2", 41 | Icon = "🌞", 42 | Type = RadialMenuButton.ButtonType.Radio 43 | }; 44 | var button13 = new RadialMenuButton 45 | { 46 | Label = "Radio #3", 47 | Icon = "🍁", 48 | Type = RadialMenuButton.ButtonType.Radio 49 | }; 50 | var button14 = new RadialMenuButton 51 | { 52 | Label = "Radio #4", 53 | Icon = "🍱", 54 | Type = RadialMenuButton.ButtonType.Radio, 55 | Submenu = new RadialMenu() 56 | }; 57 | 58 | button1.Submenu.AddButton(button11); 59 | button1.Submenu.AddButton(button12); 60 | button1.Submenu.AddButton(button13); 61 | button1.Submenu.AddButton(button14); 62 | 63 | var button2 = new RadialMenuButton 64 | { 65 | Label = "Image", 66 | IconImage = new BitmapImage(new Uri("ms-appx:///Assets/button_blue_stop.png")), 67 | Type = RadialMenuButton.ButtonType.Simple 68 | }; 69 | 70 | List fontRanges = new List(); 71 | fontRanges.Add((new MeterRangeInterval 72 | { 73 | StartValue = 5, 74 | EndValue = 11, 75 | TickInterval = 1, 76 | StartDegree = 0, 77 | EndDegree = 90 78 | })); 79 | fontRanges.Add((new MeterRangeInterval 80 | { 81 | StartValue = 11, 82 | EndValue = 12, 83 | TickInterval = 1, 84 | StartDegree = 90, 85 | EndDegree = 110 86 | })); 87 | fontRanges.Add((new MeterRangeInterval 88 | { 89 | StartValue = 12, 90 | EndValue = 28, 91 | TickInterval = 2, 92 | StartDegree = 110, 93 | EndDegree = 250 94 | })); 95 | fontRanges.Add((new MeterRangeInterval 96 | { 97 | StartValue = 28, 98 | EndValue = 36, 99 | TickInterval = 8, 100 | StartDegree = 250, 101 | EndDegree = 280 102 | })); 103 | fontRanges.Add((new MeterRangeInterval 104 | { 105 | StartValue = 36, 106 | EndValue = 48, 107 | TickInterval = 12, 108 | StartDegree = 280, 109 | EndDegree = 300 110 | })); 111 | fontRanges.Add((new MeterRangeInterval 112 | { 113 | StartValue = 48, 114 | EndValue = 72, 115 | TickInterval = 24, 116 | StartDegree = 300, 117 | EndDegree = 320 118 | })); 119 | var button3 = new RadialMenuButton 120 | { 121 | Label = "Meter", 122 | Icon = "🍜", 123 | CustomMenu = new MeterSubMenu() 124 | { 125 | MeterEndValue = 72, 126 | MeterStartValue = 5, 127 | MeterRadius = 70, 128 | StartAngle = -90, 129 | MeterPointerLength = 70, 130 | RoundSelectValue = true, 131 | OuterEdgeBrush = new SolidColorBrush(Colors.DarkGreen), 132 | Intervals = fontRanges 133 | } 134 | }; 135 | 136 | 137 | 138 | (button3.CustomMenu as MeterSubMenu).ValueSelected += MeterMenu_ValueSelected; 139 | (button3.CustomMenu as MeterSubMenu).LockedValue = 60; 140 | button3.CustomMenu.CenterButtonIcon = ""; 141 | 142 | var button4 = new RadialMenuButton 143 | { 144 | Label = "Radio", 145 | Icon = "🐙", 146 | Type = RadialMenuButton.ButtonType.Radio 147 | }; 148 | 149 | var button5 = new RadialMenuButton 150 | { 151 | Label = "Radio #1", 152 | Icon = "🐉", 153 | Type = RadialMenuButton.ButtonType.Radio 154 | }; 155 | 156 | var button6 = new RadialMenuButton 157 | { 158 | Label = "Toggle #1", 159 | Icon = "🎉", 160 | Type = RadialMenuButton.ButtonType.Toggle 161 | }; 162 | 163 | RadialMenuButton button7 = new RadialMenuButton 164 | { 165 | Label = "Custom", 166 | Icon = "💸", 167 | Type = RadialMenuButton.ButtonType.Custom, 168 | Value = "12" 169 | }; 170 | button7.ValueChanged += Button7_ValueChanged; 171 | 172 | var button8 = new RadialMenuButton 173 | { 174 | Label = "List", 175 | Icon = "💸" 176 | }; 177 | button8.CustomMenu = new ListSubMenu(); 178 | (button8.CustomMenu as ListSubMenu).ValueSelected += ListSubMenu_ValueSelected; 179 | button8.CustomMenu.CenterButtonIcon = ""; 180 | List listMenuItems = new List(); 181 | var button81 = new RadialMenuButton 182 | { 183 | Label = "Arial", 184 | Type = RadialMenuButton.ButtonType.Radio, 185 | Value = "Arial" 186 | }; 187 | var button82 = new RadialMenuButton 188 | { 189 | Label = "Calibri", 190 | Type = RadialMenuButton.ButtonType.Radio, 191 | Value = "Calibri" 192 | }; 193 | var button83 = new RadialMenuButton 194 | { 195 | Label = "Courier", 196 | Type = RadialMenuButton.ButtonType.Radio, 197 | Value = "Courier" 198 | }; 199 | var button84 = new RadialMenuButton 200 | { 201 | Label = "Times New Roman", 202 | Type = RadialMenuButton.ButtonType.Radio, 203 | Value = "Times New Roman" 204 | }; 205 | listMenuItems.Add(button81); 206 | listMenuItems.Add(button82); 207 | listMenuItems.Add(button83); 208 | listMenuItems.Add(button84); 209 | (button8.CustomMenu as ListSubMenu).ListMenuItems = listMenuItems; 210 | 211 | InitializeComponent(); 212 | MyRadialMenu.AddButton(button1); 213 | MyRadialMenu.AddButton(button2); 214 | MyRadialMenu.AddButton(button3); 215 | MyRadialMenu.AddButton(button4); 216 | MyRadialMenu.AddButton(button5); 217 | MyRadialMenu.AddButton(button6); 218 | MyRadialMenu.AddButton(button7); 219 | MyRadialMenu.AddButton(button8); 220 | 221 | LayoutRoot.DataContext = this; 222 | MyRadialMenu.PropertyChanged += RadialMenu_PropertyChanged; 223 | } 224 | 225 | private void Button7_ValueChanged(object sender, RoutedEventArgs args) 226 | { 227 | Debug.WriteLine("User updated value to: " + (sender as RadialMenuButton)?.Value); 228 | } 229 | 230 | private void ListSubMenu_ValueSelected(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs args) 231 | { 232 | Debug.WriteLine("User selected value: " + (sender as ListSubMenu)?.SelectedValue); 233 | } 234 | private void MeterMenu_ValueSelected(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs args) 235 | { 236 | Debug.WriteLine("User selected value: " + (sender as MeterSubMenu)?.SelectedValue); 237 | } 238 | 239 | private void Button1InnerArcPressed(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e) 240 | { 241 | this.Frame.Navigate(typeof (Melbourne)); 242 | } 243 | 244 | private static void RadialMenu_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) 245 | { 246 | switch (e.PropertyName) 247 | { 248 | case "SelectedItemValue": 249 | { 250 | Debug.WriteLine("Hello!"); 251 | break; 252 | } 253 | } 254 | } 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /RadialMenuDemo/Melbourne.xaml: -------------------------------------------------------------------------------- 1 |  11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | Press Shift for Access Keys 30 | 31 | 32 | 33 | 34 | 35 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 83 | 84 | 85 | 86 | 87 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 120 | 121 | 122 | 123 | 124 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | -------------------------------------------------------------------------------- /RadialMenuDemo/Melbourne.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Windows.System; 3 | using Windows.UI; 4 | using Windows.UI.Core; 5 | using Windows.UI.Xaml.Controls; 6 | using Windows.UI.Xaml.Input; 7 | using Windows.UI.Xaml.Media; 8 | using RadialMenuControl.Components; 9 | using RadialMenuControl.Extensions; 10 | using RadialMenuControl.UserControl; 11 | using Windows.UI.Xaml; 12 | 13 | // The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238 14 | 15 | namespace RadialMenuDemo 16 | { 17 | /// 18 | /// An empty page that can be used on its own or navigated to within a Frame. 19 | /// 20 | public sealed partial class Melbourne : Page 21 | { 22 | public static readonly DependencyProperty MyLockedValueProperty = 23 | DependencyProperty.Register("MyLockedValue", typeof(double), typeof(Melbourne), new PropertyMetadata((double)0)); 24 | 25 | public double MyLockedValue 26 | { 27 | set { SetValue(MyLockedValueProperty, value); } 28 | get { return (double)GetValue(MyLockedValueProperty); } 29 | } 30 | 31 | private readonly Dictionary _buttonColors = new Dictionary() 32 | { 33 | {"OuterNormalColor", Color.FromArgb(255, 56, 55, 57)}, 34 | {"OuterHoverColor", Color.FromArgb(255, 70, 102, 102)}, 35 | {"OuterDisabledColor", Color.FromArgb(255, 96, 139, 139)}, 36 | {"InnerNormalColor", Colors.White}, 37 | {"InnerHoverColor", Color.FromArgb(255, 227, 235, 235)}, 38 | {"InnerDisabledColor", Color.FromArgb(255, 227, 235, 235)}, 39 | {"InnerReleasedColor", Color.FromArgb(255, 227, 235, 235)}, 40 | }; 41 | 42 | private List opacityMeterIntervals = new List() 43 | { 44 | new MeterRangeInterval 45 | { 46 | StartValue = 0, 47 | EndValue = 50, 48 | TickInterval = 5, 49 | StartDegree = 0, 50 | EndDegree = 180 51 | }, 52 | new MeterRangeInterval 53 | { 54 | StartValue = 50, 55 | EndValue = 100, 56 | TickInterval = 5, 57 | StartDegree = 180, 58 | EndDegree = 355 59 | }, 60 | }; 61 | 62 | private List scaledMeterIntervals = new List() 63 | { 64 | new MeterRangeInterval 65 | { 66 | StartValue = 5, 67 | EndValue = 11, 68 | TickInterval = 1, 69 | StartDegree = 0, 70 | EndDegree = 90 71 | }, 72 | new MeterRangeInterval 73 | { 74 | StartValue = 11, 75 | EndValue = 12, 76 | TickInterval = 1, 77 | StartDegree = 90, 78 | EndDegree = 110 79 | }, 80 | new MeterRangeInterval 81 | { 82 | StartValue = 12, 83 | EndValue = 28, 84 | TickInterval = 2, 85 | StartDegree = 110, 86 | EndDegree = 250 87 | }, 88 | new MeterRangeInterval 89 | { 90 | StartValue = 28, 91 | EndValue = 36, 92 | TickInterval = 8, 93 | StartDegree = 250, 94 | EndDegree = 280 95 | }, 96 | new MeterRangeInterval 97 | { 98 | StartValue = 36, 99 | EndValue = 48, 100 | TickInterval = 12, 101 | StartDegree = 280, 102 | EndDegree = 300 103 | }, 104 | new MeterRangeInterval 105 | { 106 | StartValue = 48, 107 | EndValue = 72, 108 | TickInterval = 24, 109 | StartDegree = 300, 110 | EndDegree = 320 111 | } 112 | }; 113 | 114 | private RadialMenuButton CreateColorRadialMenuButton(Color sourceColor) 115 | { 116 | return new RadialMenuButton 117 | { 118 | InnerNormalColor = sourceColor, 119 | InnerHoverColor = sourceColor.Lighten(), 120 | InnerReleasedColor = sourceColor, 121 | InnerTappedColor = sourceColor, 122 | OuterNormalColor = _buttonColors["OuterNormalColor"], 123 | OuterHoverColor = _buttonColors["OuterHoverColor"], 124 | OuterDisabledColor = _buttonColors["OuterDisabledColor"] 125 | }; 126 | } 127 | 128 | private RadialMenuButton CreateColorRadialMenuButtonWithSubMenu(Color sourceColor, double subMenuButtonCount) 129 | { 130 | var colorButton = CreateColorRadialMenuButton(sourceColor); 131 | colorButton.Submenu = new RadialMenu(); 132 | 133 | for (var i = 0; i < subMenuButtonCount; i++) 134 | { 135 | var lightenFactor = (float) i/10; 136 | colorButton.Submenu.AddButton(CreateColorRadialMenuButton(sourceColor.Lighten(lightenFactor))); 137 | } 138 | 139 | return colorButton; 140 | } 141 | 142 | public Melbourne() 143 | { 144 | DataContext = this; 145 | this.InitializeComponent(); 146 | 147 | MyLockedValue = 20; 148 | 149 | Pen1Submenu.AddButton(CreateColorRadialMenuButtonWithSubMenu(Colors.Black, 10)); 150 | Pen1Submenu.AddButton(CreateColorRadialMenuButtonWithSubMenu(Colors.Red, 10)); 151 | Pen1Submenu.AddButton(CreateColorRadialMenuButtonWithSubMenu(Colors.Blue, 10)); 152 | Pen1Submenu.AddButton(CreateColorRadialMenuButtonWithSubMenu(Colors.Green, 10)); 153 | Pen1Submenu.AddButton(CreateColorRadialMenuButtonWithSubMenu(Colors.Yellow, 10)); 154 | 155 | Pen2Submenu.AddButton(CreateColorRadialMenuButtonWithSubMenu(Colors.Black, 10)); 156 | Pen2Submenu.AddButton(CreateColorRadialMenuButtonWithSubMenu(Colors.Red, 10)); 157 | Pen2Submenu.AddButton(CreateColorRadialMenuButtonWithSubMenu(Colors.Blue, 10)); 158 | Pen2Submenu.AddButton(CreateColorRadialMenuButtonWithSubMenu(Colors.Green, 10)); 159 | Pen2Submenu.AddButton(CreateColorRadialMenuButtonWithSubMenu(Colors.Yellow, 10)); 160 | 161 | Pen1StrokeMenu.Intervals = scaledMeterIntervals; 162 | Pen1OpacityMenu.Intervals = opacityMeterIntervals; 163 | Pen2StrokeMenu.Intervals = scaledMeterIntervals; 164 | Pen2OpacityMenu.Intervals = opacityMeterIntervals; 165 | 166 | CoreWindow.GetForCurrentThread().KeyDown += Melbourne_KeyDown; 167 | CoreWindow.GetForCurrentThread().KeyUp += Melbourne_KeyUp; ; 168 | } 169 | 170 | private void Melbourne_KeyUp(CoreWindow sender, KeyEventArgs args) 171 | { 172 | switch (args.VirtualKey) 173 | { 174 | case VirtualKey.Shift: 175 | MyRadialMenu.HideAccessKeyTooltips(); 176 | break; 177 | case VirtualKey.P: 178 | MyRadialMenu.ClickInnerRadialMenuButton(Pan); 179 | break; 180 | case VirtualKey.O: 181 | MyRadialMenu.ClickOuterRadialMenuButton(Pan); 182 | break; 183 | case VirtualKey.K: 184 | MyRadialMenu.ClickInnerRadialMenuButton(Pen1); 185 | break; 186 | case VirtualKey.L: 187 | MyRadialMenu.ClickOuterRadialMenuButton(Pen1); 188 | break; 189 | }; 190 | } 191 | 192 | private void Melbourne_KeyDown(CoreWindow sender, KeyEventArgs args) 193 | { 194 | if (args.VirtualKey == VirtualKey.Shift) MyRadialMenu.ShowAccessKeyTooltips(); 195 | } 196 | 197 | private void HighlightRadialMenuOnCenterButtonTapped(object sender, TappedRoutedEventArgs e) 198 | { 199 | var sendingMenu = sender as RadialMenu; 200 | if (sendingMenu != null && sendingMenu.Pie.SelectedItem != null) 201 | { 202 | Highlight.Label = sendingMenu.Pie.SelectedItem.Label; 203 | Highlight.IconImage = sendingMenu.Pie.SelectedItem.IconImage; 204 | } 205 | } 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /RadialMenuDemo/Package.appxmanifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 8 | 9 | 13 | 14 | 15 | 16 | 17 | RadialMenuDemo 18 | feriese 19 | Assets\StoreLogo.png 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /RadialMenuDemo/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("RadialMenuDemo")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("RadialMenuDemo")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Version information for an assembly consists of the following four values: 18 | // 19 | // Major Version 20 | // Minor Version 21 | // Build Number 22 | // Revision 23 | // 24 | // You can specify all the values or you can default the Build and Revision Numbers 25 | // by using the '*' as shown below: 26 | // [assembly: AssemblyVersion("1.0.*")] 27 | [assembly: AssemblyVersion("1.0.0.0")] 28 | [assembly: AssemblyFileVersion("1.0.0.0")] 29 | [assembly: ComVisible(false)] -------------------------------------------------------------------------------- /RadialMenuDemo/Properties/Default.rd.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /RadialMenuDemo/RadialMenuDemo.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | x86 7 | {F2169718-9CB4-4A32-BCAB-92E254B74213} 8 | AppContainerExe 9 | Properties 10 | RadialMenuDemo 11 | RadialMenuDemo 12 | en-US 13 | UAP 14 | 10.0.10240.0 15 | 10.0.10240.0 16 | 14 17 | true 18 | 512 19 | {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 20 | RadialMenuDemo_TemporaryKey.pfx 21 | 22 | 23 | true 24 | bin\ARM\Debug\ 25 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 26 | ;2008 27 | full 28 | ARM 29 | false 30 | prompt 31 | true 32 | 33 | 34 | bin\ARM\Release\ 35 | TRACE;NETFX_CORE;WINDOWS_UWP 36 | true 37 | ;2008 38 | pdbonly 39 | ARM 40 | false 41 | prompt 42 | true 43 | true 44 | 45 | 46 | true 47 | bin\x64\Debug\ 48 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 49 | ;2008 50 | full 51 | x64 52 | false 53 | prompt 54 | true 55 | 56 | 57 | bin\x64\Release\ 58 | TRACE;NETFX_CORE;WINDOWS_UWP 59 | true 60 | ;2008 61 | pdbonly 62 | x64 63 | false 64 | prompt 65 | true 66 | true 67 | 68 | 69 | true 70 | bin\x86\Debug\ 71 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 72 | ;2008 73 | full 74 | x86 75 | false 76 | prompt 77 | true 78 | 79 | 80 | bin\x86\Release\ 81 | TRACE;NETFX_CORE;WINDOWS_UWP 82 | true 83 | ;2008 84 | pdbonly 85 | x86 86 | false 87 | prompt 88 | true 89 | true 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | App.xaml 98 | 99 | 100 | MainPage.xaml 101 | 102 | 103 | Melbourne.xaml 104 | 105 | 106 | 107 | 108 | 109 | Designer 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | MSBuild:Compile 140 | Designer 141 | 142 | 143 | MSBuild:Compile 144 | Designer 145 | 146 | 147 | Designer 148 | MSBuild:Compile 149 | 150 | 151 | 152 | 153 | {a10ee2d6-d98a-4074-8b29-19a7702f9493} 154 | RadialMenuControl 155 | 156 | 157 | 158 | 14.0 159 | 160 | 161 | 168 | -------------------------------------------------------------------------------- /RadialMenuDemo/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "Microsoft.NETCore.UniversalWindowsPlatform": "5.0.0" 4 | }, 5 | "frameworks": { 6 | "uap10.0": {} 7 | }, 8 | "runtimes": { 9 | "win10-arm": {}, 10 | "win10-arm-aot": {}, 11 | "win10-x86": {}, 12 | "win10-x86-aot": {}, 13 | "win10-x64": {}, 14 | "win10-x64-aot": {} 15 | } 16 | } -------------------------------------------------------------------------------- /RadialMenuUITests/CodedUITest1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text.RegularExpressions; 4 | using System.Windows.Input; 5 | using System.Windows.Forms; 6 | using System.Drawing; 7 | using Microsoft.VisualStudio.TestTools.UITesting; 8 | using Microsoft.VisualStudio.TestTools.UnitTesting; 9 | using Microsoft.VisualStudio.TestTools.UITest.Extension; 10 | using Keyboard = Microsoft.VisualStudio.TestTools.UITesting.Keyboard; 11 | 12 | 13 | namespace RadialMenuUITests 14 | { 15 | /// 16 | /// Summary description for CodedUITest1 17 | /// 18 | [CodedUITest] 19 | public class CodedUITest1 20 | { 21 | public CodedUITest1() 22 | { 23 | } 24 | 25 | [TestMethod] 26 | public void CodedUITestMethod1() 27 | { 28 | // To generate code for this test, select "Generate Code for Coded UI Test" from the shortcut menu and select one of the menu items. 29 | } 30 | 31 | #region Additional test attributes 32 | 33 | // You can use the following additional attributes as you write your tests: 34 | 35 | ////Use TestInitialize to run code before running each test 36 | //[TestInitialize()] 37 | //public void MyTestInitialize() 38 | //{ 39 | // // To generate code for this test, select "Generate Code for Coded UI Test" from the shortcut menu and select one of the menu items. 40 | //} 41 | 42 | ////Use TestCleanup to run code after each test has run 43 | //[TestCleanup()] 44 | //public void MyTestCleanup() 45 | //{ 46 | // // To generate code for this test, select "Generate Code for Coded UI Test" from the shortcut menu and select one of the menu items. 47 | //} 48 | 49 | #endregion 50 | 51 | /// 52 | ///Gets or sets the test context which provides 53 | ///information about and functionality for the current test run. 54 | /// 55 | public TestContext TestContext 56 | { 57 | get 58 | { 59 | return testContextInstance; 60 | } 61 | set 62 | { 63 | testContextInstance = value; 64 | } 65 | } 66 | private TestContext testContextInstance; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /RadialMenuUITests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("RadialMenuUITests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("RadialMenuUITests")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("f1243e77-61be-4f68-afd6-41474743a11c")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /RadialMenuUITests/RadialMenuUITests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 7 | 8 | 2.0 9 | {F1243E77-61BE-4F68-AFD6-41474743A11C} 10 | Library 11 | Properties 12 | RadialMenuUITests 13 | RadialMenuUITests 14 | v4.5.2 15 | 512 16 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 17 | 10.0 18 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 19 | $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages 20 | True 21 | CodedUITest 22 | 23 | 24 | true 25 | full 26 | false 27 | bin\Debug\ 28 | DEBUG;TRACE 29 | prompt 30 | 4 31 | 32 | 33 | pdbonly 34 | true 35 | bin\Release\ 36 | TRACE 37 | prompt 38 | 4 39 | 40 | 41 | 42 | False 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | False 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | False 64 | 65 | 66 | False 67 | 68 | 69 | False 70 | 71 | 72 | False 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 93 | --------------------------------------------------------------------------------