在最后一个listviewitem之后放置一个按钮

本文关键字:一个 按钮 最后一个 listviewitem 之后 | 更新日期: 2023-09-27 17:49:21

我的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中,我定义了一个字符串变量来存储最后一项。ItemSource(一个可观察对象)可以添加或删除项目,每次我将最后一个集合设置为LastItem变量。在转换器中,将绑定内容与LastItem进行比较,如果值为true,则显示Button,如果值为false,则隐藏它。但转换器永远不会被触发。有人能帮忙吗?

在最后一个listviewitem之后放置一个按钮

我建议不要在ViewModel中有备份字段来跟踪集合中的lastItem。

您可以只使用 Converter来执行此操作,如果传递的ListViewItemListView中的最后一项,则返回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

我建议您为您的用例创建一个自定义控件。像这样:

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

中的顺序了

是的,这很简单:

  1. 用通用类型<DependencyObject>定义你的Observable集合

  2. 在集合的末尾添加一个自定义对象。(它可以是一个类似于ViewModel的按钮,如果你想添加命令或等)

  3. 不要设置ListView(或ItemsControl等)的ItemTemplate

  4. 相反,在资源中定义两个没有 x:KeyDataTemplate s ,并将它们的DataType设置为所需的类型。应该是"{x:Type local:ButtonVm}"或者"{x:Type vm:ListViewItemType}"

现在,每个项目的模板自动设置为与该项目类型匹配的数据模板。

例子:

(注意你可以移动ListView。资源到窗口。资源(如果模板可以在其他地方重用)

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 */ }