Jason Down
Jason Down

Reputation: 22161

Creating template for indexed property

I have a view model that contains a collection of Item Attributes, which in turn each contain a path to an image. There may be anywhere from 0 .. N item attributes in the collection.

A stackpanel in my view contains three identical image controls. Each image control is bound to the path of an item attribute image:

If there are more than 3 attributes, they are ignored. In order to deal with the possibility of having 0 - 2 attributes (which means 1 or more of the image controls will be bound to null), which in turn gives the error stated in this post, I have added a data trigger like so:

<DataTrigger Binding="{Binding Attribute1}" Value="{x:Null}">
    <Setter Property="Source" Value="{x:Null}"/>
</DataTrigger>

Also, in order to prevent an index out of bound issue, I have split the item attributes in my view model into three properties (i was initially returning String.Empty, but changed it to null to work with the data trigger):

public string Attribute1
{
    get { return _item.Attributes.Count > 0 ? _item.Attributes[0].Image : null; }
}
public string Attribute2
{
    get { return _item.Attributes.Count > 1 ? _item.Attributes[1].Image : null; }
}
 public string Attribute3
{
    get { return _item.Attributes.Count > 2 ? _item.Attributes[2].Image : null; }
}

So my problem is that I would like to have this data trigger work for all three image attributes and the corresponding image controls (along with some other properties like width, height, margin etc.). So I think, put it in a style and reference it as a static resource. This won't work when I have three different properties with different names (Attribute1, Attribute2, Attribute3). So now I am stuck doing it this way:

<Image>
    <Image.Style>
        <Style TargetType="Image">
            <Setter Property="Source" Value="{Binding Attribute1}" />
            <Setter Property="Width" Value="44" />
            <Setter Property="Height" Value="45" />
            <Setter Property="Margin" Value="5" />
            <Style.Triggers>
                <DataTrigger Binding="{Binding Attribute1}" Value="{x:Null}">
                    <Setter Property="Source" Value="{x:Null}" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Image.Style>
</Image>

This is repeated for the other two image controls, except the Attribute1 is substituted with Attribute2 and Attribute3.

So then I was wondering if there is a way to bind to a collection instead, such as

<DataTrigger Binding="{Binding Attributes}" Value="{x:Null}">
    <Setter Property="Source" Value="{x:Null}"/>
</DataTrigger>

... and then specifcy the index I'm interested in the image control binding, outside of the template (i guess like passing a parameter to the data trigger).

Any ideas... is there another approach if this isn't possible?

Upvotes: 3

Views: 101

Answers (1)

Fede
Fede

Reputation: 44038

<ItemsControl ItemsSource="{Binding Attributes}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Image Source={Binding Something} x:Name=Image/>
            <DataTemplate.Triggers>
                <DataTrigger Binding={Binding Something} Value={x:Null}>
                     <Setter TargetName=Image Property=Source Value={x:Null}/>
                </DataTrigger>
            </DataTemplate.Triggers>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

Typed this manually in the answer, not real XAML. But you should be able to understand what I mean.

Edit: If your Attributes are just strings, use "{Binding}" wherever I placed "{Binding Something}"

--UPDATE (I'll update your answer with what I did since your answer was correct)--

<ItemsControl ItemsSource="{Binding Attributes}"
              Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="2" Margin="5">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel Orientation="Horizontal"
                        HorizontalAlignment="Left" VerticalAlignment="Top" />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Image x:Name="Image"
                   Source="{Binding}"
                   Width="45" Height="44" Margin="5" />
            <DataTemplate.Triggers>
                <DataTrigger Binding="{Binding}" Value="{x:Null}">
                    <Setter TargetName="Image" 
                            Property="Source" Value="{x:Null}" />
                </DataTrigger>
            </DataTemplate.Triggers>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

And to limit it to the first three item attributes, this is what I had in my view model's constructor. Attributes is a SmartObservableCollection property (custom ObservableCollection with and AddRange method and a couple other goodies) with an _attributes backing field:

_attributes = new SmartObservableCollection<string>();
var images = from attributes in _item.Attributes
             select attributes.Image;

Attributes.AddRange(images.Take(3));

Upvotes: 7

Related Questions