Handling the same click event in a nested control inside ListBoxItem DataTemplate - WPF

只谈情不闲聊 提交于 2019-12-24 01:54:14

问题


When we have a custom listbox with a defined event-handling for the left-click of the mouse, and also an additional shape inside the ListBoxItem data template which needs to do some action when it is clicked how can we handle these

I have a Custom ListBox witch tries to handle the Click event :

In the ContentView:

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

In it's datatemplate we have this:

        <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>

And in the MVVM as our data context we have:

    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()
    { ... }

Here I used 2 different commands LaunchCommand as an ICommand in addition with LaunchFromXamlCommand which is called via the template.

The LaunchFromXamlRun will get triggered fine and as expected. But also as it can be guessed there will be 2 raised events and 2 commands getting triggered which I want to omit one and ignore the general ListBox event handler when that shape is get hit.

What can be the best solution for doing this?

FYI: (Maybe not so important just for a note) The App is using earlier versions of Prism (don't think that matters here) and has a modular code, everything is separated in different assemblies and the code is using MVVM pattern.

I wish we had something like e.handled = true in a mechanism that could be used in the given scenario.


回答1:


Your problem is exacerbated by having that click handler in your listbox. I don't know how you're doing that but that cannot just be click. It's probably previewmousedown. Because, of course, a listbox "eats" mousedown as part of selecting an item.

One way round this involves not using that listbox previewmousedown. Here I put my row content inside a button and bind the button's command. It doesn't look like a button of course.

I make the circle into a button and give it a transparent fill so you can click on all of it.

    <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>

My viewmodel uses relaycommands but (obviously) any implementation of ICommand would do. I had People from the last question I did some work on.

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" });
    }
}

When you click that outer button it will grab the click. Which is why I set IsSelected in the command so the item you click becomes selected via binding.



来源:https://stackoverflow.com/questions/55780718/handling-the-same-click-event-in-a-nested-control-inside-listboxitem-datatemplat

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!