[C#/WPF/prism] await에서 버튼을 누르는 데 시간이 걸리는 처리를 할 때 버튼의 연격을 방지해야 한다.
제비를 뽑다
■ 연계 방지
버튼을 눌렀을 때 시간 처리가 필요합니다. await가 진행될 때 버튼의 연격을 방지해야 합니다.
→ https://qiita.com/tera1707/items/a6f11bd3bf2dbf97dd40
버튼을 눌렀을 때 시간이 걸리는 처리가 await에서 진행될 때 버튼이 연결되는 것을 방지하기 위해 2
→ https://qiita.com/tera1707/items/946116bf32d0f1203006
하고 싶은 일
예전에는 타이틀 내용을 만들고 싶어서 prismDelegateCommand
반을 사용해 연속타를 막았다.
다만, 당시의 방식대로라면 여러 버튼으로 같은 일을 하려고 할 때 같은 로고를 몇 개 만들어야 하기 때문에 더 좋은 방법을 찾던 중 그 기사에서 @unidentifiedexe씨가 좋은 방법에 대한 평을 줬다.
코드의 샘플도 적어 직접 사용할 수 있을 것 같지만 스스로 이해했으면 하는 마음에 연습과 함께 코드를 정리하려고 한다.
샘플 코드
WPF의 화면(xaml), ViewModel 및 이번에 제작된 명령 클래스의 코드는 다음과 같습니다.(코드 우선순위 생략)
MainWindow.xaml<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow" Height="500" Width="800">
<StackPanel>
<Button Content="押すと2秒間処理をし、その間は自動で無効になるボタン" FontSize="25" Command="{Binding VmMyCommand1}" Margin="20"/>
<Button Content="ボタン1の有効無効をVMのフラグで切り替えるボタン" FontSize="25" Command="{Binding VmMyCommand2}" Margin="20"/>
</StackPanel>
</Window>
ViewModel.csusing System.ComponentModel;
using System.Diagnostics;
using System.Threading.Tasks;
namespace WpfApp1
{
class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
// ボタン押された時のCommand
public UnRepeatableAsyncCommand VmMyCommand1 { get; private set; }
public UnRepeatableAsyncCommand VmMyCommand2 { get; private set; }
public bool MyCamExecuteFlag
{
get { return _myCamExecuteFlag; }
set { _myCamExecuteFlag = value; OnPropertyChanged(nameof(MyCamExecuteFlag)); }
}
private bool _myCamExecuteFlag = true;
public ViewModel()
{
// (ボタン1) 押したら2秒かかる処理を非同期で行って、その間は自動で無効になるボタン
VmMyCommand1 = new UnRepeatableAsyncCommand(MyAsyncFunc, MyCanExecute);
VmMyCommand1.CanExecuteChanged += ((sender, e) => Debug.WriteLine("CanExecuteChanged1"));
// (ボタン2) ボタン1の有効無効をViewModelから切り替えるボタン
VmMyCommand2 = new UnRepeatableAsyncCommand(async () =>
{
MyCamExecuteFlag = !MyCamExecuteFlag; // CanExecuteで見るフラグ
VmMyCommand1.RaiseCanExecuteChanged(); // ★CanExecuteが変化したことを使えないと、フラグ切り替えても有効無効変わらない!
});
VmMyCommand2.CanExecuteChanged += ((sender, e) => Debug.WriteLine("CanExecuteChanged2"));
}
// 実験用 押したときに2秒かかる処理実施
public async Task MyAsyncFunc()
{
Debug.WriteLine("押された");
await Task.Delay(2000);
Debug.WriteLine("処理完了");
}
// フラグのON/OFFでボタンの有効無効を切り替える
public bool MyCanExecute()
{
return MyCamExecuteFlag;
}
}
}
UnRepeatableAsyncCommand.csusing System;
using System.Threading.Tasks;
using System.Windows.Input;
namespace WpfApp1
{
public class UnRepeatableAsyncCommand : ICommand
{
Func<Task> execute;
Func<bool> canExecute;
public event EventHandler CanExecuteChanged;
// 処理中フラグ
private bool isExecuting = false;
public bool IsExecuting
{
get { return isExecuting; }
set
{
isExecuting = value;
RaiseCanExecuteChanged();
}
}
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
// 本クラスを使う側が設定するCanExecuteに加え、処理中フラグのON/OFFを有効無効条件に加える
public bool CanExecute(object parameter) => (canExecute != null) ? (canExecute() && !isExecuting) : (!isExecuting);
// 処理実行の前後に、無効化→有効化、の処理を追加する
public async void Execute(object parameter)
{
IsExecuting = true;
await execute();
IsExecuting = false;
}
public UnRepeatableAsyncCommand(System.Func<Task> execute)
{
this.execute = execute;
}
public UnRepeatableAsyncCommand(System.Func<Task> execute, System.Func<bool> canExecute)
{
this.execute = execute;
this.canExecute = canExecute;
}
}
}
UnRepeatableAsyncCommand
이번 제작의 연격 방지 Command 설치반.
남아 있는 의문
의문
코드로 작업
예전에는 타이틀 내용을 만들고 싶어서 prism
DelegateCommand
반을 사용해 연속타를 막았다.다만, 당시의 방식대로라면 여러 버튼으로 같은 일을 하려고 할 때 같은 로고를 몇 개 만들어야 하기 때문에 더 좋은 방법을 찾던 중 그 기사에서 @unidentifiedexe씨가 좋은 방법에 대한 평을 줬다.
코드의 샘플도 적어 직접 사용할 수 있을 것 같지만 스스로 이해했으면 하는 마음에 연습과 함께 코드를 정리하려고 한다.
샘플 코드
WPF의 화면(xaml), ViewModel 및 이번에 제작된 명령 클래스의 코드는 다음과 같습니다.(코드 우선순위 생략)
MainWindow.xaml<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow" Height="500" Width="800">
<StackPanel>
<Button Content="押すと2秒間処理をし、その間は自動で無効になるボタン" FontSize="25" Command="{Binding VmMyCommand1}" Margin="20"/>
<Button Content="ボタン1の有効無効をVMのフラグで切り替えるボタン" FontSize="25" Command="{Binding VmMyCommand2}" Margin="20"/>
</StackPanel>
</Window>
ViewModel.csusing System.ComponentModel;
using System.Diagnostics;
using System.Threading.Tasks;
namespace WpfApp1
{
class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
// ボタン押された時のCommand
public UnRepeatableAsyncCommand VmMyCommand1 { get; private set; }
public UnRepeatableAsyncCommand VmMyCommand2 { get; private set; }
public bool MyCamExecuteFlag
{
get { return _myCamExecuteFlag; }
set { _myCamExecuteFlag = value; OnPropertyChanged(nameof(MyCamExecuteFlag)); }
}
private bool _myCamExecuteFlag = true;
public ViewModel()
{
// (ボタン1) 押したら2秒かかる処理を非同期で行って、その間は自動で無効になるボタン
VmMyCommand1 = new UnRepeatableAsyncCommand(MyAsyncFunc, MyCanExecute);
VmMyCommand1.CanExecuteChanged += ((sender, e) => Debug.WriteLine("CanExecuteChanged1"));
// (ボタン2) ボタン1の有効無効をViewModelから切り替えるボタン
VmMyCommand2 = new UnRepeatableAsyncCommand(async () =>
{
MyCamExecuteFlag = !MyCamExecuteFlag; // CanExecuteで見るフラグ
VmMyCommand1.RaiseCanExecuteChanged(); // ★CanExecuteが変化したことを使えないと、フラグ切り替えても有効無効変わらない!
});
VmMyCommand2.CanExecuteChanged += ((sender, e) => Debug.WriteLine("CanExecuteChanged2"));
}
// 実験用 押したときに2秒かかる処理実施
public async Task MyAsyncFunc()
{
Debug.WriteLine("押された");
await Task.Delay(2000);
Debug.WriteLine("処理完了");
}
// フラグのON/OFFでボタンの有効無効を切り替える
public bool MyCanExecute()
{
return MyCamExecuteFlag;
}
}
}
UnRepeatableAsyncCommand.csusing System;
using System.Threading.Tasks;
using System.Windows.Input;
namespace WpfApp1
{
public class UnRepeatableAsyncCommand : ICommand
{
Func<Task> execute;
Func<bool> canExecute;
public event EventHandler CanExecuteChanged;
// 処理中フラグ
private bool isExecuting = false;
public bool IsExecuting
{
get { return isExecuting; }
set
{
isExecuting = value;
RaiseCanExecuteChanged();
}
}
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
// 本クラスを使う側が設定するCanExecuteに加え、処理中フラグのON/OFFを有効無効条件に加える
public bool CanExecute(object parameter) => (canExecute != null) ? (canExecute() && !isExecuting) : (!isExecuting);
// 処理実行の前後に、無効化→有効化、の処理を追加する
public async void Execute(object parameter)
{
IsExecuting = true;
await execute();
IsExecuting = false;
}
public UnRepeatableAsyncCommand(System.Func<Task> execute)
{
this.execute = execute;
}
public UnRepeatableAsyncCommand(System.Func<Task> execute, System.Func<bool> canExecute)
{
this.execute = execute;
this.canExecute = canExecute;
}
}
}
UnRepeatableAsyncCommand
이번 제작의 연격 방지 Command 설치반.
남아 있는 의문
의문
코드로 작업
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow" Height="500" Width="800">
<StackPanel>
<Button Content="押すと2秒間処理をし、その間は自動で無効になるボタン" FontSize="25" Command="{Binding VmMyCommand1}" Margin="20"/>
<Button Content="ボタン1の有効無効をVMのフラグで切り替えるボタン" FontSize="25" Command="{Binding VmMyCommand2}" Margin="20"/>
</StackPanel>
</Window>
using System.ComponentModel;
using System.Diagnostics;
using System.Threading.Tasks;
namespace WpfApp1
{
class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
// ボタン押された時のCommand
public UnRepeatableAsyncCommand VmMyCommand1 { get; private set; }
public UnRepeatableAsyncCommand VmMyCommand2 { get; private set; }
public bool MyCamExecuteFlag
{
get { return _myCamExecuteFlag; }
set { _myCamExecuteFlag = value; OnPropertyChanged(nameof(MyCamExecuteFlag)); }
}
private bool _myCamExecuteFlag = true;
public ViewModel()
{
// (ボタン1) 押したら2秒かかる処理を非同期で行って、その間は自動で無効になるボタン
VmMyCommand1 = new UnRepeatableAsyncCommand(MyAsyncFunc, MyCanExecute);
VmMyCommand1.CanExecuteChanged += ((sender, e) => Debug.WriteLine("CanExecuteChanged1"));
// (ボタン2) ボタン1の有効無効をViewModelから切り替えるボタン
VmMyCommand2 = new UnRepeatableAsyncCommand(async () =>
{
MyCamExecuteFlag = !MyCamExecuteFlag; // CanExecuteで見るフラグ
VmMyCommand1.RaiseCanExecuteChanged(); // ★CanExecuteが変化したことを使えないと、フラグ切り替えても有効無効変わらない!
});
VmMyCommand2.CanExecuteChanged += ((sender, e) => Debug.WriteLine("CanExecuteChanged2"));
}
// 実験用 押したときに2秒かかる処理実施
public async Task MyAsyncFunc()
{
Debug.WriteLine("押された");
await Task.Delay(2000);
Debug.WriteLine("処理完了");
}
// フラグのON/OFFでボタンの有効無効を切り替える
public bool MyCanExecute()
{
return MyCamExecuteFlag;
}
}
}
using System;
using System.Threading.Tasks;
using System.Windows.Input;
namespace WpfApp1
{
public class UnRepeatableAsyncCommand : ICommand
{
Func<Task> execute;
Func<bool> canExecute;
public event EventHandler CanExecuteChanged;
// 処理中フラグ
private bool isExecuting = false;
public bool IsExecuting
{
get { return isExecuting; }
set
{
isExecuting = value;
RaiseCanExecuteChanged();
}
}
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
// 本クラスを使う側が設定するCanExecuteに加え、処理中フラグのON/OFFを有効無効条件に加える
public bool CanExecute(object parameter) => (canExecute != null) ? (canExecute() && !isExecuting) : (!isExecuting);
// 処理実行の前後に、無効化→有効化、の処理を追加する
public async void Execute(object parameter)
{
IsExecuting = true;
await execute();
IsExecuting = false;
}
public UnRepeatableAsyncCommand(System.Func<Task> execute)
{
this.execute = execute;
}
public UnRepeatableAsyncCommand(System.Func<Task> execute, System.Func<bool> canExecute)
{
this.execute = execute;
this.canExecute = canExecute;
}
}
}
의문
코드로 작업
연격 방지 명령 클래스
public async void Execute(object parameter)
중 2곳CanExecuteChanged?.Invoke(this, EventArgs.Empty);
에 대한 논평을 하면 단추가 무효지만 외관은 회색으로 변하지 않습니다.제가 이해가 부족한 부분이긴 한데.
ICommand
의CanExecuteChanged
이벤트 처리 프로그램이Can Execute Changed에 미리 넣는 방법이 아니라면 WPF의 프레임워크는Can Execute가 변할 때 임의로 넣는 방법을 부르나요?(즉, 내가 그 이벤트 프로세서 같은 것을 부르는 것은 아니라고 생각한다)
샘플 코드에서 보듯이
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
에 해주면 겉모습도 바뀌는데 왜 그런 동작이 됐을까요?현재 상황을 몰라...스케줄러:근데,움직일것으로변해버려서,노트 대신남긴건데...
의문에 대한 대응(21/03/23 보충)
albireo의 평론을 바탕으로 코드를 수정해 보았습니다.
(즉, 플래그의 변경 사항=CanExecute의 변경)
UnRepeatableAsyncCommand
클래스, CanExecuteChanged()
변경RaiseCanExecuteChanged()
구현 추가RaiseCanExecuteChanged()
를 호출참고 자료
CanExecuteChangd의 번거로운 문제를 조사해 봤습니다.
https://qiita.com/204504bySE/items/0c7d5ac6913673dc10f5
Reference
이 문제에 관하여([C#/WPF/prism] await에서 버튼을 누르는 데 시간이 걸리는 처리를 할 때 버튼의 연격을 방지해야 한다.), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://qiita.com/tera1707/items/946116bf32d0f1203006
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
Reference
이 문제에 관하여([C#/WPF/prism] await에서 버튼을 누르는 데 시간이 걸리는 처리를 할 때 버튼의 연격을 방지해야 한다.), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://qiita.com/tera1707/items/946116bf32d0f1203006텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)