gpt4 book ai didi

c# - 如何知道 ListView 中点击了哪个元素?

转载 作者:行者123 更新时间:2023-12-03 10:58:05 25 4
gpt4 key购买 nike

这个问题在这里已经有了答案:





How to get clicked item in ListView

(5 个回答)


6年前关闭。




我有一个像这样的 DataTemplate 的 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>

ItemsClickCommand 就是这样定义的
public static class ItemsClickCommand
{
public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached("Command", typeof(BindableCommand), typeof(ItemsClickCommand), new PropertyMetadata(null, OnCommandPropertyChanged));
public static void SetCommand(DependencyObject d, BindableCommand value)
{
d.SetValue(CommandProperty, value);
}
public static BindableCommand GetCommand(DependencyObject d)
{
return (BindableCommand)d.GetValue(CommandProperty);
}
private static void OnCommandPropertyChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var control = d as ListViewBase;
if (control != null)
control.ItemClick += OnItemClick;
}
private static void OnItemClick(object sender, ItemClickEventArgs e)
{
var control = sender as ListViewBase;
var command = GetCommand(control);
if (command != null && command.CanExecute(e.OriginalSource))
command.ExecuteWithMoreParameters(e.OriginalSource, e.ClickedItem);
}
}

我要问的是如何知道用户是否点击了 TextBlock 或 Button。
我尝试在 ViewModel 中以这种方式处理 ItemClickCommand 事件以在 VisualTree 中搜索控件(这是最好的解决方案吗?),但转换为 DependencyObject 不起作用(始终返回 null)
public void ItemClicked(object originalSource, object clickedItem)
{
var source = originalSourceas DependencyObject;
if (source == null)
return;
}

最佳答案

想到了几个解决方案

解决方案一

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

请注意 ListView 如何将名称设置为“父级”,其属性为: x:Name="parent"以及按钮命令的绑定(bind)如何使用它。另请注意,该命令将提供一个参数,该参数是对被单击元素的数据源的引用。

此页面的 View 模型将如下所示:
public class MainViewModel : MvxViewModel
{
public ObservableCollection<MySource> Source { get; private set; }

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

public MainViewModel()
{
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);
}
}

'MvxCommand' 只是 ICommand 的一个特定实现。我在示例代码中使用了 MvvMCross,但您不必这样做 - 您可以使用所需的任何 MvvM 实现。

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

解决方案二

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

ListView 的 XAML:
<ListView 
ItemsSource="{Binding Source}"
IsItemClickEnabled="True"
Margin="20">
<ListView.ItemTemplate>
<DataTemplate>
<uc:MyElement DataContext="{Binding Converter={StaticResource MySourceToMyElementViewModelConverter}}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

代表一个元素的用户控件的 XAML:
<Grid>
<StackPanel>
<TextBlock Text="{Binding Source.A}" />
<Button Content="{Binding Source.B}" Command="{Binding BCommand}" />
</StackPanel>
</Grid>

MySourceToMyElementViewModelConverter 的源代码:
public class MySourceToMyElementViewModelConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
return new MyElementViewModel((MySource)value);
}

public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotSupportedException();
}
}

主页面的 View 模型:
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"),
};
}
}

表示列表中一个元素的用户控件的 View 模型:
public class MyElementViewModel : MvxViewModel
{
public MySource Source { get; private set; }
public MvxCommand BCommand { get; private set; }

public MyElementViewModel(MySource source)
{
Source = source;
BCommand = new MvxCommand(ExecuteBCommand);
}

private void ExecuteBCommand()
{
Debug.WriteLine("ExecuteBCommand. Source: A={0}, B={1}", Source.A, Source.B);
}
}

解决方案3

您的示例假定主页的 View 模型公开了数据模型元素列表。像这样的东西:
public ObservableCollection<MySource> Source { get; private set; }

可以更改主页的 View 模型,以便改为公开 View 模型元素列表。像这样的东西:
public ObservableCollection<MyElementViewModel> ElementViewModelList { get; private set; }
ElementViewModelList 中的每个元素将对应于 Source 中的一个元素.如果 Source 的内容,此解决方案可能会稍微复杂一些。运行时的变化。主页面的 View 模型需要观察 Source并更改 ElementViewModelList因此。进一步走这条路,您可能想要抽象集合映射器的概念(与 ICollectionView 类似的东西)并为此提供一些通用代码。

对于此解决方案,XAML 将如下所示:
<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>

解决方案 1、2 和 3 的注释

我看到您的原始示例将命令与元素内部的按钮相关联,而不是与整个元素相关联。这就提出了一个问题:你打算用内部按钮做什么?您是否会遇到用户可以单击元素或内部按钮的情况?就 UI/UX 而言,这可能不是最好的解决方案。请注意这一点。作为一个练习并且为了更接近您的原始样本,如果您想将指挥与整个元素相关联,您可以执行以下操作。
使用自定义样式将整个元素包裹在一个按钮中。该样式将修改视觉处理单击的方式。最简单的形式是让点击不产生任何视觉效果。应用于解决方案 1 的此更改(它也可以轻松应用于解决方案 2 和解决方案 3)看起来像这样:
<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>

在这种情况下,您必须编写 NoVisualEffectButtonStyle 但这是一项简单的任务。您还需要决定要与内部按钮关联的命令类型(否则为什么会有内部按钮)。或者,您更有可能将内部按钮转换为文本框之类的东西。

解决方案4

使用行为。

首先,添加对“行为 SDK”的引用。 adding a reference to Behaviors SDK .然后修改您的 XAML 代码:
...
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
...

<Grid>

<ListView ItemsSource="{Binding Source}" IsItemClickEnabled="True" Margin="20">

<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="ItemClick">
<core:InvokeCommandAction
Command="{Binding BCommand}"
InputConverter="{StaticResource ItemClickedToMySourceConverter}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>

<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding A}" />
<TextBlock Text="{Binding B}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

</Grid>

ItemClickedToMySourceConverter 只是一个普通值转换器:
public class ItemClickedToMySourceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
return (MySource)(((ItemClickEventArgs)value).ClickedItem);
}

public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotSupportedException();
}
}

View 模型将如下所示:
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);
}
}

关于c# - 如何知道 ListView 中点击了哪个元素?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32882090/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com