From 944187c3144bfd8c9e5cf16db6fc452fbbab3a8f Mon Sep 17 00:00:00 2001 From: Yun Chan Date: Tue, 2 Jun 2026 10:17:21 +0900 Subject: [PATCH] =?UTF-8?q?refactor(P5b-7):=20=EC=9E=94=EC=97=AC=20?= =?UTF-8?q?=EC=9D=B4=EB=B2=A4=ED=8A=B8=20=ED=95=B8=EB=93=A4=EB=9F=AC=20?= =?UTF-8?q?=EC=A0=84=EB=B6=80=20=EC=BB=A4=EB=A7=A8=EB=93=9C/behavior?= =?UTF-8?q?=EB=A1=9C=20=E2=80=94=20XAML=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20?= =?UTF-8?q?=ED=95=B8=EB=93=A4=EB=9F=AC=200=EA=B0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Microsoft.Xaml.Behaviors.Wpf 도입. MainWindow.xaml에 직접 이벤트 핸들러(Click/SelectionChanged/Drop/ MouseUp 등) 0개 — 전부 선언적 바인딩·커맨드·InvokeCommandAction으로 전환. - 드래그앤드롭(DragOver/DragLeave/Drop) → i:EventTrigger + InvokeCommandAction(PassEventArgsToCommand), HandleDragOver/HandleFilesDropped(DragEventArgs) 추출. - 동적 포맷 콤보 SelectionChanged → OutputFormatChangedCommand(OnOutputFormatSelected). - 큐·이력 행 PreviewMouseLeftButtonUp → QueueRow/PastRowCommand(MouseButtonEventArgs, IsInsideButton 가드 보존 + e.OriginalSource.DataContext로 항목 해소). - ProcessQueueButton Click → ProcessQueueCommand. - 검증: 빌드 0/0, 83 테스트 그린, publish 후 메인 창 크래시 없이 로드(behaviors self-contained 정상). 드래그앤드롭·행 미리보기·포맷 변경 실동작은 GUI 스모크 권장(비치명적 실패 모드). --- src/Directory.Packages.props | 1 + .../Everything2Everything.App.csproj | 1 + .../Views/MainWindow.xaml | 45 ++++++++++++++----- .../Views/MainWindow.xaml.cs | 36 +++++++++------ 4 files changed, 58 insertions(+), 25 deletions(-) diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index fbc347b..48f95e8 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -32,6 +32,7 @@ + diff --git a/src/Everything2Everything.App/Everything2Everything.App.csproj b/src/Everything2Everything.App/Everything2Everything.App.csproj index 21197fe..00dfb60 100644 --- a/src/Everything2Everything.App/Everything2Everything.App.csproj +++ b/src/Everything2Everything.App/Everything2Everything.App.csproj @@ -22,6 +22,7 @@ + diff --git a/src/Everything2Everything.App/Views/MainWindow.xaml b/src/Everything2Everything.App/Views/MainWindow.xaml index ac6d56a..259136b 100644 --- a/src/Everything2Everything.App/Views/MainWindow.xaml +++ b/src/Everything2Everything.App/Views/MainWindow.xaml @@ -2,6 +2,7 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml" + xmlns:i="http://schemas.microsoft.com/xaml/behaviors" Title="FormatShift Utility" Icon="pack://application:,,,/Assets/app-icon.png" Width="1280" Height="960" @@ -11,9 +12,6 @@ WindowCornerPreference="Round" WindowStartupLocation="CenterScreen" AllowDrop="True" - Drop="OnFilesDropped" - DragOver="OnDragOver" - DragLeave="OnDragLeave" TextOptions.TextFormattingMode="Display" UseLayoutRounding="True"> @@ -31,6 +29,18 @@ + + + + + + + + + + + + @@ -97,8 +107,13 @@ HorizontalAlignment="Center" VerticalAlignment="Center"/> + Margin="8,0,0,0" VerticalAlignment="Center"> + + + + + + + Command="{Binding ProcessQueueCommand, RelativeSource={RelativeSource AncestorType=Window}}"/> @@ -456,9 +471,12 @@ Background="{StaticResource FsBgPanel}" BorderBrush="{StaticResource FsBorderSubtle}" BorderThickness="1" - Cursor="Hand" - PreviewMouseLeftButtonUp="OnQueueRowClick" - Tag="{Binding}"> + Cursor="Hand"> + + + + + @@ -570,9 +588,12 @@ + Background="Transparent"> + + + + + diff --git a/src/Everything2Everything.App/Views/MainWindow.xaml.cs b/src/Everything2Everything.App/Views/MainWindow.xaml.cs index 2296a29..414b6f0 100644 --- a/src/Everything2Everything.App/Views/MainWindow.xaml.cs +++ b/src/Everything2Everything.App/Views/MainWindow.xaml.cs @@ -53,6 +53,12 @@ public partial class MainWindow : Wpf.Ui.Controls.FluentWindow public ICommand TabCommand { get; } public ICommand ConflictRuleCommand { get; } public ICommand CombineToggleCommand { get; } + public ICommand OutputFormatChangedCommand { get; } + public ICommand DragOverCommand { get; } + public ICommand DragLeaveCommand { get; } + public ICommand DropCommand { get; } + public ICommand QueueRowCommand { get; } + public ICommand PastRowCommand { get; } public MainWindow(ConversionEngine engine, ISettingsStore settings, IReadOnlyList? initialFiles = null) { @@ -78,6 +84,12 @@ public partial class MainWindow : Wpf.Ui.Controls.FluentWindow TabCommand = new RelayCommand(p => ShowTab(p as string ?? "Past")); ConflictRuleCommand = new RelayCommand(p => SetConflictRule(p as string)); CombineToggleCommand = new RelayCommand(_ => UpdateCombineState(SelectedOutputExtension)); + OutputFormatChangedCommand = new RelayCommand(_ => OnOutputFormatSelected()); + DragOverCommand = new RelayCommand(p => HandleDragOver(p as DragEventArgs)); + DragLeaveCommand = new RelayCommand(_ => DropHintOverlay.Visibility = Visibility.Collapsed); + DropCommand = new RelayCommand(p => HandleFilesDropped(p as DragEventArgs)); + QueueRowCommand = new RelayCommand(p => HandleQueueRowClick(p as MouseButtonEventArgs)); + PastRowCommand = new RelayCommand(p => HandlePastRowClick(p as MouseButtonEventArgs)); InitializeComponent(); @@ -164,8 +176,9 @@ public partial class MainWindow : Wpf.Ui.Controls.FluentWindow // ============== Drag & Drop ============== - private void OnDragOver(object sender, DragEventArgs e) + private void HandleDragOver(DragEventArgs? e) { + if (e is null) return; e.Effects = e.Data.GetDataPresent(DataFormats.FileDrop) ? DragDropEffects.Copy : DragDropEffects.None; DropHintOverlay.Visibility = e.Effects == DragDropEffects.Copy @@ -173,15 +186,10 @@ public partial class MainWindow : Wpf.Ui.Controls.FluentWindow e.Handled = true; } - private void OnDragLeave(object sender, DragEventArgs e) + private void HandleFilesDropped(DragEventArgs? e) { DropHintOverlay.Visibility = Visibility.Collapsed; - } - - private void OnFilesDropped(object sender, DragEventArgs e) - { - DropHintOverlay.Visibility = Visibility.Collapsed; - if (!e.Data.GetDataPresent(DataFormats.FileDrop)) return; + if (e is null || !e.Data.GetDataPresent(DataFormats.FileDrop)) return; if (e.Data.GetData(DataFormats.FileDrop) is not string[] paths) return; AddToQueue(ExpandPaths(paths)); @@ -308,20 +316,22 @@ public partial class MainWindow : Wpf.Ui.Controls.FluentWindow private string? _selectedPreviewPath; private CancellationTokenSource? _previewCts; - private async void OnQueueRowClick(object sender, MouseButtonEventArgs e) + private async void HandleQueueRowClick(MouseButtonEventArgs? e) { + if (e is null) return; if (e.OriginalSource is DependencyObject src && IsInsideButton(src)) return; - if (sender is not FrameworkElement fe || fe.Tag is not QueueItem item) return; + if ((e.OriginalSource as FrameworkElement)?.DataContext is not QueueItem item) return; _selectedPreviewItem = item; await LoadPreviewAsync(item); e.Handled = true; } - private async void OnPastRowClick(object sender, MouseButtonEventArgs e) + private async void HandlePastRowClick(MouseButtonEventArgs? e) { + if (e is null) return; if (e.OriginalSource is DependencyObject src && IsInsideButton(src)) return; - if (sender is not FrameworkElement fe || fe.Tag is not HistoryRow row) return; + if ((e.OriginalSource as FrameworkElement)?.DataContext is not HistoryRow row) return; if (row.SourcePath == "") { @@ -1036,7 +1046,7 @@ public partial class MainWindow : Wpf.Ui.Controls.FluentWindow }; } - private void OnOutputFormatChanged(object sender, SelectionChangedEventArgs e) + private void OnOutputFormatSelected() { if (_suppressFormatChanged) return; if (OutputFormatCombo.SelectedItem is ComboBoxItem item && item.Tag is string ext)