WPF привязывает несколько элементов управления к различным элементам данных - PullRequest
24 голосов
/ 25 марта 2009

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

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

UserInfo, UserExtendedInfo

Текст данных UserControl установлен на UserInfo, поэтому я могу легко связать большинство элементов управления, выполнив следующие действия:

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

Однако я не знаю, как легко связать свойства из класса UserExtendedInfo. Сначала я думал установить текстовый текст каждого элемента управления, который хочет использовать данные из 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

Ответы [ 4 ]

29 голосов
/ 25 марта 2009

Возможно, я бы подумал об обертывании вашего пользовательского объекта в отдельный класс, а затем о настройке свойств 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

19 голосов
/ 24 декабря 2009

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

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

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}">
      ...
8 голосов
/ 25 марта 2009

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

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

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

РЕДАКТИРОВАТЬ : сообщение в блоге было удалено (этот ответ довольно старый). Вот видео на YouTube:

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

7 голосов
/ 24 декабря 2009

У Рича и Бендевея были хорошие ответы. Исследуя эту же тему сегодня в Silverlight вместо WPF, я обнаружил, что нет необходимости устанавливать несколько DataContexts. Пересмотр примера Бендевея:

<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, чтобы ваши элементы управления обновлялись при сбросе UserInfo и UserExtendedInfo.

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

...