在WPF中处理嵌套控件内ListBoxItem DataTemplate的相同点击事件

3
当我们有一个定制的列表框并定义了鼠标左键单击的事件处理程序时,还有一个ListBoxItem数据模板中的附加形状需要在被单击时执行一些操作,我们该如何处理这些情况?
我有一个尝试处理单击事件的自定义ListBox:
在ContentView中:
                <ABC:AListBox
                    ClickCommand="{Binding LaunchCommand}"
                    ...>
                </ABC:AListBox>

在它的数据模板中,我们有这个:
        <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()
    { ... }

在这里我使用了两个不同的命令:LaunchCommand作为ICommand,再加上通过模板调用的LaunchFromXamlCommand。

LaunchFromXamlRun将触发并按预期运行。 但是可以猜到,将会有2个事件被触发和2个命令被触发,而我想省略一个并在该形状被点击时忽略一般的ListBox事件处理程序。

做到这一点的最佳解决方案是什么?

FYI:(也许只是一条注释,不太重要)应用程序正在使用早期版本的Prism(不认为这里很重要),并且具有模块化代码,所有内容都分开放置在不同的程序集中,代码使用MVVM模式。

我希望我们在给定情境中能够使用类似e.handled=true的机制。


如果可以的话,您可以考虑改用事件处理而不是命令绑定,然后使用 e.Handled 来阻止其传播。或者,您可以(假设椭圆形命令被路由到首位)在 LaunchFromXamlRun() 中引发一个标志,在 LaunchRun() 中如果它被引发,则不执行任何操作并重置该标志。 - o_weisman
思考了事件场景和布尔标志,但由于我离这种开发已经有一段时间了,不知道如何实现所需的结果,另一方面希望听取建议,希望能找到最好的方法。如果您提供两种工作场景作为答案,将不胜感激并将其标记为答案。谢谢。 - Kasrak
关于第二种方法,我们如何处理标志?我们应该将其存储在哪里,以确保每次唯一调用时它为false,并且在第一次命中后将其更改为true?如果我们将其作为DataContext属性存储在ViewModel中,我们可以确定这种行为吗? - Kasrak
说实话,我没有使用过Prism,所以不能确定,但是椭圆不是从列表框继承其数据上下文吗? - o_weisman
破坏了MVVM模式的哪些方面? - o_weisman
显示剩余2条评论
1个回答

1
你的问题加剧了列表框中的点击处理程序。我不知道你是怎么做的,但它不能只是click。它可能是previewmousedown。因为,当然,列表框会将mousedown作为选择项目的一部分而“吃掉”。
解决这个问题的一种方法是不使用列表框的previewmousedown。在这里,我将我的行内容放在按钮内并绑定按钮的命令。它当然看起来不像一个按钮。
我将圆形变成一个按钮,并给它透明的填充,这样你就可以点击所有的内容。
    <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>

我的视图模型使用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,以便通过绑定选择您单击的项目。

抱歉耽搁了,这些日子太忙了,明天会回到这个话题。但有两件事要提一下: - Kasrak
我之前忘了提到一件事,可能可以回答你的疑问,因为这是一个自定义列表框,我在控件定义中处理了鼠标左键单击事件,如果需要,让我知道以便我也可以发布那段代码。 - Kasrak
  1. 我们应该将ListItem改成按钮吗?这是建议还是只是一个想法?如果可能的话,我希望保留原始控件,如果不行,对于这些更改也没有问题。
- Kasrak
再次感谢您的支持,我想给您一个详细和恰当的回复,这个延迟似乎是合理的,请原谅我。+1 - Kasrak
正如我所提到的,左键单击与列表框的行选择机制相关联。我发布的标记有效。至少在我理解你的目标的范围内是这样的。你可能能够找到另一种替代方法,但当然你必须从原来的东西中做出一些改变。祝你好运。 - Andy
谢谢Andy,我会根据你的建议反馈我的试用情况。 - Kasrak

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接