Dreamine.UI.Wpf.Controls 1.0.1
Dreamine.UI.Wpf.Controls 사용자 인터페이스 기능과 구성 요소를 제공합니다.
로딩중...
검색중...
일치하는것 없음
DreamineCheckBox.cs
이 파일의 문서화 페이지로 가기
1// \file DreamineCheckBox.cs
2// \brief Custom CheckBox control for VsLibrary. Supports attached Command/Parameter/Trigger and Enter/Space keyboard behavior.
3
4using System;
5using System.Linq;
6using System.Windows;
7using System.Windows.Controls;
8using System.Windows.Controls.Primitives;
9using System.Windows.Input;
10
12{
21 public class DreamineCheckBox : CheckBox
22 {
32 {
33 DefaultStyleKeyProperty.OverrideMetadata(typeof(DreamineCheckBox),
34 new FrameworkPropertyMetadata(typeof(DreamineCheckBox)));
35
36 try
37 {
38 var uri = new Uri("/Dreamine.UI.Wpf.Themes;component/DreamineCheckBoxStyle.xaml", UriKind.RelativeOrAbsolute);
39
40 var app = Application.Current;
41 if (app != null)
42 {
43 bool alreadyAdded = app.Resources.MergedDictionaries
44 .OfType<ResourceDictionary>()
45 .Any(x => x.Source != null && x.Source.Equals(uri));
46
47 if (!alreadyAdded)
48 {
49 var dict = new ResourceDictionary { Source = uri };
50 app.Resources.MergedDictionaries.Add(dict);
51 }
52 }
53 }
54 catch
55 {
56 // \brief Guard for design-time / missing resources, etc.
57 }
58 }
59
60 #region Attached DPs (Command/Parameter/Trigger)
61
70 private static readonly DependencyProperty IsHandlersHookedProperty =
71 DependencyProperty.RegisterAttached("IsHandlersHooked", typeof(bool), typeof(DreamineCheckBox),
72 new PropertyMetadata(false));
73
106 private static bool GetIsHandlersHooked(DependencyObject d) => (bool)d.GetValue(IsHandlersHookedProperty);
107
140 private static void SetIsHandlersHooked(DependencyObject d, bool v) => d.SetValue(IsHandlersHookedProperty, v);
141
150 public new static readonly DependencyProperty CommandProperty =
151 DependencyProperty.RegisterAttached(
152 "Command",
153 typeof(ICommand),
154 typeof(DreamineCheckBox),
155 new PropertyMetadata(null, OnCommandChanged));
156
165 public new static readonly DependencyProperty CommandParameterProperty =
166 DependencyProperty.RegisterAttached(
167 "CommandParameter",
168 typeof(object),
169 typeof(DreamineCheckBox),
170 new PropertyMetadata(null));
171
180 public static readonly DependencyProperty CommandTriggerNameProperty =
181 DependencyProperty.RegisterAttached(
182 "CommandTriggerName",
183 typeof(string),
184 typeof(DreamineCheckBox),
185 new PropertyMetadata("Click"));
186
219 public static void SetCommandTriggerName(DependencyObject obj, string value)
220 => obj.SetValue(CommandTriggerNameProperty, value);
221
262 public static string GetCommandTriggerName(DependencyObject obj)
263 => (string)obj.GetValue(CommandTriggerNameProperty);
264
297 public static void SetCommand(DependencyObject obj, ICommand value)
298 => obj.SetValue(CommandProperty, value);
299
340 public static ICommand GetCommand(DependencyObject obj)
341 => (ICommand)obj.GetValue(CommandProperty);
342
375 public static void SetCommandParameter(DependencyObject obj, object value)
376 => obj.SetValue(CommandParameterProperty, value);
377
410 public static object GetCommandParameter(DependencyObject obj)
411 => obj.GetValue(CommandParameterProperty);
412
413 #endregion
414
415 #region Hook event handlers
416
441 private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
442 {
443 if (d is not UIElement element)
444 return;
445
446 // \brief 중복 핸들러 등록 방지
447 if (GetIsHandlersHooked(d))
448 return;
449
450 // \brief 마우스
451 element.AddHandler(UIElement.PreviewMouseUpEvent, new MouseButtonEventHandler((s, ev) =>
452 {
453 TryExecuteCommand(d, "PreviewMouseUp", ev);
454 }), true);
455
456 element.AddHandler(Control.MouseDoubleClickEvent, new MouseButtonEventHandler((s, ev) =>
457 {
458 TryExecuteCommand(d, "MouseDoubleClick", ev);
459 }), true);
460
461 // \brief 키보드(Down): Enter/Return은 기본 CheckBox에서 토글이 아닐 수 있으므로 직접 토글 처리
462 element.AddHandler(UIElement.PreviewKeyDownEvent, new KeyEventHandler((s, ev) =>
463 {
464 // \brief 주 키 엔터는 Return으로 들어오는 경우가 많다.
465 if (ev.Key == Key.Enter || ev.Key == Key.Return)
466 {
467 // \brief Enter/Return 토글 강제
468 if (d is ToggleButton tb)
469 {
470 bool current = tb.IsChecked == true;
471 tb.IsChecked = !current;
472 }
473
474 // \brief 트리거 이름은 "PreviewKeyDown"으로도 쓸 수 있게 한다.
475 // \note 기존 예제는 PreviewKeyUp을 많이 쓰므로 둘 다 호출.
476 TryExecuteCommand(d, "PreviewKeyDown", ev);
477 TryExecuteCommand(d, "PreviewKeyUp", ev);
478
479 // \brief Enter가 DefaultButton으로 새는게 싫으면 막아라.
480 ev.Handled = true;
481 }
482 else if (ev.Key == Key.Space)
483 {
484 // \brief Space는 WPF 기본 토글이 정상 동작한다.
485 // \note 여기서 굳이 토글하지 않는다.
486 TryExecuteCommand(d, "PreviewKeyDown", ev);
487 // ev.Handled = false; // 기본 토글에 맡김
488 }
489 }), true);
490
491 // \brief 키보드(Up): Space/Enter/Return 지원
492 element.AddHandler(UIElement.PreviewKeyUpEvent, new KeyEventHandler((s, ev) =>
493 {
494 if (ev.Key != Key.Enter && ev.Key != Key.Return && ev.Key != Key.Space)
495 return;
496
497 TryExecuteCommand(d, "PreviewKeyUp", ev);
498
499 // \brief Up에서 막고 싶으면 활성화
500 // ev.Handled = true;
501 }), true);
502
503 // \brief 터치
504 element.AddHandler(UIElement.TouchUpEvent, new EventHandler<TouchEventArgs>((s, ev) =>
505 {
506 TryExecuteCommand(d, "TouchUp", ev);
507 }));
508
509 // \brief Click
510 element.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler((s, ev) =>
511 {
512 TryExecuteCommand(d, "Click", ev);
513 }));
514
515 SetIsHandlersHooked(d, true);
516 }
517
518 #endregion
519
520 #region Execute command
521
554 private static void TryExecuteCommand(DependencyObject d, string eventName, RoutedEventArgs eventArgs)
555 {
556 var rawTrigger = GetCommandTriggerName(d);
557
558 // \brief null/empty면 실행 안 함 (명시적으로 "Click"을 기본값으로 줬음)
559 if (string.IsNullOrWhiteSpace(rawTrigger))
560 return;
561
562 var triggers = rawTrigger
563 .Split(',')
564 .Select(x => x.Trim())
565 .Where(x => !string.IsNullOrWhiteSpace(x))
566 .ToArray();
567
568 if (triggers.Length > 0 &&
569 !triggers.Contains(eventName, StringComparer.OrdinalIgnoreCase))
570 return;
571
572 var command = GetCommand(d);
573 if (command == null)
574 return;
575
576 var parameter = GetCommandParameter(d) ?? eventArgs;
577
578 if (command.CanExecute(parameter))
579 command.Execute(parameter);
580 }
581
582 #endregion
583 }
584}
static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
static string GetCommandTriggerName(DependencyObject obj)
static new readonly DependencyProperty CommandParameterProperty
static void SetCommandParameter(DependencyObject obj, object value)
static void SetCommandTriggerName(DependencyObject obj, string value)
static readonly DependencyProperty IsHandlersHookedProperty
static object GetCommandParameter(DependencyObject obj)
static void SetIsHandlersHooked(DependencyObject d, bool v)
static ICommand GetCommand(DependencyObject obj)
static void TryExecuteCommand(DependencyObject d, string eventName, RoutedEventArgs eventArgs)
static void SetCommand(DependencyObject obj, ICommand value)
static new readonly DependencyProperty CommandProperty
static readonly DependencyProperty CommandTriggerNameProperty
static bool GetIsHandlersHooked(DependencyObject d)