=== Как в ListView можно обработать двойной клик ListViewItem'а ===
Описание начну с количества лирики. Многие только увидев WPF говорят что он очень беден в отношении функционала стандартных контролов, если честно то по началу и я так думал (хотя может быть только я один так думал :). Такие мысли возникали когда приделывал ListView сортировки, выравнивание строк в ячейках и несколько других функций которыми не обладал стандартный ListView, но проделав все это понимаешь что на самом деле это не есть недостаток, а даже совсем наоборот. Т.е. я хочу сказать что весь функционал в итоге можно сделать так как ты себе его представляешь, а не пытаться переделывать то что кто то сделал ранее, как это было в WindowsForms и раньше. Ну да ладно, приступим к самой сути которую я собираюсь описать в этой статье.
Оговорюсь сразу, все подходы будут рассматриваться относительно модификации паттерна MVC которую вы могли видеть в предыдущей моей статье и которая успешно применяется в проекте разрабатываемом на фирме в которой я тружусь. Так вот, до недавнего времени для обработки двойного клика у ListViewItem применялась следующая нехитрая конструкция:
простой пример XAML разметки для списка
//.....................................
ну и естественно обработчик двойного клика MouseDoubleClick="ListView_MouseDoubleClick". Но что здесь не так, а то что данный клик обрабатывается для всего ListView независимо от того где был он сделан. Дополнительная проблема открывается если мы имеем два списка, то есть один в ListViewItem.Template другого, суть проблемы вы увидите в том какой код нужен для того чтобы вычленить именно клик ListViewItem.
private void ListView_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
var dep = (DependencyObject)e.OriginalSource;
while (dep != null)
{
dep = VisualTreeHelper.GetParent(dep);
if (dep is ListViewItem)
{
Presenter.EditDoc();
return;
}
}
}
Как видите нам приходится идти по всему визуальному дереву пока мы не определим нужный элемент. Для тех кто не понимает почему нельзя просто привязать событие к ListViewItem сделаю набольшое отступление. Рассмотрим следующий код:
//.....................................
В данном случае мы получим ошибку гласящую примерно о следующем, "чтобы назначить ItemsSource список ListViewItems должен быть пуст", ну или что то вроде этого. Как же обойти данную проблему. Так вот, немного погуглив я наткнулся на следующее решение на сайте Microsoft:
Наткнувшись на него я было обрадовался простоте решения, но немного подумал и пришёл к выводу что данный вариант почти ничем не отличается от моего первого решения. По сути он так же перенаправляет ListView_MouseDoubleClick на ListViewItem_MouseDoubleClick. И второе самое главное что не нравилось мне в обоих случаях это невозможность использовать команды. Продолжив поиски решения на просторах гугла я не нашёл больше ни одного более или менее интересного и тем более работающего примера. Но не опускать же руки. Так вот в очередной раз пришлось как говориться пораскинуть мозгами и вот к чему я пришёл. Первое что пришло в голову это то что почему бы не научить ListViewItem принимать вызовы команд самому не ожидая тычка от родителя. Нет ничего проще скажете вы, отнаследуемся от ListViewItem и вперед, но данный подход меня не устраивает, особенно учитывая то что в последнее время мне попалось несколько статей в которых наследование не особо поощрялось и говорилось о том что оно таит в себе больше зла чем добра, углубляться в эту тему не буду, а просто отмажусь фразой что мода на наследование прошла. Но какая альтернатива, а вот какая, основная мощь WPF это всякого рода темплейты, почему бы не применить их. И вот что у меня получилось.
//................................
Думаю суть предельна ясна, в ControlTemplate итема кладём контейнер который принимает на себя клики и их же мы ловим и обрабатываем. Пару слов о том, зачем я сделал два ControlTemplate, ответ прост, один для ListViewItem в обычном состоянии, а второй для выбранного ListViewItem. Вот и все, надеюсь что эта статья помогла вам и показала что в WPF главной загвоздкой является отсутствие фантазии...