1

I'm trying to format my ListBoxItem template to include a image. I can add an image to the ListBoxItem but i'm not too sure on how i would go about setting the value for that image.

Template for ListBoxItem:

        <Style TargetType="{x:Type ListBoxItem}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="ListBoxItem">
                    <Border Name="Border" Padding="2" SnapsToDevicePixels="true">
                        <StackPanel Orientation="Horizontal" >
                            <Image Source="{Binding Path=Source}" Height="16" Width="16" HorizontalAlignment="Center" VerticalAlignment="Center" />
                            <ContentPresenter Name="ContentPresenter" HorizontalAlignment="Stretch" Width="Auto" />
                        </StackPanel>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsSelected" Value="true">
                            <Setter TargetName="Border" Property="Background" Value="{StaticResource ListBoxItem_BackgroundBrush_Selected}"/>
                            <Setter TargetName="ContentPresenter" Property="TextElement.FontWeight" Value="Bold"/>
                        </Trigger>
                        <Trigger Property="IsEnabled" Value="false">
                            <Setter Property="Foreground" Value="{StaticResource TabItem_BackgroundBrush_Disabled}"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

Example ListBox code:

   <ListBox Name="listBox_LibAll" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
       <ListBoxItem Content="Item 1" />
       <ListBoxItem Content="Item 2" />
       <ListBoxItem Content="Item 3" />                                
   </ListBox>

Output: enter image description here

If you look at the picture you will note that there is a place for the image, i just dont know how to set its value. I was thinking i could somehow attach the "Source" property to the ListBoxItem

Community
  • 1
  • 1
zaza
  • 892
  • 1
  • 18
  • 37

2 Answers2

3

You can do a RelativeSource binding to the Tag or some attached property.

Source="{Binding Tag, RelativeSource={RelativeSource AncestorType=ListBoxItem}}"

<ListBoxItem Tag="SomePath" />
Source="{Binding (ns:AttachedProperties.Source), RelativeSource={RelativeSource AncestorType=ListBoxItem}}"

<ListBoxItem ns:AttachedProperties.Source="SomePath" />

You could also use dynamic resources as shown here.

The cleanest solution however would be to make the content complex, i.e. make the ListBoxItem host a UserControl or custom control for example which actually has a proper property for the image. You should usually not override the control template of ListBoxItems either but rather use the ItemTemplate of the ListBox to data template the data.

Community
  • 1
  • 1
H.B.
  • 166,899
  • 29
  • 327
  • 400
  • Thanks, this works perfectly. Its also easy to work with in c#. Thanks, – zaza Jan 02 '12 at 00:12
  • @Dmitry: Some [XML namespace](http://msdn.microsoft.com/en-us/library/ms747086.aspx) that needs to be defined to point to the a CLR-namespace in which the class containing the attached property is defined. – H.B. Jan 02 '12 at 03:05
  • @zaza: I added another paragraph since i think you are taking the wrong approach, my previous suggestions work but i would not really recommend them, using data templates (i.e. complex content) is much cleaner in my opinion. – H.B. Jan 02 '12 at 03:16
  • @H.B. Is there any thing wrong if i use your first method? I just made tested it with my own custom control and i found it incenses memory usage by 8MB (610 items in the `ListBox`). This program is intended to have thousands of items in that `ListBox`. Also i dont need to use the `Tag` property for any other purpose. – zaza Jan 02 '12 at 05:30
  • @zaza: It should usually not be dramatic if you do not override the control template at the same time (because you will have the same controls but in another place). Either way, if you have thousands of items you should use virtualization anyway so that only items which are visible are created. – H.B. Jan 02 '12 at 12:43
0

you were almost there.

Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }

    public class ImageItem: DependencyObject
    {
        public static readonly DependencyProperty SourceProperty =
            DependencyProperty.RegisterAttached("Source",
            typeof(string),
            typeof(DependencyObject),
            new PropertyMetadata((o, e) => 
            {
                //System.Diagnostics.Debugger.Break();
            }));

        public static string GetSource(DependencyObject o)
        {
            return (string)o.GetValue(ImageItem.SourceProperty);
        }

        public static void SetSource(DependencyObject o, string e)
        {
            o.SetValue(ImageItem.SourceProperty, e);
        }
    }
}

Markup:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication1"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.Resources>
            <Style TargetType="{x:Type ListBoxItem}">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="ListBoxItem">
                            <Border Name="Border" Padding="2" SnapsToDevicePixels="true">
                                <StackPanel Orientation="Horizontal" >
                                    <Image Source="{Binding Path=Source, 
                                           RelativeSource={RelativeSource AncestorType=ListBoxItem}}" 
                                           Height="16" 
                                           Width="16" 
                                           HorizontalAlignment="Center" VerticalAlignment="Center" />
                                    <ContentPresenter Name="ContentPresenter" HorizontalAlignment="Stretch" Width="Auto" />
                                </StackPanel>
                            </Border>
                            <ControlTemplate.Triggers>
                                <Trigger Property="IsSelected" Value="true">
                                    <Setter TargetName="Border" Property="Background" Value="{StaticResource ListBoxItem_BackgroundBrush_Selected}"/>
                                    <Setter TargetName="ContentPresenter" Property="TextElement.FontWeight" Value="Bold"/>
                                </Trigger>
                                <Trigger Property="IsEnabled" Value="false">
                                    <Setter Property="Foreground" Value="{StaticResource TabItem_BackgroundBrush_Disabled}"/>
                                </Trigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </Grid.Resources>
        <ListBox Name="listBox_LibAll" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
            <ListBoxItem local:ImageItem.Source="/WpfApplication1;component/Penguins.jpg" Content="Item 1" />
            <ListBoxItem Content="Item 2" />
            <ListBoxItem Content="Item 3" />
        </ListBox>
    </Grid>
</Window>
  • There are several things wrong here. 1. `ImageItem` should not inherit from anything as it is not an object. 2. `ImageItem` should be a static class as having instances of it makes no sense, it just hosts a property. 3. The type of the property should be `ImageSource`, a string can still be assigned in XAML, you want type safety on the property though. 4. The owner type should be `ImageItem` not `DepedencyObject` since the property has be defined in that class. (Also i would say your answer is too noisy, too much (even redundant and partially wrong) code with too little explanaition) – H.B. Jan 02 '12 at 03:32
  • H.B. - How did you manage to get 30K and still have such questions? ImageItems must inehrit from DO, otherwise you won't be able register attached properties for it. DO allows for the widest range of objects, that's why I used it. Brr Grrr... Just get the code and compile it. At least I didn't use ns:) LOL. –  Jan 02 '12 at 03:35
  • Wrong and wrong, attached properties are attached, they do not require the owning type to be a dependency object. And the owning type is different from where the property can be used, every attached property can be used on every dependency object. – H.B. Jan 02 '12 at 03:36
  • Hey, I understand your disappointment, you've got lost in 2 woods (not even three). Get the solution and learn how to use DPs. Remember critisizing is always easier than creating . –  Jan 02 '12 at 03:40
  • I've aremoved :DO and it works, my bad. Otherwise it's a sound solution, comparing to the set or rough guidences you gave. –  Jan 02 '12 at 03:42
  • 1
    It's not rough guidance, if the asked does not know how to define an attached property he can easily look it up in the link i provided, *there is no need for me to add that redundant code*. – H.B. Jan 02 '12 at 03:44
  • I was always wondering how a beginner being provided with the very high level instructions reports back in 5 minutes about successful implementation? You man clearly see your man is not on a ball with WPF at all, so he took something quite vague and then what? Figured he'll never get it and just plussed your answer:) –  Jan 02 '12 at 03:47