WPF ListBox数据绑定

时间:2016-04-11 17:20:11

标签: c# wpf xaml listbox

WPF新手。正在构建的应用程序具有从数据库中提取的用户列表,以便在"用户"中显示。窗口,可通过" Main"窗口。该列表似乎转移到后面的代码,但用户列表不会显示在"用户" Window ListBox。有谁知道为什么这不显示?非常感谢提前!

"主"窗口指示:

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();

用户窗口代码:

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; }
}

编辑(已解决): Ed Plunkett的评论和答案直接解决了原始的ListBox问题,并且使用了与ThyArtIsCode相结合的输入,这一切都由Monty巧妙地呈现,这个过程更加优雅。感谢所有回复的人 - 这里有很多很棒的学习资料。

3 个答案:

答案 0 :(得分:2)

我发现有些事情错了......

首先,您的ViewModel继承Window。如果没有特别的原因,请摆脱它。如果要通知UI对集合所做的更改(理想情况下应该是视图模型的一部分),请使视图模型继承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"
    
  3. 最后一件事,虽然其他人建议使用ObservableCollection,但在使用大数据时也会带来性能损失。 即使您不打算拥有大数据,也总是更安全地使用BindingList。实际上,ObservableCollection在处理较小的数据集时占了上风。

    它们更快,并且与OC共享许多类似的属性。

答案 1 :(得分:2)

你在这里有一些问题需要解决,但没有什么比这更复杂了。只是很多MVVM / XAML管家的东西。

MVVM在XAML中的工作方式是您的视图模型不了解您的观点 - 理想情况下,他们根本不了解任何UI。要使用消息框和文件打开对话框之类的东西来实现这一点可能会涉及一些扭曲,但我们现在还没有去那里。顺便说一下,你绝对不想从Window派生出一个视图模型 - 这是一个UI类,并且它没有做任何视图模型需要基类来做的事情。

因此,您的视图模型具有您已获得的公共属性,但是当这些属性发生变化时,它们应该将通知发送到黑暗中。为此,您在viewmodel上实现INotifyPropertyChanged,并在属性更改时触发PropertyChanged事件。用户界面将订阅这些通知 - 如果您的视图模型是其属性绑定的元素的DataContext(清除为泥浆 - 稍后会更多)。

当viewmodel公开集合时,它通常使用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并在其自己的属性值发生更改时触发通知。

接下来,ItemsSource上对ListBox的XAML绑定是正确的,但是在后面的代码中为该属性分配一个集合会破坏它。 XAML中的{Binding ...}不仅仅是一个赋值:它创建了一个Binding类的实例,它位于中间并管理我上面提到的所有通知事件业务。您可以以编程方式创建绑定,但在XAML中执行此操作要简单得多,并且在99.5 +%的情况下会执行您需要的所有操作。

最重要的是,窗口需要知道您的viewmodel。通过将UsersViewModel的实例分配给窗口DataContext来实现这一目标。窗口的子控件将继承DataContext,并且将在该上下文中评估所有绑定。

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

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

您可以通过窗口的构造函数让窗口的创建者在UsersViewModel实例中传递。

答案 2 :(得分:1)

好的,试试这个......

...视图模型

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 = "JohnDoe@yahoo.com", 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 .....

设置Window x:Name ....

<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 = "JohnDoe@yahoo.com", EndDate = "02-01-2016", Position = "Developer", UserID = "AADD543" });
        this.Users.Add(new UserViewModel() { FirstName = "Fred", LastName = "Doe", EMail = "FredDoe@yahoo.com", 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中生成新的(空行) ....