views:

3132

answers:

6

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.

A: 

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.

Steven Robbins
I may not have made that clear enough, I have an IEnumerable<MyDataType> bound to an ItemsControl. Inside the DataTemplate of my ItemsControl I need to reference the subproperty of a specific item in the complex property.
gsnerf
+1  A: 

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

Shawn Wildermuth
Thx for your answer, unfortunately this gives me an System.ArgumentException "Invalid Binding Path; character".How would a converter for this look like? I thought converters would be normaly used for formating data in a specific way
gsnerf
A: 

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...

HTH, Kent

Kent Boogaart
That page is for WPF, not Silverlight. Silverlight doesn't support indexed properties in binding.
mattmanser
The title of the question says "Silverlight/WPF".
Kent Boogaart
The indexing method seems to only work for some cases in silverlight, thx nevertheless!
gsnerf
Silverlight 3 supports indexed properties in binding.
JasonRShaver
+3  A: 

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;
        }
    }
}
sipwiz
I will give that a try, thx!
gsnerf
A: 

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.

Eric
A: 

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.

Peter Wone