Как создать оверлей для каждого элемента в ItemsControl? - PullRequest
1 голос
/ 21 января 2010

Я пытаюсь украсить ItemsControl, чтобы у каждого элемента была кнопка «Удалить», которая плавает над содержимым элемента в определенных условиях, в некоторой степени вдохновленных пользовательским интерфейсом iPhone. У меня есть несколько способов понять, как я могу подойти к этому, но я мог бы использовать некоторые рекомендации от других людей WPF, которые могли бы иметь лучшее представление о том, как это лучше всего сделать. Ниже приведен макет, чтобы помочь выразить то, что я пытаюсь сделать.

Example mock-up

Моя текущая мысль - попытаться сделать это, используя только XAML, используя только стили, шаблоны и, возможно, присоединенные свойства, если это необходимо. Идея состоит в том, чтобы создать условный DataTemplate для элемента управления элементами, который каким-то образом обернул бы исходное содержимое рекламодателем, содержащим мою кнопку «Удалить». Чтобы иметь состояние на моем ItemsControl, чтобы знать, нахожусь ли я в режиме удаления или нет, я думаю, возможно, создать присоединенное свойство, которое я затем могу установить различными способами, такими как привязка например, в состояние переключателя или флажка.

На данный момент концепция имеет смысл, но мне неясно, какие тонкие детали мне лучше понять, является ли использование ItemTemplate лучшим вариантом, поскольку в некоторых случаях ItemTemplate может уже существовать для данного ItemsControl, и я не хочу его перезаписывать вместо этого только хотел бы обернуть это (если это имеет смысл). Я думаю, что если я потяну, если не правильно, я смогу применить это к любому элементу управления, указав стиль и, возможно, присоединенное свойство.

Если кто-нибудь может помочь проиллюстрировать эти более мелкие детали или предложить какие-либо более полезные предложения о том, как я могу поступить, пожалуйста, поделитесь.

Ответы [ 2 ]

2 голосов
/ 22 января 2010

Обычно вы добавляете что-то подобное в ItemContainerStyle, чтобы не связываться с самим DataTemplate каждый раз, когда вы хотите применить это. Хотя это просто с ListBox, где вы можете изменить шаблон для ListBoxItem, к сожалению, базовый ItemsControl использует только ContentPresenter в качестве своего контейнера и поэтому не может быть настроен таким же образом.

Если вы действительно хотите, чтобы это можно было многократно использовать, я бы посоветовал вам обернуть его в новый пользовательский элемент ItemsControl, который вы можете вставить для замены стандартного без необходимости изменения конкретного используемого шаблона данных. Это также позволит вам обернуть свойство, которое вы в противном случае создадите извне, как прикрепленную подпорку и команду удаления в самом элементе управления.

Очевидно, что логика удаления и визуальное оформление здесь не были выполнены, но вам стоит начать:

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 голос
/ 21 января 2010

Я создал следующий макет, чтобы проиллюстрировать один простой способ сделать это, используя один 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);
    }
}

И результат, очевидно, можно сделать гораздо более привлекательным, но я чуть не продублировал вашу идею, ха-ха:

альтернативный текст http://img697.imageshack.us/img697/7033/foodswindow.png

...