На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: maxim84_
  
> Как в WPF ListView можно обработать двойной клик ListViewItem'а
    Описание начну с количества лирики. Многие только увидев WPF говорят что он очень беден в отношении функционала стандартных контролов, если чесно то по началу и я так думал (хотя может быть только я один так думал :whistle: ). Такие мысли возникали когда приделывал ListView сортировки, выравнивание строк в ячейках и несколько других функций которыми не обладал стандартный ListView, но проделав все это понимаешь что на самом деле это не есть недостаток, а даже совсем наоборот. Т.е. я хочу сказать что весь функционал в итоге можно сделать так как ты себе его представляешь, а не пытаться переделывать то что кто то сделал ранее, как это было в WindowsForms и раньше. Ну да ладно, приступим к самой сути которуя я собираюсь описать в этой статье.

    Оговорюсь сразу, все подходы будут расматриваться относительно модификации паттерна MVC которую вы могли видеть в предыдущей моей статье и которая успешно применяется в проекте разрабатываемом на фирме в которой я тружусь. Так вот, до недавнего времени для обработки двойного клика у ListViewItem применялась следующая нехитрая конструкция:

    простой пример XAML разметки для списка

    ExpandedWrap disabled
              <ListView x:Name='lv' Grid.Row="2" ItemsSource="{Binding Data}" SelectedValue="{Binding SelectedItem}" MouseDoubleClick="ListView_MouseDoubleClick">
                  <ListView.View>
                      <GridView AllowsColumnReorder="true">
                          <GridViewColumn DisplayMemberBinding="{Binding Path=Id}" Width="50" Header="Код"/>
                          //.....................................
                          <GridViewColumn DisplayMemberBinding="{Binding Path=Notes}" Width="150" Header="Примечание"/>
                      </GridView>
                  </ListView.View>
              </ListView>


    ну и естественно обработчик двойного клика MouseDoubleClick="ListView_MouseDoubleClick". Но что здесь не так, а то что данный клик обрабатывается для всего ListView независимо от того где был он сделан. Дополнительная проблемма открывается если мы имеем два списка, то есть один в ListViewItem.Template другого, суть проблемы вы увидете в том какой код нужен для того чтобы вычленить именно клик ListViewItem.


    ExpandedWrap disabled
              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 сделаю набольшое отступление. Рассмотрим следующий код

    ExpandedWrap disabled
              <ListView x:Name='lv' Grid.Row="2" ItemsSource="{Binding Data}" SelectedValue="{Binding SelectedItem}">
                  <ListViewItem MouseDoubleClick="ListView_MouseDoubleClick"/>
                  <ListView.View>
                      <GridView AllowsColumnReorder="true">
                          <GridViewColumn DisplayMemberBinding="{Binding Path=Id}" Width="50" Header="Код"/>
                          //.....................................
                          <GridViewColumn DisplayMemberBinding="{Binding Path=Notes}" Width="150" Header="Примечание"/>
                      </GridView>
                  </ListView.View>
              </ListView>



    В данном случае мы получим ошибку гласящую примерно о следующем, "чтобы назначить ItemsSource список ListViewItems должен быть пуст", ну или что то вроде этого. Как же обойти данную проблему. Так вот, немного погуглив я наткнулся на следующее решение на сайте Microsoft

    ExpandedWrap disabled
      <Style TargetType="ListViewItem">
                  <EventSetter Event="MouseDoubleClick" Handler="ListViewItem_MouseDoubleClick"/>
      </Style>


    Наткнувшись на него я было обрадовался простоте решения, но немного подумал и пришол к выводу что данный вариант почти ничем не отличается от моего первого решения. По сути он так же перенаправляет ListView_MouseDoubleClick на ListViewItem_MouseDoubleClick. И второе самое главное что не нравилось мне в обоих случаях это невозможность использовать команды. Продолжив поиски решения на просторах гугла я не нашол больше ниодного более или менее интересного и тем более работающего примера. Но не опускать же руки. Так вот в очередной раз пришлось как говориться пораскинуть мозгами и вот к чему я пришол. Первое что пришло в голову это то что почему бы не научить ListViewItem принимать вызовы команд самому не ожидая тычка от родителя. Нет ничего проще скажете вы, отнаследуемся от ListViewItem и вперед, но данный подход меня не устраивает, особенно учитывая то что в последнее время мне попалось несколько статей в которых наследование не особо поощрялось и говорилось о том что оно таит в себе больше зла чем добра, углубляться в эту тему не буду, а просто отмажусь фразой что мода на наследование прошла. Но какая альтернатива, а вот какая, основная мощь WPF это всякого рода темплейты, почему бы не применить их. И вот что у меня получилось.

    ExpandedWrap disabled
                  <Grid>
                      <Grid.Resources>
                          <ControlTemplate x:Key="xTmpl" TargetType='{x:Type ListViewItem}'>
                              <StackPanel>
                                  <StackPanel.InputBindings>
                                      <MouseBinding Command="ApplicationCommands.Open" MouseAction="LeftDoubleClick"/>
                                  </StackPanel.InputBindings>
                                  <GridViewRowPresenter Content="{TemplateBinding Content}"
                                                Columns="{TemplateBinding GridView.ColumnCollection}"/>
                              </StackPanel>
                          </ControlTemplate>
                          <ControlTemplate x:Key="Selected" TargetType='{x:Type ListViewItem}'>
                              <StackPanel Background="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}">
                                  <StackPanel.InputBindings>
                                      <MouseBinding Command="ApplicationCommands.Open" MouseAction="LeftDoubleClick"/>
                                  </StackPanel.InputBindings>
                                  <GridViewRowPresenter Content="{TemplateBinding Content}"
                                                Columns="{TemplateBinding GridView.ColumnCollection}" TextBlock.Foreground="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
                              </StackPanel>
                          </ControlTemplate>
                      </Grid.Resources>
                      <ListView ItemsSource="{Binding }" SelectedValue="{Binding }" >
                          <ListView.ItemContainerStyle>
                              <Style TargetType="{x:Type ListViewItem}"  >
                                  <Setter Property="Template" Value="{StaticResource xTmpl}"/>
                                  <Style.Triggers>
                                      <Trigger Property="IsSelected" Value="True">
                                          <Setter Property="Template" Value="{StaticResource Selected}"/>
                                      </Trigger>
                                  </Style.Triggers>
                              </Style>
                          </ListView.ItemContainerStyle>
                          <ListView.View>
                              <GridView AllowsColumnReorder="True">
                                  //................................
                              </GridView>
                          </ListView.View>
                      </ListView>
                  </Grid>


    Думаю суть предельна ясна, в ControlTemplate итема ложим контейнер который принимает на себя клики и их же мы ловим и обрабатываем. Пару слов о том, зачем я сделал два ControlTemplate, ответ прост, один для ListViewItem в обычном состоянии, а второй для выбранного ListViewItem. Вот и все, надеюсь что эта статья помогла вам и пказала что в WPF главной загвоздкой является отсутсвие фантазии........
    0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
    0 пользователей:


    Рейтинг@Mail.ru
    [ Script execution time: 0,0270 ]   [ 15 queries used ]   [ Generated: 25.04.24, 06:07 GMT ]