2009-03-25 6 views
20

У меня есть сценарий, в котором я действительно не знаю, как привязывать данные к элементам управления, размещенным в UserControl, к нескольким datacontexts.WPF привязывает несколько элементов управления к различным 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 

ответ

25

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

+0

Хорошо, что имеет большое значение. Так много нового в WPF :) – Alex

+1

Вместо создания новой модели, единственной целью которой является поддержка этого конкретного представления, вы должны просто добавить UserInfo и UserExtendedInfo как общедоступные свойства ViewModel, которые вы можете привязать к представлению. (См. Rich [ответ] [http://stackoverflow.com/questions/679933/wpf-binding-multiple-controls-to-different-datacontexts/680052#680052)). – totorocat

7

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

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

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

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

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

+0

просто примечание, ViewModel не нуждается в свойствах зависимостей. Просто нужно реализовать INotifyPropertyChanged, если вы хотите, чтобы элементы управления отображали изменения в одном из своих свойств. –

+0

Ссылка мертва, не могли бы вы обновить ссылку? –

+0

@HassanTareq Найденный на YouTube и обновил ссылку – Rich

6

Как Богатые и bendewey были хорошие ответы. Изучая эту же тему сегодня в 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, чтобы ваши элементы управления обновлялись при сбросе UserInfo и UserExtendedInfo.

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

+1

Это улучшенное ответное решение (+1), но я бы улучшил еще несколько вещей: вам не нужно «Path =», а в 95% случаев я будет использовать анонимные типы C# вместо явного класса. –

15

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

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

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}"> 
     ... 
+0

Спасибо! Помог мне тонну! –

 Смежные вопросы

  • Нет связанных вопросов^_^