views:

64

answers:

3

Hi,

I have a data source ('SampleAppearanceDefinitions'), which holds a single collection ('Definitions'). Each item in the collection has several properties, including Color, which is what I'm interested in here.

I want, in XAML, to display the Color of a particular item in the collection as text. I can do this just fine using this code below...

Text="{Binding Source={StaticResource SampleAppearanceDefinitions}, Path=Definitions[0].Color}"

The only problem is, this requires me to hard-code the index of the item in the Definitions collection (I've used 0 in the example above). What I want to do in fact is to get that value from a property in my current DataContext ('AppearanceID'). One might imagine the correct code to look like this....

Text="{Binding Source={StaticResource SampleAppearanceDefinitions}, Path=Definitions[{Binding AppearanceID}].Color}"

...but of course, this is wrong.

Can anyone tell me what the correct way to do this is? Is it possible in XAML only? It feels like it ought to be, but I can't work out or find how to do it.

Any help would be greatly appreciated!

Thanks!

AT

A: 

Even if it could be possible you'd better not do that this way, but instead use a dedicated property in your view model or in the code behind of your view if it has only a pure graphical meaning.

This property, say "CurrentAppearance", would expose a Color property you could bind from your Xaml :

Text="{Binding CurrentAppearance.Color}"

which is more understandable.

As a general advice : avoid to spoil your Xaml with plumbing code : Xaml should be as readable as possible, particularly if you work with a team of designers that have no coding skills and do not want to be concerned with the way you are managing the data.

Moreover, if later you decide to change the way data are managed you would not have to change your Xaml.

Serious
+1  A: 

MultiBinding is your friend here:

Assuming you have a TextBlock:

<TextBlock>
    <TextBlock.Text>
        <MultiBinding Converter="{StaticResource AppearanceIDConverter}">
            <Binding Source="{StaticResource SampleAppearanceDefinitions}" />
            <Binding Path="AppearanceID" />
        </MultiBinding>
    </TextBlock.Text>
</TextBlock>

And define a MultiValueConverter to return what you wish to see:

public class AppearanceIDConverter : IMultiValueConverter
{
    #region IMultiValueConverter Members

    public object Convert(object[] values, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        List<item> items = (List<item>)values[0]; //Assuming its items in a List
        int id = (int)values[1]; //Assuming AppearanceID is an integer
        return items.First(i => i.ID == id).Color; //Select your item based on the appearanceID.. I used LINQ, but a foreach will work just fine as well
    }

    public object[] ConvertBack(object value, System.Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new System.NotImplementedException();
    }

    #endregion
}

Of course, you will need to set the converter as a resource in your Resource dictionary, like you did SampleAppearanceDefinitions. You can also ditch the multibinding and use a regular binding to AppearanceID with a IValueConverter, if you can get to the SampleAppearanceDefinitions collection through code ;).

Hope this helps

Arcturus
How will it work with a TwoWay binding ?
Serious
If you want multiple datas resposible for your value, you can use a MultiBinding in stead of a normal Binding; For TwoWay Bindings you will have to provide some logic in the ConvertBack method.
Arcturus
Precisely, this implementation does not seem obvious : it would be tricky to retrieve in which object is the collection, in which collection and at which index the bound value is; and what if it is present in more than one collection ? The converter would need more context : I guess this context could be passed with the ConverterParameter but the resulting Xaml might be heavy. Could you provide a simple implementation ? Thanks.
Serious
Thats nice and all, but that does not seem to be what Andy was asking. It seemed all he wanted was an OneWay (readonly) binding to retrieve a Color. If you want to use a MultiBinding for a TwoWay binding, the resulting object (in this case Color) should be enough to determine the new values. Compare it with a FirstName and LastName versus FullName. Of course you can use the ConverterParameter to give it a bit more context, but IMO, the value should contain all that info already!
Arcturus
A: 

MultiBinding might actually work if your list is on a viewmodel instead of a staticresource. I was suprised myself to see that the object passed on to the view is actually a pointer to the object on the model, so changing the object in the view (eg. typing in new test in the textbox) directly affects the model object.

This worked for me. The ConvertBack method is never useed.

public class PropertyIdToPropertyConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values.Length == 2)
        {
            var properties = values[0] as ObservableCollection<PropertyModel>;

            if (properties != null)
            {
                var id = (int)values[1];
                return properties.Where(model => model.Id == id).FirstOrDefault();
            }
        }

        return null;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}
David