Обработка того же события щелчка во вложенном элементе управления внутри ListBoxItem DataTemplate — WPF

#c# #wpf #mvvm #prism #datatemplate

#c# #wpf ( ВП ) #mvvm #призма #табличка с данными #wpf #datatemplate

Вопрос:

Когда у нас есть пользовательский listbox с определенной обработкой событий для щелчка левой кнопкой мыши, а также дополнительная форма внутри шаблона данных ListBoxItem, которая должна выполнять некоторые действия при нажатии на нее, как мы можем обработать эти

У меня есть пользовательский список, который пытается обработать событие Click :

В contentView:

                 <ABC:AListBox
                    ClickCommand="{Binding LaunchCommand}"
                    ...>
                </ABC:AListBox>
  

В его datatemplate у нас есть это:

         <DataTemplate x:Key="ThisListTemplate">
            <StackPanel ...>
                <Border Grid.Column="1" VerticalAlignment="Center">
                    <TextBlock
                        FontSize="15"
                        Foreground="White"
                        Text="{Binding Path=ItemTitle}" />      
                </Border>
                <Canvas Height ="12" Width ="12" >
                  <Ellipse Name = "TheEllipse" Stroke="Black" Height ="12"                                  
                           Width ="12" Cursor="Hand" Canvas.Left="185" Canvas.Top="12">                          
                  </Ellipse>
                    <Ellipse.InputBindings>
                        <MouseBinding Gesture="LeftClick"
                                      Command="{Binding DataContext.LaunchFromXamlCommand , RelativeSource={RelativeSource AncestorType=ABC:AListBox}}"
                                      CommandParameter="{Binding}" />
                    </Ellipse.InputBindings>                      
                </Canvas> 
            </StackPanel>                   
        </DataTemplate>
  

И в MVVM в качестве нашего контекста данных мы имеем:

     public ICommand LaunchCommand { get; private set; }
    public DelegateCommand<object> LaunchFromXamlCommand { get; private set; }

    // Initialization on load:

    this.LaunchCommand = new DelegateCommand(this.LaunchRun);
    this.LaunchFromXamlCommand = new DelegateCommand<object>(this.LaunchFromXamlRun);


    //---------     
    private void LaunchFromXamlRun(object param)
    {
            TheListItem app = (TheListItem)param;   
    ...
    }   

    private void LaunchRun()
    { ... }
  

Здесь я использовал 2 разные команды LaunchCommand в качестве ICommand в дополнение к LaunchFromXamlCommand, которая вызывается через шаблон.

LaunchFromXamlRun Сработает нормально и так, как ожидалось. Но также, как можно догадаться, будут вызваны 2 события и 2 команды, которые я хочу опустить один и игнорировать общий обработчик событий ListBox при попадании в эту форму.

Что может быть лучшим решением для этого?

(Возможно, не так важно, просто для заметки) К вашему сведению: Приложение использует более ранние версии Prism (не думаю, что здесь это имеет значение) и имеет модульный код, все разделено в разных сборках, а код использует шаблон MVVM.

Хотелось бы, чтобы у нас было что-то подобное e.handled = true в механизме, который можно было бы использовать в данном сценарии.

Комментарии:

1. Возможно, вы могли бы переключиться на обработку событий вместо привязки к команде, а затем использовать e.Handled, чтобы заблокировать его распространение, если это приемлемо. В качестве альтернативы вы можете (при условии, что команда ellipse маршрутизируется первой) поднять флаг в LaunchFromXamlRun() и в LaunchRun(), если он поднят, ничего не делать и сбросить флаг.

2. Думал о сценарии события, а также о логическом флаге, но, поскольку я некоторое время был далек от этого типа разработки, не знал точно, как можно достичь желаемого результата, с другой стороны, хотел услышать совет по нему с надеждой найти лучший способ. если вы предоставите оба ваших подхода в качестве рабочих сценариев, мы будем признательны и пометим это как ответ. Спасибо.

3. О 2-м подходе, как мы можем обработать флаг? где оно должно храниться, чтобы мы были уверены, что при каждом уникальном вызове оно будет false, и после первого попадания мы изменим его на true? Если это будет в нашей ViewModel как свойство нашего DataContext, можем ли мы быть уверены в этом поведении?

4. TBH Я не использовал Prism, поэтому не могу быть уверен, но разве эллипс не наследует свой контекст данных из окна списка?

5. Думаю, верно, весь проект должен использовать один и тот же DataContext :ContentViewModel. Prism не должен так сильно менять здесь, слышал и читал ранее, что его DelegateCommand аналогичен RelayCommand другого фреймворка MVVM и реализует ICommand — может быть, поможет —

Ответ №1:

Ваша проблема усугубляется наличием этого обработчика щелчка в вашем listbox. Я не знаю, как вы это делаете, но это не может быть просто щелчком. Вероятно, это предварительный просмотр. Потому что, конечно, listbox «съедает» наведение курсора мыши как часть выбора элемента.

Один из способов обойти это заключается в том, чтобы не использовать этот предварительный просмотр listbox. Здесь я помещаю содержимое своей строки внутри кнопки и привязываю команду кнопки. Конечно, это не похоже на кнопку.

Я превращаю круг в кнопку и придаю ему прозрачную заливку, чтобы вы могли нажимать на все это.

     <ListBox ItemsSource="{Binding People}">
        <ListBox.ItemContainerStyle>
            <Style TargetType="{x:Type ListBoxItem}">
                <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
            </Style>
        </ListBox.ItemContainerStyle>
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Button  Command="{Binding DataContext.ItemClickCommand, RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}"
                         CommandParameter="{Binding}"
                         >
                    <Button.Template>
                        <ControlTemplate>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="{Binding LastName}"/>
                                <Button Command="{Binding DataContext.EllipseCommand, RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}"
                                >
                                    <Button.Template>
                                        <ControlTemplate>
                                            <Ellipse Name = "TheEllipse" Stroke="Black" 
                                         Fill="Transparent"
                                         Height ="12"                                  
                                         Width="12" Cursor="Hand">
                                            </Ellipse>
                                        </ControlTemplate>
                                    </Button.Template>
                                </Button>

                            </StackPanel>
                        </ControlTemplate>
                    </Button.Template>
                </Button>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
  

Моя viewmodel использует relaycommands, но (очевидно) подойдет любая реализация ICommand. У меня были люди из последнего вопроса, над которым я немного поработал.

 public class MainWindowViewModel : BaseViewModel
{
    private RelayCommand ellipseCommand;
    public RelayCommand EllipseCommand
    {
        get
        {
            return ellipseCommand
            ?? (ellipseCommand = new RelayCommand(
              () =>
             {
                 Console.WriteLine("CIRCLE clicked");
             }
             ));
        }
    }
    private RelayCommand<Person> itemClickCommand;
    public RelayCommand<Person> ItemClickCommand
    {
        get
        {
            return itemClickCommand
            ?? (itemClickCommand = new RelayCommand<Person>(
              (person) =>
              {
                  Console.WriteLine($"You clicked {person.LastName}");
                  person.IsSelected = true;
              }
             ));
        }
    }
    private ObservableCollection<Person> people = new ObservableCollection<Person>();

    public ObservableCollection<Person> People
    {
        get { return people; }
        set { people = value; }
    }

    public ListCollectionView LCV { get; set; }
    public MainWindowViewModel()
    {
        People.Add(new Person { FirstName = "Chesney", LastName = "Brown" });
        People.Add(new Person { FirstName = "Gary", LastName = "Windass" });
        People.Add(new Person { FirstName = "Liz", LastName = "McDonald" });
        People.Add(new Person { FirstName = "Carla", LastName = "Connor" });
    }
}
  

Когда вы нажимаете эту внешнюю кнопку, она захватывает щелчок. Именно поэтому я устанавливаю IsSelected в команде, чтобы элемент, который вы нажимаете, выбирался через привязку.

Комментарии:

1. Извините за задержку, был слишком занят в эти дни, вернусь к теме завтра. Но следует упомянуть 2 вещи:

2. 1. Я забыл упомянуть ранее, что есть кое-что, что может ответить на одно из ваших сомнений и вопросов, поскольку это пользовательский listbox, я обработал щелчок левой кнопкой мыши в определении элемента управления, при необходимости дайте мне знать, чтобы опубликовать этот код также.

3. 2. Должны ли мы изменить ListItem на кнопки? Это рекомендация или просто идея? Если возможно, я хотел бы сохранить исходные данные элементы управления, если нет, то с этими изменениями проблем нет.

4. 1 и еще раз спасибо, поскольку я хочу дать подробный и правильный ответ, эта задержка кажется разумной, поэтому простите меня.

5. Как я уже упоминал, щелчок левой кнопкой мыши связан с механизмом выбора строки в listbox. Разметка, которую я опубликовал, работает. По крайней мере, в пределах моего понимания ваших целей. Возможно, вы сможете найти альтернативу, но, конечно, вам придется что-то изменить по сравнению с оригиналом. Удачи с этим.