C# 如何知道在ListView中点击了哪个元素?

C# 如何知道在ListView中点击了哪个元素?,c#,xaml,mvvm,binding,windows-phone-8.1,C#,Xaml,Mvvm,Binding,Windows Phone 8.1,我有一个ListView,它有这样一个数据模板,使用MVVM模式 <ListView ItemsSource="{Binding Source}" IsItemClickEnabled="True" commands:ItemsClickCommand.Command="{Binding ItemClickedCommand}"> <ListView.ItemTemplate> &l

我有一个ListView,它有这样一个数据模板,使用MVVM模式

<ListView ItemsSource="{Binding Source}"
          IsItemClickEnabled="True"
          commands:ItemsClickCommand.Command="{Binding ItemClickedCommand}">
          <ListView.ItemTemplate>
               <DataTemplate>
                   <StackPanel>
                       <TextBlock Text="{Binding A}" />
                       <Button Content="{Binding B}" />
                   </StackPanel>
               </DataTemplate>
          </ListView.ItemTemplate>
</ListView>
我想问的是,我如何知道用户是否点击了文本块或按钮。 我尝试在ViewModel中以这种方式处理ItemClickCommand事件,以在VisualTree中搜索控件(这是最佳解决方案吗?),但强制转换为DependencyObject不起作用(始终返回null)


我想到了一些解决方案

解决方案1

<ListView 
    x:Name="parent"
    ItemsSource="{Binding Source}" 
    IsItemClickEnabled="True" 
    Margin="20">
    <ListView.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <TextBlock Text="{Binding A}" />
                <Button 
                    Content="{Binding B}" 
                    Command="{Binding DataContext.BCommand, ElementName=parent}"
                    CommandParameter="{Binding}" />
            </StackPanel>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
“MvxCommand”只是ICommand的一个特定实现。我在示例代码中使用了MvvMCross,但您不必这样做——您可以使用您需要的任何MvvM实现

如果处理命令的责任在于包含列表的页面的视图模型,则此解决方案是合适的

解决方案2

在视图模型中为包含列表的页面处理命令可能并不总是合适的。您可能希望在代码中移动该逻辑,使其更接近正在单击的元素。在这种情况下,将元素的数据模板隔离在其自己的用户控件中,创建一个与该用户控件背后的逻辑相对应的视图模型类,并在该视图模型中实现该命令。下面是代码的样子:

ListView的XAML:

<ListView 
    ItemsSource="{Binding Source}" 
    IsItemClickEnabled="True" 
    Margin="20">
    <ListView.ItemTemplate>
        <DataTemplate>
            <uc:MyElement DataContext="{Binding Converter={StaticResource MySourceToMyElementViewModelConverter}}" />
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
主页的视图模型:

public class MainViewModel : MvxViewModel
{
    public ObservableCollection<MySource> Source { get; private set; }

    public MainViewModel()
    {
        Source = new ObservableCollection<MySource>()
        {
            new MySource("e1", "b1"),
            new MySource("e2", "b2"),
            new MySource("e3", "b3"),
        };
    }
}
解决方案3

您的示例假定主页的视图模型公开了数据模型元素的列表。大概是这样的:

public ObservableCollection<MySource> Source { get; private set; }
public ObservableCollection<MyElementViewModel> ElementViewModelList { get; private set; }
<ListView 
    ItemsSource="{Binding ElementViewModelList}" 
    IsItemClickEnabled="True" 
    Margin="20">
    <ListView.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <TextBlock Text="{Binding A}" />
                <Button Content="{Binding B}" Command="{Binding BCommand}" />
            </StackPanel>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
<ListView 
    x:Name="parent"
    ItemsSource="{Binding Source}" 
    IsItemClickEnabled="True" 
    Margin="20">
    <ListView.ItemTemplate>
        <DataTemplate>
            <Button 
                Command="{Binding DataContext.BCommand, ElementName=parent}"
                CommandParameter="{Binding}" 
                Style="{StaticResource NoVisualEffectButtonStyle}">
                <StackPanel>
                    <TextBlock Text="{Binding A}" />
                    <TextBlock Text="{Binding B}" />
                </StackPanel>
            </Button>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
public class Main4ViewModel : MvxViewModel
{
    public ObservableCollection<MySource> Source { get; private set; }

    public MvxCommand<MySource> BCommand { get; private set; }

    public Main4ViewModel()
    {
        Source = new ObservableCollection<MySource>()
        {
            new MySource("e1", "b1"),
            new MySource("e2", "b2"),
            new MySource("e3", "b3"),
        };

        BCommand = new MvxCommand<MySource>(ExecuteBCommand);
    }

    private void ExecuteBCommand(MySource source)
    {
        Debug.WriteLine("ExecuteBCommand. Source: A={0}, B={1}", source.A, source.B);
    }
}
解决方案1、2和3的说明

我看到您的原始示例将命令与整个元素关联,而不是与元素内部的按钮关联。这就提出了一个问题:你将如何处理内部按钮?您是否会遇到用户可以单击元素或内部按钮的情况?就UI/UX而言,这可能不是最好的解决方案。请注意这一点。作为练习,为了更接近您的原始示例,如果您希望将命令与整个元素关联起来,可以执行以下操作。
使用自定义样式将整个元素包装在按钮中。该样式将修改可视化处理单击的方式。最简单的形式是让点击不产生任何视觉效果。应用于解决方案1的此更改(也可以很容易地应用于解决方案2和解决方案3)如下所示:

public ObservableCollection<MySource> Source { get; private set; }
public ObservableCollection<MyElementViewModel> ElementViewModelList { get; private set; }
<ListView 
    ItemsSource="{Binding ElementViewModelList}" 
    IsItemClickEnabled="True" 
    Margin="20">
    <ListView.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <TextBlock Text="{Binding A}" />
                <Button Content="{Binding B}" Command="{Binding BCommand}" />
            </StackPanel>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
<ListView 
    x:Name="parent"
    ItemsSource="{Binding Source}" 
    IsItemClickEnabled="True" 
    Margin="20">
    <ListView.ItemTemplate>
        <DataTemplate>
            <Button 
                Command="{Binding DataContext.BCommand, ElementName=parent}"
                CommandParameter="{Binding}" 
                Style="{StaticResource NoVisualEffectButtonStyle}">
                <StackPanel>
                    <TextBlock Text="{Binding A}" />
                    <TextBlock Text="{Binding B}" />
                </StackPanel>
            </Button>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
public class Main4ViewModel : MvxViewModel
{
    public ObservableCollection<MySource> Source { get; private set; }

    public MvxCommand<MySource> BCommand { get; private set; }

    public Main4ViewModel()
    {
        Source = new ObservableCollection<MySource>()
        {
            new MySource("e1", "b1"),
            new MySource("e2", "b2"),
            new MySource("e3", "b3"),
        };

        BCommand = new MvxCommand<MySource>(ExecuteBCommand);
    }

    private void ExecuteBCommand(MySource source)
    {
        Debug.WriteLine("ExecuteBCommand. Source: A={0}, B={1}", source.A, source.B);
    }
}
视图模型将如下所示:

public ObservableCollection<MySource> Source { get; private set; }
public ObservableCollection<MyElementViewModel> ElementViewModelList { get; private set; }
<ListView 
    ItemsSource="{Binding ElementViewModelList}" 
    IsItemClickEnabled="True" 
    Margin="20">
    <ListView.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <TextBlock Text="{Binding A}" />
                <Button Content="{Binding B}" Command="{Binding BCommand}" />
            </StackPanel>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
<ListView 
    x:Name="parent"
    ItemsSource="{Binding Source}" 
    IsItemClickEnabled="True" 
    Margin="20">
    <ListView.ItemTemplate>
        <DataTemplate>
            <Button 
                Command="{Binding DataContext.BCommand, ElementName=parent}"
                CommandParameter="{Binding}" 
                Style="{StaticResource NoVisualEffectButtonStyle}">
                <StackPanel>
                    <TextBlock Text="{Binding A}" />
                    <TextBlock Text="{Binding B}" />
                </StackPanel>
            </Button>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
public class Main4ViewModel : MvxViewModel
{
    public ObservableCollection<MySource> Source { get; private set; }

    public MvxCommand<MySource> BCommand { get; private set; }

    public Main4ViewModel()
    {
        Source = new ObservableCollection<MySource>()
        {
            new MySource("e1", "b1"),
            new MySource("e2", "b2"),
            new MySource("e3", "b3"),
        };

        BCommand = new MvxCommand<MySource>(ExecuteBCommand);
    }

    private void ExecuteBCommand(MySource source)
    {
        Debug.WriteLine("ExecuteBCommand. Source: A={0}, B={1}", source.A, source.B);
    }
}
public类Main4ViewModel:MvxViewModel
{
公共ObservableCollection源{get;private set;}
公共MvxCommand BCommand{get;private set;}
公共Main4ViewModel()
{
Source=新的ObservableCollection()
{
新MySource(“e1”、“b1”),
新MySource(“e2”、“b2”),
新MySource(“e3”、“b3”),
};
BCommand=新的MvxCommand(ExecuteBCommand);
}
私有void ExecuteBCommand(MySource源)
{
WriteLine(“ExecuteBCommand.Source:A={0},B={1}”,Source.A,Source.B);
}
}

我想到了一些解决方案

解决方案1

<ListView 
    x:Name="parent"
    ItemsSource="{Binding Source}" 
    IsItemClickEnabled="True" 
    Margin="20">
    <ListView.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <TextBlock Text="{Binding A}" />
                <Button 
                    Content="{Binding B}" 
                    Command="{Binding DataContext.BCommand, ElementName=parent}"
                    CommandParameter="{Binding}" />
            </StackPanel>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
“MvxCommand”只是ICommand的一个特定实现。我在示例代码中使用了MvvMCross,但您不必这样做——您可以使用您需要的任何MvvM实现

如果处理命令的责任在于包含列表的页面的视图模型,则此解决方案是合适的

解决方案2

在视图模型中为包含列表的页面处理命令可能并不总是合适的。您可能希望在代码中移动该逻辑,使其更接近正在单击的元素。在这种情况下,将元素的数据模板隔离在其自己的用户控件中,创建一个与该用户控件背后的逻辑相对应的视图模型类,并在该视图模型中实现该命令。下面是代码的样子:

ListView的XAML:

<ListView 
    ItemsSource="{Binding Source}" 
    IsItemClickEnabled="True" 
    Margin="20">
    <ListView.ItemTemplate>
        <DataTemplate>
            <uc:MyElement DataContext="{Binding Converter={StaticResource MySourceToMyElementViewModelConverter}}" />
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
主页的视图模型:

public class MainViewModel : MvxViewModel
{
    public ObservableCollection<MySource> Source { get; private set; }

    public MainViewModel()
    {
        Source = new ObservableCollection<MySource>()
        {
            new MySource("e1", "b1"),
            new MySource("e2", "b2"),
            new MySource("e3", "b3"),
        };
    }
}
解决方案3

您的示例假定主页的视图模型公开了数据模型元素的列表。大概是这样的:

public ObservableCollection<MySource> Source { get; private set; }
public ObservableCollection<MyElementViewModel> ElementViewModelList { get; private set; }
<ListView 
    ItemsSource="{Binding ElementViewModelList}" 
    IsItemClickEnabled="True" 
    Margin="20">
    <ListView.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <TextBlock Text="{Binding A}" />
                <Button Content="{Binding B}" Command="{Binding BCommand}" />
            </StackPanel>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
<ListView 
    x:Name="parent"
    ItemsSource="{Binding Source}" 
    IsItemClickEnabled="True" 
    Margin="20">
    <ListView.ItemTemplate>
        <DataTemplate>
            <Button 
                Command="{Binding DataContext.BCommand, ElementName=parent}"
                CommandParameter="{Binding}" 
                Style="{StaticResource NoVisualEffectButtonStyle}">
                <StackPanel>
                    <TextBlock Text="{Binding A}" />
                    <TextBlock Text="{Binding B}" />
                </StackPanel>
            </Button>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
public class Main4ViewModel : MvxViewModel
{
    public ObservableCollection<MySource> Source { get; private set; }

    public MvxCommand<MySource> BCommand { get; private set; }

    public Main4ViewModel()
    {
        Source = new ObservableCollection<MySource>()
        {
            new MySource("e1", "b1"),
            new MySource("e2", "b2"),
            new MySource("e3", "b3"),
        };

        BCommand = new MvxCommand<MySource>(ExecuteBCommand);
    }

    private void ExecuteBCommand(MySource source)
    {
        Debug.WriteLine("ExecuteBCommand. Source: A={0}, B={1}", source.A, source.B);
    }
}
解决方案1、2和3的说明

我看到您的原始示例将命令与整个元素关联,而不是与元素内部的按钮关联。这就提出了一个问题:你将如何处理内部按钮?您是否会遇到用户可以单击元素或内部按钮的情况?就UI/UX而言,这可能不是最好的解决方案。请注意这一点。作为练习,为了更接近您的原始示例,如果您希望将命令与整个元素关联起来,可以执行以下操作。
使用自定义样式将整个元素包装在按钮中。该样式将修改可视化处理单击的方式。最简单的形式是让点击不产生任何视觉效果。应用于解决方案1的此更改(也可以很容易地应用于解决方案2和解决方案3)如下所示:

public ObservableCollection<MySource> Source { get; private set; }
public ObservableCollection<MyElementViewModel> ElementViewModelList { get; private set; }
<ListView 
    ItemsSource="{Binding ElementViewModelList}" 
    IsItemClickEnabled="True" 
    Margin="20">
    <ListView.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <TextBlock Text="{Binding A}" />
                <Button Content="{Binding B}" Command="{Binding BCommand}" />
            </StackPanel>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
<ListView 
    x:Name="parent"
    ItemsSource="{Binding Source}" 
    IsItemClickEnabled="True" 
    Margin="20">
    <ListView.ItemTemplate>
        <DataTemplate>
            <Button 
                Command="{Binding DataContext.BCommand, ElementName=parent}"
                CommandParameter="{Binding}" 
                Style="{StaticResource NoVisualEffectButtonStyle}">
                <StackPanel>
                    <TextBlock Text="{Binding A}" />
                    <TextBlock Text="{Binding B}" />
                </StackPanel>
            </Button>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
public class Main4ViewModel : MvxViewModel
{
    public ObservableCollection<MySource> Source { get; private set; }

    public MvxCommand<MySource> BCommand { get; private set; }

    public Main4ViewModel()
    {
        Source = new ObservableCollection<MySource>()
        {
            new MySource("e1", "b1"),
            new MySource("e2", "b2"),
            new MySource("e3", "b3"),
        };

        BCommand = new MvxCommand<MySource>(ExecuteBCommand);
    }

    private void ExecuteBCommand(MySource source)
    {
        Debug.WriteLine("ExecuteBCommand. Source: A={0}, B={1}", source.A, source.B);
    }
}
视图模型将如下所示:

public ObservableCollection<MySource> Source { get; private set; }
public ObservableCollection<MyElementViewModel> ElementViewModelList { get; private set; }
<ListView 
    ItemsSource="{Binding ElementViewModelList}" 
    IsItemClickEnabled="True" 
    Margin="20">
    <ListView.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <TextBlock Text="{Binding A}" />
                <Button Content="{Binding B}" Command="{Binding BCommand}" />
            </StackPanel>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
<ListView 
    x:Name="parent"
    ItemsSource="{Binding Source}" 
    IsItemClickEnabled="True" 
    Margin="20">
    <ListView.ItemTemplate>
        <DataTemplate>
            <Button 
                Command="{Binding DataContext.BCommand, ElementName=parent}"
                CommandParameter="{Binding}" 
                Style="{StaticResource NoVisualEffectButtonStyle}">
                <StackPanel>
                    <TextBlock Text="{Binding A}" />
                    <TextBlock Text="{Binding B}" />
                </StackPanel>
            </Button>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
public class Main4ViewModel : MvxViewModel
{
    public ObservableCollection<MySource> Source { get; private set; }

    public MvxCommand<MySource> BCommand { get; private set; }

    public Main4ViewModel()
    {
        Source = new ObservableCollection<MySource>()
        {
            new MySource("e1", "b1"),
            new MySource("e2", "b2"),
            new MySource("e3", "b3"),
        };

        BCommand = new MvxCommand<MySource>(ExecuteBCommand);
    }

    private void ExecuteBCommand(MySource source)
    {
        Debug.WriteLine("ExecuteBCommand. Source: A={0}, B={1}", source.A, source.B);
    }
}
public类Main4ViewModel:MvxViewModel
{
公共ObservableCollection源{get;private set;}
公共MvxCommand BCommand{get;private set;}
公共Main4ViewModel()
{
Source=新的ObservableCollection()
{
新MySource(“e1”、“b1”),
新MySource(“e2”、“b2”),
新MySource(“e3”、“b3”),
};
BCommand=新的MvxCommand(ExecuteBCommand);
}
私有void ExecuteBCommand(MySource源)
{
WriteLine(“ExecuteBCommand.Source:A={0},B={1}”,Source.A,Source.B);
}
}

我想到了一些解决方案

解决方案1

<ListView 
    x:Name="parent"
    ItemsSource="{Binding Source}" 
    IsItemClickEnabled="True" 
    Margin="20">
    <ListView.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <TextBlock Text="{Binding A}" />
                <Button 
                    Content="{Binding B}" 
                    Command="{Binding DataContext.BCommand, ElementName=parent}"
                    CommandParameter="{Binding}" />
            </StackPanel>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
“MvxCommand”只是ICommand的一个特定实现。我使用MvvMCross作为示例代码,但您没有