Привязка данных Silverlight, избегайте ошибок пути BindingExpression в отсутствующих свойствах, вместо этого скрывайте элементы управления - PullRequest
4 голосов
/ 22 июля 2010

представьте следующие простые модели (пример для простоты; на самом деле у нас есть MVVM, но это не имеет значения):

public class User {
  public string Username { get; set; }
}

public class StackOverflowUser : User {
  public int Reputation { get; set; }
}

Теперь у нас есть Silverlight UserControl, который содержит следующее Controls (опять же, это всего лишь пример, урезанный до сути):

<Grid>
    <TextBlock Text="Username:" />
    <TextBlock Text="{Binding Path=Username}" />

    <TextBlock Text="Reputation:" />
    <TextBlock Text="{Binding Path=Reputation}" />
</Grid>

Теперь я бы хотел, чтобы этот UserControl был совместим с обеими моделями, User и StackOverflowUser. Я мог бы установить DataContext UserControl на User или StackOverflowUser Тип:

this.DataContext = new User { Username = "john.doe" };

Если установлено значение StackOverflowUser, все работает нормально. Если установлено значение User, я получаю «Ошибка пути BindingExpression», потому что свойство Reputation отсутствует в модели User. Что я полностью понимаю.

Есть ли способ 1) избежать этого исключение и 2) управления видимость органов управления, развал когда привязанное свойство недоступно?

Конечно, мы предпочитаем элегантное решение, где проблема решается путем настройки выражения привязки и / или использования конвертеров и т. Д., И по возможности избегайте тонны кода.

Заранее спасибо за помощь и предложения,
С наилучшими пожеланиями,

Thomas

Ответы [ 4 ]

1 голос
/ 23 июля 2010

Вы упомянули, что используете MVVM. В этом ценность вашей модели представления - формировать данные модели при подготовке к просмотру. Модель представления может иметь доступные свойства как для имени пользователя, так и для репутации (и, возможно, даже другой объект bool для привязки видимости). Модель представления будет включать в себя всю логику о том, как заполнить эти свойства из любой модели (User или StackOverflowUser). Представление не будет знать объект User или StackOverflowUser, только модель представления.

1 голос
/ 22 июля 2010

К сожалению, Silverlight ограничен в своем полиморфном поведении в отношении DataTemplates, я могу только придумать обходной путь:

Дайте классу User свойство Reputation, но сделайте его бессмысленным, например, -1. Затем примените стиль к репутации TextBlocks:

  <Page.Resources>
    <Style Key="Reputation">
      <Style.Triggers>
        <DataTrigger Binding="{Binding Path=Reputation} Value="-1">
          <Setter Property="Visibility" Value="Invisible" />
        </DataTrigger>    
      </Style.Triggers>
    </Style>
  </Page.Resources>

...

  <TextBlock Text="Reputation:" Style="{StaticResource Reputation}">
  <TextBlock Text="{Binding Path=Reputation}" Style="{StaticResource Reputation}">

Вы также можете попробовать (я не могу это проверить):

  1. давая классу пользователя новое свойство, которое идентифицирует его тип,
  2. создать второй стиль для второго TextBlock
  3. связывает свой DataTrigger со свойством определения типа и перемещает объявление {Binding Path = Reputation} в Setter:

    <Style Key="ReputationContent">
      <Style.Triggers>
        <DataTrigger Binding="{Binding Path=Type} Value="StackOverflow">
          <Setter Property="Visibility" Value="Invisible" />
          <Setter Property="Text" Value="{Binding Path=Reputation}" />
        </DataTrigger>    
      </Style.Triggers>
    </Style>
    

Но вы видите, что нет элегантного способа, обидно, что DataTemplate не имеет свойства DataType в Silverlight.

0 голосов
/ 29 мая 2013

Я знаю, что на этот вопрос уже дан ответ, но я все еще думаю, что этот пост того стоит. Используя отражение, вы можете иметь свойство в вашей ViewModel, которое будет легко обрабатывать объекты Dto, которые только иногда имеют это свойство. Отражение может быть дорогим, поэтому взвесите это с вашим решением.

public int? Reputation
    {
        get
        {
            var prop = Dto.GetType().GetProperty("Reputation");
            return (prop != null)? (int)prop.GetValue(Dto, null) : null;
        }
        set
        {
            var prop = Dto.GetType().GetProperty("Reputation");
            if(prop !=null) prop.SetValue(Dto,value, null);
        }
    }
0 голосов
/ 05 августа 2010

Я наконец-то решил свою проблему. Сотрудник наконец-то реализовал решение, включающее обходной путь для атрибута DataTemplates WPFs DataType (или вообще DataTemplateSelector). Это не очень красиво (я думаю, нет обходного пути), но это работает. К сожалению, я не могу опубликовать какие-либо фрагменты кода, потому что это закрытый источник. Но потом я нашел несколько ссылок, обеспечивающих довольно похожее решение, например: Silverlight: порт DataTemplateSelector . Если у вас есть аналогичная проблема, это также поможет вам. Здесь или там больше мыслей на эту тему.

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

Спасибо!

С наилучшими пожеланиями,

Thomas

...