我的ListView绑定到ObservableCollection,有没有办法在最后一个listviewitem之后定位按钮?我所做的是在DataTemplate中定义按钮,如下所示:
<DataTemplate x:Key="TestDataTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="SeletedFilterText" Text="{Binding}" />
<Button Command="{Binding DataContext.TestCommand,ElementName=TestListView}"
Content="Test"
Visibility="{Binding Converter={StaticResource testConverter}}"
Grid.Column="1"/>
</Grid>
</DataTemplate>
在我的ViewModel中,我定义了一个字符串变量来存储最后一项。每次我将Collection的最后一个设置为LastItem变量时,ItemSource(一个Observable)可以添加或删除项目。在转换器中,将绑定内容与LastItem进行比较,如果值为true,则显示Button,如果为false,则隐藏它。但转换器永远不会被触发。有人可以帮忙吗?
答案 0 :(得分:3)
我建议不要在ViewModel中使用备份字段来跟踪集合中的lastItem。
只有 Converter
可以返回true或false,如果ListViewItem
是ListView
中的最后一项而不是,则返回true或false。
如果您想在基础ObservableCollection
添加/删除项目时调用转换器,我建议将Count
属性传递给转换器,以便每当添加项目时转换器都会被触发/从集合中删除。
转换代码:
public class IsLastItemInContainerConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter,
CultureInfo culture)
{
DependencyObject item = (DependencyObject)values[0];
ItemsControl ic = ItemsControl.ItemsControlFromItemContainer(item);
if (ic != null)
{
return ic.ItemContainerGenerator.IndexFromContainer(item)
== ic.Items.Count - 1;
}
else
return false;
}
public object[] ConvertBack(object value, Type[] targetTypes,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
<强> XAML:强>
<Button Content="Test">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding
Converter="{StaticResource IsLastItemInContainerConverter}">
<Binding Path="."
RelativeSource="{RelativeSource Mode=FindAncestor,
AncestorType=ListViewItem}"/>
<Binding Path="DataContext.SourceCollection.Count"
RelativeSource="{RelativeSource Mode=FindAncestor,
AncestorType=ListView}"/>
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
将SourceCollection
替换为dataTrigger中的ObservableCollection name
。
答案 1 :(得分:2)
我建议你为你的用例创建一个自定义控件。像这样:
public class ButtonListView : ListView
{
static ButtonListView()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ButtonListView), new FrameworkPropertyMetadata(typeof(ButtonListView)));
}
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register(
"Command", typeof (ICommand), typeof (ButtonListView), new PropertyMetadata(default(ICommand)));
public ICommand Command
{
get { return (ICommand) GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public static readonly DependencyProperty ButtonContentProperty = DependencyProperty.Register(
"ButtonContent", typeof (object), typeof (ButtonListView), new PropertyMetadata(default(object)));
public object ButtonContent
{
get { return (object) GetValue(ButtonContentProperty); }
set { SetValue(ButtonContentProperty, value); }
}
}
并使用这种风格:
<SolidColorBrush x:Key="ListBox.Disabled.Background" Color="#FFFFFFFF" />
<SolidColorBrush x:Key="ListBox.Disabled.Border" Color="#FFD9D9D9" />
<Style TargetType="{x:Type local:ButtonListView}" BasedOn="{StaticResource {x:Type ListBox}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ButtonListView}">
<Border Name="Bd"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
SnapsToDevicePixels="true"
Padding="1">
<ScrollViewer Padding="{TemplateBinding Padding}"
Focusable="false">
<StackPanel>
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
<Button Content="{TemplateBinding ButtonContent}" Command="{TemplateBinding Command}"></Button>
</StackPanel>
</ScrollViewer>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="Bd" Property="Background" Value="{StaticResource ListBox.Disabled.Background}" />
<Setter TargetName="Bd" Property="BorderBrush" Value="{StaticResource ListBox.Disabled.Border}" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsGrouping" Value="true" />
<Condition Property="VirtualizingPanel.IsVirtualizingWhenGrouping" Value="false" />
</MultiTrigger.Conditions>
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
然后您可以这样使用它:
<wpfSandbox:ButtonListView ButtonContent="Press" Command="{Binding ...}"/>
这样您就不需要跟踪ObservableCollection
答案 2 :(得分:1)
是的,这很容易:
使用泛型类型<DependencyObject>
将自定义对象添加到集合的末尾。 (如果你想为它添加命令等,它可以像Button的ViewModel一样)
不要设置ListView(或ItemsControl等)的ItemTemplate
相反,在资源中定义两个DataTemplate
s 不带 x:Key
,并将其DataType
设置为所需类型。它应该像"{x:Type local:ButtonVm}"
或"{x:Type vm:ListViewItemType}"
现在,每个项目的模板会自动设置为与该项目类型相匹配的数据模板。
(请注意,如果模板可以在其他地方重复使用,您可以将ListView.Resources移动到Window.Resources)
MainWindow.xaml:
<ListView ItemsSource="{Binding Items}">
<ListView.Resources>
<DataTemplate DataType="{x:Type vm:ListItemVm}">
<TextBlock Text="{Binding ItemText}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:ButtonVm}">
<Button Command="{Binding ButtonCommand}">
<TextBlock Text="{Binding ButtonText}"/>
</Button>
</DataTemplate>
</ListView.Resources>
</ListView>
MainWindow.xaml.cs:
public MainWindow()
{
InitializeComponent();
DataContext = this;
Items.Add(new ListItemVm { ItemText = "something" } );
Items.Add(new ListItemVm { ItemText = "something" } );
Items.Add(new ListItemVm { ItemText = "something" } );
Items.Add(new ButtonVm { ButtonText = "click here" } );
}
private ObservableCollection<DependencyObject> _items = new ObservableCollection<DependencyObject>();
public ObservableCollection<DependencyObject> Items { get { return _items; } }
每种类型的项目都有一个viewModel:
public class ListItemVm : DependencyObject
{
public string ItemText
{
get { return (string)GetValue(ItemTextProperty); }
set { SetValue(ItemTextProperty, value); }
}
public static readonly DependencyProperty ItemTextProperty =
DependencyProperty.Register("ItemText", typeof(string), typeof(ListItemVm), new UIPropertyMetadata(""));
}
public class ButtonVm : DependencyObject
{
public string ButtonText
{
get { return (string)GetValue(ButtonTextProperty); }
set { SetValue(ButtonTextProperty, value); }
}
public static readonly DependencyProperty ButtonTextProperty =
DependencyProperty.Register("ButtonText", typeof(string), typeof(ButtonVm), new UIPropertyMetadata(""));
public Command ButtonCommand
{
get { return (string)GetValue(ButtonCommandProperty); }
set { SetValue(ButtonCommandProperty, value); }
}
public static readonly DependencyProperty ButtonCommandProperty =
DependencyProperty.Register("ButtonCommand", typeof(Command), typeof(ButtonVm), new UIPropertyMetadata(""));
}
public class Command : ICommand { /* simple implementation of ICommand */ }