gsnerf
gsnerf

Reputation: 572

Binding complex properties in Silverlight/WPF

Lets say I have a custom data type that looks something like this:

public class MyDataType
{
  public string SimpleProp1;
  public string SimpleProp2;
  public List<SomeType> ComplexProp;
}

now I hava a data bound control (i.e. ItemsControl or DataGrid), that is created dynamically. How would the binding defined in xaml code look like to acces a subproperty of the complex property? I thought it should look something like this:

<TextBox Text="{Binding simpleSubProp, path=ComplexProp[0]}" />

or

<TextBox Text="{Binding path=ComplexProp[0].simpleSubProp}" />

but both of those give me xml parse errors. How should it look correctly? Is it even possible to refer to a specific item of a collection property in souch a way? If it is not, what other options do I have?

EDIT, The scenario doesn't seem to be clear enough:

I have an

IEnumberable<MyDataType>

that is bound to an ItemsControl, inside the DataTemplate I have multiple TextBoxes that need to refer to subproperties of an object in the List of the complex property.

Upvotes: 9

Views: 11442

Answers (7)

Kent Boogaart
Kent Boogaart

Reputation: 178660

According to the Path Syntax on MSDN, you can just do:

<TextBox Text="{Binding ComplexProp[0].simpleSubProp}" />

It may be the lowercase "path=" that gave you errors? Try "Path=". Also, not sure if this works in Silverlight...

Upvotes: 1

Stephen Price
Stephen Price

Reputation: 1649

This now works ok in Silverlight 4.

One thing to note, the class has three public fields. Change them to Properties (and would be an idea to implement INotifyPropertyChanged as well) and then the following code works.

<StackPanel>
    <TextBlock Text="{Binding Path=SimpleProp1}" />
    <TextBox Text="{Binding Path=ComplexProp[0].SimpleSubProp, Mode=TwoWay}" Width="200" Height="60"/>
</StackPanel>

Case is also important as the bindings are case sensitive.

Upvotes: 2

Peter Wone
Peter Wone

Reputation: 18765

To generalise this all the way, I suggest you use a value converter as mentioned by others, and use the ConverterParam option to pass an index.

Upvotes: 1

Eric
Eric

Reputation: 1472

I do this kind of thing all the time, there are two ways I would approach this problem depending on what you want out of it.

If you really want only the one specific member of the list, you can use a converter. The XAML would look something like this:

<TextBox Text="{Binding MyDataTypeInstance, Converter={StatacResources SpecificInstanceConverter}}" />

That's not usually what I need though, usually I need one control to display the whole comlex object, if that's the case the way to do it is more like as follows:

<StackPanel>
     <Textblock Text="{Binding SimpleProp1}"/>
     <TextBlock Text="{Bidning SimpleProp2}"/>
     <ItemsControl ItemsSource="{Binding ComplexProp}>
          <ItemsControl.ItemsTemplate>
               <DataTemplate>
                    <StackPanel>
                         <TextBlock Text="{Binding ComplexPropertyName}/>
                         <InputToolkit:NumericUpDown Value="{Binding ComplexPropertyValue}/>
                    </StackPanel>
               </DataTemplate>
          </ItemsControl.ItemsTemplate>
     </ItemsControl>
</StackPanel>

I like this method because my GUI matches my business objects, and as a result it usually ends up a lot cleaner than if I'm checking for specific indexes in code-behind.

Upvotes: 4

sipsorcery
sipsorcery

Reputation: 30699

Looks like poperty path indexers are broken in Silverlight Indexers in property paths are broken. The way to get around it is as suggested in the post and to use an IValueConverter.

XAML

<UserControl x:Class="Silverlight.Mine.Page"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
  xmlns:sys="System"
  xmlns:sm="clr-namespace:Silverlight.Mine;assembly=Silverlight.Mine"
  Width="400" Height="300">
    <UserControl.Resources> 
       <sm:SomeTypeConverter x:Key="MySomeTypeConverter" />
    </UserControl.Resources>    
    <Grid x:Name="LayoutRoot" Background="White">
        <TextBlock x:Name="myTextBlock" Text="{Binding Path=SomeDates, Converter={StaticResource MySomeTypeConverter}}" />
    </Grid>
</UserControl>

C# Page.xaml.cs

namespace Silverlight.Mine
{
    public partial class Page : UserControl
    {
        private SomeType m_mySomeType = new SomeType();

        public Page()
        {
            InitializeComponent();
            myTextBlock.DataContext = m_mySomeType;
        }
    }
}

C# SomeType.cs

namespace Silverlight.Mine
{
    public class SomeType
    {
        public List<DateTime> SomeDates { get; set; }

        public SomeType()
        {
            SomeDates = new List<DateTime>();
            SomeDates.Add(DateTime.Now.AddDays(-1));
            SomeDates.Add(DateTime.Now);
            SomeDates.Add(DateTime.Now.AddDays(1));
        }
    }

    public class SomeTypeConverter : IValueConverter
    {
        public object Convert(object value,
                       Type targetType,
                       object parameter,
                       CultureInfo culture)
        {
            if (value != null)
            {
                List<DateTime> myList = (List<DateTime>)value;
                return myList[0].ToString("dd MMM yyyy");
            }
            else
            {
                 return String.Empty;
            }
        }

        public object ConvertBack(object value,
                              Type targetType,
                              object parameter,
                              CultureInfo culture)
        {
            if (value != null)
            {
                return (List<DateTime>)value;
            }
            return null;
        }
    }
}

Upvotes: 7

Shawn Wildermuth
Shawn Wildermuth

Reputation: 7458

Try {Binding ComplexProp(0).simpleSubProp}. If that doesn't work, you can write a simple Converter to do this too.

Upvotes: 2

Steven Robbins
Steven Robbins

Reputation: 26599

I'm not sure you can do that. Usually you will bind a list to something like a listbox (or another "repeating" control) and then each item inside that will be able to bind to the relevent element in the list.

Upvotes: 1

Related Questions