C# 如何知道在ListView中点击了哪个元素?
我有一个ListView,它有这样一个数据模板,使用MVVM模式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 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作为示例代码,但您没有