이번 프로젝트에서는 수동 전체 선택을 구현하게 되었습니다.
🤹장작 6시간의 대장정..
무엇이 문제이고, 어떻게 해결했는지 정리해봅니다.
사실 전체 선택을 누르면 전체가 선택되고, 전체 선택을 해제하면 전체가 해제되도록 만드는 것은 쉽습니다.
View
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<Window>
<Grid>
<StackPanel Orientation="Vertical">
<CheckBox Content="All"
IsChecked="{Binding IsAllChecked, Mode=TwoWay}"
/>
<Separator/>
<CheckBox Content="1"
IsChecked="{Binding IsOneChecked, Mode=TwoWay}"
/>
<CheckBox Content="2"
IsChecked="{Binding IsTwoChecked, Mode=TwoWay}"
/>
<CheckBox Content="3"
IsChecked="{Binding IsThreeChecked, Mode=TwoWay}"
/>
<CheckBox Content="4"
IsChecked="{Binding IsFourChecked, Mode=TwoWay}"
/>
</StackPanel>
</Grid>
</Window>
|
ViewModel
1
2
3
4
5
6
7
|
private void ChangeCheckboxFromAll()
{
IsOneChecked = IsAllChecked;
IsTwoChecked = IsAllChecked;
IsThreeChecked = IsAllChecked;
IsFourChecked = IsAllChecked;
}
|
AllCheckbox에 변경이 일어날 때마다 ChangeCheckboxFromAll 메서드를 실행해서 각 Checkbox의 값을 업데이트 시켜주면 되기 때문이죠.
🫠 하지만, 문제는 개별 선택 변경으로 인한 전체 선택 변경에서 일어납니다.
즉, 1, 2, 3만 선택한 상황에서 4를 추가로 선택하면 전체 선택이 표시되어야 하고
전체 선택 된 상태에서 4를 체크 해제하면 전체 선택이 해제 되어야 합니다.
그래서 다음과 같이 코드를 추가하면,
ViewModel
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
private void ChangeCheckboxFromAll()
{
IsOneChecked = IsAllChecked;
IsTwoChecked = IsAllChecked;
IsThreeChecked = IsAllChecked;
IsFourChecked = IsAllChecked;
}
private void ChangeCheckboxFromEach()
{
bool alreadyCheckedAll = IsOneChecked && IsTwoChecked && IsThreeChecked && IsFourChecked;
if (IsAllChecked == alreadyCheckedAll)
{
return;
}
else
{
IsAllChecked = alreadyCheckedAll;
}
}
|
개별 체크 박스마다 ChangeCheckboxFromEach를 연결하면, 개별 체크 박스의 상태 변화를 All Checkbox와 연결지을 수 있습니다.
☠️ 그러나, 그 때부터 문제는 시작됩니다.
왜냐면 전체 선택 체크박스 체크 → ChangeCheckFromAll()→ 1번 체크박스 체크 → ChangeCheckFromEach(): else 구문 실행 → 전체 선택 체크박스 체크 해제 → ChangeCheckFromAll()→ 1번 체크박스 체크 해제 → ChangeCheckFromEach(): if 구문 실행
결국 전체 선택 체크박스는 체크가 안되고 끝이 납니다...
안녕 전체 선택..👋
다른 방법을 찾아보았지만, 최대한 간단하게 끝내고 싶은 마음에 플래그를 사용하는 것으로 합의를 보았습니다.
인터페이스를 활용한 방법도 있었는데, 궁금하신 분들을 아래 주소로 들어가보시면 됩니다.
ViewModel
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
|
public event PropertyChangedEventHandler PropertyChanged;
private bool _ignoreCheckAll;
private bool _isAllChecked;
public bool IsAllChecked
{
get => _isAllChecked;
set
{
if (_isAllChecked != value)
{
_isAllChecked = value;
OnPropertyChanged(nameof(IsAllChecked));
if (!_ignoreCheckAll)
{
_ignoreCheckAll = true;
IsOneChecked = value;
IsTwoChecked = value;
IsThreeChecked = value;
IsFourChecked = value;
_ignoreCheckAll = false;
}
}
}
}
private bool _isOneChecked;
public bool IsOneChecked
{
get => _isOneChecked;
set
{
if (_isOneChecked != value)
{
_isOneChecked = value;
OnPropertyChanged(nameof(IsOneChecked));
UpdateIsAllChecked();
}
}
}
private bool _isTwoChecked;
public bool IsTwoChecked
{
get => _isTwoChecked;
set
{
if (_isTwoChecked != value)
{
_isTwoChecked = value;
OnPropertyChanged(nameof(IsTwoChecked));
UpdateIsAllChecked();
}
}
}
private bool _isThreeChecked;
public bool IsThreeChecked
{
get => _isThreeChecked;
set
{
if (_isThreeChecked != value)
{
_isThreeChecked = value;
OnPropertyChanged(nameof(IsThreeChecked));
UpdateIsAllChecked();
}
}
}
private bool _isFourChecked;
public bool IsFourChecked
{
get => _isFourChecked;
set
{
if (_isFourChecked != value)
{
_isFourChecked = value;
OnPropertyChanged(nameof(IsFourChecked));
UpdateIsAllChecked();
}
}
}
private void UpdateIsAllChecked()
{
if (!_ignoreCheckAll)
{
_ignoreCheckAll = true;
IsAllChecked = _isOneChecked && _isTwoChecked && _isThreeChecked && _isFourChecked;
_ignoreCheckAll = false;
}
}
|
중요한 점은 전체 선택 체크 박스를 클릭해서 일어난 상태변화는 다시 전체 선택 체크 박스에 영향을 미치지 않도록 하는 것입니다.
따라서 _ignoreCheckAll을 플래그로 선언해서 전체 선택 체크 박스를 클릭해서 일어난 개별 체크 박스의 상태변화가 다시 전체 선택 체크박스로 오지 않도록 막았습니다.
물론 이 방법은 체크박스와 관련된 모든 checkbox의 상태 변화 발생 시, 메서드를 호출하므로 성능 저하 문제가 발생할 수도 있겠지만 작은 데이터 규모에서는, 그리고 달리 방도를 못찾은 상황에서는 대안으로 사용할 수 있습니다🤓.
⭐ 오늘의 교훈: 메서드 간 상호 호출 현상을 방지하는 방법 중 하나는 Flag를 이용하는 것이다.
'C# > WPF' 카테고리의 다른 글
[WPF] ControlTemplate의 영향력 (0) | 2024.06.30 |
---|---|
[WPF] 생성자 단위 초기화와 필드 단위 초기화 (0) | 2024.06.04 |
[WPF] ObservableCollection (0) | 2024.05.15 |
[WPF] View, ViewModel Singleton (0) | 2024.05.12 |
[WPF] 생성자 Overloading (2) (0) | 2024.05.11 |