├── .gitignore ├── Common ├── Borders │ ├── ClippingBorder.cs │ ├── InnerGlowBorder.cs │ └── OuterGlowBorder.cs ├── DropShadowTextBlock.cs ├── HorizontalToggleSwitch.cs ├── Icons │ ├── ToggleSwitch.HorizontalToggleSwitch.Expression.Large.png │ ├── ToggleSwitch.HorizontalToggleSwitch.Expression.Small.png │ └── ToggleSwitch.HorizontalToggleSwitch.VisualStudio.bmp ├── ToggleSwitchBase.cs ├── Utils │ ├── ActualSizePropertyProxy.cs │ ├── CornerRadiusValueConverter.cs │ ├── HelperExtensions.cs │ └── ScalarValueConverter.cs └── VerticalToggleSwitch.cs ├── README.md ├── WPF ├── Demo │ ├── App.xaml │ ├── App.xaml.cs │ ├── Assets │ │ ├── Brushes.xaml │ │ ├── Fonts.xaml │ │ ├── Styles.xaml │ │ └── ToggleSwitchStyles.xaml │ ├── Demo.csproj │ ├── DemoViewModel.cs │ ├── Images │ │ ├── Knob.png │ │ ├── lightOff.png │ │ └── lightOn.png │ ├── MainWindow.xaml │ ├── MainWindow.xaml.cs │ ├── Properties │ │ ├── AssemblyInfo.cs │ │ ├── Resources.Designer.cs │ │ ├── Resources.resx │ │ ├── Settings.Designer.cs │ │ └── Settings.settings │ ├── Views │ │ ├── Basics.xaml │ │ ├── Basics.xaml.cs │ │ ├── Events.xaml │ │ ├── Events.xaml.cs │ │ ├── Extending.xaml │ │ ├── Extending.xaml.cs │ │ ├── Styling.xaml │ │ └── Styling.xaml.cs │ └── app.config ├── ToggleSwitch.sln └── ToggleSwitch │ ├── Properties │ └── AssemblyInfo.cs │ ├── Themes │ └── Generic.xaml │ ├── ToggleSwitch.csproj │ └── ToggleSwitchKey.snk └── icon.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Build Folders (you can keep bin if you'd like, to store dlls and pdbs) 2 | bin 3 | obj 4 | 5 | # mstest test results 6 | TestResults 7 | 8 | # user files 9 | *.user 10 | *.suo 11 | *ReSharper.* -------------------------------------------------------------------------------- /Common/Borders/ClippingBorder.cs: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------- 2 | // 3 | // (c) 2008 Microsoft Corporation. All rights reserved. 4 | // This source is subject to the Microsoft Public License. 5 | // See http://www.microsoft.com/resources/sharedsource/licensingbasics/sharedsourcelicenses.mspx. 6 | // 7 | // 07-Oct-2008 8 | // Martin Grayson 9 | // A border that clips its contents. 10 | //----------------------------------------------------------------------- 11 | using System; 12 | using System.ComponentModel; 13 | using System.Windows; 14 | using System.Windows.Controls; 15 | using System.Windows.Media; 16 | 17 | namespace ToggleSwitch.Borders 18 | { 19 | /// 20 | /// A border that clips its contents. 21 | /// 22 | public class ClippingBorder : ContentControl 23 | { 24 | /// 25 | /// The corner radius property. 26 | /// 27 | public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.Register("CornerRadius", typeof(CornerRadius), 28 | typeof(ClippingBorder), 29 | new PropertyMetadata(CornerRadiusChanged)); 30 | 31 | /// 32 | /// The clip content property. 33 | /// 34 | public static readonly DependencyProperty ClipContentProperty = DependencyProperty.Register("ClipContent", typeof(bool), typeof(ClippingBorder), 35 | new PropertyMetadata(ClipContentChanged)); 36 | 37 | /// 38 | /// Stores the main border. 39 | /// 40 | private Border _border; 41 | 42 | /// 43 | /// Stores the clip responsible for clipping the bottom left corner. 44 | /// 45 | private RectangleGeometry _bottomLeftClip; 46 | 47 | /// 48 | /// Stores the bottom left content control. 49 | /// 50 | private ContentControl _bottomLeftContentControl; 51 | 52 | /// 53 | /// Stores the clip responsible for clipping the bottom right corner. 54 | /// 55 | private RectangleGeometry _bottomRightClip; 56 | 57 | /// 58 | /// Stores the bottom right content control. 59 | /// 60 | private ContentControl _bottomRightContentControl; 61 | 62 | /// 63 | /// Stores the clip responsible for clipping the top left corner. 64 | /// 65 | private RectangleGeometry _topLeftClip; 66 | 67 | /// 68 | /// Stores the top left content control. 69 | /// 70 | private ContentControl _topLeftContentControl; 71 | 72 | /// 73 | /// Stores the clip responsible for clipping the top right corner. 74 | /// 75 | private RectangleGeometry _topRightClip; 76 | 77 | /// 78 | /// Stores the top right content control. 79 | /// 80 | private ContentControl _topRightContentControl; 81 | 82 | /// 83 | /// ClippingBorder constructor. 84 | /// 85 | public ClippingBorder() 86 | { 87 | DefaultStyleKey = typeof(ClippingBorder); 88 | SizeChanged += ClippingBorderSizeChanged; 89 | } 90 | 91 | /// 92 | /// Gets or sets the border corner radius. 93 | /// This is a thickness, as there is a problem parsing CornerRadius types. 94 | /// 95 | [Category("Appearance"), Description("Sets the corner radius on the border.")] 96 | public CornerRadius CornerRadius 97 | { 98 | get 99 | { 100 | return (CornerRadius)GetValue(CornerRadiusProperty); 101 | } 102 | 103 | set 104 | { 105 | SetValue(CornerRadiusProperty, value); 106 | } 107 | } 108 | 109 | /// 110 | /// Gets or sets a value indicating whether the content is clipped. 111 | /// 112 | [Category("Appearance"), Description("Sets whether the content is clipped or not.")] 113 | public bool ClipContent 114 | { 115 | get 116 | { 117 | return (bool)GetValue(ClipContentProperty); 118 | } 119 | set 120 | { 121 | SetValue(ClipContentProperty, value); 122 | } 123 | } 124 | 125 | /// 126 | /// Gets the UI elements out of the template. 127 | /// 128 | public override void OnApplyTemplate() 129 | { 130 | base.OnApplyTemplate(); 131 | 132 | _border = GetTemplateChild("PART_Border") as Border; 133 | _topLeftContentControl = GetTemplateChild("PART_TopLeftContentControl") as ContentControl; 134 | _topRightContentControl = GetTemplateChild("PART_TopRightContentControl") as ContentControl; 135 | _bottomRightContentControl = GetTemplateChild("PART_BottomRightContentControl") as ContentControl; 136 | _bottomLeftContentControl = GetTemplateChild("PART_BottomLeftContentControl") as ContentControl; 137 | 138 | if (_topLeftContentControl != null) 139 | { 140 | _topLeftContentControl.SizeChanged += ContentControlSizeChanged; 141 | } 142 | 143 | _topLeftClip = GetTemplateChild("PART_TopLeftClip") as RectangleGeometry; 144 | _topRightClip = GetTemplateChild("PART_TopRightClip") as RectangleGeometry; 145 | _bottomRightClip = GetTemplateChild("PART_BottomRightClip") as RectangleGeometry; 146 | _bottomLeftClip = GetTemplateChild("PART_BottomLeftClip") as RectangleGeometry; 147 | 148 | UpdateClipContent(ClipContent); 149 | 150 | UpdateCornerRadius(CornerRadius); 151 | } 152 | 153 | /// 154 | /// Sets the corner radius. 155 | /// 156 | /// The new corner radius. 157 | internal void UpdateCornerRadius(CornerRadius newCornerRadius) 158 | { 159 | if (_border != null) 160 | { 161 | _border.CornerRadius = newCornerRadius; 162 | } 163 | 164 | if (_topLeftClip != null) 165 | { 166 | _topLeftClip.RadiusX = _topLeftClip.RadiusY = newCornerRadius.TopLeft - (Math.Min(BorderThickness.Left, BorderThickness.Top) / 2); 167 | } 168 | 169 | if (_topRightClip != null) 170 | { 171 | _topRightClip.RadiusX = _topRightClip.RadiusY = newCornerRadius.TopRight - (Math.Min(BorderThickness.Top, BorderThickness.Right) / 2); 172 | } 173 | 174 | if (_bottomRightClip != null) 175 | { 176 | _bottomRightClip.RadiusX = _bottomRightClip.RadiusY = newCornerRadius.BottomRight - (Math.Min(BorderThickness.Right, BorderThickness.Bottom) / 2); 177 | } 178 | 179 | if (_bottomLeftClip != null) 180 | { 181 | _bottomLeftClip.RadiusX = _bottomLeftClip.RadiusY = newCornerRadius.BottomLeft - (Math.Min(BorderThickness.Bottom, BorderThickness.Left) / 2); 182 | } 183 | 184 | UpdateClipSize(new Size(ActualWidth, ActualHeight)); 185 | } 186 | 187 | /// 188 | /// Updates whether the content is clipped. 189 | /// 190 | /// Whether the content is clipped. 191 | internal void UpdateClipContent(bool clipContent) 192 | { 193 | if (clipContent) 194 | { 195 | if (_topLeftContentControl != null) 196 | { 197 | _topLeftContentControl.Clip = _topLeftClip; 198 | } 199 | 200 | if (_topRightContentControl != null) 201 | { 202 | _topRightContentControl.Clip = _topRightClip; 203 | } 204 | 205 | if (_bottomRightContentControl != null) 206 | { 207 | _bottomRightContentControl.Clip = _bottomRightClip; 208 | } 209 | 210 | if (_bottomLeftContentControl != null) 211 | { 212 | _bottomLeftContentControl.Clip = _bottomLeftClip; 213 | } 214 | 215 | UpdateClipSize(new Size(ActualWidth, ActualHeight)); 216 | } 217 | else 218 | { 219 | if (_topLeftContentControl != null) 220 | { 221 | _topLeftContentControl.Clip = null; 222 | } 223 | 224 | if (_topRightContentControl != null) 225 | { 226 | _topRightContentControl.Clip = null; 227 | } 228 | 229 | if (_bottomRightContentControl != null) 230 | { 231 | _bottomRightContentControl.Clip = null; 232 | } 233 | 234 | if (_bottomLeftContentControl != null) 235 | { 236 | _bottomLeftContentControl.Clip = null; 237 | } 238 | } 239 | } 240 | 241 | /// 242 | /// Updates the corner radius. 243 | /// 244 | /// The clipping border. 245 | /// Dependency Property Changed Event Args 246 | private static void CornerRadiusChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs) 247 | { 248 | var clippingBorder = (ClippingBorder)dependencyObject; 249 | clippingBorder.UpdateCornerRadius((CornerRadius)eventArgs.NewValue); 250 | } 251 | 252 | /// 253 | /// Updates the content clipping. 254 | /// 255 | /// The clipping border. 256 | /// Dependency Property Changed Event Args 257 | private static void ClipContentChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs) 258 | { 259 | var clippingBorder = (ClippingBorder)dependencyObject; 260 | clippingBorder.UpdateClipContent((bool)eventArgs.NewValue); 261 | } 262 | 263 | /// 264 | /// Updates the clips. 265 | /// 266 | /// The clipping border 267 | /// Size Changed Event Args. 268 | private void ClippingBorderSizeChanged(object sender, SizeChangedEventArgs e) 269 | { 270 | if (ClipContent) 271 | { 272 | UpdateClipSize(e.NewSize); 273 | } 274 | } 275 | 276 | /// 277 | /// Updates the clip size. 278 | /// 279 | /// A content control. 280 | /// Size Changed Event Args 281 | private void ContentControlSizeChanged(object sender, SizeChangedEventArgs e) 282 | { 283 | if (ClipContent) 284 | { 285 | UpdateClipSize(new Size(ActualWidth, ActualHeight)); 286 | } 287 | } 288 | 289 | /// 290 | /// Updates the clip size. 291 | /// 292 | /// The control size. 293 | private void UpdateClipSize(Size size) 294 | { 295 | if (size.Width > 0 || size.Height > 0) 296 | { 297 | double contentWidth = Math.Max(0, size.Width - BorderThickness.Left - BorderThickness.Right); 298 | double contentHeight = Math.Max(0, size.Height - BorderThickness.Top - BorderThickness.Bottom); 299 | 300 | if (_topLeftClip != null) 301 | { 302 | _topLeftClip.Rect = new Rect(0, 0, contentWidth + (CornerRadius.TopLeft * 2), contentHeight + (CornerRadius.TopLeft * 2)); 303 | } 304 | 305 | if (_topRightClip != null) 306 | { 307 | _topRightClip.Rect = new Rect(0 - CornerRadius.TopRight, 0, contentWidth + CornerRadius.TopRight, contentHeight + CornerRadius.TopRight); 308 | } 309 | 310 | if (_bottomRightClip != null) 311 | { 312 | _bottomRightClip.Rect = new Rect(0 - CornerRadius.BottomRight, 0 - CornerRadius.BottomRight, contentWidth + CornerRadius.BottomRight, 313 | contentHeight + CornerRadius.BottomRight); 314 | } 315 | 316 | if (_bottomLeftClip != null) 317 | { 318 | _bottomLeftClip.Rect = new Rect(0, 0 - CornerRadius.BottomLeft, contentWidth + CornerRadius.BottomLeft, contentHeight + CornerRadius.BottomLeft); 319 | } 320 | } 321 | } 322 | } 323 | } -------------------------------------------------------------------------------- /Common/Borders/InnerGlowBorder.cs: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------- 2 | // 3 | // (c) 2008 Microsoft Corporation. All rights reserved. 4 | // This source is subject to the Microsoft Public License. 5 | // See http://www.microsoft.com/resources/sharedsource/licensingbasics/sharedsourcelicenses.mspx. 6 | // 7 | // 03-Oct-2008 8 | // Martin Grayson 9 | // A border that also shows an inner glow. 10 | //----------------------------------------------------------------------- 11 | using System; 12 | using System.ComponentModel; 13 | using System.Windows; 14 | using System.Windows.Controls; 15 | using System.Windows.Media; 16 | 17 | namespace ToggleSwitch.Borders 18 | { 19 | /// 20 | /// Content control that draws a glow around its inside. 21 | /// 22 | public class InnerGlowBorder : ContentControl 23 | { 24 | /// 25 | /// The inner glow opacity property. 26 | /// 27 | public static readonly DependencyProperty InnerGlowOpacityProperty = DependencyProperty.Register("InnerGlowOpacity", typeof(double), 28 | typeof(InnerGlowBorder), null); 29 | 30 | /// 31 | /// The inner glow size property. 32 | /// 33 | public static readonly DependencyProperty InnerGlowSizeProperty = DependencyProperty.Register("InnerGlowSize", typeof(Thickness), 34 | typeof(InnerGlowBorder), 35 | new PropertyMetadata(InnerGlowSizeChanged)); 36 | 37 | /// 38 | /// The corner radius property. 39 | /// 40 | public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.Register("CornerRadius", typeof(CornerRadius), 41 | typeof(InnerGlowBorder), null); 42 | 43 | /// 44 | /// The inner glow color. 45 | /// 46 | public static readonly DependencyProperty InnerGlowColorProperty = DependencyProperty.Register("InnerGlowColor", typeof(Color), 47 | typeof(InnerGlowBorder), 48 | new PropertyMetadata( 49 | Colors.Black, 50 | InnerGlowColorChanged)); 51 | 52 | /// 53 | /// The clip content property. 54 | /// 55 | public static readonly DependencyProperty ClipContentProperty = DependencyProperty.Register("ClipContent", typeof(bool), typeof(InnerGlowBorder), null); 56 | 57 | /// 58 | /// The content z-index property. 59 | /// 60 | public static readonly DependencyProperty ContentZIndexProperty = DependencyProperty.Register("ContentZIndex", typeof(int), typeof(InnerGlowBorder), null); 61 | 62 | /// 63 | /// Stores the bottom glow border. 64 | /// 65 | private Border _bottomGlow; 66 | 67 | /// 68 | /// Stores the bottom glow stop 0; 69 | /// 70 | private GradientStop _bottomGlowStop0; 71 | 72 | /// 73 | /// Stores the bottom glow stop 1. 74 | /// 75 | private GradientStop _bottomGlowStop1; 76 | 77 | /// 78 | /// Stores the left glow border. 79 | /// 80 | private Border _leftGlow; 81 | 82 | /// 83 | /// Stores the left glow stop 0; 84 | /// 85 | private GradientStop _leftGlowStop0; 86 | 87 | /// 88 | /// Stores the left glow stop 1; 89 | /// 90 | private GradientStop _leftGlowStop1; 91 | 92 | /// 93 | /// Stores the right glow border. 94 | /// 95 | private Border _rightGlow; 96 | 97 | /// 98 | /// Stores the right glow stop 0; 99 | /// 100 | private GradientStop _rightGlowStop0; 101 | 102 | /// 103 | /// Stores the right glow stop 1. 104 | /// 105 | private GradientStop _rightGlowStop1; 106 | 107 | /// 108 | /// Stores the top glow border. 109 | /// 110 | private Border _topGlow; 111 | 112 | /// 113 | /// Stores the top glow stop 0; 114 | /// 115 | private GradientStop _topGlowStop0; 116 | 117 | /// 118 | /// Stores the top glow stop 1; 119 | /// 120 | private GradientStop _topGlowStop1; 121 | 122 | /// 123 | /// InnerGlowBorder constructor. 124 | /// 125 | public InnerGlowBorder() 126 | { 127 | DefaultStyleKey = typeof(InnerGlowBorder); 128 | } 129 | 130 | /// 131 | /// Gets or sets a value indicating whether the content is clipped. 132 | /// 133 | [Category("Appearance"), Description("Sets whether the content is clipped or not.")] 134 | public bool ClipContent 135 | { 136 | get 137 | { 138 | return (bool)GetValue(ClipContentProperty); 139 | } 140 | set 141 | { 142 | SetValue(ClipContentProperty, value); 143 | } 144 | } 145 | 146 | /// 147 | /// Gets or sets the content z-index. 0 for behind shadow, 1 for in-front. 148 | /// 149 | [Category("Appearance"), Description("Set 0 for behind the shadow, 1 for in front.")] 150 | public int ContentZIndex 151 | { 152 | get 153 | { 154 | return (int)GetValue(ContentZIndexProperty); 155 | } 156 | set 157 | { 158 | SetValue(ContentZIndexProperty, value); 159 | } 160 | } 161 | 162 | /// 163 | /// Gets or sets the inner glow opacity. 164 | /// 165 | [Category("Appearance"), Description("The inner glow opacity.")] 166 | public double InnerGlowOpacity 167 | { 168 | get 169 | { 170 | return (double)GetValue(InnerGlowOpacityProperty); 171 | } 172 | set 173 | { 174 | SetValue(InnerGlowOpacityProperty, value); 175 | } 176 | } 177 | 178 | /// 179 | /// Gets or sets the inner glow color. 180 | /// 181 | [Category("Appearance"), Description("The inner glow color.")] 182 | public Color InnerGlowColor 183 | { 184 | get 185 | { 186 | return (Color)GetValue(InnerGlowColorProperty); 187 | } 188 | 189 | set 190 | { 191 | SetValue(InnerGlowColorProperty, value); 192 | } 193 | } 194 | 195 | /// 196 | /// Gets or sets the inner glow size. 197 | /// 198 | [Category("Appearance"), Description("The inner glow size.")] 199 | public Thickness InnerGlowSize 200 | { 201 | get 202 | { 203 | return (Thickness)GetValue(InnerGlowSizeProperty); 204 | } 205 | 206 | set 207 | { 208 | SetValue(InnerGlowSizeProperty, value); 209 | UpdateGlowSize(value); 210 | } 211 | } 212 | 213 | /// 214 | /// Gets or sets the border corner radius. 215 | /// This is a thickness, as there is a problem parsing CornerRadius types. 216 | /// 217 | [Category("Appearance"), Description("Sets the corner radius on the border.")] 218 | public CornerRadius CornerRadius 219 | { 220 | get 221 | { 222 | return (CornerRadius)GetValue(CornerRadiusProperty); 223 | } 224 | 225 | set 226 | { 227 | SetValue(CornerRadiusProperty, value); 228 | } 229 | } 230 | 231 | /// 232 | /// Gets the template parts out. 233 | /// 234 | public override void OnApplyTemplate() 235 | { 236 | base.OnApplyTemplate(); 237 | 238 | _leftGlow = GetTemplateChild("PART_LeftGlow") as Border; 239 | _topGlow = GetTemplateChild("PART_TopGlow") as Border; 240 | _rightGlow = GetTemplateChild("PART_RightGlow") as Border; 241 | _bottomGlow = GetTemplateChild("PART_BottomGlow") as Border; 242 | 243 | _leftGlowStop0 = GetTemplateChild("PART_LeftGlowStop0") as GradientStop; 244 | _leftGlowStop1 = GetTemplateChild("PART_LeftGlowStop1") as GradientStop; 245 | _topGlowStop0 = GetTemplateChild("PART_TopGlowStop0") as GradientStop; 246 | _topGlowStop1 = GetTemplateChild("PART_TopGlowStop1") as GradientStop; 247 | _rightGlowStop0 = GetTemplateChild("PART_RightGlowStop0") as GradientStop; 248 | _rightGlowStop1 = GetTemplateChild("PART_RightGlowStop1") as GradientStop; 249 | _bottomGlowStop0 = GetTemplateChild("PART_BottomGlowStop0") as GradientStop; 250 | _bottomGlowStop1 = GetTemplateChild("PART_BottomGlowStop1") as GradientStop; 251 | 252 | UpdateGlowColor(InnerGlowColor); 253 | UpdateGlowSize(InnerGlowSize); 254 | } 255 | 256 | /// 257 | /// Updates the inner glow color. 258 | /// 259 | /// The new color. 260 | internal void UpdateGlowColor(Color color) 261 | { 262 | if (_leftGlowStop0 != null) 263 | { 264 | _leftGlowStop0.Color = color; 265 | } 266 | 267 | if (_leftGlowStop1 != null) 268 | { 269 | _leftGlowStop1.Color = Color.FromArgb(0, color.R, color.G, color.B); 270 | } 271 | 272 | if (_topGlowStop0 != null) 273 | { 274 | _topGlowStop0.Color = color; 275 | } 276 | 277 | if (_topGlowStop1 != null) 278 | { 279 | _topGlowStop1.Color = Color.FromArgb(0, color.R, color.G, color.B); 280 | } 281 | 282 | if (_rightGlowStop0 != null) 283 | { 284 | _rightGlowStop0.Color = color; 285 | } 286 | 287 | if (_rightGlowStop1 != null) 288 | { 289 | _rightGlowStop1.Color = Color.FromArgb(0, color.R, color.G, color.B); 290 | } 291 | 292 | if (_bottomGlowStop0 != null) 293 | { 294 | _bottomGlowStop0.Color = color; 295 | } 296 | 297 | if (_bottomGlowStop1 != null) 298 | { 299 | _bottomGlowStop1.Color = Color.FromArgb(0, color.R, color.G, color.B); 300 | } 301 | } 302 | 303 | /// 304 | /// Sets the glow size. 305 | /// 306 | /// The new glow size. 307 | internal void UpdateGlowSize(Thickness newGlowSize) 308 | { 309 | if (_leftGlow != null) 310 | { 311 | _leftGlow.Width = Math.Abs(newGlowSize.Left); 312 | } 313 | 314 | if (_topGlow != null) 315 | { 316 | _topGlow.Height = Math.Abs(newGlowSize.Top); 317 | } 318 | 319 | if (_rightGlow != null) 320 | { 321 | _rightGlow.Width = Math.Abs(newGlowSize.Right); 322 | } 323 | 324 | if (_bottomGlow != null) 325 | { 326 | _bottomGlow.Height = Math.Abs(newGlowSize.Bottom); 327 | } 328 | } 329 | 330 | /// 331 | /// Updates the inner glow color when the DP changes. 332 | /// 333 | /// The inner glow border. 334 | /// The new property event args. 335 | private static void InnerGlowColorChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs) 336 | { 337 | if (eventArgs.NewValue != null) 338 | { 339 | var innerGlowBorder = (InnerGlowBorder)dependencyObject; 340 | innerGlowBorder.UpdateGlowColor((Color)eventArgs.NewValue); 341 | } 342 | } 343 | 344 | /// 345 | /// Updates the glow size. 346 | /// 347 | /// The inner glow border. 348 | /// Dependency Property Changed Event Args 349 | private static void InnerGlowSizeChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs) 350 | { 351 | var innerGlowBorder = (InnerGlowBorder)dependencyObject; 352 | innerGlowBorder.UpdateGlowSize((Thickness)eventArgs.NewValue); 353 | } 354 | } 355 | } -------------------------------------------------------------------------------- /Common/Borders/OuterGlowBorder.cs: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------- 2 | // 3 | // (c) 2008 Microsoft Corporation. All rights reserved. 4 | // This source is subject to the Microsoft Public License. 5 | // See http://www.microsoft.com/resources/sharedsource/licensingbasics/sharedsourcelicenses.mspx. 6 | // 7 | // 15-Sep-2008 8 | // Martin Grayson 9 | // A border that also shows an outer glow. 10 | //----------------------------------------------------------------------- 11 | using System; 12 | using System.ComponentModel; 13 | using System.Windows; 14 | using System.Windows.Controls; 15 | using System.Windows.Media; 16 | 17 | namespace ToggleSwitch.Borders 18 | { 19 | /// 20 | /// Content control that draws and outer glow around itself. 21 | /// 22 | public class OuterGlowBorder : ContentControl 23 | { 24 | /// 25 | /// The outer glow opacity property. 26 | /// 27 | public static readonly DependencyProperty OuterGlowOpacityProperty = DependencyProperty.Register("OuterGlowOpacity", typeof(double), 28 | typeof(OuterGlowBorder), null); 29 | 30 | /// 31 | /// The outer glow size property. 32 | /// 33 | public static readonly DependencyProperty OuterGlowSizeProperty = DependencyProperty.Register("OuterGlowSize", typeof(double), 34 | typeof(OuterGlowBorder), null); 35 | 36 | /// 37 | /// The corner radius property. 38 | /// 39 | public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.Register("CornerRadius", typeof(CornerRadius), 40 | typeof(OuterGlowBorder), null); 41 | 42 | /// 43 | /// The shadow corner radius property. 44 | /// 45 | public static readonly DependencyProperty ShadowCornerRadiusProperty = DependencyProperty.Register("ShadowCornerRadius", typeof(CornerRadius), 46 | typeof(OuterGlowBorder), null); 47 | 48 | /// 49 | /// The outer glow color. 50 | /// 51 | public static readonly DependencyProperty OuterGlowColorProperty = DependencyProperty.Register("OuterGlowColor", typeof(Color), 52 | typeof(OuterGlowBorder), 53 | new PropertyMetadata( 54 | Colors.Black, 55 | OuterGlowColorChanged)); 56 | 57 | /// 58 | /// The clip content property. 59 | /// 60 | public static readonly DependencyProperty ClipContentProperty = DependencyProperty.Register("ClipContent", typeof(bool), typeof(OuterGlowBorder), 61 | null); 62 | 63 | /// 64 | /// Stores the outer glow border. 65 | /// 66 | private Border _outerGlowBorder; 67 | 68 | /// 69 | /// Stores the left gradient stop. 70 | /// 71 | private GradientStop _shadowHorizontal1; 72 | 73 | /// 74 | /// Stores the right gradient stop. 75 | /// 76 | private GradientStop _shadowHorizontal2; 77 | 78 | /// 79 | /// The top out gradient stop. 80 | /// 81 | private GradientStop _shadowOuterStop1; 82 | 83 | /// 84 | /// The bottom outer gradient stop. 85 | /// 86 | private GradientStop _shadowOuterStop2; 87 | 88 | /// 89 | /// Stores the top gradient stop. 90 | /// 91 | private GradientStop _shadowVertical1; 92 | 93 | /// 94 | /// Stores the bottom gradient stop. 95 | /// 96 | private GradientStop _shadowVertical2; 97 | 98 | /// 99 | /// Out glow border constructor. 100 | /// 101 | public OuterGlowBorder() 102 | { 103 | DefaultStyleKey = typeof(OuterGlowBorder); 104 | SizeChanged += OuterGlowContentControlSizeChanged; 105 | } 106 | 107 | /// 108 | /// Gets or sets a value indicating whether the content is clipped. 109 | /// 110 | [Category("Appearance"), Description("Sets whether the content is clipped or not.")] 111 | public bool ClipContent 112 | { 113 | get 114 | { 115 | return (bool)GetValue(ClipContentProperty); 116 | } 117 | set 118 | { 119 | SetValue(ClipContentProperty, value); 120 | } 121 | } 122 | 123 | /// 124 | /// Gets or sets the outer glow opacity. 125 | /// 126 | [Category("Appearance"), Description("The outer glow opacity.")] 127 | public double OuterGlowOpacity 128 | { 129 | get 130 | { 131 | return (double)GetValue(OuterGlowOpacityProperty); 132 | } 133 | set 134 | { 135 | SetValue(OuterGlowOpacityProperty, value); 136 | } 137 | } 138 | 139 | /// 140 | /// Gets or sets the outer glow size. 141 | /// 142 | [Category("Appearance"), Description("The outer glow size.")] 143 | public double OuterGlowSize 144 | { 145 | get 146 | { 147 | return (double)GetValue(OuterGlowSizeProperty); 148 | } 149 | 150 | set 151 | { 152 | SetValue(OuterGlowSizeProperty, value); 153 | UpdateGlowSize(OuterGlowSize); 154 | UpdateStops(new Size(ActualWidth, ActualHeight)); 155 | } 156 | } 157 | 158 | /// 159 | /// Gets or sets the outer glow color. 160 | /// 161 | [Category("Appearance"), Description("The outer glow color.")] 162 | public Color OuterGlowColor 163 | { 164 | get 165 | { 166 | return (Color)GetValue(OuterGlowColorProperty); 167 | } 168 | 169 | set 170 | { 171 | SetValue(OuterGlowColorProperty, value); 172 | } 173 | } 174 | 175 | /// 176 | /// Gets or sets the border corner radius. 177 | /// This is a thickness, as there is a problem parsing CornerRadius types. 178 | /// 179 | [Category("Appearance"), Description("Sets the corner radius on the border.")] 180 | public CornerRadius CornerRadius 181 | { 182 | get 183 | { 184 | return (CornerRadius)GetValue(CornerRadiusProperty); 185 | } 186 | 187 | set 188 | { 189 | SetValue(CornerRadiusProperty, value); 190 | 191 | ShadowCornerRadius = new CornerRadius(Math.Abs(value.TopLeft * 1.5), Math.Abs(value.TopRight * 1.5), Math.Abs(value.BottomRight * 1.5), 192 | Math.Abs(value.BottomLeft * 1.5)); 193 | } 194 | } 195 | 196 | /// 197 | /// Gets or sets the border corner radius. 198 | /// This is a thickness, as there is a problem parsing CornerRadius types. 199 | /// 200 | [Category("Appearance"), Description("Sets the corner radius on the border.")] 201 | public CornerRadius ShadowCornerRadius 202 | { 203 | get 204 | { 205 | return (CornerRadius)GetValue(ShadowCornerRadiusProperty); 206 | } 207 | 208 | set 209 | { 210 | SetValue(ShadowCornerRadiusProperty, value); 211 | } 212 | } 213 | 214 | /// 215 | /// Gets the parts out of the template. 216 | /// 217 | public override void OnApplyTemplate() 218 | { 219 | base.OnApplyTemplate(); 220 | _shadowOuterStop1 = (GradientStop)GetTemplateChild("PART_ShadowOuterStop1"); 221 | _shadowOuterStop2 = (GradientStop)GetTemplateChild("PART_ShadowOuterStop2"); 222 | _shadowVertical1 = (GradientStop)GetTemplateChild("PART_ShadowVertical1"); 223 | _shadowVertical2 = (GradientStop)GetTemplateChild("PART_ShadowVertical2"); 224 | _shadowHorizontal1 = (GradientStop)GetTemplateChild("PART_ShadowHorizontal1"); 225 | _shadowHorizontal2 = (GradientStop)GetTemplateChild("PART_ShadowHorizontal2"); 226 | _outerGlowBorder = (Border)GetTemplateChild("PART_OuterGlowBorder"); 227 | UpdateGlowSize(OuterGlowSize); 228 | UpdateGlowColor(OuterGlowColor); 229 | } 230 | 231 | /// 232 | /// Updates the glow size. 233 | /// 234 | /// The new size. 235 | internal void UpdateGlowSize(double size) 236 | { 237 | if (_outerGlowBorder != null) 238 | { 239 | _outerGlowBorder.Margin = new Thickness(-Math.Abs(size)); 240 | } 241 | } 242 | 243 | /// 244 | /// Updates the outer glow color. 245 | /// 246 | /// The new color. 247 | internal void UpdateGlowColor(Color color) 248 | { 249 | if (_shadowVertical1 != null) 250 | { 251 | _shadowVertical1.Color = color; 252 | } 253 | 254 | if (_shadowVertical2 != null) 255 | { 256 | _shadowVertical2.Color = color; 257 | } 258 | 259 | if (_shadowOuterStop1 != null) 260 | { 261 | _shadowOuterStop1.Color = Color.FromArgb(0, color.R, color.G, color.B); 262 | } 263 | 264 | if (_shadowOuterStop2 != null) 265 | { 266 | _shadowOuterStop2.Color = Color.FromArgb(0, color.R, color.G, color.B); 267 | } 268 | } 269 | 270 | /// 271 | /// Updates the outer glow color when the DP changes. 272 | /// 273 | /// The outer glow border. 274 | /// The new property event args. 275 | private static void OuterGlowColorChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs) 276 | { 277 | if (eventArgs.NewValue != null) 278 | { 279 | var outerGlowBorder = (OuterGlowBorder)dependencyObject; 280 | outerGlowBorder.UpdateGlowColor((Color)eventArgs.NewValue); 281 | } 282 | } 283 | 284 | /// 285 | /// Updates the gradient stops on the drop shadow. 286 | /// 287 | /// The outer glow border. 288 | /// Size changed event args. 289 | private void OuterGlowContentControlSizeChanged(object sender, SizeChangedEventArgs e) 290 | { 291 | UpdateStops(e.NewSize); 292 | } 293 | 294 | /// 295 | /// Updates the gradient stops. 296 | /// 297 | /// The size of the control. 298 | private void UpdateStops(Size size) 299 | { 300 | if (size.Width > 0 && size.Height > 0) 301 | { 302 | if (_shadowHorizontal1 != null) 303 | { 304 | _shadowHorizontal1.Offset = Math.Abs(OuterGlowSize) / (size.Width + Math.Abs(OuterGlowSize) + Math.Abs(OuterGlowSize)); 305 | } 306 | 307 | if (_shadowHorizontal2 != null) 308 | { 309 | _shadowHorizontal2.Offset = 1 - (Math.Abs(OuterGlowSize) / (size.Width + Math.Abs(OuterGlowSize) + Math.Abs(OuterGlowSize))); 310 | } 311 | 312 | if (_shadowVertical1 != null) 313 | { 314 | _shadowVertical1.Offset = Math.Abs(OuterGlowSize) / (size.Height + Math.Abs(OuterGlowSize) + Math.Abs(OuterGlowSize)); 315 | } 316 | 317 | if (_shadowVertical2 != null) 318 | { 319 | _shadowVertical2.Offset = 1 - (Math.Abs(OuterGlowSize) / (size.Height + Math.Abs(OuterGlowSize) + Math.Abs(OuterGlowSize))); 320 | } 321 | } 322 | } 323 | } 324 | } -------------------------------------------------------------------------------- /Common/DropShadowTextBlock.cs: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------- 2 | // 3 | // (c) 2008 Microsoft Corporation. All rights reserved. 4 | // This source is subject to the Microsoft Public License. 5 | // See http://www.microsoft.com/resources/sharedsource/licensingbasics/sharedsourcelicenses.mspx. 6 | // 7 | // 09-Oct-2008 8 | // Martin Grayson 9 | // A control that displays text, with a drop shadow. 10 | //----------------------------------------------------------------------- 11 | using System; 12 | using System.ComponentModel; 13 | using System.Windows; 14 | using System.Windows.Controls; 15 | using System.Windows.Media; 16 | 17 | namespace Demo.Controls 18 | { 19 | /// 20 | /// A control that displays text, with a drop shadow. 21 | /// 22 | public class DropShadowTextBlock : Control 23 | { 24 | /// 25 | /// The drop shadow color property. 26 | /// 27 | public static readonly DependencyProperty DropShadowColorProperty = DependencyProperty.Register("DropShadowColor", typeof(Color), typeof(DropShadowTextBlock), new PropertyMetadata(DropShadowColorChanged)); 28 | 29 | /// 30 | /// The drop shadow opacity property. 31 | /// 32 | public static readonly DependencyProperty DropShadowOpacityProperty = DependencyProperty.Register("DropShadowOpacity", typeof(double), typeof(DropShadowTextBlock), new PropertyMetadata(DropShadowOpacityChanged)); 33 | 34 | /// 35 | /// The text property. 36 | /// 37 | public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(DropShadowTextBlock), null); 38 | 39 | /// 40 | /// The text decorations property. 41 | /// 42 | public static readonly DependencyProperty TextDecorationsProperty = DependencyProperty.Register("TextDecorations", typeof(TextDecorationCollection), typeof(DropShadowTextBlock), null); 43 | 44 | /// 45 | /// The text wrapping property. 46 | /// 47 | public static readonly DependencyProperty TextWrappingProperty = DependencyProperty.Register("TextWrapping", typeof(TextWrapping), typeof(DropShadowTextBlock), null); 48 | 49 | /// 50 | /// The drop shadow distance property. 51 | /// 52 | public static readonly DependencyProperty DropShadowDistanceProperty = DependencyProperty.Register("DropShadowDistance", typeof(double), typeof(DropShadowTextBlock), new PropertyMetadata(DropShadowDistanceChanged)); 53 | 54 | /// 55 | /// The drop shadow angle property. 56 | /// 57 | public static readonly DependencyProperty DropShadowAngleProperty = DependencyProperty.Register("DropShadowAngle", typeof(double), typeof(DropShadowTextBlock), new PropertyMetadata(DropShadowAngleChanged)); 58 | 59 | /// 60 | /// Stores the drop shadow brush. 61 | /// 62 | private SolidColorBrush _dropShadowBrush; 63 | 64 | /// 65 | /// Stores the drop shadow translate transform. 66 | /// 67 | private TranslateTransform _dropShadowTranslate; 68 | 69 | /// 70 | /// DropShadowTextBlock constructor. 71 | /// 72 | public DropShadowTextBlock() 73 | { 74 | DefaultStyleKey = typeof(DropShadowTextBlock); 75 | } 76 | 77 | /// 78 | /// Gets or sets the drop shadow color. 79 | /// 80 | [Category("Appearance"), Description("The drop shadow color.")] 81 | public Color DropShadowColor 82 | { 83 | get 84 | { 85 | return (Color)GetValue(DropShadowColorProperty); 86 | } 87 | set 88 | { 89 | SetValue(DropShadowColorProperty, value); 90 | } 91 | } 92 | 93 | /// 94 | /// Gets or sets the drop shadow opacity. 95 | /// 96 | [Category("Appearance"), Description("The drop shadow opacity.")] 97 | public double DropShadowOpacity 98 | { 99 | get 100 | { 101 | return (double)GetValue(DropShadowOpacityProperty); 102 | } 103 | set 104 | { 105 | SetValue(DropShadowOpacityProperty, value); 106 | } 107 | } 108 | 109 | /// 110 | /// Gets or sets the link text. 111 | /// 112 | [Category("Common Properties"), Description("The text content.")] 113 | public string Text 114 | { 115 | get 116 | { 117 | return (string)GetValue(TextProperty); 118 | } 119 | set 120 | { 121 | SetValue(TextProperty, value); 122 | } 123 | } 124 | 125 | /// 126 | /// Gets or sets the text decorations. 127 | /// 128 | [Category("Common Properties"), Description("The text decorations.")] 129 | public TextDecorationCollection TextDecorations 130 | { 131 | get 132 | { 133 | return (TextDecorationCollection)GetValue(TextDecorationsProperty); 134 | } 135 | set 136 | { 137 | SetValue(TextDecorationsProperty, value); 138 | } 139 | } 140 | 141 | /// 142 | /// Gets or sets the text wrapping. 143 | /// 144 | [Category("Common Properties"), Description("Whether the text wraps.")] 145 | public TextWrapping TextWrapping 146 | { 147 | get 148 | { 149 | return (TextWrapping)GetValue(TextWrappingProperty); 150 | } 151 | set 152 | { 153 | SetValue(TextWrappingProperty, value); 154 | } 155 | } 156 | 157 | /// 158 | /// Gets or sets the drop shadow distance. 159 | /// 160 | [Category("Appearance"), Description("The drop shadow distance.")] 161 | public double DropShadowDistance 162 | { 163 | get 164 | { 165 | return (double)GetValue(DropShadowDistanceProperty); 166 | } 167 | set 168 | { 169 | SetValue(DropShadowDistanceProperty, value); 170 | } 171 | } 172 | 173 | /// 174 | /// Gets or sets the drop shadow angle. 175 | /// 176 | [Category("Appearance"), Description("The drop shadow angle.")] 177 | public double DropShadowAngle 178 | { 179 | get 180 | { 181 | return (double)GetValue(DropShadowAngleProperty); 182 | } 183 | set 184 | { 185 | SetValue(DropShadowAngleProperty, value); 186 | } 187 | } 188 | 189 | /// 190 | /// Gets the UI elements out of the template. 191 | /// 192 | public override void OnApplyTemplate() 193 | { 194 | base.OnApplyTemplate(); 195 | _dropShadowTranslate = GetTemplateChild("PART_DropShadowTranslate") as TranslateTransform; 196 | _dropShadowBrush = GetTemplateChild("PART_DropShadowBrush") as SolidColorBrush; 197 | UpdateDropShadowPosition(); 198 | UpdateDropShadowBrush(); 199 | } 200 | 201 | /// 202 | /// Converts degrees into radians. 203 | /// 204 | /// The degree value. 205 | /// The degrees as radians. 206 | private static double DegreesToRadians(double degrees) 207 | { 208 | return degrees * (Math.PI / 180); 209 | } 210 | 211 | /// 212 | /// Gets a point offset by a distance and angle (in degrees). 213 | /// 214 | /// The angle in degrees. 215 | /// The distance. 216 | /// The offset point. 217 | private static Point GetOffset(double angle, double distance) 218 | { 219 | double x = Math.Cos(DegreesToRadians(angle)) * distance; 220 | double y = Math.Tan(DegreesToRadians(angle)) * x; 221 | return new Point(x, y); 222 | } 223 | 224 | /// 225 | /// Updates the drop shadow. 226 | /// 227 | internal void UpdateDropShadowPosition() 228 | { 229 | if (_dropShadowTranslate != null) 230 | { 231 | Point offset = GetOffset(DropShadowAngle, DropShadowDistance); 232 | 233 | _dropShadowTranslate.X = offset.X; 234 | _dropShadowTranslate.Y = offset.Y; 235 | } 236 | } 237 | 238 | /// 239 | /// Updates the drop shadow brush. 240 | /// 241 | internal void UpdateDropShadowBrush() 242 | { 243 | if (_dropShadowBrush != null) 244 | { 245 | _dropShadowBrush.Color = DropShadowColor; 246 | _dropShadowBrush.Opacity = DropShadowOpacity; 247 | } 248 | } 249 | 250 | /// 251 | /// Updates the drop shadow. 252 | /// 253 | /// The drop shadow text block. 254 | /// Dependency Property Changed Event Args 255 | private static void DropShadowDistanceChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs) 256 | { 257 | var dropShadowTextBlock = (DropShadowTextBlock)dependencyObject; 258 | dropShadowTextBlock.UpdateDropShadowPosition(); 259 | } 260 | 261 | /// 262 | /// Updates the drop shadow. 263 | /// 264 | /// The drop shadow text block. 265 | /// Dependency Property Changed Event Args 266 | private static void DropShadowAngleChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs) 267 | { 268 | var dropShadowTextBlock = (DropShadowTextBlock)dependencyObject; 269 | dropShadowTextBlock.UpdateDropShadowPosition(); 270 | } 271 | 272 | /// 273 | /// Updates the drop shadow. 274 | /// 275 | /// The drop shadow text block. 276 | /// Dependency Property Changed Event Args 277 | private static void DropShadowColorChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs) 278 | { 279 | var dropShadowTextBlock = (DropShadowTextBlock)dependencyObject; 280 | dropShadowTextBlock.UpdateDropShadowBrush(); 281 | } 282 | 283 | /// 284 | /// Updates the drop shadow. 285 | /// 286 | /// The drop shadow text block. 287 | /// Dependency Property Changed Event Args 288 | private static void DropShadowOpacityChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs) 289 | { 290 | var dropShadowTextBlock = (DropShadowTextBlock)dependencyObject; 291 | dropShadowTextBlock.UpdateDropShadowBrush(); 292 | } 293 | } 294 | } -------------------------------------------------------------------------------- /Common/HorizontalToggleSwitch.cs: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------- 2 | // 3 | // (c) 2011 Eric Jensen. All rights reserved. 4 | // This source is subject to the Microsoft Public License. 5 | // See http://www.opensource.org/licenses/MS-PL. 6 | // 7 | // 15-Sept-2011 8 | // Eric Jensen 9 | // Horizontally oriented toggle switch control. 10 | //----------------------------------------------------------------------- 11 | using System; 12 | using System.Windows; 13 | using System.Windows.Controls; 14 | using System.Windows.Controls.Primitives; 15 | 16 | namespace ToggleSwitch 17 | { 18 | /// 19 | /// Horizontally oriented toggle switch control. 20 | /// 21 | public class HorizontalToggleSwitch : ToggleSwitchBase 22 | { 23 | public HorizontalToggleSwitch() 24 | { 25 | DefaultStyleKey = typeof(HorizontalToggleSwitch); 26 | } 27 | 28 | protected override double Offset 29 | { 30 | get { return Canvas.GetLeft(SwitchThumb); } 31 | set 32 | { 33 | SwitchTrack.BeginAnimation(Canvas.LeftProperty, null); 34 | SwitchThumb.BeginAnimation(Canvas.LeftProperty, null); 35 | Canvas.SetLeft(SwitchTrack, value); 36 | Canvas.SetLeft(SwitchThumb, value); 37 | } 38 | } 39 | 40 | protected override PropertyPath SlidePropertyPath 41 | { 42 | get { return new PropertyPath("(Canvas.Left)"); } 43 | } 44 | 45 | protected override void OnDragDelta(object sender, DragDeltaEventArgs e) 46 | { 47 | DragOffset += e.HorizontalChange; 48 | Offset = Math.Max(UncheckedOffset, Math.Min(CheckedOffset, DragOffset)); 49 | } 50 | 51 | protected override void LayoutControls() 52 | { 53 | if (SwitchThumb == null || SwitchRoot == null) 54 | { 55 | return; 56 | } 57 | 58 | double fullThumbWidth = SwitchThumb.ActualWidth + SwitchThumb.BorderThickness.Left + SwitchThumb.BorderThickness.Right; 59 | 60 | if (SwitchChecked != null && SwitchUnchecked != null) 61 | { 62 | SwitchChecked.Width = SwitchUnchecked.Width = Math.Max(0, SwitchRoot.ActualWidth - fullThumbWidth / 2); 63 | SwitchChecked.Padding = new Thickness(0, 0, (SwitchThumb.ActualWidth + SwitchThumb.BorderThickness.Left) / 2, 0); 64 | SwitchUnchecked.Padding = new Thickness((SwitchThumb.ActualWidth + SwitchThumb.BorderThickness.Right) / 2, 0, 0, 0); 65 | } 66 | 67 | SwitchThumb.Margin = new Thickness(SwitchRoot.ActualWidth - fullThumbWidth, SwitchThumb.Margin.Top, 0, SwitchThumb.Margin.Bottom); 68 | UncheckedOffset = -SwitchRoot.ActualWidth + fullThumbWidth - SwitchThumb.BorderThickness.Left; 69 | CheckedOffset = SwitchThumb.BorderThickness.Right; 70 | 71 | if (!IsDragging) 72 | { 73 | Offset = IsChecked ? CheckedOffset : UncheckedOffset; 74 | ChangeCheckStates(false); 75 | } 76 | } 77 | 78 | protected override void OnDragCompleted(object sender, DragCompletedEventArgs e) 79 | { 80 | IsDragging = false; 81 | bool click = false; 82 | double fullThumbWidth = SwitchThumb.ActualWidth + SwitchThumb.BorderThickness.Left + SwitchThumb.BorderThickness.Right; 83 | 84 | if ((!IsChecked && DragOffset > (SwitchRoot.ActualWidth - fullThumbWidth) * (Elasticity - 1.0)) 85 | || (IsChecked && DragOffset < (SwitchRoot.ActualWidth - fullThumbWidth) * -Elasticity)) 86 | { 87 | double edge = IsChecked ? CheckedOffset : UncheckedOffset; 88 | if (Offset != edge) 89 | { 90 | click = true; 91 | } 92 | } 93 | else if (DragOffset == CheckedOffset || DragOffset == UncheckedOffset) 94 | { 95 | click = true; 96 | } 97 | else 98 | { 99 | ChangeCheckStates(true); 100 | } 101 | 102 | if (click) 103 | { 104 | OnClick(); 105 | } 106 | 107 | DragOffset = 0; 108 | } 109 | } 110 | } -------------------------------------------------------------------------------- /Common/Icons/ToggleSwitch.HorizontalToggleSwitch.Expression.Large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ejensen/toggle-switch-control/1761c450de096c7cea7ec1d81f337ed7f9e69624/Common/Icons/ToggleSwitch.HorizontalToggleSwitch.Expression.Large.png -------------------------------------------------------------------------------- /Common/Icons/ToggleSwitch.HorizontalToggleSwitch.Expression.Small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ejensen/toggle-switch-control/1761c450de096c7cea7ec1d81f337ed7f9e69624/Common/Icons/ToggleSwitch.HorizontalToggleSwitch.Expression.Small.png -------------------------------------------------------------------------------- /Common/Icons/ToggleSwitch.HorizontalToggleSwitch.VisualStudio.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ejensen/toggle-switch-control/1761c450de096c7cea7ec1d81f337ed7f9e69624/Common/Icons/ToggleSwitch.HorizontalToggleSwitch.VisualStudio.bmp -------------------------------------------------------------------------------- /Common/ToggleSwitchBase.cs: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------- 2 | // 3 | // (c) 2011 Eric Jensen. All rights reserved. 4 | // This source is subject to the Microsoft Public License. 5 | // See http://www.opensource.org/licenses/MS-PL. 6 | // 7 | // 15-Sept-2011 8 | // Eric Jensen 9 | // Base class for the toggle switch control. 10 | //----------------------------------------------------------------------- 11 | using System; 12 | using System.ComponentModel; 13 | using System.Windows; 14 | using System.Windows.Controls; 15 | using System.Windows.Controls.Primitives; 16 | using System.Windows.Input; 17 | using System.Windows.Media; 18 | using System.Windows.Media.Animation; 19 | using ToggleSwitch.Utils; 20 | 21 | namespace ToggleSwitch 22 | { 23 | /// 24 | /// Base class for the toggle switch control. 25 | /// 26 | [TemplateVisualState(Name = NormalState, GroupName = CommonStates)] 27 | [TemplateVisualState(Name = DisabledState, GroupName = CommonStates)] 28 | [TemplateVisualState(Name = MouseOverState, GroupName = CommonStates)] 29 | [TemplateVisualState(Name = FocusedState, GroupName = FocusStates)] 30 | [TemplateVisualState(Name = UnfocusedState, GroupName = FocusStates)] 31 | [TemplateVisualState(Name = CheckedState, GroupName = CheckStates)] 32 | [TemplateVisualState(Name = UncheckedState, GroupName = CheckStates)] 33 | [TemplateVisualState(Name = DraggingState + CheckedState, GroupName = CheckStates)] 34 | [TemplateVisualState(Name = DraggingState + UncheckedState, GroupName = CheckStates)] 35 | [TemplatePart(Name = SwitchCheckedPart, Type = typeof(Control))] 36 | [TemplatePart(Name = SwitchUncheckedPart, Type = typeof(Control))] 37 | [TemplatePart(Name = SwitchThumbPart, Type = typeof(Thumb))] 38 | [TemplatePart(Name = SwitchRootPart, Type = typeof(FrameworkElement))] 39 | [TemplatePart(Name = SwitchTrackPart, Type = typeof(FrameworkElement))] 40 | [Description("A control which when clicked or dragged toggles between on and off states.")] 41 | public abstract class ToggleSwitchBase : Control 42 | { 43 | #region Constants 44 | 45 | private const string CommonStates = "CommonStates"; 46 | private const string NormalState = "Normal"; 47 | private const string DisabledState = "Disabled"; 48 | private const string MouseOverState = "MouseOver"; 49 | 50 | private const string CheckStates = "CheckStates"; 51 | private const string CheckedState = "Checked"; 52 | private const string DraggingState = "Dragging"; 53 | private const string UncheckedState = "Unchecked"; 54 | 55 | private const string FocusStates = "FocusStates"; 56 | private const string FocusedState = "Focused"; 57 | private const string UnfocusedState = "Unfocused"; 58 | 59 | private const string SwitchRootPart = "SwitchRoot"; 60 | private const string SwitchCheckedPart = "SwitchChecked"; 61 | private const string SwitchUncheckedPart = "SwitchUnchecked"; 62 | private const string SwitchThumbPart = "SwitchThumb"; 63 | private const string SwitchTrackPart = "SwitchTrack"; 64 | 65 | private const string CommonPropertiesCategory = "Common Properties"; 66 | private const string AppearanceCategory = "Appearance"; 67 | 68 | #endregion 69 | 70 | #region Fields 71 | 72 | /// 73 | /// True if the mouse has been captured by this control, false otherwise. 74 | /// 75 | private bool _isMouseCaptured; 76 | 77 | /// 78 | /// True if the SPACE key is currently pressed, false otherwise. 79 | /// 80 | private bool _isSpaceKeyDown; 81 | 82 | /// 83 | /// True if the mouse's left button is currently down, false otherwise. 84 | /// 85 | private bool _isMouseLeftButtonDown; 86 | 87 | /// 88 | /// Last known position of the mouse with respect to this Button. 89 | /// 90 | private Point _mousePosition; 91 | 92 | /// 93 | /// True if visual state changes are suspended; false otherwise. 94 | /// 95 | private bool _suspendStateChanges; 96 | 97 | #endregion 98 | 99 | #region Properties 100 | 101 | protected Thumb SwitchThumb { get; set; } 102 | protected Control SwitchChecked { get; set; } 103 | protected Control SwitchUnchecked { get; set; } 104 | protected FrameworkElement SwitchRoot { get; set; } 105 | protected FrameworkElement SwitchTrack { get; set; } 106 | 107 | /// 108 | /// The current offset of the Thumb. 109 | /// 110 | protected abstract double Offset { get; set; } 111 | 112 | /// 113 | /// The current offset of the Thumb when it's in the Checked state. 114 | /// 115 | protected double CheckedOffset { get; set; } 116 | 117 | /// 118 | /// The current offset of the Thumb when it's in the Unchecked state. 119 | /// 120 | protected double UncheckedOffset { get; set; } 121 | 122 | /// 123 | /// The offset of the thumb while it's being dragged. 124 | /// 125 | protected double DragOffset { get; set; } 126 | 127 | /// 128 | /// Gets or sets whether the thumb position is being manipulated. 129 | /// 130 | protected bool IsDragging { get; set; } 131 | 132 | /// 133 | /// Gets a value that indicates whether a ToggleSwitch is currently pressed. 134 | /// 135 | public bool IsPressed { get; protected set; } 136 | 137 | protected abstract PropertyPath SlidePropertyPath { get; } 138 | 139 | #endregion 140 | 141 | #region Dependency Properties 142 | 143 | #region ContentTemplate (DependencyProperty) 144 | 145 | /// 146 | /// DependencyProperty for the ControlTemplate property. 147 | /// 148 | public static readonly DependencyProperty ContentTemplateProperty = 149 | DependencyProperty.Register("ContentTemplate", typeof(ControlTemplate), typeof(ToggleSwitchBase), 150 | new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure, OnLayoutDependancyPropertyChanged)); 151 | 152 | /// 153 | /// The template applied to the Checked and Unchecked content properties. 154 | /// 155 | [Description("The template applied to the Checked and Unchecked content properties.")] 156 | public ControlTemplate ContentTemplate 157 | { 158 | get { return (ControlTemplate)GetValue(ContentTemplateProperty); } 159 | set { SetValue(ContentTemplateProperty, value); } 160 | } 161 | 162 | #endregion 163 | 164 | #region CheckedContent (Dependency Property) 165 | 166 | /// 167 | /// DependencyProperty for the CheckedContent property. 168 | /// 169 | public static readonly DependencyProperty CheckedContentProperty = 170 | DependencyProperty.Register("CheckedContent", typeof(object), typeof(ToggleSwitchBase), 171 | new FrameworkPropertyMetadata("ON", FrameworkPropertyMetadataOptions.AffectsArrange, OnLayoutDependancyPropertyChanged)); 172 | 173 | /// 174 | /// The content shown on the checked side of the toggle switch 175 | /// 176 | [Category(CommonPropertiesCategory)] 177 | [Description("The content shown on the checked side of the toggle switch")] 178 | public object CheckedContent 179 | { 180 | get { return GetValue(CheckedContentProperty); } 181 | set { SetValue(CheckedContentProperty, value); } 182 | } 183 | 184 | #endregion 185 | 186 | #region CheckedForeground (Dependency Property) 187 | 188 | /// 189 | /// DependencyProperty for the CheckedForeground property. 190 | /// 191 | public static readonly DependencyProperty CheckedForegroundProperty = 192 | DependencyProperty.Register("CheckedForeground", typeof(Brush), typeof(ToggleSwitchBase), null); 193 | 194 | /// 195 | /// The brush used for the foreground of the checked side of the toggle switch. 196 | /// 197 | [Description("The brush used for the foreground of the checked side of the toggle switch.")] 198 | public Brush CheckedForeground 199 | { 200 | get { return (Brush)GetValue(CheckedForegroundProperty); } 201 | set { SetValue(CheckedForegroundProperty, value); } 202 | } 203 | 204 | #endregion 205 | 206 | #region CheckedBackground (Dependency Property) 207 | 208 | /// 209 | /// DependencyProperty for the CheckedBackground property. 210 | /// 211 | public static readonly DependencyProperty CheckedBackgroundProperty = 212 | DependencyProperty.Register("CheckedBackground", typeof(Brush), typeof(ToggleSwitchBase), null); 213 | 214 | /// 215 | /// The brush used for the background of the checked side of the toggle switch. 216 | /// 217 | [Description("The brush used for the background of the checked side of the toggle switch.")] 218 | public Brush CheckedBackground 219 | { 220 | get { return (Brush)GetValue(CheckedBackgroundProperty); } 221 | set { SetValue(CheckedBackgroundProperty, value); } 222 | } 223 | 224 | #endregion 225 | 226 | #region UncheckedContent (Dependency Property) 227 | 228 | /// 229 | /// DependencyProperty for the UncheckedContent property. 230 | /// 231 | public static readonly DependencyProperty UncheckedContentProperty = 232 | DependencyProperty.Register("UncheckedContent", typeof(object), typeof(ToggleSwitchBase), 233 | new FrameworkPropertyMetadata("OFF", FrameworkPropertyMetadataOptions.AffectsArrange, OnLayoutDependancyPropertyChanged)); 234 | 235 | /// 236 | /// The content shown on the unchecked side of the toggle switch. 237 | /// 238 | [Category(CommonPropertiesCategory)] 239 | [Description("The content shown on the unchecked side of the toggle switch.")] 240 | public object UncheckedContent 241 | { 242 | get { return GetValue(UncheckedContentProperty); } 243 | set { SetValue(UncheckedContentProperty, value); } 244 | } 245 | 246 | #endregion 247 | 248 | #region UncheckedForeground (Dependency Property) 249 | 250 | /// 251 | /// DependencyProperty for the UncheckedForeground property. 252 | /// 253 | public static readonly DependencyProperty UncheckedForegroundProperty = 254 | DependencyProperty.Register("UncheckedForeground", typeof(Brush), typeof(ToggleSwitchBase), null); 255 | 256 | /// 257 | /// The brush used for the foreground of the Unchecked side of the toggle switch. 258 | /// 259 | [Description("The brush used for the foreground of the Unchecked side of the toggle switch.")] 260 | public Brush UncheckedForeground 261 | { 262 | get { return (Brush)GetValue(UncheckedForegroundProperty); } 263 | set { SetValue(UncheckedForegroundProperty, value); } 264 | } 265 | 266 | #endregion 267 | 268 | #region UncheckedBackground (Dependency Property) 269 | 270 | /// 271 | /// DependencyProperty for the UncheckedBackground property. 272 | /// 273 | public static readonly DependencyProperty UncheckedBackgroundProperty = 274 | DependencyProperty.Register("UncheckedBackground", typeof(Brush), typeof(ToggleSwitchBase), null); 275 | 276 | /// 277 | /// The brush used for the background of the Unchecked side of the toggle switch. 278 | /// 279 | [Description("The brush used for the background of the Unchecked side of the toggle switch.")] 280 | public Brush UncheckedBackground 281 | { 282 | get { return (Brush)GetValue(UncheckedBackgroundProperty); } 283 | set { SetValue(UncheckedBackgroundProperty, value); } 284 | } 285 | 286 | #endregion 287 | 288 | #region Elasticity (Dependency Property) 289 | 290 | /// 291 | /// DependencyProperty for the Elasticity property. 292 | /// 293 | public static readonly DependencyProperty ElasticityProperty = 294 | DependencyProperty.Register("Elasticity", typeof(double), typeof(ToggleSwitchBase), new PropertyMetadata(0.5)); 295 | 296 | /// 297 | /// Determines the percentage of the way the thumb must be dragged before the switch changes it's IsChecked state. 298 | /// 299 | /// 300 | /// This value must be within the range of 0.0 - 1.0. 301 | /// 302 | [Category(CommonPropertiesCategory)] 303 | [Description("Determines the percentage of the way the thumb must be dragged before the switch changes it's IsChecked state.")] 304 | public double Elasticity 305 | { 306 | get { return ((double)GetValue(ElasticityProperty)).Clamp(0.0, 1.0); } 307 | set { SetValue(ElasticityProperty, value.Clamp(0, 1.0)); } 308 | } 309 | 310 | #endregion 311 | 312 | #region ThumbTemplate (DependencyProperty) 313 | 314 | /// 315 | /// DependencyProperty for the ThumbTemplate property. 316 | /// 317 | public static readonly DependencyProperty ThumbTemplateProperty = 318 | DependencyProperty.Register("ThumbTemplate", typeof(ControlTemplate), typeof(ToggleSwitchBase), 319 | new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure, OnLayoutDependancyPropertyChanged)); 320 | 321 | /// 322 | /// The thumb's control template. 323 | /// 324 | [Description("The thumb's control template.")] 325 | public ControlTemplate ThumbTemplate 326 | { 327 | get { return (ControlTemplate)GetValue(ThumbTemplateProperty); } 328 | set { SetValue(ThumbTemplateProperty, value); } 329 | } 330 | 331 | #endregion 332 | 333 | #region ThumbBrush (Dependency Property) 334 | 335 | /// 336 | /// DependencyProperty for the ThumbBrush property. 337 | /// 338 | public static readonly DependencyProperty ThumbBrushProperty = 339 | DependencyProperty.Register("ThumbBrush", typeof(Brush), typeof(ToggleSwitchBase), null); 340 | 341 | /// 342 | /// The brush used to fill the thumb. 343 | /// 344 | [Description("The brush used to fill the thumb.")] 345 | public Brush ThumbBrush 346 | { 347 | get { return (Brush)GetValue(ThumbBrushProperty); } 348 | set { SetValue(ThumbBrushProperty, value); } 349 | } 350 | 351 | #endregion 352 | 353 | #region ThumbSize (DependencyProperty) 354 | 355 | /// 356 | /// DependencyProperty for the ThumbSize property. 357 | /// 358 | public static readonly DependencyProperty ThumbSizeProperty = 359 | DependencyProperty.Register("ThumbSize", typeof(double), typeof(ToggleSwitchBase), 360 | new FrameworkPropertyMetadata(40.0, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure, OnLayoutDependancyPropertyChanged)); 361 | 362 | /// 363 | /// The size of the toggle switch's thumb. 364 | /// 365 | [Category(AppearanceCategory)] 366 | [Description("The size of the toggle switch's thumb.")] 367 | public double ThumbSize 368 | { 369 | get { return (double)GetValue(ThumbSizeProperty); } 370 | set { SetValue(ThumbSizeProperty, value); } 371 | } 372 | 373 | #endregion 374 | 375 | #region IsChecked (DependencyProperty) 376 | 377 | /// 378 | /// DependencyProperty for the IsChecked property. 379 | /// 380 | public static readonly DependencyProperty IsCheckedProperty = 381 | DependencyProperty.Register("IsChecked", typeof(bool), typeof(ToggleSwitchBase), 382 | new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnIsCheckedChanged)); 383 | 384 | /// 385 | /// Gets or sets whether the control is in the checked state. 386 | /// 387 | [Category(CommonPropertiesCategory)] 388 | [Description("Gets or sets whether the control is in the checked state.")] 389 | public bool IsChecked 390 | { 391 | get { return (bool)GetValue(IsCheckedProperty); } 392 | set { SetValue(IsCheckedProperty, value); } 393 | } 394 | 395 | private static void OnIsCheckedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 396 | { 397 | var control = (ToggleSwitchBase)d; 398 | 399 | if (e.NewValue != e.OldValue) 400 | { 401 | if ((bool)e.NewValue) 402 | { 403 | control.InvokeChecked(new RoutedEventArgs()); 404 | } 405 | else 406 | { 407 | control.InvokeUnchecked(new RoutedEventArgs()); 408 | } 409 | } 410 | 411 | control.ChangeCheckStates(true); 412 | } 413 | 414 | #endregion 415 | 416 | #endregion 417 | 418 | #region Events 419 | 420 | /// 421 | /// Event raised when the toggle switch is unchecked. 422 | /// 423 | public event RoutedEventHandler Unchecked; 424 | 425 | protected void InvokeUnchecked(RoutedEventArgs e) 426 | { 427 | Unchecked?.Invoke(this, e); 428 | } 429 | 430 | /// 431 | /// Event raised when the toggle switch is checked. 432 | /// 433 | public event RoutedEventHandler Checked; 434 | 435 | protected void InvokeChecked(RoutedEventArgs e) 436 | { 437 | Checked?.Invoke(this, e); 438 | } 439 | 440 | #endregion 441 | 442 | /// 443 | /// Initializes a new instance of the ToggleSwitchBase class. 444 | /// 445 | protected ToggleSwitchBase() 446 | { 447 | Loaded += delegate { UpdateVisualState(false); }; 448 | IsEnabledChanged += OnIsEnabledChanged; 449 | } 450 | 451 | /// 452 | /// Raised while dragging the Thumb. 453 | /// 454 | /// 455 | /// 456 | protected abstract void OnDragDelta(object sender, DragDeltaEventArgs e); 457 | 458 | /// 459 | /// Raised when the dragging of the Thumb has completed. 460 | /// 461 | /// 462 | /// 463 | protected abstract void OnDragCompleted(object sender, DragCompletedEventArgs e); 464 | 465 | /// 466 | /// Recalculated the layout of the control. 467 | /// 468 | protected abstract void LayoutControls(); 469 | 470 | public override void OnApplyTemplate() 471 | { 472 | base.OnApplyTemplate(); 473 | 474 | RemoveEventHandlers(); 475 | GetTemplateChildren(); 476 | AddEventHandlers(); 477 | 478 | LayoutControls(); 479 | 480 | VisualStateManager.GoToState(this, IsEnabled ? NormalState : DisabledState, false); 481 | ChangeCheckStates(false); 482 | } 483 | 484 | /// 485 | /// Initializes the control's template parts. 486 | /// 487 | protected virtual void GetTemplateChildren() 488 | { 489 | SwitchRoot = GetTemplateChild(SwitchRootPart) as FrameworkElement; 490 | SwitchThumb = GetTemplateChild(SwitchThumbPart) as Thumb; 491 | SwitchChecked = GetTemplateChild(SwitchCheckedPart) as Control; 492 | SwitchUnchecked = GetTemplateChild(SwitchUncheckedPart) as Control; 493 | SwitchTrack = GetTemplateChild(SwitchTrackPart) as FrameworkElement; 494 | } 495 | 496 | /// 497 | /// Subscribe event listeners. 498 | /// 499 | protected virtual void AddEventHandlers() 500 | { 501 | if (SwitchThumb != null) 502 | { 503 | SwitchThumb.DragStarted += OnDragStarted; 504 | SwitchThumb.DragDelta += OnDragDelta; 505 | SwitchThumb.DragCompleted += OnDragCompleted; 506 | } 507 | SizeChanged += OnSizeChanged; 508 | } 509 | 510 | /// 511 | /// Unsubscribe event listeners. 512 | /// 513 | protected virtual void RemoveEventHandlers() 514 | { 515 | if (SwitchThumb != null) 516 | { 517 | SwitchThumb.DragStarted -= OnDragStarted; 518 | SwitchThumb.DragDelta -= OnDragDelta; 519 | SwitchThumb.DragCompleted -= OnDragCompleted; 520 | } 521 | SizeChanged -= OnSizeChanged; 522 | } 523 | 524 | /// 525 | /// Raised when a drag has started on the Thumb. 526 | /// 527 | /// 528 | /// 529 | protected virtual void OnDragStarted(object sender, DragStartedEventArgs e) 530 | { 531 | IsDragging = true; 532 | DragOffset = Offset; 533 | ChangeCheckStates(false); 534 | } 535 | 536 | /// 537 | /// Called when the control is clicked. 538 | /// 539 | protected void OnClick() 540 | { 541 | SetCurrentValue(IsCheckedProperty, !IsChecked); 542 | } 543 | 544 | /// 545 | /// Raised when the size of the control has changed. 546 | /// 547 | /// 548 | /// 549 | protected virtual void OnSizeChanged(object sender, SizeChangedEventArgs e) 550 | { 551 | LayoutControls(); 552 | } 553 | 554 | /// 555 | /// Capture the mouse. 556 | /// 557 | internal void CaptureMouseInternal() 558 | { 559 | if (!_isMouseCaptured) 560 | { 561 | _isMouseCaptured = CaptureMouse(); 562 | } 563 | } 564 | 565 | /// 566 | /// Release mouse capture if we already had it. 567 | /// 568 | protected internal void ReleaseMouseCaptureInternal() 569 | { 570 | ReleaseMouseCapture(); 571 | _isMouseCaptured = false; 572 | } 573 | 574 | /// 575 | /// Raised when a dependency property that affects the control's layout has changed. 576 | /// 577 | /// The ToggleSwitch control 578 | /// 579 | private static void OnLayoutDependancyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 580 | { 581 | if (e.NewValue != e.OldValue) 582 | { 583 | ((ToggleSwitchBase)d).LayoutControls(); 584 | } 585 | } 586 | 587 | /// 588 | /// Called when the IsEnabled property changes. 589 | /// 590 | /// 591 | /// 592 | private void OnIsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e) 593 | { 594 | _suspendStateChanges = true; 595 | if (!IsEnabled) 596 | { 597 | IsPressed = false; 598 | _isMouseCaptured = false; 599 | _isSpaceKeyDown = false; 600 | _isMouseLeftButtonDown = false; 601 | } 602 | 603 | _suspendStateChanges = false; 604 | UpdateVisualState(); 605 | } 606 | 607 | /// 608 | /// Responds to the LostFocus event. 609 | /// 610 | /// The event data for the LostFocus event. 611 | protected override void OnLostFocus(RoutedEventArgs e) 612 | { 613 | base.OnLostFocus(e); 614 | IsPressed = false; 615 | ReleaseMouseCaptureInternal(); 616 | _isSpaceKeyDown = false; 617 | 618 | _suspendStateChanges = false; 619 | UpdateVisualState(); 620 | } 621 | 622 | /// 623 | /// Responds to the KeyDown event. 624 | /// 625 | /// The event data for the KeyDown event. 626 | protected override void OnKeyDown(KeyEventArgs e) 627 | { 628 | base.OnKeyDown(e); 629 | if (e.Handled) 630 | { 631 | return; 632 | } 633 | 634 | if (OnKeyDownInternal(e.Key)) 635 | { 636 | e.Handled = true; 637 | } 638 | } 639 | 640 | /// 641 | /// Handles the KeyDown event for ButtonBase. 642 | /// 643 | /// 644 | /// The keyboard key associated with the event. 645 | /// 646 | /// True if the event was handled, false otherwise. 647 | /// 648 | /// This method exists for the purpose of unit testing since we can't 649 | /// set KeyEventArgs.Key to simulate key press events. 650 | /// 651 | private bool OnKeyDownInternal(Key key) 652 | { 653 | bool handled = false; 654 | 655 | if (IsEnabled) 656 | { 657 | if (key == Key.Space) 658 | { 659 | if (!_isMouseCaptured && !_isSpaceKeyDown) 660 | { 661 | _isSpaceKeyDown = true; 662 | IsPressed = true; 663 | CaptureMouseInternal(); 664 | 665 | handled = true; 666 | } 667 | } 668 | else if (key == Key.Enter) 669 | { 670 | _isSpaceKeyDown = false; 671 | IsPressed = false; 672 | ReleaseMouseCaptureInternal(); 673 | 674 | OnClick(); 675 | 676 | handled = true; 677 | } 678 | else if (_isSpaceKeyDown) 679 | { 680 | IsPressed = false; 681 | _isSpaceKeyDown = false; 682 | ReleaseMouseCaptureInternal(); 683 | } 684 | } 685 | 686 | return handled; 687 | } 688 | 689 | /// 690 | /// Responds to the KeyUp event. 691 | /// 692 | /// The event data for the KeyUp event. 693 | protected override void OnKeyUp(KeyEventArgs e) 694 | { 695 | base.OnKeyUp(e); 696 | if (e.Handled) 697 | { 698 | return; 699 | } 700 | 701 | if (OnKeyUpInternal(e.Key)) 702 | { 703 | e.Handled = true; 704 | } 705 | } 706 | 707 | /// 708 | /// Handles the KeyUp event for ButtonBase. 709 | /// 710 | /// The keyboard key associated with the event. 711 | /// True if the event was handled, false otherwise. 712 | /// 713 | /// This method exists for the purpose of unit testing since we can't 714 | /// set KeyEventArgs.Key to simulate key press events. 715 | /// 716 | private bool OnKeyUpInternal(Key key) 717 | { 718 | bool handled = false; 719 | 720 | if (IsEnabled && (key == Key.Space)) 721 | { 722 | _isSpaceKeyDown = false; 723 | 724 | if (!_isMouseLeftButtonDown) 725 | { 726 | ReleaseMouseCaptureInternal(); 727 | if (IsPressed) 728 | { 729 | OnClick(); 730 | } 731 | 732 | IsPressed = false; 733 | } 734 | else if (_isMouseCaptured) 735 | { 736 | bool isValid = IsValidMousePosition(); 737 | IsPressed = isValid; 738 | if (!isValid) 739 | { 740 | ReleaseMouseCaptureInternal(); 741 | } 742 | } 743 | 744 | handled = true; 745 | } 746 | 747 | return handled; 748 | } 749 | 750 | /// 751 | /// Responds to the MouseLeftButtonDown event. 752 | /// 753 | /// 754 | /// The event data for the MouseLeftButtonDown event. 755 | /// 756 | protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) 757 | { 758 | base.OnMouseLeftButtonDown(e); 759 | if (e.Handled) 760 | { 761 | return; 762 | } 763 | 764 | _isMouseLeftButtonDown = true; 765 | 766 | if (!IsEnabled) 767 | { 768 | return; 769 | } 770 | 771 | e.Handled = true; 772 | _suspendStateChanges = true; 773 | Focus(); 774 | 775 | CaptureMouseInternal(); 776 | if (_isMouseCaptured) 777 | { 778 | IsPressed = true; 779 | } 780 | 781 | _suspendStateChanges = false; 782 | UpdateVisualState(); 783 | } 784 | 785 | /// 786 | /// Responds to the MouseLeftButtonUp event. 787 | /// 788 | /// 789 | /// The event data for the MouseLeftButtonUp event. 790 | /// 791 | protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) 792 | { 793 | base.OnMouseLeftButtonUp(e); 794 | if (e.Handled) 795 | { 796 | return; 797 | } 798 | 799 | _isMouseLeftButtonDown = false; 800 | 801 | if (!IsEnabled) 802 | { 803 | return; 804 | } 805 | 806 | e.Handled = true; 807 | if (!_isSpaceKeyDown && IsPressed) 808 | { 809 | OnClick(); 810 | } 811 | 812 | if (!_isSpaceKeyDown) 813 | { 814 | ReleaseMouseCaptureInternal(); 815 | IsPressed = false; 816 | } 817 | } 818 | 819 | /// 820 | /// Responds to the MouseMove event. 821 | /// 822 | /// The event data for the MouseMove event. 823 | protected override void OnMouseMove(MouseEventArgs e) 824 | { 825 | base.OnMouseMove(e); 826 | _mousePosition = e.GetPosition(this); 827 | 828 | if (_isMouseLeftButtonDown && 829 | IsEnabled && 830 | _isMouseCaptured && 831 | !_isSpaceKeyDown) 832 | { 833 | IsPressed = IsValidMousePosition(); 834 | } 835 | } 836 | 837 | /// 838 | /// Determine if the mouse is above the button based on its last known 839 | /// position. 840 | /// 841 | /// 842 | /// True if the mouse is considered above the button, false otherwise. 843 | /// 844 | private bool IsValidMousePosition() 845 | { 846 | return (_mousePosition.X >= 0.0) && 847 | (_mousePosition.X <= ActualWidth) && 848 | (_mousePosition.Y >= 0.0) && 849 | (_mousePosition.Y <= ActualHeight); 850 | } 851 | 852 | protected bool GoToState(bool useTransitions, string stateName) 853 | { 854 | return VisualStateManager.GoToState(this, stateName, useTransitions); 855 | } 856 | 857 | protected virtual void ChangeVisualState(bool useTransitions) 858 | { 859 | if (!IsEnabled) 860 | { 861 | GoToState(useTransitions, DisabledState); 862 | } 863 | else 864 | { 865 | GoToState(useTransitions, IsMouseOver ? MouseOverState : NormalState); 866 | } 867 | 868 | if (IsFocused && IsEnabled) 869 | { 870 | GoToState(useTransitions, FocusedState); 871 | } 872 | else 873 | { 874 | GoToState(useTransitions, UnfocusedState); 875 | } 876 | } 877 | 878 | protected void UpdateVisualState(bool useTransitions = true) 879 | { 880 | if (!_suspendStateChanges) 881 | { 882 | ChangeVisualState(useTransitions); 883 | } 884 | } 885 | 886 | /// 887 | /// Updates the control's layout to reflect the current IsChecked state. 888 | /// 889 | /// Whether to use transitions during the layout change. 890 | protected virtual void ChangeCheckStates(bool useTransitions) 891 | { 892 | var state = IsChecked ? CheckedState : UncheckedState; 893 | 894 | if (IsDragging) 895 | { 896 | VisualStateManager.GoToState(this, DraggingState + state, useTransitions); 897 | } 898 | else 899 | { 900 | VisualStateManager.GoToState(this, state, useTransitions); 901 | if (SwitchThumb != null) 902 | { 903 | VisualStateManager.GoToState(SwitchThumb, state, useTransitions); 904 | } 905 | } 906 | 907 | if (SwitchThumb == null || SwitchTrack == null) 908 | { 909 | return; 910 | } 911 | 912 | var storyboard = new Storyboard(); 913 | var duration = new Duration(useTransitions ? TimeSpan.FromMilliseconds(100) : TimeSpan.Zero); 914 | var backgroundAnimation = new DoubleAnimation(); 915 | var thumbAnimation = new DoubleAnimation(); 916 | 917 | backgroundAnimation.Duration = duration; 918 | thumbAnimation.Duration = duration; 919 | 920 | double offset = IsChecked ? CheckedOffset : UncheckedOffset; 921 | backgroundAnimation.To = offset; 922 | thumbAnimation.To = offset; 923 | 924 | storyboard.Children.Add(backgroundAnimation); 925 | storyboard.Children.Add(thumbAnimation); 926 | 927 | Storyboard.SetTarget(backgroundAnimation, SwitchTrack); 928 | Storyboard.SetTarget(thumbAnimation, SwitchThumb); 929 | 930 | Storyboard.SetTargetProperty(backgroundAnimation, SlidePropertyPath); 931 | Storyboard.SetTargetProperty(thumbAnimation, SlidePropertyPath); 932 | 933 | storyboard.Begin(); 934 | } 935 | } 936 | } 937 | -------------------------------------------------------------------------------- /Common/Utils/ActualSizePropertyProxy.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Windows; 3 | 4 | namespace ToggleSwitch.Utils 5 | { 6 | public class ActualSizePropertyProxy : FrameworkElement, INotifyPropertyChanged 7 | { 8 | public event PropertyChangedEventHandler PropertyChanged; 9 | 10 | #region ElementProperty (Dependancy Property) 11 | 12 | public static readonly DependencyProperty ElementProperty = 13 | DependencyProperty.Register("Element", typeof(FrameworkElement), typeof(ActualSizePropertyProxy), 14 | new PropertyMetadata(null, OnElementPropertyChanged)); 15 | 16 | private static void OnElementPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 17 | { 18 | if (d != null) 19 | { 20 | ((ActualSizePropertyProxy)d).OnElementChanged(e); 21 | } 22 | } 23 | 24 | public FrameworkElement Element 25 | { 26 | get { return (FrameworkElement)GetValue(ElementProperty); } 27 | set { SetValue(ElementProperty, value); } 28 | } 29 | 30 | #endregion 31 | 32 | public double ActualHeightValue 33 | { 34 | get { return Element == null ? 0 : Element.ActualHeight; } 35 | } 36 | 37 | public double ActualWidthValue 38 | { 39 | get { return Element == null ? 0 : Element.ActualWidth; } 40 | } 41 | 42 | private void OnElementChanged(DependencyPropertyChangedEventArgs e) 43 | { 44 | var oldElement = (FrameworkElement)e.OldValue; 45 | var newElement = (FrameworkElement)e.NewValue; 46 | 47 | if (oldElement != null) 48 | { 49 | oldElement.SizeChanged -= ElementSizeChanged; 50 | } 51 | 52 | if (newElement != null) 53 | { 54 | newElement.SizeChanged += ElementSizeChanged; 55 | } 56 | 57 | NotifyPropertyChanged(); 58 | } 59 | 60 | private void ElementSizeChanged(object sender, SizeChangedEventArgs e) 61 | { 62 | NotifyPropertyChanged(); 63 | } 64 | 65 | private void NotifyPropertyChanged() 66 | { 67 | if (PropertyChanged != null) 68 | { 69 | PropertyChanged(this, new PropertyChangedEventArgs(nameof(ActualWidthValue))); 70 | PropertyChanged(this, new PropertyChangedEventArgs(nameof(ActualHeightValue))); 71 | } 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /Common/Utils/CornerRadiusValueConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows; 4 | using System.Windows.Data; 5 | 6 | namespace Demo.Utils 7 | { 8 | public class CornerRadiusValueConverter : IValueConverter 9 | { 10 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 11 | { 12 | var radius = Double.Parse(value.ToString(), culture); 13 | if (parameter != null) 14 | { 15 | radius *= Double.Parse(parameter.ToString(), culture); 16 | } 17 | return new CornerRadius(radius); 18 | } 19 | 20 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 21 | { 22 | throw new NotImplementedException(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Common/Utils/HelperExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ToggleSwitch.Utils 4 | { 5 | internal static class HelperExtensions 6 | { 7 | public static T Clamp(this T val, T min, T max) where T : IComparable 8 | { 9 | return val.CompareTo(min) < 0 ? min : (val.CompareTo(max) > 0 ? max : val); 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /Common/Utils/ScalarValueConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows.Data; 4 | 5 | namespace Demo.Utils 6 | { 7 | public class ScalarValueConverter : IValueConverter 8 | { 9 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 10 | { 11 | var oldValue = Double.Parse(value.ToString(), culture); 12 | if (parameter != null) 13 | { 14 | oldValue *= Double.Parse(parameter.ToString(), culture); 15 | } 16 | return oldValue; 17 | } 18 | 19 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 20 | { 21 | var oldValue = Double.Parse(value.ToString(), culture); 22 | if (parameter != null) 23 | { 24 | oldValue /= Double.Parse(parameter.ToString(), culture); 25 | } 26 | return oldValue; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Common/VerticalToggleSwitch.cs: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------- 2 | // 3 | // (c) 2011 Eric Jensen. All rights reserved. 4 | // This source is subject to the Microsoft Public License. 5 | // See http://www.opensource.org/licenses/MS-PL. 6 | // 7 | // 15-Sept-2011 8 | // Eric Jensen 9 | // Vertically oriented toggle switch control. 10 | //----------------------------------------------------------------------- 11 | using System; 12 | using System.Windows; 13 | using System.Windows.Controls; 14 | using System.Windows.Controls.Primitives; 15 | using ToggleSwitch; 16 | 17 | namespace Demo.Controls 18 | { 19 | /// 20 | /// Vertically oriented toggle switch control. 21 | /// 22 | public class VerticalToggleSwitch : ToggleSwitchBase 23 | { 24 | public VerticalToggleSwitch() 25 | { 26 | DefaultStyleKey = typeof(VerticalToggleSwitch); 27 | } 28 | 29 | protected override double Offset 30 | { 31 | get { return Canvas.GetTop(SwitchThumb); } 32 | set 33 | { 34 | SwitchTrack.BeginAnimation(Canvas.TopProperty, null); 35 | SwitchThumb.BeginAnimation(Canvas.TopProperty, null); 36 | Canvas.SetTop(SwitchThumb, value); 37 | Canvas.SetTop(SwitchTrack, value); 38 | } 39 | } 40 | 41 | protected override PropertyPath SlidePropertyPath 42 | { 43 | get { return new PropertyPath("(Canvas.Top)"); } 44 | } 45 | 46 | protected override void OnDragDelta(object sender, DragDeltaEventArgs e) 47 | { 48 | DragOffset += e.VerticalChange; 49 | Offset = Math.Min(UncheckedOffset, Math.Max(CheckedOffset, DragOffset)); 50 | } 51 | 52 | protected override void LayoutControls() 53 | { 54 | if (SwitchThumb == null || SwitchRoot == null) 55 | { 56 | return; 57 | } 58 | 59 | double fullThumbHeight = SwitchThumb.ActualHeight + SwitchThumb.BorderThickness.Top + SwitchThumb.BorderThickness.Bottom; 60 | 61 | if (SwitchChecked != null && SwitchUnchecked != null) 62 | { 63 | SwitchChecked.Height = SwitchUnchecked.Height = Math.Max(0, SwitchRoot.ActualHeight - fullThumbHeight / 2); 64 | SwitchChecked.Padding = new Thickness(0, 0, 0, (SwitchThumb.ActualHeight + +SwitchThumb.BorderThickness.Bottom) / 2); 65 | SwitchUnchecked.Padding = new Thickness(0, (SwitchThumb.ActualHeight + +SwitchThumb.BorderThickness.Top) / 2, 0, 0); 66 | } 67 | 68 | UncheckedOffset = SwitchRoot.ActualHeight - SwitchThumb.ActualHeight - SwitchThumb.Margin.Top - SwitchThumb.Margin.Bottom; 69 | CheckedOffset = 0; 70 | 71 | if (!IsDragging) 72 | { 73 | Offset = IsChecked ? CheckedOffset : UncheckedOffset; 74 | ChangeCheckStates(false); 75 | } 76 | } 77 | 78 | protected override void OnDragCompleted(object sender, DragCompletedEventArgs e) 79 | { 80 | IsDragging = false; 81 | bool click = false; 82 | 83 | if ((!IsChecked && DragOffset < ((SwitchRoot.ActualHeight - SwitchThumb.ActualHeight) * (1.0 - Elasticity))) 84 | || (IsChecked && DragOffset > ((SwitchRoot.ActualHeight - SwitchThumb.ActualHeight) * Elasticity))) 85 | { 86 | double edge = IsChecked ? CheckedOffset : UncheckedOffset; 87 | if (Offset != edge) 88 | { 89 | click = true; 90 | } 91 | } 92 | else if (DragOffset == CheckedOffset || DragOffset == UncheckedOffset) 93 | { 94 | click = true; 95 | } 96 | else 97 | { 98 | ChangeCheckStates(true); 99 | } 100 | 101 | if (click) 102 | { 103 | OnClick(); 104 | } 105 | 106 | DragOffset = 0; 107 | } 108 | } 109 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Toggle Switch Control Library 2 | ===================== 3 | The Toggle Switch Control Library creates highly customizable toggle switch controls for WPF ~~and Silverlight~~ apps. 4 | 5 | ![Screenshot](https://yetilabs.org/toggleswitch/gallery.png) 6 | 7 | Available as a [NuGet package](https://www.nuget.org/packages/ToggleSwitch). -------------------------------------------------------------------------------- /WPF/Demo/App.xaml: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /WPF/Demo/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | namespace Demo 4 | { 5 | public partial class App : Application 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /WPF/Demo/Assets/Brushes.xaml: -------------------------------------------------------------------------------- 1 |  4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /WPF/Demo/Assets/Fonts.xaml: -------------------------------------------------------------------------------- 1 |  5 | 6 | Segoe UI, Lucida Sans Unicode, Verdana 7 | Segoe UI, Lucida Sans Unicode, Verdana 8 | 32 9 | 10 | -------------------------------------------------------------------------------- /WPF/Demo/Assets/Styles.xaml: -------------------------------------------------------------------------------- 1 |  5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 97 | 98 | 99 | 117 | 118 | 148 | 149 | 150 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 278 | 279 | 291 | M -5,-9.5 A 1 1 0 0 1 -4,-10.5 L 4,-10.5 A 1 1 0 0 1 5,-9.5 L 5,9.5 A 1 1 0 0 1 4,10.5 L -4,10.5 A 1 1 0 0 1 -5,9.5 Z 292 | M -4,-9.5 L 4,-9.5 L 4,9.5 L -4,9.5 Z 293 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 442 | 443 | 444 | 445 | 446 | 494 | 606 | -------------------------------------------------------------------------------- /WPF/Demo/Demo.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | netcoreapp3.1 6 | true 7 | 8 | 9 | Toggle Switch Demo 10 | Toggle Switch Demo 11 | Copyright © Eric Jensen 2020 12 | 1.2.0.0 13 | 1.2.0.0 14 | https://github.com/ejensen/toggle-switch-control 15 | https://github.com/ejensen/toggle-switch-control 16 | Eric Jensen 17 | Yeti Labs 18 | Demo.App 19 | 1.2.0 20 | MIT 21 | git 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | Controls\DropShadowTextBlock.cs 30 | 31 | 32 | Utils\CornerRadiusValueConverter.cs 33 | 34 | 35 | Utils\ScalarValueConverter.cs 36 | 37 | 38 | Controls\VerticalToggleSwitch.cs 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /WPF/Demo/DemoViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Windows.Threading; 4 | 5 | namespace Demo 6 | { 7 | public class DemoViewModel : INotifyPropertyChanged 8 | { 9 | private readonly DispatcherTimer _restartEventTimer = new DispatcherTimer(); 10 | 11 | public DemoViewModel() 12 | { 13 | _restartEventTimer.Interval = TimeSpan.FromSeconds(1.5); 14 | _restartEventTimer.Tick += delegate { _restartEventTimer.Stop(); RestartToggleChecked = false; }; 15 | } 16 | 17 | private bool _restartToggleChecked; 18 | public bool RestartToggleChecked 19 | { 20 | get 21 | { 22 | return _restartToggleChecked; 23 | } 24 | set 25 | { 26 | if (_restartToggleChecked != value) 27 | { 28 | _restartToggleChecked = value; 29 | InvokePropertyChanged("RestartToggleChecked"); 30 | 31 | if (_restartToggleChecked) 32 | { 33 | _restartEventTimer.Start(); 34 | } 35 | } 36 | } 37 | } 38 | 39 | public event PropertyChangedEventHandler PropertyChanged; 40 | public void InvokePropertyChanged(string propertyName) 41 | { 42 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /WPF/Demo/Images/Knob.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ejensen/toggle-switch-control/1761c450de096c7cea7ec1d81f337ed7f9e69624/WPF/Demo/Images/Knob.png -------------------------------------------------------------------------------- /WPF/Demo/Images/lightOff.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ejensen/toggle-switch-control/1761c450de096c7cea7ec1d81f337ed7f9e69624/WPF/Demo/Images/lightOff.png -------------------------------------------------------------------------------- /WPF/Demo/Images/lightOn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ejensen/toggle-switch-control/1761c450de096c7cea7ec1d81f337ed7f9e69624/WPF/Demo/Images/lightOn.png -------------------------------------------------------------------------------- /WPF/Demo/MainWindow.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 | -------------------------------------------------------------------------------- /WPF/Demo/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | namespace Demo 4 | { 5 | public partial class MainWindow : Window 6 | { 7 | public MainWindow() 8 | { 9 | InitializeComponent(); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /WPF/Demo/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Resources; 3 | using System.Runtime.InteropServices; 4 | using System.Windows; 5 | 6 | // General Information about an assembly is controlled through the following 7 | // set of attributes. Change these attribute values to modify the information 8 | // associated with an assembly. 9 | [assembly: AssemblyTrademark("")] 10 | [assembly: AssemblyCulture("")] 11 | 12 | // Setting ComVisible to false makes the types in this assembly not visible 13 | // to COM components. If you need to access a type in this assembly from 14 | // COM, set the ComVisible attribute to true on that type. 15 | [assembly: ComVisible(false)] 16 | 17 | //In order to begin building localizable applications, set 18 | //CultureYouAreCodingWith in your .csproj file 19 | //inside a . For example, if you are using US english 20 | //in your source files, set the to en-US. Then uncomment 21 | //the NeutralResourceLanguage attribute below. Update the "en-US" in 22 | //the line below to match the UICulture setting in the project file. 23 | 24 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 25 | 26 | 27 | [assembly: ThemeInfo( 28 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 29 | //(used if a resource is not found in the page, 30 | // or application resource dictionaries) 31 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 32 | //(used if a resource is not found in the page, 33 | // app, or any theme specific resource dictionaries) 34 | )] 35 | 36 | 37 | // Version information for an assembly consists of the following four values: 38 | // 39 | // Major Version 40 | // Minor Version 41 | // Build Number 42 | // Revision 43 | // 44 | // You can specify all the values or you can default the Build and Revision Numbers 45 | // by using the '*' as shown below: 46 | // [assembly: AssemblyVersion("1.0.*")] 47 | [assembly: NeutralResourcesLanguageAttribute("en")] 48 | -------------------------------------------------------------------------------- /WPF/Demo/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.225 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Demo.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Demo.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /WPF/Demo/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /WPF/Demo/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.225 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Demo.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /WPF/Demo/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /WPF/Demo/Views/Basics.xaml: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 26 | 27 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /WPF/Demo/Views/Basics.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | 3 | namespace Demo 4 | { 5 | public partial class Basics : UserControl 6 | { 7 | public Basics() 8 | { 9 | InitializeComponent(); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /WPF/Demo/Views/Events.xaml: -------------------------------------------------------------------------------- 1 |  5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /WPF/Demo/Views/Events.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | 3 | namespace Demo 4 | { 5 | public partial class Events : UserControl 6 | { 7 | public Events() 8 | { 9 | InitializeComponent(); 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /WPF/Demo/Views/Extending.xaml: -------------------------------------------------------------------------------- 1 |  5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 18 | 19 | -------------------------------------------------------------------------------- /WPF/Demo/Views/Extending.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | 3 | namespace Demo 4 | { 5 | public partial class Extending : UserControl 6 | { 7 | public Extending() 8 | { 9 | InitializeComponent(); 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /WPF/Demo/Views/Styling.xaml: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 27 | 28 | 29 | 30 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 64 | 65 | 66 | 67 | 70 | 71 | 72 | 73 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /WPF/Demo/Views/Styling.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | 3 | namespace Demo 4 | { 5 | public partial class Styling : UserControl 6 | { 7 | public Styling() 8 | { 9 | InitializeComponent(); 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /WPF/Demo/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /WPF/ToggleSwitch.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual Studio 2010 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Demo", "Demo\Demo.csproj", "{41423C97-80FA-4D0E-81A6-6E173A97A5B0}" 5 | EndProject 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ToggleSwitch", "ToggleSwitch\ToggleSwitch.csproj", "{F9426059-27DB-45F0-9012-B1234965FBE0}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {41423C97-80FA-4D0E-81A6-6E173A97A5B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {41423C97-80FA-4D0E-81A6-6E173A97A5B0}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {41423C97-80FA-4D0E-81A6-6E173A97A5B0}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {41423C97-80FA-4D0E-81A6-6E173A97A5B0}.Release|Any CPU.Build.0 = Release|Any CPU 18 | {F9426059-27DB-45F0-9012-B1234965FBE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {F9426059-27DB-45F0-9012-B1234965FBE0}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {F9426059-27DB-45F0-9012-B1234965FBE0}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {F9426059-27DB-45F0-9012-B1234965FBE0}.Release|Any CPU.Build.0 = Release|Any CPU 22 | EndGlobalSection 23 | GlobalSection(SolutionProperties) = preSolution 24 | HideSolutionNode = FALSE 25 | EndGlobalSection 26 | EndGlobal 27 | -------------------------------------------------------------------------------- /WPF/ToggleSwitch/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Resources; 4 | using System.Runtime.InteropServices; 5 | using System.Windows; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | [assembly: AssemblyTrademark("")] 11 | [assembly: AssemblyCulture("")] 12 | [assembly: ThemeInfo(ResourceDictionaryLocation.SourceAssembly, ResourceDictionaryLocation.SourceAssembly)] 13 | 14 | // Setting ComVisible to false makes the types in this assembly not visible 15 | // to COM components. If you need to access a type in this assembly from 16 | // COM, set the ComVisible attribute to true on that type. 17 | [assembly: ComVisible(false)] 18 | 19 | // The following GUID is for the ID of the typelib if this project is exposed to COM 20 | [assembly: Guid("528c2286-1b0e-4419-972a-b28dd5a33232")] 21 | 22 | [assembly: NeutralResourcesLanguageAttribute("en")] 23 | [assembly: CLSCompliant(true)] -------------------------------------------------------------------------------- /WPF/ToggleSwitch/Themes/Generic.xaml: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 64 | 65 | 66 | 149 | 150 | 151 | 201 | 202 | 203 | 207 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 223 | 224 | 225 | 226 | 227 | 231 | 232 | 233 | 234 | 235 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 269 | 270 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 289 | 296 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | Visible 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 344 | 345 | 348 | 352 | 353 | 356 | 369 | 370 | 371 | 384 | 385 | 386 | 387 | 396 | 405 | 406 | 407 | 411 | 412 | 413 | 459 | -------------------------------------------------------------------------------- /WPF/ToggleSwitch/ToggleSwitch.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Library 4 | net46;netcoreapp3.1 5 | true 6 | 7 | 8 | Toggle Switch Control Library 9 | Toggle Switch Control Library 10 | Copyright © Eric Jensen 2020 11 | 1.2.0.0 12 | 1.2.0.0 13 | true 14 | https://github.com/ejensen/toggle-switch-control 15 | https://github.com/ejensen/toggle-switch-control 16 | Yeti Labs 17 | true 18 | Eric Jensen 19 | 1.2.0 20 | icon.png 21 | MIT 22 | git 23 | 24 | 25 | 26 | Borders\ClippingBorder.cs 27 | 28 | 29 | Borders\InnerGlowBorder.cs 30 | 31 | 32 | Borders\OuterGlowBorder.cs 33 | 34 | 35 | HorizontalToggleSwitch.cs 36 | 37 | 38 | ToggleSwitchBase.cs 39 | 40 | 41 | Utils\ActualSizePropertyProxy.cs 42 | 43 | 44 | Utils\HelperExtensions.cs 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | True 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /WPF/ToggleSwitch/ToggleSwitchKey.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ejensen/toggle-switch-control/1761c450de096c7cea7ec1d81f337ed7f9e69624/WPF/ToggleSwitch/ToggleSwitchKey.snk -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ejensen/toggle-switch-control/1761c450de096c7cea7ec1d81f337ed7f9e69624/icon.png --------------------------------------------------------------------------------