6차 B-1: 병합 뒤 목록이 바로 서고 충돌 목록에서 3분할로 연다
병합하고 파일 비교 탭으로 오면 목록이 병합 전 커밋 파일(304개) 그대로 였고, [새로고침] 을 한 번 더 눌러야 충돌 23개가 나왔다. 충돌 탭 목록을 두 번 눌러도 아무 일이 없었고, 안내 글은 없는 "홈 탭" 을 가리켰다. 원인: 새로고침은 게이트를 쥔 채 화면 목록을 갈아 끼우고, 끝나자마자 Git 탭이 게이트를 다시 잡는다. 파일 비교 화면이 그 틈에 목록을 세우려다 "다른 작업 진행 중" 으로 건너뛰었다. - OnWorkspaceRefreshed: 게이트가 풀릴 때까지 기다렸다 목록을 세운다 - GitWorkspace.RequestOpenConflict: 충돌 파일을 파일 비교 탭에서 열어 달라는 요청을 남겨 두고 탭을 옮긴다(폴더 비교의 넘겨주기와 같은 방식) - OpenConflictAsync: 목록에 설 때까지 기다렸다 고른다. 이 요청 때문에 화면이 새로 만들어졌으면 기본 저장소를 새로 열지 않는다 - 충돌 목록: 두 번 누르기(MouseDoubleClick), Enter, [3분할로 열기] - 안내 글과 병합 알림의 "홈 탭" 을 실제 탭 이름으로 화면 확인 17건 통과(병합 뒤 목록 23개, 두 번 누르기와 단추와 Enter 로 네 칸, 파일 비교 탭을 안 연 새 앱에서도 열림, 병합 취소 뒤 Git 탭 새로고침만으로 목록 304개). 경고 0, 오류 0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
04b8ef8a60
commit
9b597fc50f
@ -317,3 +317,57 @@ CP949 로 쓰이고 양쪽 한글이 다 읽히는지까지 본다.
|
||||
A 의 다섯 가지(줄끝 · 반쯤 된 실패 · 상대 칸 · 지운 파일 · Both)가 모두 끝났다. 끝까지 풀어 커밋까지 가는 확인은 D-1 에서
|
||||
처음부터 다시 한다. A 를 하다 B-1 으로 넘긴 것 - **새로고침 도중 게이트를 쥔 채 목록 다시 세우기가 불려 건너뛰는 뿌리** -
|
||||
는 아직 그대로다(지금은 목록이 비지 않고 옛 목록이 남는다).
|
||||
|
||||
|
||||
---
|
||||
|
||||
## B-1. 충돌 목록에서 3분할 화면까지 가는 길이 끊겨 있다 (2026-09-14, 완료)
|
||||
|
||||
### 고치기 전에 다시 났다
|
||||
|
||||
새로 만든 저장소에서 파일 비교 탭을 한 번 열어 둔 채 Git 탭으로 `feature/theirs` 를 병합했다(충돌 23개).
|
||||
|
||||
| 본 것 | 값 |
|
||||
|---|---|
|
||||
| 파일 비교 탭 파일 목록 ([새로고침] 안 누름) | **304개** — 병합 전 커밋 파일 그대로. 충돌 23개가 아니다 |
|
||||
| 충돌 탭 목록을 두 번 누름 | **아무 일 없음** |
|
||||
| 충돌 탭 안내 글 · 병합 알림 | "**홈 탭**의 3분할 화면" · "**홈 탭**에서 해결하세요" — 그런 탭은 없다 |
|
||||
|
||||
5차 C-1 때는 목록이 **0개** 였다. 6차 A-2 에서 "건너뛴 목록 불러오기가 목록을 비우지 않게" 막아서 이제는 **옛 목록이 남는다.**
|
||||
|
||||
### 무엇이 원인이었나
|
||||
|
||||
**목록.** 새로고침은 저장소 상태를 읽는 동안(게이트를 쥔 채) 화면 목록을 갈아 끼우고, 끝나자마자 Git 탭이 제 일로
|
||||
게이트를 다시 잡는다. 파일 비교 화면은 새로고침 신호를 받자마자 목록을 다시 세우려다 **"다른 작업 진행 중" 으로 건너뛰었다.**
|
||||
A-2 · A-4 에서는 저장한 뒤에만 기다렸다 다시 세우게 했고, 새로고침 자체는 그대로였다.
|
||||
|
||||
**두 번 누르기.** 충돌 목록과 파일 비교 화면을 잇는 통로가 없었다. 폴더 비교 → 파일 비교에는 있던 것이다.
|
||||
|
||||
### 고친 것
|
||||
|
||||
| 자리 | 무엇 |
|
||||
|---|---|
|
||||
| `HomeViewModel.OnWorkspaceRefreshed` | 새로고침 뒤 목록 세우기를 **게이트가 풀릴 때까지 기다렸다** 한다(`RebuildListAfterRefreshAsync`). 막히면 몇 번 더 해 본다 |
|
||||
| `GitWorkspace.RequestOpenConflict` (새 통로) | "이 충돌 파일을 3분할로 열어 달라" 요청을 **남겨 두고** 파일 비교 탭으로 옮긴다. 폴더 비교의 넘겨주기와 같은 방식이다 - 파일 비교 화면은 탭을 처음 열 때 만들어지므로 받을 사람이 없을 수 있다 |
|
||||
| `HomeViewModel.OpenConflictAsync` | 요청을 받으면 목록에 그 파일이 설 때까지 기다렸다 고른다. 충돌 파일이라 A-3 대로 **네 칸**으로 열린다. 이 요청 때문에 화면이 새로 만들어졌으면 **기본 저장소를 새로 열지 않고** 지금 저장소로 목록만 세운다 |
|
||||
| Git 탭 충돌 목록 | 두 번 누르기 · Enter · **[3분할로 열기]** 단추. 이진 파일은 줄 단위로 못 본다고 알린다 |
|
||||
| 두 번 누르기 받는 법 | `MouseBinding` 이 아니라 목록의 `MouseDoubleClick` - 항목이 마우스 누름을 먼저 처리해 `MouseBinding` 은 항목 위에서 안 온다 |
|
||||
| 안내 글 · 병합 알림 | "…줄마다 고르려면 목록을 두 번 누르거나 [3분할로 열기] 를 누르세요. 파일 비교 탭에서 세 칸으로 열립니다." · "충돌 23건 - 충돌 탭에서 파일을 두 번 누르면 파일 비교 탭에서 세 칸으로 풉니다." |
|
||||
|
||||
### 확인한 것 (새로 만든 저장소, MergeHub 로 병합)
|
||||
|
||||
| 확인 | 결과 |
|
||||
|---|---|
|
||||
| 병합 알림 | "충돌 23건 - 충돌 탭에서 파일을 두 번 누르면…" (홈 탭 없음) |
|
||||
| 병합 뒤 파일 비교 탭, [새로고침] 안 누름 | 목록 **23개** |
|
||||
| 충돌 탭 안내 글 | 두 번 누르기 · [3분할로 열기] 를 알려 줌, 홈 탭 없음 |
|
||||
| 목록 두 번 누르기 | 파일 비교 탭으로 넘어가 `Io/Unit20.cs` 가 네 칸으로 열림, 충돌 잡힘 (약 1초) |
|
||||
| [3분할로 열기] | `Motion/Unit0.cs` 가 네 칸으로 (약 0.8초) |
|
||||
| Enter | `Io/LineEnding.cs` 가 네 칸으로 (약 1초) |
|
||||
| **파일 비교 탭을 한 번도 안 연 새 앱**에서 두 번 누르기 | 파일 비교 탭이 새로 만들어지며 `Io/Unit12.cs` 가 네 칸으로, 저장소는 `mergehub-c1` 그대로, 목록 23개 |
|
||||
| `git merge --abort` 뒤 **Git 탭에서만** [새로고침] | 파일 비교 탭 목록이 커밋 파일 304개로 돌아옴, REMOTE 칸 없음 |
|
||||
| 열기만 하는 동안 git 충돌 수 | 23 그대로 |
|
||||
|
||||
화면 확인 **17건 통과**(Enter 1건은 처음에 목록 아래쪽 행을 골랐다가 자동 조작이 못 찾아 실패 - 가상화된 목록이라 화면 밖 행은
|
||||
안 만들어진다. 위쪽 행으로 다시 해 통과). 이번에 고친 파일은 검사 하네스가 쓰지 않아 앞의 785건이 그대로 유효하다.
|
||||
빌드 경고 0 · 오류 0.
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
|
||||
| 날짜 | 항목 | 커밋 |
|
||||
|---|---|---|
|
||||
| 2026-09-14 | **6차 B-1** — 새로고침 뒤 파일 목록을 게이트가 풀릴 때까지 기다렸다 세움(병합 뒤 304개 → 23개). 충돌 목록 두 번 누르기·Enter·[3분할로 열기] 로 파일 비교 탭 네 칸에서 연다. 없는 '홈 탭' 글을 고침 | 아래 |
|
||||
| 2026-09-14 | **6차 A-5** — 파일 단위 Accept Both 가 두 벌을 글로 풀어 이어 붙이고 내 쪽 형식으로 쓴다(가운데 BOM 사라짐). 누르기 전에 무엇이 일어나는지 묻는다. A 묶음 끝 | 아래 |
|
||||
| 2026-09-14 | **6차 A-4** — 한쪽이 지운 충돌(UD·DU)은 줄 단위 3분할 대신 남은 파일을 두 칸으로 보이고 [파일 남기기] · [파일 지우기] 로 푼다. 거꾸로 세던 범례(Theirs only 89)가 없어짐 | 아래 |
|
||||
| 2026-09-14 | **6차 A-3** — 병합 중 충돌 파일을 열면 콤보를 안 건드려도 네 칸이 뜬다. REMOTE 제목에 상대 브랜치 이름(MERGE_HEAD → 병합 메시지 → 짧은 해시) | 아래 |
|
||||
|
||||
@ -103,7 +103,7 @@
|
||||
|
||||
| 상태 | 항목 |
|
||||
|---|---|
|
||||
| 대기 | B-1 충돌 목록에서 3분할 화면까지 가는 길이 끊겨 있다 |
|
||||
| **완료** | B-1 충돌 목록에서 3분할 화면까지 가는 길이 끊겨 있다 — 병합 뒤 목록이 바로 서고, 두 번 누르기·Enter·[3분할로 열기] |
|
||||
| 대기 | B-2 확인 창이 엉뚱한 파일 이름을 댄다 |
|
||||
| 대기 | B-3 커밋 메시지가 엉뚱하게 남는다 |
|
||||
| 대기 | B-4 MERGED 칸 줄 번호가 네 자리에서 잘린다 |
|
||||
|
||||
@ -523,6 +523,40 @@ namespace MergeHub.Services
|
||||
PlainCompareRequested.Invoke(pending.Left, pending.Right);
|
||||
}
|
||||
|
||||
// ================= 충돌 파일을 파일 비교 탭에서 열기 (6차 B-1) =================
|
||||
|
||||
/// <summary>
|
||||
/// "이 충돌 파일을 3분할로 열어 달라" - Git 탭의 충돌 목록이 보내고 파일 비교 탭이 받는다.
|
||||
///
|
||||
/// 예전에는 충돌 목록을 두 번 눌러도 아무 일이 없었다. 줄마다 고르려면 파일 비교 탭으로 가서
|
||||
/// [새로고침] 을 누르고 콤보에서 같은 파일을 다시 찾아야 했다.
|
||||
/// </summary>
|
||||
public event Action<string>? OpenConflictRequested;
|
||||
|
||||
/// <summary>아직 아무도 받지 않은 요청. 파일 비교 화면은 탭을 처음 열 때 만들어지므로 남겨 둔다.</summary>
|
||||
private string? _pendingOpenConflict;
|
||||
|
||||
public bool HasPendingOpenConflict => _pendingOpenConflict is not null;
|
||||
|
||||
public void RequestOpenConflict(string path)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(path)) return;
|
||||
|
||||
_pendingOpenConflict = path;
|
||||
OnUi(DeliverPendingOpenConflict);
|
||||
ActivateWhenReady(MergeHub.ViewIds.Home);
|
||||
}
|
||||
|
||||
/// <summary>남아 있는 요청을 넘긴다. 파일 비교 화면이 새로 만들어지면 스스로 한 번 불러 준다.</summary>
|
||||
public void DeliverPendingOpenConflict()
|
||||
{
|
||||
if (_pendingOpenConflict is not { } path) return;
|
||||
if (OpenConflictRequested is null) return;
|
||||
|
||||
_pendingOpenConflict = null;
|
||||
OpenConflictRequested.Invoke(path);
|
||||
}
|
||||
|
||||
// ================= 켤 때 넘어온 인자 =================
|
||||
|
||||
/// <summary>폴더 비교 화면이 아직 없을 때 남겨 두는 요청. 파일 비교 쪽과 같은 방식이다.</summary>
|
||||
|
||||
@ -148,7 +148,7 @@ namespace MergeHub.ViewModels
|
||||
else Summary = "오른쪽 폴더를 고르고 비교를 누르세요.";
|
||||
}
|
||||
|
||||
/// <summary>홈 탭에서 이 두 파일을 비교해 달라고 알린다.</summary>
|
||||
/// <summary>파일 비교 탭에서 이 두 파일을 비교해 달라고 알린다.</summary>
|
||||
public event EventHandler<(string Left, string Right)>? OpenInDiffRequested;
|
||||
|
||||
public ObservableCollection<FolderNode> Nodes { get; } = new ObservableCollection<FolderNode>();
|
||||
|
||||
@ -237,7 +237,7 @@ namespace MergeHub.ViewModels
|
||||
CommitMessage = $"Merge branch '{branch}' into {_workspace.CurrentBranch}";
|
||||
|
||||
if (result.HasConflicts)
|
||||
MessageUtil.ShowWarningToast($"충돌 {result.Conflicts.Count}건 - 홈 탭에서 해결하세요.");
|
||||
MessageUtil.ShowWarningToast($"충돌 {result.Conflicts.Count}건 - 충돌 탭에서 파일을 두 번 누르면 파일 비교 탭에서 세 칸으로 풉니다.");
|
||||
else
|
||||
MessageUtil.ShowToast(result.Message);
|
||||
});
|
||||
@ -285,6 +285,25 @@ namespace MergeHub.ViewModels
|
||||
return go ? ResolveAsync(ConflictResolution.Both) : Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 고른 충돌 파일을 파일 비교 탭의 3분할로 연다(6차 B-1). 목록을 두 번 누르거나 Enter 로도 온다.
|
||||
/// 켜짐/꺼짐을 따로 두지 않는다 - 고른 것이 없으면 아무 일도 안 한다. 이진 파일은 줄 단위로 볼 수 없어 알린다.
|
||||
/// </summary>
|
||||
[RelayCommand]
|
||||
private void OpenConflictInCompare()
|
||||
{
|
||||
var conflict = SelectedConflict;
|
||||
if (conflict is null) return;
|
||||
|
||||
if (conflict.IsBinary)
|
||||
{
|
||||
MessageUtil.ShowWarningToast($"이진 파일이라 줄 단위로 볼 수 없습니다. 아래 Accept 단추로 한쪽을 고르세요: {conflict.Path}");
|
||||
return;
|
||||
}
|
||||
|
||||
_workspace.RequestOpenConflict(conflict.Path);
|
||||
}
|
||||
|
||||
private Task ResolveAsync(ConflictResolution resolution) =>
|
||||
_workspace.RunAndRefreshAsync($"충돌 해결({resolution})",
|
||||
() => _workspace.Git.ResolveConflict(SelectedConflict!.Path, resolution));
|
||||
|
||||
@ -36,6 +36,7 @@ namespace MergeHub.ViewModels
|
||||
_workspace.Refreshed += OnWorkspaceRefreshed;
|
||||
_workspace.StateChanged += RaiseCanExecuteChanged;
|
||||
_workspace.PlainCompareRequested += OnPlainCompareRequested;
|
||||
_workspace.OpenConflictRequested += OnOpenConflictRequested;
|
||||
_workspace.Recent.Changed += (_, _) => _workspace.OnUi(RefreshRecent);
|
||||
_workspace.QueueChanged += (_, _) => RefreshQueue();
|
||||
|
||||
@ -50,6 +51,16 @@ namespace MergeHub.ViewModels
|
||||
IsGitMode = _workspace.IsRepositoryOpen;
|
||||
RefreshRemoteBranches();
|
||||
|
||||
// Git 탭의 충돌 목록에서 파일을 두 번 눌러 이 화면이 처음 만들어진 경우(6차 B-1).
|
||||
// 저장소는 Git 탭이 이미 열었다. 아래의 기본 저장소 열기로 가면 다른 저장소를 열 수 있다 -
|
||||
// 지금 저장소로 목록만 세우고 그 파일을 연다.
|
||||
if (_workspace.HasPendingOpenConflict)
|
||||
{
|
||||
OnWorkspaceRefreshed();
|
||||
_workspace.DeliverPendingOpenConflict();
|
||||
return;
|
||||
}
|
||||
|
||||
// 폴더 비교에서 파일을 골라 이 화면이 만들어진 경우라면 그 파일을 먼저 연다.
|
||||
// 이 화면은 탭을 처음 열 때 만들어지므로, 요청이 우리보다 먼저 도착해 있을 수 있다.
|
||||
if (_workspace.HasPendingPlainCompare)
|
||||
@ -868,10 +879,57 @@ namespace MergeHub.ViewModels
|
||||
|
||||
OnPropertyChanged(nameof(SelectedCommit));
|
||||
RaiseCommitDetail();
|
||||
_ = LoadCommitAsync();
|
||||
_ = RebuildListAfterRefreshAsync();
|
||||
// 여기서 다시 세우는 것은 사용자가 고른 것이 아니다. LoadCommitAsync 안에서 묻지 않게 한다.
|
||||
}
|
||||
|
||||
private void OnOpenConflictRequested(string path) => _ = OpenConflictAsync(path);
|
||||
|
||||
/// <summary>
|
||||
/// Git 탭 충돌 목록에서 고른 파일을 연다(6차 B-1). 충돌 파일이므로 A-3 대로 세 칸으로 열린다.
|
||||
///
|
||||
/// 목록에 그 파일이 아직 없을 수 있다 - 병합 직후라 새로고침 뒤 목록 세우기가 아직 안 끝났거나,
|
||||
/// 이 화면이 방금 만들어졌다. 게이트가 풀릴 때까지 기다렸다 목록을 세우고 나서 고른다.
|
||||
/// 고르는 일은 <see cref="SelectedFile"/> 에 맡긴다 - 저장 안 한 편집이 있으면 거기서 묻는다.
|
||||
/// </summary>
|
||||
private async Task OpenConflictAsync(string path)
|
||||
{
|
||||
if (!_workspace.IsRepositoryOpen) return;
|
||||
IsGitMode = true;
|
||||
|
||||
for (var attempt = 0; attempt < 5 && !Files.Contains(path); attempt++)
|
||||
{
|
||||
await _workspace.WhenIdleAsync();
|
||||
await LoadCommitAsync();
|
||||
}
|
||||
|
||||
if (!Files.Contains(path))
|
||||
{
|
||||
MessageUtil.ShowWarningToast($"파일 목록에서 찾지 못했습니다: {path}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!string.Equals(SelectedFile, path, StringComparison.Ordinal))
|
||||
SelectedFile = path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 새로고침 뒤 파일 목록을 <b>반드시</b> 다시 세운다(6차 B-1).
|
||||
///
|
||||
/// 새로고침은 저장소 상태를 읽는 동안(게이트를 쥔 채) 화면 목록을 갈아 끼우고, 끝나자마자 Git 탭이 제 일로
|
||||
/// 게이트를 다시 잡는다. 그 틈에 목록 세우기를 부르면 "다른 작업 진행 중" 으로 건너뛰었다.
|
||||
/// 병합하고 파일 비교 탭으로 오면 목록이 병합 전 커밋 파일(304개) 그대로였고, [새로고침] 을 한 번 더 눌러야 충돌 23개가 나왔다.
|
||||
/// 게이트가 풀릴 때까지 기다렸다가 세우고, 또 막히면 몇 번 더 해 본다.
|
||||
/// </summary>
|
||||
private async Task RebuildListAfterRefreshAsync()
|
||||
{
|
||||
for (var attempt = 0; attempt < 5; attempt++)
|
||||
{
|
||||
await _workspace.WhenIdleAsync();
|
||||
if (await LoadCommitAsync()) return;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>선택 커밋이 담고 있는 파일 목록을 채우고, 가능하면 이전 선택을 유지한다.</summary>
|
||||
/// <returns>
|
||||
/// 목록을 다시 세웠거나 세울 것이 없으면 true. <b>게이트에 막혀 건너뛰었으면 false</b> -
|
||||
|
||||
@ -240,11 +240,18 @@
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0" Style="{StaticResource Muted}" Margin="0,0,0,6" TextWrapping="Wrap"
|
||||
Text="파일 전체를 한쪽으로 채택하려면 아래 버튼을 쓰고, 줄 단위로 고르려면 홈 탭의 3분할 화면에서 처리하세요."/>
|
||||
Text="파일 전체를 한쪽으로 채택하려면 아래 버튼을 쓰고, 줄마다 고르려면 목록을 두 번 누르거나 [3분할로 열기] 를 누르세요. 파일 비교 탭에서 세 칸으로 열립니다."/>
|
||||
|
||||
<!-- 두 번 누르거나 Enter 로 파일 비교 탭의 3분할에서 연다(6차 B-1).
|
||||
두 번 누르기는 MouseBinding 이 아니라 목록의 MouseDoubleClick 으로 받는다 -
|
||||
항목이 마우스 누름을 먼저 처리해 버려 MouseBinding 은 항목 위에서 안 온다. -->
|
||||
<ListBox Grid.Row="1" ItemsSource="{Binding Workspace.Conflicts}"
|
||||
SelectedItem="{Binding SelectedConflict}" HorizontalContentAlignment="Stretch"
|
||||
MouseDoubleClick="Conflicts_MouseDoubleClick"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
|
||||
<ListBox.InputBindings>
|
||||
<KeyBinding Key="Enter" Command="{Binding OpenConflictInCompareCommand}"/>
|
||||
</ListBox.InputBindings>
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
@ -271,6 +278,8 @@
|
||||
</ListBox>
|
||||
|
||||
<WrapPanel Grid.Row="2" Margin="0,6,0,0">
|
||||
<Button Content="3분할로 열기" Command="{Binding OpenConflictInCompareCommand}" MinWidth="100"
|
||||
ToolTip="고른 파일을 파일 비교 탭에서 세 칸으로 엽니다. 목록을 두 번 누르거나 Enter 를 눌러도 됩니다"/>
|
||||
<Button Content="Accept Mine" Command="{Binding AcceptMineCommand}" MinWidth="100" Style="{StaticResource FilledButton}"
|
||||
Background="{DynamicResource Ubisam.Brush.Success}"
|
||||
Foreground="{DynamicResource Ubisam.Brush.Accent.OnFill}"/>
|
||||
|
||||
@ -36,6 +36,14 @@ namespace MergeHub.Views
|
||||
}
|
||||
|
||||
// PasswordBox 는 보안상 Password 를 바인딩할 수 없어 코드 비하인드로 전달한다.
|
||||
/// <summary>충돌 목록을 두 번 누르면 파일 비교 탭의 3분할에서 연다(6차 B-1). 항목 위가 아니면 무시한다.</summary>
|
||||
private void Conflicts_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
|
||||
{
|
||||
if (e.OriginalSource is not System.Windows.DependencyObject hit) return;
|
||||
if (System.Windows.Controls.ItemsControl.ContainerFromElement((System.Windows.Controls.ItemsControl)sender, hit) is null) return;
|
||||
if (DataContext is MergeHub.ViewModels.GitViewModel vm && vm.OpenConflictInCompareCommand.CanExecute(null))
|
||||
vm.OpenConflictInCompareCommand.Execute(null);
|
||||
}
|
||||
private void Password_Changed(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is PasswordBox box && ViewModel is { } vm) vm.Workspace.Password = box.Password;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user