如何为ItemsControl中的每个项目创建叠加层?

时间:2010-01-21 19:17:47

标签: wpf xaml overlay itemscontrol adorner

我正在尝试装饰ItemsControl,以便每个项目都有一个删除按钮,该按钮在特定条件下浮动在项目的内容上,受到iPhone UI的启发。我有几种方法可以解决这个问题,但我可以使用其他WPF人员的一些指导,这些人可能会更好地了解如何最好地完成这项工作。下面是一个模拟图像,以帮助表达我正在尝试做的事情。

Example mock-up

我目前的想法是尝试通过仅使用样式,模板以及必要时附加属性的XAML来尝试此操作。我们的想法是为items控件创建条件DataTemplate,它会以某种方式用包含我的Delete按钮的装饰器包装原始内容。为了在我的ItemsControl上有一个状态来知道我是否处于删除模式,我想是可能创建一个附加属性,然后我可以通过各种方式设置,如例如,将其绑定到切换按钮或复选框的状态。

此时这个概念是有道理的,但对于我来说,使用ItemTemplate是否是最好的移动是不是很清楚,因为在某些情况下,对于给定的ItemsControl,ItemTemplate可能已经存在并且我不想覆盖它但是相反,只想包装它(如果这是有道理的)。我想如果我向右拉,我应该能够通过指定样式和附加属性将其应用于任何物品控制。

如果有人可以帮助说明这些更精细的细节,或者提供更好的建议,请分享。

2 个答案:

答案 0 :(得分:2)

通常你会在ItemContainerStyle中添加这样的东西,这样每次你想要应用它时都不会弄乱DataTemplate。虽然使用ListBox可以修改ListBoxItem的模板很简单,但遗憾的是,基于ItemsControl只使用ContentPresenter作为其容器,因此不能以相同的方式进行模板化。

如果您真的希望这可以重复使用,我建议您将其包装到一个新的自定义ItemsControl中,您可以将其替换为标准的,而无需修改正在使用的特定DataTemplate。这也将允许您将您在外部创建的属性作为附加的prop以及控件本身中的delete命令进行打包。

显然删除逻辑和视觉样式还没有在这里完成,但这应该让你开始:

public class DeleteItemsControl : ItemsControl
{
    public static readonly DependencyProperty CanDeleteProperty = DependencyProperty.Register(
        "CanDelete",
        typeof(bool),
        typeof(DeleteItemsControl),
        new UIPropertyMetadata(null));

    public bool CanDelete
    {
        get { return (bool)GetValue(CanDeleteProperty); }
        set { SetValue(CanDeleteProperty, value); }
    }

    public static RoutedCommand DeleteCommand { get; private set; }

    static DeleteItemsControl()
    {
        DeleteCommand = new RoutedCommand("DeleteCommand", typeof(DeleteItemsControl));
        DefaultStyleKeyProperty.OverrideMetadata(typeof(DeleteItemsControl), new FrameworkPropertyMetadata(typeof(DeleteItemsControl)));
    }

    protected override DependencyObject GetContainerForItemOverride()
    {
        return new DeleteItem();
    }

    protected override bool IsItemItsOwnContainerOverride(object item)
    {
        return item is DeleteItem;
    }
}

public class DeleteItem : ContentControl
{
    static DeleteItem()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(DeleteItem), new FrameworkPropertyMetadata(typeof(DeleteItem)));
    }

}

这可以在Generic.xaml中使用,或者您可以像应用程序中的普通样式一样应用它们:

<Style TargetType="{x:Type local:DeleteItemsControl}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:DeleteItemsControl}">
                <Border Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                    <ItemsPresenter/>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<Style TargetType="{x:Type local:DeleteItem}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:DeleteItem}">
                <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}"
                    BorderThickness="{TemplateBinding BorderThickness}">
                    <DockPanel>
                        <Button Command="local:DeleteItemsControl.DeleteCommand" Content="X" HorizontalAlignment="Left" VerticalAlignment="Center"
                            Visibility="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:DeleteItemsControl}}, Path=CanDelete, Converter={StaticResource BooleanToVisibilityConverter}}"/>
                        <ContentPresenter VerticalAlignment="{TemplateBinding VerticalContentAlignment}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"/>
                    </DockPanel>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

答案 1 :(得分:1)

我创建了以下模型来说明一种简单的方法,使用单个DataTemplate几个绑定。你肯定是在正确的轨道上。

<Window x:Class="TestWpfApplication.Foods"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestWpfApplication"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="Foods" ResizeMode="NoResize"
SizeToContent="WidthAndHeight"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Window.Resources>
    <local:BoolToVisConverter x:Key="BoolToVisConverter"/>
</Window.Resources>
<StackPanel Background="LightGray">
    <ToggleButton Name="EditModeToggle" Content="Edit" HorizontalAlignment="Right" FontFamily="Arial" Padding="4" 
                  Background="#7FA4E6" Foreground="White" BorderBrush="Black" Width="60" Margin="5,5,5,0"/>
    <ListBox ItemsSource="{Binding Items}" 
             Background="#999" BorderBrush="Black" Margin="5">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Border CornerRadius="8" Background="#3565BC" Padding="8" 
                        BorderBrush="#333" BorderThickness="1" Margin="2" Width="255">
                    <StackPanel Orientation="Horizontal">
                        <Button Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=RemoveItemCommand}"
                                Visibility="{Binding ElementName=EditModeToggle, Path=IsChecked, Converter={StaticResource BoolToVisConverter}}">
                            <Button.Content>
                                <TextBlock Foreground="Red" Text="X" FontWeight="Bold"/>
                            </Button.Content>
                        </Button>
                        <TextBlock Text="{Binding}" Margin="12,0" Foreground="#AAA" VerticalAlignment="Center"
                                   FontSize="14" FontWeight="Bold" FontFamily="Arial"/>
                    </StackPanel>
                </Border>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</StackPanel>

您不需要太多代码就可以完成这项工作,但仍有一些代码隐藏,主要针对RoutedCommand

public partial class Foods : Window
{
    private ObservableCollection<String> items = new ObservableCollection<string>();
    private RoutedCommand removeItemCommand = new RoutedCommand();

    public Foods()
    {
        InitializeComponent();

        items.Add("Ice Cream");
        items.Add("Pizza");
        items.Add("Apple");

        CommandBindings.Add(new CommandBinding(removeItemCommand, ExecutedRemoveItem));
    }

    public ObservableCollection<String> Items
    {
        get { return items; }
    }

    public RoutedCommand RemoveItemCommand
    {
        get { return removeItemCommand; }
    }

    private void ExecutedRemoveItem(object sender, ExecutedRoutedEventArgs e)
    {
        DependencyObject container = 
            ItemsControl.ContainerFromElement(e.Source as ItemsControl, e.OriginalSource as DependencyObject);
        ListBoxItem item = container as ListBoxItem;
        items.Remove(item.Content as String);
    }
}

显然,结果可能在视觉上更具吸引力,但我几乎重复了你的想法哈哈:

alt text http://img697.imageshack.us/img697/7033/foodswindow.png