Подтвердить что ты не робот

WPF привязывает несколько элементов управления к различным datacontexts

У меня есть сценарий, в котором я действительно не знаю, как привязывать данные к элементам управления, размещенным в UserControl, к нескольким datacontexts.

Данные, которые я хочу связывать, поступают из 2 классов

UserInfo, UserExtendedInfo

В datacontext UserControl устанавливается UserInfo, поэтому я могу привязать большинство элементов управления, выполнив следующие

<Label Name="LblEmail" Text="{Binding Email}" />

Однако я не знаю, как легко связывать свойства из класса UserExtendedInfo. Моя первоначальная мысль заключалась в том, чтобы установить datacontext каждого элемента управления, который хочет использовать данные из UserExtendedInfo, чтобы я мог сделать то же самое. Но это кажется громоздким, так как мне придется вручную назначать каждый из них. Данные для UserExtendedInfo должны извлекаться из базы данных каждый раз, когда UserControl становится видимым, чтобы он не выходил из синхронизации.

XAML:

<Label Name="LblTest" Text="{Binding Locale}" />

Код за:

Private Sub UserManager_IsVisibleChanged(ByVal sender As System.Object, ByVal e As System.Windows.DependencyPropertyChangedEventArgs)  
        If DirectCast(e.NewValue, Boolean) Then
            Dim user As UserInfo = DirectCast(DataContext, UserInfo)

            If user IsNot Nothing Then
                Dim usrExt As UserExtenedInfo = UserController.GetUserExtended(user.userID)

                LblTest.DataContext = usrExt
            Else
                Throw New ArgumentException("UserId doesn't exist or is less than 1")
            End If
        End If
    End Sub
4b9b3361

Ответ 1

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

Например:

public class UserDataContext
{
  public UserInfo UserInfo { get; set; }
  public UserExtendedInfo UserExtendedInfo { get; set; }
}

Затем в UserControl.xaml:

<!-- Binding for the UserControl should be set in its parent, but for clarity -->
<UserControl DataContext="{Binding UserDataContext}">
  <StackPanel>
    <Grid DataContext="{Binding UserInfo}">
       <TextBlock Text="{Binding Email}" />
    </Grid>
    <Grid DataContext="{Binding UserExtendedInfo}">
       <TextBlock Text="{Binding Locale}" />
       <TextBlock Text="{Binding AboutMe}" />
    </Grid>
  </StackPanel>
</UserControl>

Это предполагает, что ваш класс UserInfo имеет свойство Email

и

Что ваш класс UserExtendedInfo имеет свойство Locale и AboutMe

Ответ 2

Вот простейший метод для всех, и он работает очень хорошо.

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

DataContext = new
{
  info = FetchUserInfoFromDatabase(),
  extendedInfo = FetchExtendedUserInfoFromDatabase(),
};

В XAML вы можете привязываться ко всему:

<UserControl>
  <StackPanel>
    <TextBlock Text="{Binding info.Email}" />
    <TextBlock Text="{Binding extendedInfo.Locale} />
  ...

В качестве альтернативы вы можете связываться на двух уровнях, как описано в других ответах:

<UserControl>
  <StackPanel>
    <Grid DataContext="{Binding info}">
      <TextBlock Text={Binding Email}">
      ...

Ответ 3

Здесь M-V-VM пригодится. Идея (как я понимаю, по крайней мере, для меня все еще очень новая) заключается в том, что само окно связано с классом "ViewModel". Класс ViewModel - это всего лишь класс, который представляет все данные таким образом, чтобы вся ваша страница имела доступ ко всему, что ему нужно... она просто объединяет все различные объекты, которые вам нужно связать в одном классе... и вы устанавливаете DataContext окна (или страницы) экземпляру этого класса. Ваши экземпляры UserInfo и UserInfoExtended являются общедоступными объектами объекта ViewModel, и вы просто используете Path вашего элемента привязки, чтобы получить нужные свойства соответствующих объектов, к которым вы хотите привязать каждый элемент управления.

Там есть большое (но довольно длинное) видео, объясняющее этот шаблон, и оно проходит через полный пример, который иллюстрирует многие способы решения этой проблемы и множество разных причин, почему это удобная и масштабируемая модель для использования в приложении WPF. Он также охватывает многие функции WPF, а также введение в инъекцию зависимостей, которые также являются очень важными темами, на примере.

Здесь ссылка на сообщение в блоге, которое содержит ссылку на видео, о котором я говорю:

EDIT: сообщение в блоге удалено (этот ответ довольно старый). Вот видео на YouTube:

https://www.youtube.com/watch?v=BRxnZahCPFQ

Ответ 4

И Рич, и Бендвей были хорошие ответы. Изучая эту же тему сегодня в Silverlight вместо WPF, я обнаружил, что нет необходимости устанавливать несколько DataContext. Пересмотр примера bendewey:
<UserControl DataContext="{Binding UserDataContext}">
  <StackPanel>
       <TextBlock Text="{Binding Path=UserInfo.Email}" />
       <TextBlock Text="{Binding Path=UserExtendedInfo.Locale}" />
       <TextBlock Text="{Binding Path=UserExtendedInfo.AboutMe}" />
  </StackPanel>
</UserControl>

Используя Binding Path, вы получаете гибкость для смешивания и сопоставления привязок к свойствам разных классов без учета DataContext контейнеров элементов управления.

Вы также можете расширить возможности класса UserDataContext bendewey, добавив свойства, которые управляют свойствами классов UserInfo и UserExtendedInfo. Вы можете, например, объединить имя и фамилию.

Возможно, вы захотите реализовать INotifyPropertyChanged, чтобы ваши элементы управления обновлялись, когда вы reset UserInfo и UserExtendedInfo.

Возможно, архитектурно предпочтительнее полностью изолировать базовые классы UserInfo и UserExtendedInfo от XAML, предоставив необходимые свойства непосредственно в UserDataContext, тем самым устраняя необходимость в Binding Path.