ListBox는 많은 프로젝트에서 가장 많이 사용하면서도 쓰기 어려운 컨트롤중에 하나죠. 여기서 가끔 사용하게 되는 것이 이미 Select된 객체를 취소시키는 것입니다. 코드로 Select를 하는 방법은 두가지가 있죠. 하나는 SelectedItem을 이용하는 방법이고 하나는 SelectedIndex를 사용하는 방법입니다.
SelectedIndex는 선택된 객체의 순서를 반환해주고 SelectedItem은 선택된 객체의 Binding된 Data 값을 반환해주죠.
셋팅을 해줄 때도 역시 선택할 객체의 Index값을 SelectedIndex에 넣어주거나 우리가 선택하고 싶은 Data를 SelectedItem에 셋팅해줌으로써 Select된 객체를 바꿀 수 있습니다.
그리고 선택이 되지 않은 초기 값은 SelctedIndex 는 -1 이며 SelectedItem 은 null 값이 됩니다.
그러면 반대로 선택을 해제 시키려면 SelectedIndex 에 -1값을 넣어주거나 SelectedItem에 null값을 집어넣어주면 되겠죠...
그런데 문제는 이것이 잘 안먹는다는데 있습니다. 간단하게 테스트를 해보죠.
Xaml 코드에서 ListBox를 하나 생성해준 뒤 이름을 그냥 "list" 라고 넣어두었습니다.
그리고 코드는 다음과 같습니다.
public Page()
{
InitializeComponent();
list.ItemsSource = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
list.SelectionChanged += new SelectionChangedEventHandler(list_SelectionChanged);
}
void list_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
{
list.SelectedItem = null; // 혹은 list.SelectedIndex = -1;
}
선택하자 마자. Select를 풀어주자는 것이죠. 그런데 결과를 보면 절대로 Select는 풀리지 않죠.. Select가 풀리지 않으니 한번 선택한 객체를 다시 선택 이벤트를 받는 것은 불가능해지는 것이죠... --;; 어쩐다..
해결 방법은 간단합니다. " list.SelectedItem = null;" 요 부분을 다음과 같이 바꿔주면 정상 작동합니다.
if (e.AddedItems.Count != 0)
list.Dispatcher.BeginInvoke(() => { list.SelectedItem = null; });
내부적인 작동은 알수 없지만 추측해보자면.. SelectionChanged 이벤트가 일어나는 타이밍의 문제가 아닐까 싶습니다.
SelectionChanged가 일어났을때는 아직 SelectedIndex나 SelectedItem 설정에 대한 로직이 아직 진행중인 상태인 거죠. 로직이 완전히 끝났을 때 다시 SelectedIndex를 설정해주어야만 정상작동하게 되는 것이죠. Dispatcher는 현재 UI스레드 작업이 완료되면 그 다음 작업을 실행시켜주는 것이니 현재는 아마 처음 Selection 에대한 작업이 실행될 것이고 이 작업이 끝나면 자동적으로 SelectedIndex나 SelectedItem설정에 대한 로직도 끝나 있는 것이죠. 그리고 그 후에 Disptcher에 등록시켜둔 list.SelectedItem = null 이라는 명령을 수행하게 되면 정상작동하게 되는게 아닐까 합니다.
순전히 추측일뿐 정확한 이야기는 아닐 수 있습니다. 확인 방법은 ListBox 의 내부 코드를 뜯어보는 수밖에..(사실 예전에 뜯어봤는데 지금 다시 뜯어보기 귀찮아서..--;;)
일단 간단히 SelectionChanged에서 Select 된 객체를 바꿔주거나 해제시켜주고 싶을 때는 Dispatcher를 사용하면 된다는 것입니다.
여기서 좀더 흥미로운 실험을 더 해보도록 하겠습니다. 신기하게도 제가 이사실을 발견하고 Gilbert에게 이 사실에 대해 알려주었을 때 Gilbert군은 그냥 SelectedItem 에 null 값을 넣어주면 아이템이 해제된다고 하더군요.. 그래서 Gilbert가 구현한 코드를 보았습니다. 신기하게도 Dispatcher를 사용하지 않고 정상작동되더군요.
Gilbert가 구현한 부분은 여러개의 리스트 박스가 있어 여러개의 리스트 박스 중 한개의 리스트 박스에만 Selected Item이 존재하도록 하는 것이었습니다. 그래서 하나의 리스트 박스에서 SelectionChanged이벤트로 Selection이 일어났을 때 다른 ListBox객체들의 SelectedItem 값을 null로 만들어주어서 다른 리스트 박스의 선택값을 해제시켜주는 것이죠.
Gibert군의 코드에서 이건 정말 잘 작동하였습니다. 그래서 저는 다시 테스트를 해보기로 했습니다. 이번에는 ListBox를 하나더 추가하고 이름을 "list2"라고 지어줬습니다.
그리고 아래와 같이 코드를 구성했습니다.
public Page()
{
InitializeComponent();
list.ItemsSource = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
list.SelectionChanged += new SelectionChangedEventHandler(list_SelectionChanged);
list2.ItemsSource = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
list2.SelectionChanged += new SelectionChangedEventHandler(list2_SelectionChanged);
}
void list2_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
list.SelectedItem = null;
}
{
list.SelectedItem = null;
}
void list_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
list2.SelectedItem = null;
}
{
list2.SelectedItem = null;
}
결과는... 일단 처음에 list에 3을 클릭하고 list2의 4를 클릭했을 때 분명히 list의 3이 선택해제가 되었습니다. 오.. 이것은 되는 구나 했지만... 그다음에 바로 오작동이 시작되었습니다. 다시 list의 3을 클릭했을 때 list2의 4는 선택해제가 되지 않았습니다. 뿐만 아니라 그 이후에 list의 3을 클릭해도 list2의 4를 클릭해도 전혀 SelectionChaged 이벤트가 들어오지 않더군요..
이 오작동을 가지고 Gilbert가 실제로 구현해 놓은 부분에 가서 확인을 해보았습니다. 그런데 정말 신기하게도 이런 오작동도 발견되지 않더군요. 그래서 다음처럼 구현을 해보았습니다.
public Page()
{
InitializeComponent();
list.ItemsSource = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
list.SelectionChanged += new SelectionChangedEventHandler(list_SelectionChanged);
list2.ItemsSource = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
list2.SelectionChanged += new SelectionChangedEventHandler(list_SelectionChanged);
}
{
InitializeComponent();
list.ItemsSource = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
list.SelectionChanged += new SelectionChangedEventHandler(list_SelectionChanged);
list2.ItemsSource = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
list2.SelectionChanged += new SelectionChangedEventHandler(list_SelectionChanged);
}
void list_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.AddedItems.Count == 0)
return;
{
if (e.AddedItems.Count == 0)
return;
if (sender == list)
{
list2.SelectedItem = null;
}
else
{
list.SelectedItem = null;
}
}
{
list2.SelectedItem = null;
}
else
{
list.SelectedItem = null;
}
}
list와 list2에서 같은 EventHandler를 사용하는 것이죠. 이렇게 하는 것이 무슨 차이점이 있는지 알 수 없지만 Gilbert군이 구현해놓은 코드에는 이런식으로 구현이 되어있었습니다.
결과는.... 정말 잘 작동합니다....
이게 잘 작동하는 이유는 정말 알 수가 없군요...
결국 정리하자면 이렇습니다.
SelectionChaged 이벤트에서 listBox의 Selection을 바꿔주고 싶을 때는 왠만하면 Dispatcher를 쓰자!!!!
그럼 모두 삽질 덜하시길...^^
- smile -
[출처] 실버라이트 네이버 카페