2016-04-11 2 views
0

Новое в WPF здесь. При построении приложения есть список пользователей, которые вытаскиваются из базы данных для отображения в окне «Пользователи», доступное из главного окна. Список, кажется, переводится в код позади, но список пользователей не отображается в окне «Пользователи» ListBox. Кто-нибудь видит, почему это не отображается? Спасибо заранее!WPF ListBox привязка данных

"Main" Окно режиссура:

UsersViewModel Usersvm = new UsersViewModel(); 
Usersvm.Users = new List<UserViewModel>(); 
DbEntities db = new DbEntities(); 
var pulledUsers = db.uspGetUsers().ToList(); 
foreach (var result in pulledUsers) 
{ 
    var pulledUser = new UserViewModel 
    { 
     FirstName = result.FirstName, 
     LastName = result.LastName, 
     EMail = result.Email, 
     UserID = result.UserID, 
     Position = result.Position, 
     EndDate = result.EndDate, 
    }; 
    Usersvm.Users.Add(pulledUser); 
} 
new UsersWindow(Usersvm).Show(); 

код UsersWindow за:

public partial class UsersWindow : Window 
{ 
    public UsersWindow(UsersViewModel uvm) 
    { 
     InitializeComponent(); 
     listboxUsers.ItemsSource = uvm.Users; 
    } 
} 

UsersWindow.xaml:

<Window x:Class="DbEntities.UsersWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:local="clr-namespace:DbEntities" 
    mc:Ignorable="d" 
    Title="UsersWindow" Height="Auto" Width="900"> 
    <Window.Resources> 
     <Style x:Key="borderBase" TargetType="Border"> 
      <Setter Property="BorderBrush" Value="Black" /> 
      <Setter Property="BorderThickness" Value="1" /> 
     </Style> 
    </Window.Resources> 
    <StackPanel> 
     <TextBlock x:Name="textBlock" Height="21" Margin="0,0,161,0" TextWrapping="Wrap" 
      Text="Users Page" VerticalAlignment="Top" RenderTransformOrigin="1.022,0.409" HorizontalAlignment="Right" Width="344"/> 
     <Grid> 
      <Grid Grid.IsSharedSizeScope="True"> 
       <Grid.RowDefinitions> 
        <RowDefinition Height="Auto"/> 
        <RowDefinition Height="Auto" /> 
        <RowDefinition Height="*" /> 
       </Grid.RowDefinitions> 
       <Grid> 
        <Grid.ColumnDefinitions> 
         <ColumnDefinition Width="151*" /> 
         <ColumnDefinition Width="95*" /> 
         <ColumnDefinition Width="110*" /> 
         <ColumnDefinition Width="351*" /> 
         <ColumnDefinition Width="75*" /> 
         <ColumnDefinition Width="110*" /> 
        </Grid.ColumnDefinitions> 
        <Border Style="{StaticResource borderBase}"> 
         <TextBlock HorizontalAlignment="Center" Text="Last Name" /> 
        </Border> 
        <Border Grid.Column="1" Style="{StaticResource borderBase}"> 
         <TextBlock HorizontalAlignment="Center" Text="First Name" /> 
        </Border> 
        <Border Grid.Column="2" Style="{StaticResource borderBase}"> 
         <TextBlock HorizontalAlignment="Center" Text="Position" /> 
        </Border> 
        <Border Grid.Column="3" Style="{StaticResource borderBase}"> 
         <TextBlock HorizontalAlignment="Center" Text="Email" /> 
        </Border> 
        <Border Grid.Column="4" Style="{StaticResource borderBase}"> 
         <TextBlock HorizontalAlignment="Center" Text="End Date" /> 
        </Border> 
        <Border Grid.Column="5" Style="{StaticResource borderBase}"> 
         <TextBlock HorizontalAlignment="Center" /> 
        </Border> 
        <ListBox x:Name="listboxUsers" HorizontalAlignment="Center" Height="Auto" Margin="3,25,0,0" VerticalAlignment="Top" Width="889" 
        ItemsSource="{Binding Users}" Grid.ColumnSpan="6"> 
         <ListBox.ItemTemplate> 
          <DataTemplate> 
           <Grid> 
            <Grid.ColumnDefinitions> 
             <ColumnDefinition SharedSizeGroup="LastNameColumn" /> 
            </Grid.ColumnDefinitions> 
            <Border Style="{StaticResource borderBase}"> 
             <TextBlock Text="{Binding LastName}"/> 
            </Border> 
            <Border Style="{StaticResource borderBase}"> 
             <TextBlock Text="{Binding FirstName}"/> 
            </Border> 
            <Border Style="{StaticResource borderBase}"> 
             <TextBlock Text="{Binding Position}"/> 
            </Border> 
            <Border Style="{StaticResource borderBase}"> 
             <TextBlock Text="{Binding Email}"/> 
            </Border> 
            <Border Style="{StaticResource borderBase}"> 
             <TextBlock Text="{Binding EndDate}"/> 
            </Border> 
            <Border Style="{StaticResource borderBase}"> 
             <Button Content="Edit" x:Name="editButton" Click="editButton_Click"/> 
            </Border> 
           </Grid> 
          </DataTemplate> 
         </ListBox.ItemTemplate> 
        </ListBox> 
       </Grid> 
      </Grid> 
     </Grid> 
    </StackPanel> 
</Window> 

И, наконец, UsersViewModel со списком из контактная информация пользователя:

public partial class UsersViewModel : Window 
{ 
    public List<UserViewModel> Users { get; set; } 
} 

EDIT (Раскрыты): комментарии Эд Планкетт в и ответить непосредственно решить оригинальный ListBox вопрос, и использовать этот вход в сочетании с ThyArtIsCode, который был аккуратно представленный Монти, процесс гораздо более элегантно. Спасибо всем, кто ответил - здесь есть масса отличного учебного материала.

+0

если вы установили точку останова в этой строке Usersvm.Users.Add (pullUser); вы видите какие-либо значения в pullUser? – Claudius

+2

** UsersViewModel: Window ** почему у унаследовал класс окна в UserViewModel ?? –

+1

Первое: 'Usersvm.Users' должно быть типа' ObservableCollection ', а не' List'. «Список» не будет уведомлять тогда пользовательский интерфейс, когда его содержимое изменится. 'ObservableCollection' будет. Во-вторых, UserViewModel не должен быть окном. Окно не имеет ничего общего с viewmodel. Затем он должен реализовать INotifyPropertyChanged и fire 'PropertyChanged', когда пользователям присваивается новая коллекция. Далее не назначайте ItemsSource в codebehind. Это не привязка. С учетом вышеизложенного, привязка к этому в вашем XAML должна работать правильно. –

ответ

1

У вас есть кое-что, чтобы исправить здесь, но ничего сложного. Просто много вещей для MVVM/XAML.

Способ работы MVVM в XAML заключается в том, что ваши модели просмотра не знают о ваших взглядах - в идеале они вообще не знают о каком-либо интерфейсе. Чтобы это происходило с такими вещами, как окна сообщений и открытые диалоги файлов, могут возникать некоторые искажения, но мы не пойдем туда прямо сейчас. Кстати, вы определенно не хотите выводить модель представления из Window - это UI-класс, и он не делает ничего, что нужно для моделей, которым нужен базовый класс.

Таким образом, ваши модели просмотра имеют общедоступные свойства, которые у вас есть, но когда эти свойства меняются, они должны отключать уведомления в темноте. Для этого вы реализуете INotifyPropertyChanged на своей модели просмотра, и вы запускаете событие PropertyChanged, когда свойство изменяется. Пользовательский интерфейс будет подписаться на эти уведомления - , если ваша модель просмотра - это элемент DataContext элемента, свойство которого привязано (ясно, как грязь - подробнее об этом позже).

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

So UsersViewModel.Users должно быть типа ObservableCollection<UserViewModel><UserViewModel>, а при замене коллекции пожара PropertyChanged.

private ObservableCollection<UserViewModel> _users = 
    new ObservableCollection<UserViewModel>(); 
ObservableCollection<UserViewModel> Users { 
    get { return _users; } 
    set { 
     _users = value; 
     // Implementations of this are everywhere on Google, very simple. 
     OnPropertyChanged("Users"); 
     // Or in C#6 
     //PropertyChanged?.Invoke(new PropertyChangedEventArgs(nameof(Users))); 
    } 
} 

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

Далее, ваша привязка XAML для ItemsSource на ListBox верна, но присвоение коллекции этому свойству в коде позади приведет к ее разрыву. A {Binding ...} в XAML - это не просто назначение: он создает экземпляр класса Binding, который находится посередине и управляет всем бизнесом уведомлений, упомянутым выше. You может создавать привязки программно, но делать это в XAML намного проще, а в 99,5 +% случаев делает все, что вам нужно.

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

public partial class UsersWindow : Window 
{ 
    public UsersWindow(UsersViewModel uvm) 
    { 
     InitializeComponent(); 

     var vm = new UsersViewModel(); 
     // initialize vm if needed 
     DataContext = vm; 
    } 
} 

Вы могли бы создателю Пропустите окна в UsersViewModel экземпляра с помощью конструктора окна, а также.

+1

Спасибо Ed! Это замечательное объяснение. Последние несколько абзацев особенно помогли решить ближайшую проблему. Теперь я перейду к первым частям ответа, чтобы рассмотреть удобство использования. – jle

2

Я вижу пару вещей неправильно ...

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

Вы также связывание с ListBox здесь:

ItemsSource="{Binding Users}" 

И установки ItemsSource снова здесь:

listboxUsers.ItemsSource = uvm.Users; 

BAD! Если вы привязываетесь к XAML, нет необходимости снова устанавливать ItemsSource. Нужно изменить коллекцию? Делайте это непосредственно с коллекцией.

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

  1. Если вы хотите вещи, чтобы идти быстрее, добавьте IsAsync=True в свой ListBox связывание. Это позволит использовать асинхронную привязку (удивительно, я знаю).
  2. виртуализация дерьмо из этого ListBox (просто добавьте следующее ListBox):

    VirtualizingPanel.IsVirtualizing="True"     
    VirtualizingPanel.VirtualizationMode="Recycling" 
    

И последнее, хотя другие предлагали с помощью ObservableCollection, он также поставляется с падением производительности при использовании большие данные. Даже если вы не собираетесь иметь большие данные, в любом случае безопаснее использовать BindingList. Фактически, ObservableCollection имеет преимущество при работе с меньшими наборами данных.

Они намного быстрее и обладают многими аналогичными свойствами, такими как OC.

+1

Нельзя использовать BindingList. У него больше накладных расходов, чем ObservableCollection. Они также были предназначены для использования с WinForms, а не с WPF. Вот хороший пример того, как у него больше накладных расходов: http://www.biglittleendian.com/article/21 Также есть много других сообщений SO, которые описывают его. –

+1

Спасибо за примечание. Я основываю свои знания на этом сообщении: http://stackoverflow.com/questions/3305383/wpf-whats-the-most-efficient-fast-way-of-adding-items-to-a-listview, что указывает на BindingList лучше работает при обработке больших наборов данных в отличие от ObservableCollection (что я согласен, предназначено для Windows Forms и должно использоваться в WPF экономно). –

+1

Интересно .. Заставляет меня придумать тест, сравнивая два. Если я это сделаю, завтра брось его на CodeProject! –

1

OK попробовать это .....

ViewModel ....

class Base_ViewModel : INotifyPropertyChanged 
{ 
    public RelayCommand<UserViewModel> editButton_Click_Command { get; set; } 

    public Base_ViewModel() 
    { 
     editButton_Click_Command = new RelayCommand<UserViewModel>(OneditButton_Click_Command); 

     this.Users = new ObservableCollection<UserViewModel>(); 

     this.Users.Add(new UserViewModel() { FirstName = "John", LastName = "Doe", EMail = "[email protected]", EndDate = "02-01-2016", Position = "Developer", UserID = "AADD543" }); 
    } 

    private ObservableCollection<UserViewModel> _Users; 
    public ObservableCollection<UserViewModel> Users 
    { 
     get { return _Users; } 
     set { _Users = value; NotifyPropertyChanged("Users"); } 
    } 

    private void OneditButton_Click_Command(UserViewModel obj) 
    { // put a break-point here and you will see the data you want to Edit in obj 

    } 

    public event PropertyChangedEventHandler PropertyChanged; 

    private void NotifyPropertyChanged(String info) 
    { 
     if (PropertyChanged != null) 
     { 
      PropertyChanged(this, new PropertyChangedEventArgs(info)); 
     } 
    } 
} 

Класс пользователя .....

public class UserViewModel : INotifyPropertyChanged 
{ 
    private string _FirstName; 
    public string FirstName 
    { 
     get { return _FirstName; } 
     set { _FirstName = value; NotifyPropertyChanged("FirstName"); } 
    } 

    private string _LastName; 
    public string LastName 
    { 
     get { return _LastName; } 
     set { _LastName = value; NotifyPropertyChanged("LastName"); } 
    } 

    private string _EMail ; 
    public string EMail 
    { 
     get { return _EMail; } 
     set { _EMail = value; NotifyPropertyChanged("EMail"); } 
    } 

    private string _UserID; 
    public string UserID 
    { 
     get { return _UserID; } 
     set { _UserID = value; NotifyPropertyChanged("UserID"); } 
    } 

    private string _Position; 
    public string Position 
    { 
     get { return _Position; } 
     set { _Position = value; NotifyPropertyChanged("Position"); } 
    } 

    private string _EndDate; 
    public string EndDate 
    { 
     get { return _EndDate; } 
     set { _EndDate = value; NotifyPropertyChanged("EndDate"); } 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 

    private void NotifyPropertyChanged(String info) 
    { 
     if (PropertyChanged != null) 
     { 
      PropertyChanged(this, new PropertyChangedEventArgs(info)); 
     } 
    } 
} 

XAML .....

Установить окно x: Название ....

<Window x:Name="Base_V"...... 

DataContext

<Window.DataContext> 
    <ViewModels:Base_ViewModel/> 
</Window.DataContext> 

И остальная часть View ....

<Grid> 
    <DataGrid Name="DataGrid1" ItemsSource="{Binding Users}"> 
     <DataGrid.Columns> 
      <DataGridTemplateColumn> 
       <DataGridTemplateColumn.CellTemplate> 
        <DataTemplate> 
         <Button Command="{Binding DataContext.editButton_Click_Command, ElementName=Base_V}" CommandParameter="{Binding}">Edit</Button> 
        </DataTemplate> 
       </DataGridTemplateColumn.CellTemplate> 
      </DataGridTemplateColumn> 
     </DataGrid.Columns> 
    </DataGrid> 
</Grid> 
</Window> 

вы должны в конечном итоге с чем-то вроде этого .... Screen Shot

Update 1

В конструкторе Base_ViewModel

 this.Users.Add(new UserViewModel() { FirstName = "John", LastName = "Doe", EMail = "[email protected]", EndDate = "02-01-2016", Position = "Developer", UserID = "AADD543" }); 
     this.Users.Add(new UserViewModel() { FirstName = "Fred", LastName = "Doe", EMail = "[email protected]", EndDate = "02-01-2016", Position = "Developer", UserID = "AADD543" }); 
     // empty record to allow the use to Add a new record 
     this.Users.Add(new UserViewModel()); 

Когда пользователь выбирает кнопку «Редактировать» для пустой записи, они фактически заполняют пустую запись. После того, как они заполнили ее, обязательно добавьте еще одну пустую запись, чтобы создать новую (пустую строку) в DataGrid ....

+0

Большое спасибо за ваш комментарий и этот ответ, Монти! Я попытался реализовать это после того, как исходная проблема была решена, чтобы добавить более элегантное решение, но натолкнулось на ошибки: пространство имен или сборочная ошибка на строках «RelayCommand» в ViewModel и введите ошибку в объявлении DataContext представления. Я не определил ICommand для «RelayCommand», так может ли это быть причиной ошибки ViewModel? Также, что может быть причиной DataContext не распознавая ViewModel? – jle

+0

Да, вам нужно будет добавить xmlns: ViewModels = "clr-namespace: DbEntities" и класс RelayCommand можно найти здесь ... http://www.kellydun.com/wpf-relaycommand-with- Параметр/ – Monty

+0

Monty, это отлично работает - большое спасибо! – jle

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