Связывание сложных свойств в Silverlight / WPF

Допустим, у меня есть собственный тип данных, который выглядит примерно так:

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

теперь у меня есть элемент управления с привязкой к данным (например, ItemsControl или DataGrid), который создается динамически. Как будет выглядеть привязка, определенная в коде xaml, для доступа к подсвойству сложного свойства? Я думал, это должно выглядеть примерно так:

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

or

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

но оба они дают мне ошибки синтаксического анализа xml. Как это должно выглядеть правильно? Можно ли каким-то образом ссылаться на конкретный элемент свойства коллекции каким-либо образом? Если нет, то какие еще варианты у меня есть?

РЕДАКТИРОВАТЬ, сценарий кажется недостаточно ясным:

у меня есть

IEnumberable<MyDataType>

который привязан к ItemsControl, внутри DataTemplate у меня есть несколько текстовых полей, которые должны ссылаться на подсвойства объекта в списке сложного свойства.


person gsnerf    schedule 01.04.2009    source источник


Ответы (7)


Похоже, что в Silverlight не работают индексаторы пути к свойствам. Индексаторы в путях к свойствам не работают. Способ обойти это предлагается в сообщении и использовать 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;
        }
    }
}
person sipsorcery    schedule 01.04.2009

Я делаю такие вещи все время, есть два способа подойти к этой проблеме в зависимости от того, чего вы от нее хотите.

Если вам действительно нужен только один конкретный член списка, вы можете использовать конвертер. XAML будет выглядеть примерно так:

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

Обычно это не то, что мне нужно, обычно мне нужен один элемент управления для отображения всего сложного объекта, в этом случае способ сделать это больше похож на следующий:

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

Мне нравится этот метод, потому что мой графический интерфейс соответствует моим бизнес-объектам, и в результате он обычно оказывается намного чище, чем если бы я проверял определенные индексы в коде программной части.

person Eric    schedule 24.11.2009

Попробуйте {Binding ComplexProp (0) .simpleSubProp}. Если это не сработает, вы можете написать простой конвертер, который тоже сделает это.

person Shawn Wildermuth    schedule 01.04.2009
comment
Спасибо за ваш ответ, к сожалению, это дает мне System.ArgumentException Invalid Binding Path; персонаж. Как бы для этого выглядел конвертер? Я думал, что конвертеры будут нормально использоваться для форматирования данных определенным образом. - person gsnerf; 01.04.2009

Теперь это нормально работает в Silverlight 4.

Следует отметить, что у класса есть три общедоступных поля. Измените их на Свойства (и было бы неплохо реализовать INotifyPropertyChanged), и тогда следующий код будет работать.

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

Регистр также важен, поскольку привязки чувствительны к регистру.

person Stephen Price    schedule 03.07.2011

Я не уверен, что ты сможешь это сделать. Обычно вы привязываете список к чему-то вроде списка (или другого «повторяющегося» элемента управления), а затем каждый элемент внутри, который сможет привязаться к соответствующему элементу в списке.

person Steven Robbins    schedule 01.04.2009
comment
Возможно, я не сказал это достаточно ясно, у меня есть IEnumerable ‹MyDataType›, привязанный к ItemsControl. Внутри DataTemplate моего ItemsControl мне нужно сослаться на подсвойство определенного элемента в сложном свойстве. - person gsnerf; 01.04.2009

Чтобы обобщить это полностью, я предлагаю вам использовать преобразователь значений, как упоминалось другими, и использовать параметр ConverterParam для передачи индекса.

person Peter Wone    schedule 05.02.2010

Согласно синтаксису пути в MSDN, вы можете просто сделать :

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

Может быть, ошибка "path =" в нижнем регистре? Попробуйте «Путь =». Кроме того, не уверен, работает ли это в Silverlight ...

person Kent Boogaart    schedule 01.04.2009
comment
Эта страница предназначена для WPF, а не для Silverlight. Silverlight не поддерживает индексированные свойства в привязке. - person mattmanser; 01.04.2009
comment
В заголовке вопроса написано Silverlight / WPF. - person Kent Boogaart; 01.04.2009
comment
Метод индексации, похоже, работает только в некоторых случаях в silverlight, тем не менее, спасибо! - person gsnerf; 01.04.2009
comment
Silverlight 3 поддерживает индексированные свойства в привязке. - person JasonRShaver; 09.10.2009