Dreamine.UI.Wpf.Controls 1.0.1
Dreamine.UI.Wpf.Controls 사용자 인터페이스 기능과 구성 요소를 제공합니다.
로딩중...
검색중...
일치하는것 없음
DreamineNavigationBar.xaml.cs
이 파일의 문서화 페이지로 가기
1using Dreamine.MVVM.ViewModels;
2using System.Collections.ObjectModel;
3using System.Diagnostics;
4using System.Windows;
5using System.Windows.Controls;
6using System.Windows.Input;
7using System.Windows.Media;
8using System.Windows.Media.Imaging;
9using System.Windows.Threading;
11using Dreamine.UI.Wpf.Localization;
12
14{
23 public class DesignButtonDatas : ObservableCollection<ButtonData>
24 {
34 {
35 Add(new ButtonData { Content = "LoginAsync" });
36 Add(new ButtonData { Content = "Main" });
37 Add(new ButtonData { Content = "Manual" });
38 Add(new ButtonData { Content = "Setting" });
39 Add(new ButtonData { Content = "Register" });
40 Add(new ButtonData { Content = "About", Visibility = Visibility.Hidden });
41 Add(new ButtonData { Content = "About", Visibility = Visibility.Hidden });
42 Add(new ButtonData { Content = "About", Visibility = Visibility.Hidden });
43 Add(new ButtonData { Content = "About", Visibility = Visibility.Hidden });
44 Add(new ButtonData { Content = "Exit" });
45
46 this[Count - 1].Margin = new Thickness(0);
47 }
48 }
49
58 public partial class DreamineNavigationBar : UserControl
59 {
76 public static event EventHandler? AutoLogoutOccurred;
77
87 {
88 get => (int)GetValue(AutoLogoutTimeoutProperty);
89 set => SetValue(AutoLogoutTimeoutProperty, value);
90 }
91
100 public static readonly DependencyProperty AutoLogoutTimeoutProperty =
101 DependencyProperty.Register(
102 nameof(AutoLogoutTimeout),
103 typeof(int),
104 typeof(DreamineNavigationBar),
105 new PropertyMetadata(0, OnAutoLogoutTimeoutChanged));
106
131 private static void OnAutoLogoutTimeoutChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
132 {
133 if (d is DreamineNavigationBar nav)
134 {
135 nav.ResetLogoutTimerIfNeeded();
136 }
137 }
138
147 private static readonly HashSet<DreamineNavigationBar> _instances = new();
148
157 private static volatile bool _isInputHooked = false;
158
167 private static void EnsureGlobalInputHook()
168 {
169 if (_isInputHooked == false)
170 {
171 InputManager.Current.PreProcessInput += OnGlobalInputStatic;
172 _isInputHooked = true;
173 }
174 }
175
208 private static void OnGlobalInputStatic(object sender, PreProcessInputEventArgs e)
209 {
210 if (e.StagingItem.Input is MouseEventArgs or KeyboardEventArgs or TouchEventArgs)
211 {
212 foreach (var nav in _instances)
213 {
214 if (nav.AutoLogoutTimeout > 0)
215 nav.InitializeOrResetLogoutTimer();
216 }
217 }
218 }
219
228 private DispatcherTimer? _logoutTimer;
229
239 {
240 if (AutoLogoutTimeout > 0)
242 else
244 }
245
263 {
264 if (_logoutTimer == null)
265 {
266 _logoutTimer = new DispatcherTimer
267 {
268 Interval = TimeSpan.FromSeconds(AutoLogoutTimeout)
269 };
270 _logoutTimer.Tick += (s, e) =>
271 {
272 _logoutTimer.Stop();
273
274 OnAutoLogout();
275 };
276 }
277
278 _logoutTimer.Stop();
279 _logoutTimer.Interval = TimeSpan.FromSeconds(AutoLogoutTimeout);
280 _logoutTimer.Start();
281 }
282
291 private void StopLogoutTimer()
292 {
293 _logoutTimer?.Stop();
294 }
295
312 private void OnAutoLogout()
313 {
314 foreach (Window window in Application.Current.Windows)
315 {
316 if (window.Owner != null && window.IsVisible)
317 window.Hide();
318 }
319
320 foreach (var btn in ButtonDatas)
321 btn.Grade = 0;
322
323 AutoLogoutOccurred?.Invoke(this, EventArgs.Empty);
324
326 "You have been automatically logged out due to inactivity.",
327 "Auto Logout",
328 MessageBoxButton.OK,
329 MessageBoxImage.Information,
330 autoClick: MessageBoxResult.OK,
331 autoClickDelaySeconds: 5);
332 }
333
342 public bool OnceLogin
343 {
344 get => (bool)GetValue(OnceLoginProperty);
345 set => SetValue(OnceLoginProperty, value);
346 }
347
356 public static readonly DependencyProperty OnceLoginProperty =
357 DependencyProperty.Register(
358 nameof(OnceLogin),
359 typeof(bool),
360 typeof(DreamineNavigationBar),
361 new PropertyMetadata(false, OnOnceLoginChanged));
362
395 private static void OnOnceLoginChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
396 {
397 if (d is DreamineNavigationBar nav)
398 VsNavigationHelper.IsOnceLoginEnabled = (bool)e.NewValue;
399 }
400
410 {
411 InitializeComponent();
412
413 if (ButtonDatas == null)
414 ButtonDatas = new ObservableCollection<ButtonData>();
415
417 _instances.Add(this);
418 Unloaded += (_, _) => _instances.Remove(this);
419
420 // Window.Closed 보장: Unloaded가 발생하지 않을 때도 GC 루트 제거
421 Loaded += (_, _) =>
422 {
423 var parentWindow = Window.GetWindow(this);
424 if (parentWindow != null)
425 parentWindow.Closed += (_, _) => _instances.Remove(this);
426 };
427 }
428
438 {
439 get => (Language)GetValue(LanguageProperty);
440 set => SetValue(LanguageProperty, value);
441 }
442
451 public static readonly new DependencyProperty LanguageProperty =
452 DependencyProperty.Register(nameof(Language), typeof(Language), typeof(DreamineNavigationBar), new PropertyMetadata(Language.English));
453
462 public ObservableCollection<ButtonData> ButtonDatas
463 {
464 get => (ObservableCollection<ButtonData>)GetValue(ButtonDatasProperty);
465 set => SetValue(ButtonDatasProperty, value);
466 }
467
476 public static readonly DependencyProperty ButtonDatasProperty =
477 DependencyProperty.Register(
478 nameof(ButtonDatas),
479 typeof(ObservableCollection<ButtonData>),
480 typeof(DreamineNavigationBar),
481 new PropertyMetadata(new ObservableCollection<ButtonData>(), OnButtonDatasChanged));
482
507 private static void OnButtonDatasChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
508 {
509 if (d is DreamineNavigationBar nav)
510 {
511 if (e.OldValue is ObservableCollection<ButtonData> oldList)
512 oldList.CollectionChanged -= nav.ButtonDatas_CollectionChanged;
513
514 if (e.NewValue is ObservableCollection<ButtonData> newList)
515 newList.CollectionChanged += nav.ButtonDatas_CollectionChanged;
516
517 nav.UpdateMarginAndAlignment();
518 }
519 }
520
545 private void ButtonDatas_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
546 {
548 }
549
559 {
560 get => (NavigationBarPosition)GetValue(PositionProperty);
561 set => SetValue(PositionProperty, value);
562 }
563
572 public static readonly DependencyProperty PositionProperty =
573 DependencyProperty.Register(
574 nameof(Position),
575 typeof(NavigationBarPosition),
576 typeof(DreamineNavigationBar),
577 new PropertyMetadata(NavigationBarPosition.Top, OnPositionChanged));
578
603 private static void OnPositionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
604 {
605 if (d is DreamineNavigationBar nav)
606 nav.UpdateMarginAndAlignment();
607 }
608
618 {
619 Thickness defaultMargin = Position switch
620 {
621 NavigationBarPosition.Right => new Thickness(0, 0, 0, 4),
622 NavigationBarPosition.Left => new Thickness(0, 0, 0, 4),
623 _ => new Thickness(0, 0, 4, 0)
624 };
625 Thickness lastMargin = Position switch
626 {
627 NavigationBarPosition.Right => new Thickness(0, 0, 0, 4),
628 NavigationBarPosition.Left => new Thickness(0, 0, 0, 4),
629 _ => new Thickness(0)
630 };
631
632 ApplyLastButtonMargin(defaultMargin, lastMargin);
633 }
634
659 private void ApplyLastButtonMargin(Thickness defaultMargin, Thickness lastMargin)
660 {
661 if (ButtonDatas == null || ButtonDatas.Count == 0)
662 return;
663
664 var visibleIndices = ButtonDatas
665 .Select((btn, idx) => new { btn, idx })
666 .Where(x => x.btn.Visibility == Visibility.Visible)
667 .Select(x => x.idx)
668 .ToList();
669
670 if (visibleIndices.Count == 0)
671 return;
672
673 foreach (var btn in ButtonDatas)
674 {
675 btn.Margin = defaultMargin;
676 }
677 ButtonDatas[visibleIndices.Last()].Margin = lastMargin;
678 }
679
704 public static void NotifyButtonClicked(ButtonData clickedButton)
705 {
706 var navBars = Application.Current.Windows
707 .OfType<Window>()
708 .SelectMany(w => FindVisualChildren<DreamineNavigationBar>(w))
709 .ToList();
710
711 ButtonData buttonData = null!;
712
713 foreach (var nav in navBars)
714 {
715 if (!nav.ButtonDatas.Contains(clickedButton))
716 continue;
717
718 foreach (var btn in nav.ButtonDatas)
719 {
720 if (btn.IsSelected == true && btn.IsFocusableEx == true)
721 {
722 buttonData = btn;
723 }
724 btn.IsSelected = false;
725 }
726 if (clickedButton.IsFocusableEx)
727 {
728 clickedButton.IsSelected = true;
729 }
730 else
731 {
732 foreach (var btn in nav.ButtonDatas)
733 {
734 if (btn == buttonData)
735 {
736 btn.IsSelected = true;
737 }
738 }
739 }
740 }
741 }
742
775 private static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
776 {
777 if (depObj == null)
778 yield break;
779
780 for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
781 {
782 DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
783
784 if (child is T t)
785 yield return t;
786
787 foreach (var childOfChild in FindVisualChildren<T>(child))
788 yield return childOfChild;
789 }
790 }
791
824 private void DreamineButton_Click(object sender, RoutedEventArgs e)
825 {
826 }
827 }
828
876
885 public class ButtonData : ViewModelBase
886 {
895 private string _content = string.Empty;
904 public string Content { get => _content; set { if (SetProperty(ref _content, value)) OnPropertyChanged(nameof(ImageSource)); } }
905
914 private string _imagePath = string.Empty;
923 public string ImagePath
924 {
925 get => _imagePath;
926 set { if (SetProperty(ref _imagePath, value)) OnPropertyChanged(nameof(ImageSource)); }
927 }
928
938 {
939 get
940 {
941 if (string.IsNullOrWhiteSpace(ImagePath)) return null!;
942 try { return new BitmapImage(new Uri(ImagePath, UriKind.RelativeOrAbsolute)); }
943 catch (Exception ex) { Debug.WriteLine($"[ButtonData] ImageSource 로드 실패({ImagePath}): {ex.Message}"); return null!; }
944 }
945 }
946
955 private Brush _foreground = Brushes.Black;
964 public Brush Foreground { get => _foreground; set => SetProperty(ref _foreground, value); }
965
974 private Brush _shineColor = Brushes.LightBlue;
983 public Brush ShineColor { get => _shineColor; set => SetProperty(ref _shineColor, value); }
984
993 private Brush _shineColorBottom = Brushes.White;
1002 public Brush ShineColorBottom { get => _shineColorBottom; set => SetProperty(ref _shineColorBottom, value); }
1003
1012 private Brush _backgroundTop = Brushes.Wheat;
1021 public Brush BackgroundTop { get => _backgroundTop; set => SetProperty(ref _backgroundTop, value); }
1022
1031 private Brush _background = Brushes.DarkBlue;
1040 public Brush Background { get => _background; set => SetProperty(ref _background, value); }
1041
1050 public IconPosition ImagePosition { get; set; } = IconPosition.Left;
1051
1060 private bool _isEnabled = true;
1069 public bool IsEnabled { get => _isEnabled; set => SetProperty(ref _isEnabled, value); }
1070
1079 public FontWeight FontWeight { get; set; } = FontWeights.Normal;
1088 public Visibility Visibility { get; set; } = Visibility.Visible;
1097 public Thickness Margin { get; set; } = new Thickness(0, 0, 4, 0);
1106 public object CommandParameter { get; set; } = null!;
1115 public IInputElement CommandTarget { get; set; } = null!;
1116
1125 private ICommand? _command;
1134 public ICommand? Command { get => _command; set => SetProperty(ref _command, value); }
1135
1144 private bool _isSelected = false;
1153 public bool IsSelected { get => _isSelected; set => SetProperty(ref _isSelected, value); }
1154
1163 private bool _isFocusableEx = true;
1172 public bool IsFocusableEx { get => _isFocusableEx; set => SetProperty(ref _isFocusableEx, value); }
1173
1182 private int _grade = 0;
1191 public int Grade { get => _grade; set => SetProperty(ref _grade, value); }
1192
1201 private int _minimumGrade = 0;
1210 public int MinimumGrade { get => _minimumGrade; set => SetProperty(ref _minimumGrade, value); }
1211 }
1212
1221 public static class VsNavigationHelper
1222 {
1231 public static bool IsOnceLoginEnabled { get; set; } = false;
1232
1241 public static object? CurrentUser { get; set; } = null;
1242
1323 public static ButtonData Create(
1324 string content,
1325 ICommand? command = null,
1326 int minGrade = 0,
1327 string? imagePath = null,
1328 bool isSelected = false,
1329 bool isFocusable = true,
1330 Visibility visibility = Visibility.Visible,
1331 IconPosition imagePosition = IconPosition.Top)
1332 {
1333 return new ButtonData
1334 {
1335 Content = content,
1336 Command = command,
1337 MinimumGrade = minGrade,
1338 ImagePath = imagePath!,
1339 ImagePosition = imagePosition,
1340 FontWeight = FontWeights.Bold,
1341 Foreground = GetColor("#143a5a"),
1342 ShineColor = GetColor("#104E8B"),
1343 ShineColorBottom = Brushes.White,
1344 BackgroundTop = Brushes.White,
1345 Background = Brushes.LightGray,
1346 IsSelected = isSelected,
1347 IsFocusableEx = isFocusable,
1348 Visibility = visibility
1349 };
1350 }
1351
1392 private static SolidColorBrush GetColor(string hex)
1393 {
1394 return new SolidColorBrush((Color)ColorConverter.ConvertFromString(hex));
1395 }
1396 }
1397}
static void ShowAsync(string message, string title="Information", MessageBoxButton buttons=MessageBoxButton.OK, MessageBoxImage icon=MessageBoxImage.None, Action< MessageBoxResult >? callback=null, MessageBoxResult autoClick=MessageBoxResult.None, int autoClickDelaySeconds=0, int enableDelaySeconds=0)
static void OnPositionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
void ButtonDatas_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
static readonly HashSet< DreamineNavigationBar > _instances
static void OnButtonDatasChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
static void OnOnceLoginChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
static IEnumerable< T > FindVisualChildren< T >(DependencyObject depObj)
static void OnGlobalInputStatic(object sender, PreProcessInputEventArgs e)
void ApplyLastButtonMargin(Thickness defaultMargin, Thickness lastMargin)
static void OnAutoLogoutTimeoutChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
static ButtonData Create(string content, ICommand? command=null, int minGrade=0, string? imagePath=null, bool isSelected=false, bool isFocusable=true, Visibility visibility=Visibility.Visible, IconPosition imagePosition=IconPosition.Top)