我想是时候寻求帮助了!我有一个列出 UserControls 的 ItemsControl(称为CompoundCardControl):
<ScrollViewer Grid.Row = "1" VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding Method.Compounds}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<control:CompoundCardControl/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
UserControl 包含一个按钮,用于从 ListView(和 ViewModel)中删除项目:
<Grid Width="{Binding ActualWidth, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="25"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Margin="5,0,0,0" Text="{Binding Name}" />
<TextBlock Grid.Column="1" Margin="5,0,0,0" Foreground="Gray" FontSize="10" VerticalAlignment="Center" Text="RT"/>
<TextBlock Grid.Column="2" Margin="5,0,0,0" Text="{Binding RetentionTime , StringFormat= '\{0\} minutes'}" />
<Button Grid.Column="3" HorizontalAlignment="Right" Style="{StaticResource menuBarButton}"
Command="{Binding DataContext.DeleteCompoundCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}}"
CommandParameter="{Binding Compound}">
<Image Width="12" Height="12" Source="/Resources/Icons/Delete.png"/>
</Button>
</Grid>
这是我的父窗口的 ViewModel(但它显然不起作用)”:
public class MethodDialogViewModel : ViewModelBase
{
//properties
public DialogModeEnum DialogMode { get; private set; }
public string WindowTitle { get { return DialogMode == DialogModeEnum.Add ? "New method" : "Edit method"; } }
public string ButtonText { get { return DialogMode == DialogModeEnum.Add ? "Add" : "Update"; } }
public Method Method { get; set; }
public MethodCompound Compound { get; set; } = new MethodCompound();
//constructor
public MethodDialogViewModel(Method method, DialogModeEnum dialogMode)
{
DialogMode = dialogMode;
Method = method;
}
//commands
public RelayCommand DeleteCompoundCommand => new(execute => DeleteCompound(Compound));
private void DeleteCompound(MethodCompound methodCompound)
{
//Logic not implimented. I just want to see if I can get the object "methodCompound"
if (methodCompound != null)
{
MessageBox.Show(methodCompound.ID);
}
else
{
MessageBox.Show("Please select a compound.", "Delete compound", MessageBoxButton.OK, MessageBoxImage.Information);
}
}
}
基本上我想将 ItemsControl 中的 Item(实际上是底层对象)传递给 ViewModel。我怎么做?还是我完全搞砸了?
您的代码存在一些奇怪之处,这似乎导致了为什么没有任何内容按预期工作。
RelayCommand 委托似乎是错误的。根据参数名称
execute
,您似乎得到了错误的委托签名。 RelayCommand
实现的原始签名是ICommand.Execute(object commandParameter): void
。这意味着 lambda 的 execute
参数应命名为,例如commandParameter
。然后您实际上必须将该参数传递给执行委托。否则,Button.CommandParameter
值将丢失,即不转发:
公共 RelayCommand 删除复合命令 => new(commandParameter => DeleteCompound((MethodCompound )commandParameter));
MethodDialogViewModel.Compound
属性始终返回相同的实例
Grid.Width
内CompoundCardControl
上的绑定是多余的,因为Grid
会自然拉伸以填充可用空间。
因为您想要滚动 ItemsControl
内部
的项目,而不是
ItemsControl
本身,所以必须将
ScrollViewer
添加到
ControlTemplate
的
ItemsControl
中,它包裹着
ItemsPresenter
。
<ControlTemplate TargetType="ItemsControl">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}">
<ScrollViewer>
<ItemsPresenter />
</ScrollViewer>
</Border>
</ControlTemplate>
ItemsControl
不提供任何性能功能(例如 UI 虚拟化),这可以显着改善较大列表或包含昂贵项目或项目容器的列表的体验。另一方面,
ListBox
是一种高级的
ItemsControl
,带有滚动和 具有 UI 虚拟化和一些有用的属性,例如返回当前所选项目的
ListBox.SelectedItem
属性。在您的情况下,您可以将其绑定到
MethodDialogViewModel.Compound
属性。要消除突出显示,您只需覆盖
ListBoxItem.Template
:
<!--
A ListBoxItem without highlighting.
You can add this template to a resource to reuse it.
-->
<ControlTemplate x:Key="NoHighlightListBoxItemTemplate"
TargetType="ListBoxItem">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}">
<ContentPresenter />
</Border>
</ControlTemplate>
UserControl
)永远不应该使用自己的
DataContext
。例如,当
DataContext
发生变化时,控件就会损坏。由于
Binding.RelativeSource
的目标元素是自定义控件之外的,因此将自定义控件移动到可视化树中的其他位置也会破坏自定义控件。为了防止这种情况,请添加请求将所需数据传递到自定义控件的依赖属性,例如通过数据绑定。然后将内部控件绑定到这些依赖属性。目前尚不清楚为什么在这种情况下使用
UserControl
。您可以通过直接在
UserControl
中定义项目的布局来安全地避免
DataTemplate
的开销。如果需要分隔,您可以将
DataTemplate
移动到其自己的文件中。这与解决您的问题无关,但如果您关心良好的控制设计,则与此相关。这些是一些基础知识。
CompoundCardControl.xaml.cs
public partial class CompoundCardControl : UserControl
{
public MethodCompound Compound
{
get => (MethodCompound)GetValue(CompoundProperty);
set => SetValue(CompoundProperty, value);
}
public static readonly DependencyProperty CompoundProperty = DependencyProperty.Register(
"Compound",
typeof(MethodCompound),
typeof(CompoundCardControl),
new PropertyMetadata(default));
public ICommand DeleteCommand
{
get => (ICommand)GetValue(DeleteCommandProperty);
set => SetValue(DeleteCommandProperty, value);
}
public static readonly DependencyProperty DeleteCommandProperty = DependencyProperty.Register(
"DeleteCommand",
typeof(ICommand),
typeof(CompoundCardControl),
new PropertyMetadata(default));
public CompoundCardControl()
{
InitializeComponent();
}
}
CompoundCardControl.xaml
<UserControl x:Name="Root">
<Button Style="{StaticResource menuBarButton}"
Command="{Binding ElementName=Root, Path=DeleteCommand}"
CommandParameter="{Binding ElementName=Root, Path=Compound}">
<Image Source="/Resources/Icons/Delete.png"/>
</Button>
</UserControl>
MainWindow.xaml
<Window>
<Window.DataContext>
<MethodDialogViewModel />
</Window.DataContext>
<Window.Resources>
<!-- A ListBoxItem without highlighting -->
<ControlTemplate x:Key="NoHighlightListBoxItemTemplate"
TargetType="ListBoxItem">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter />
</Border>
</ControlTemplate>
</Window.Resources>
<ListBox ItemsSource={Binding Method.Compounds}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Template"
Value="{StaticResource NoHighlightListBoxItemTemplate}" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type MethodCompound}">
<control:CompoundCardControl Command="{Biding DeleteCompoundCommand}"
Compound="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Window>
MethodDialogViewModel.cs
public class MethodDialogViewModel : ViewModelBase
{
//properties
public DialogModeEnum DialogMode { get; private set; }
public string WindowTitle { get { return DialogMode == DialogModeEnum.Add ? "New method" : "Edit method"; } }
public string ButtonText { get { return DialogMode == DialogModeEnum.Add ? "Add" : "Update"; } }
public Method Method { get; set; }
// This does never change. Consider to bind it to the ListBox.SelectedItem property
public MethodCompound Compound { get; set; } = new MethodCompound();
//constructor
public MethodDialogViewModel(Method method, DialogModeEnum dialogMode)
{
DialogMode = dialogMode;
Method = method;
}
//commands
public RelayCommand DeleteCompoundCommand => new(ExecuteDeleteCompoundCommand);
private void ExecuteDeleteCompoundCommand(object commandParameter)
=> DeleteCompound((MethodCompound)commandParameter);
private void DeleteCompound(MethodCompound methodCompound)
{
// methodCompund will never be NULL in the current scenario
this.Method.Compunds.Remove(methodCompound);
}
}