2016-04-11 125 views
0

这里是WPF的新手。正在构建的应用程序具有从数据库中拉出的用户列表,以显示在“用户”窗口中,并可从“主”窗口导航。该列表似乎转移到后面的代码中,但用户列表未显示在“用户”窗口列表框中。有没有人看到为什么这不显示?提前谢谢了!WPF列表框数据绑定

“主” 窗口指导:

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(解决): 埃德宾吉意见,回答直接解决了原来的列表框的问题,并使用该输入与ThyArtIsCode的结合,这是所有整齐蒙蒂提出的,这个过程是更优雅。感谢所有回复的人 - 这里有很多很棒的学习资料。

+0

如果你在这一行放置一个断点Usersvm.Users.Add(pullingUser);你有没有看到pullUser中的任何值? – Claudius

+2

** UsersViewModel:Window **为什么你在UsersViewModel中继承了Window类? –

+1

第一:'Usersvm.Users'需要是'ObservableCollection '类型,而不是'List'。当List的内容改变时,List不会通知UI。 'ObservableCollection'将会。其次,UsersViewModel不应该是一个窗口。一个窗口与视图模型无关。接下来,它应该实现INotifyPropertyChanged并在用户分配新集合时触发'PropertyChanged'接下来,不要在代码隐藏中分配给ItemsSource。这不是约束力。用上面的东西,你的XAML中的绑定应该能正常工作。 –

回答

1

你有几件事要在这里解决,但没有什么非常复杂的。只是很多MVVM/XAML内务处理的东西。

MVVM在XAML中的工作方式是您的视图模型不知道您的视图 - 理想情况下,他们根本不知道任何UI。为了使这种情况发生,像消息框和文件打开对话框可能会涉及一些扭曲,但我们现在不去那里。顺便提一下,你绝对不想从Window派生视图模型 - 这是一个UI类,它不会做任何视图模型需要基类去做的事情。

因此,您的视图模型具有公共属性,但是当这些属性更改时,它们应该将通知发送到黑暗中。为此,您在视图模型上执行INotifyPropertyChanged,并在属性更改时触发PropertyChanged事件。 UI将订阅这些通知 - 如果您的视图模型是其属性绑定的元素的DataContext(清除为泥 - 稍后会更详细)。

当视图模型公开一个集合时,它通常使用ObservableCollection,因为该类会在add/remove/etc中触发通知。 List不这样做。 ObservableCollection附带一些来自所有通知内容的开销,所以不要在任何地方使用它 - 当您需要的只是List时仍然使用List

所以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,当其自身的属性值改变触发通知。

接下来,您在ListBox上的ItemsSource的XAML绑定是正确的,但在后面的代码中将该集合分配给该属性将会破坏它。 XAML中的{Binding ...}不仅仅是一项任务:它创建了一个Binding类的实例,该实例位于中间并管理上面提到的所有通知事件业务。你可以可以以编程方式创建绑定,但在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

谢谢埃德!这是一个很好的解释。最后几段特别帮助解决了眼前的问题。我现在要解决答案的第一部分以解决可用性问题。 – jle

2

我看到一些东西错了......

首先,你的ViewModel是继承Window。如果没有特别的理由,请去除它。如果您想通知UI对您的集合所做的更改(理想情况下应该是您的视图模型的一部分),请使视图模型继承INotifyPropertyChanged。

您还绑定到列表框的位置:

ItemsSource="{Binding Users}" 

,并再次在这里设置的ItemsSource:

listboxUsers.ItemsSource = uvm.Users; 

BAD!如果您在XAML中进行绑定,则完全不需要再次设置ItemsSource。需要修改集合吗?直接在收藏中这样做。

而且,因为你是新来WPF中,我想我会增加一些建议,帮助我,当我第一次开始学习:

  1. 如果你想要的东西去更快,添加IsAsync=True到您的列表框捆绑。这将启用异步绑定(惊人的,我知道)。
  2. 虚拟化的废话了该列表框的(只需添加以下到列表框):

    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窗体,应该在WPF中谨慎使用)更好。 –

+1

有趣的..使我想要拿出一个测试,比较两个。如果我这样做,今晚就把它丢在CodeProject上吧! –

1

OK试试这个.....

视图模型....

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> 

和视图的其余部分....

<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

更新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

非常感谢您的评论和这个答案,Monty!我试图在解决原始问题后添加更优雅的解决方案来实现此功能,但遇到了以下错误:名称空间或程序集错误在ViewModel的“RelayCommand”行;并在视图的DataContext声明中键入错误我没有为“RelayCommand”定义一个ICommand,所以这可能是ViewModel错误的原因呢?另外,什么可能是DataContext的原因不承认ViewModel? – jle

+0

是的,你必须添加xmlns:ViewModels =“clr-namespace:DbEntities”和RelayCommand类可以在这里找到... http://www.kellydun.com/wpf-relaycommand-with-参数/ – Monty

+0

Monty,这个工程非常棒 - 非常感谢你! – jle