Dreamine.MVVM.ViewModels 1.0.5
Dreamine.MVVM.ViewModels 프로젝트의 API와 구성 요소를 제공합니다.
로딩중...
검색중...
일치하는것 없음
RelayCommand.cs
이 파일의 문서화 페이지로 가기
1using System.Windows.Input;
2
4{
13 public sealed class RelayCommand : ICommand
14 {
23 private readonly Action _execute;
32 private readonly Func<bool>? _canExecute;
33
42 private readonly SynchronizationContext? _syncContext;
43
76 public RelayCommand(Action execute, Func<bool>? canExecute = null)
77 {
78 // Argument 검증
79 _execute = execute ?? throw new ArgumentNullException(nameof(execute));
80 _canExecute = canExecute;
81
82 // 생성 시점의 SynchronizationContext를 캡처하여 UI 스레드 마샬링에 사용
83 _syncContext = SynchronizationContext.Current;
84 }
85
110 public bool CanExecute(object? parameter)
111 {
112 // canExecute 미지정 시 항상 실행 가능
113 return _canExecute?.Invoke() ?? true;
114 }
115
132 public void Execute(object? parameter)
133 {
134 if (CanExecute(parameter))
135 _execute();
136 }
137
146 public event EventHandler? CanExecuteChanged;
147
157 {
158 // 이벤트 핸들러가 없으면 빠른 종료
159 var handler = CanExecuteChanged;
160 if (handler is null)
161 return;
162
163 // 캡처한 컨텍스트가 있고 현재 컨텍스트가 다르면 Post로 UI 스레드 호출
164 if (_syncContext != null && !ReferenceEquals(SynchronizationContext.Current, _syncContext))
165 {
166 _syncContext.Post(static state =>
167 {
168 // state는 (RelayCommand, EventHandler) 튜플
169 var tuple = ((RelayCommand cmd, EventHandler evt))state!;
170 tuple.evt.Invoke(tuple.cmd, EventArgs.Empty);
171 }, (this, handler));
172
173 return;
174 }
175
176 // 동일 컨텍스트(보통 UI)에서 즉시 호출
177 handler.Invoke(this, EventArgs.Empty);
178 }
179 }
180
197 public sealed class RelayCommand<T> : ICommand
198 {
207 private readonly Action<T> _execute;
216 private readonly Func<T, bool>? _canExecute;
217
226 private readonly SynchronizationContext? _syncContext;
227
260 public RelayCommand(Action<T> execute, Func<T, bool>? canExecute = null)
261 {
262 // Argument 검증
263 _execute = execute ?? throw new ArgumentNullException(nameof(execute));
264 _canExecute = canExecute;
265
266 // 생성 시점 SynchronizationContext 캡처
267 _syncContext = SynchronizationContext.Current;
268 }
269
294 public bool CanExecute(object? parameter)
295 {
296 // canExecute 미지정 시 항상 실행 가능
297 if (_canExecute is null)
298 return true;
299
300 // 파라미터 변환 실패 시 실행 불가 처리(예외 대신 false)
301 if (!TryGetParameter(parameter, out var value))
302 return false;
303
304 return _canExecute.Invoke(value);
305 }
306
331 public void Execute(object? parameter)
332 {
333 if (!CanExecute(parameter))
334 return;
335
336 if (!TryGetParameter(parameter, out var value))
337 throw new ArgumentException($"Command parameter is not assignable to {typeof(T).FullName}.", nameof(parameter));
338
339 _execute(value);
340 }
341
350 public event EventHandler? CanExecuteChanged;
351
361 {
362 // 핸들러 스냅샷
363 var handler = CanExecuteChanged;
364 if (handler is null)
365 return;
366
367 // 다른 컨텍스트라면 Post로 이벤트 발생
368 if (_syncContext != null && !ReferenceEquals(SynchronizationContext.Current, _syncContext))
369 {
370 _syncContext.Post(static state =>
371 {
372 // state는 (RelayCommand<T>, EventHandler) 튜플
373 var tuple = ((RelayCommand<T> cmd, EventHandler evt))state!;
374 tuple.evt.Invoke(tuple.cmd, EventArgs.Empty);
375 }, (this, handler));
376
377 return;
378 }
379
380 handler.Invoke(this, EventArgs.Empty);
381 }
382
415 private static bool TryGetParameter(object? parameter, out T value)
416 {
417 // null 처리
418 if (parameter is null)
419 {
420 // 참조 타입 또는 Nullable이면 default 허용
421 if (default(T) is null)
422 {
423 value = default!;
424 return true;
425 }
426
427 // non-nullable value type은 null 불가
428 value = default!;
429 return false;
430 }
431
432 // 이미 T면 그대로 사용
433 if (parameter is T t)
434 {
435 value = t;
436 return true;
437 }
438
439 // 타입 불일치
440 value = default!;
441 return false;
442 }
443 }
444}
readonly? Func< bool > _canExecute
readonly? SynchronizationContext _syncContext
bool CanExecute(object? parameter)
RelayCommand(Action execute, Func< bool >? canExecute=null)
static bool TryGetParameter(object? parameter, out T value)
readonly? SynchronizationContext _syncContext
readonly? Func< T, bool > _canExecute
RelayCommand(Action< T > execute, Func< T, bool >? canExecute=null)