2016-04-11 120 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