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

时间:2014-03-10 11:53:34

标签: c# wpf xaml listview

我的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,则隐藏它。但转换器永远不会被触发。有人可以帮忙吗?

3 个答案:

答案 0 :(得分:3)

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

只有 Converter可以返回true或false,如果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

答案 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)

是的,这很容易:

  1. 使用泛型类型<DependencyObject>

  2. 定义Observable集合
  3. 将自定义对象添加到集合的末尾。 (如果你想为它添加命令等,它可以像Button的ViewModel一样)

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

  5. 相反,在资源中定义两个DataTemplate s 不带 x:Key,并将其DataType设置为所需类型。它应该像"{x:Type local:ButtonVm}""{x:Type vm:ListViewItemType}"

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

    实施例

    (请注意,如果模板可以在其他地方重复使用,您可以将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 */ }