【问题标题】:WPF ListBox data bindingWPF ListBox 数据绑定
【发布时间】:2016-04-11 17:20:10
【问题描述】:

这里是 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; }
}

编辑(已解决): Ed Plunkett 的 cmets 和 answer 直接解决了最初的 ListBox 问题,并将该输入与 Monty 巧妙呈现的 ThyArtIsCode 结合使用,过程更加优雅。感谢所有回复的人 - 这里有很多很棒的学习资料。

【问题讨论】:

  • 如果你在这一行设置断点 Usersvm.Users.Add(pulledUser);您在 pullUser 中看到任何值吗?
  • UsersViewModel : Window 为什么你继承了 UsersViewModel 中的窗口类??
  • 首先:Usersvm.Users 的类型必须是 ObservableCollection&lt;UserViewModel&gt;,而不是 ListList 不会在其内容更改时通知 UI。 ObservableCollection 将。其次,UsersViewModel 不应该是一个窗口。窗口与视图模型无关。接下来,它应该实现 INotifyPropertyChanged 并在为用户分配新集合时触发 PropertyChanged 接下来,不要在代码隐藏中分配给 ItemsSource。那不是绑定。有了上面的内容,XAML 中的绑定应该可以正常工作。
  • 但最重要的是,UsersViewModel 的实例必须是 Window 的 DataContext,才能使 XAML 绑定起作用。在InitializeComponent();之后的public UsersWindow,只做var vm = new UsersViewModel();,初始化它,然后DataContext = vm;
  • @jle 我认为您的代码有太多错误无法给出简单的答案,如果这是真正的工作代码,请将其还给原始开发人员(如果他仍然与您合作),如果这是你自己的家庭代码,你可能想看看这个rachel53461.wordpress.com/2011/05/08/simplemvvmexample

标签: c# wpf xaml listbox


【解决方案1】:

您需要在这里解决一些问题,但没有什么很复杂的。只是很多 MVVM/XAML 内务处理的东西。

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

因此,您的视图模型具有您已经拥有的公共属性,但是当这些属性发生变化时,它们应该将通知发送到黑暗中。为此,您在视图模型上实现INotifyPropertyChanged,并在属性更改时触发PropertyChanged 事件。 UI 将订阅这些通知——如果您的视图模型是其属性被绑定的元素的DataContext(如泥浆般清晰——稍后会详细介绍)。

当视图模型公开一个集合时,它通常使用ObservableCollection,因为该类会在添加/删除/等时触发通知。 List 不这样做。 ObservableCollection 带来了所有通知内容的一些开销,所以不要到处使用它——当你只需要 List 时,仍然使用 List

所以UsersViewModel.Users 需要是ObservableCollection&lt;UserViewModel&gt;&lt;UserViewModel&gt; 类型,并且当集合被替换时,触发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 实例。

【讨论】:

  • 谢谢埃德!这是一个奇妙的解释。最后几段特别有助于解决眼前的问题。我现在将着手解决答案的第一部分以解决可用性问题。
【解决方案2】:

我发现有几处不对劲...

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

您还在这里绑定到 ListBox:

ItemsSource="{Binding Users}"

并在此处再次设置 ItemsSource:

listboxUsers.ItemsSource = uvm.Users;

糟糕!如果您在 XAML 中绑定,则绝对不需要再次设置 ItemsSource。需要修改收藏?直接对集合执行此操作。

另外,由于您是 WPF 的新手,我想我会添加一些在我刚开始学习时对我有帮助的建议:

  1. 如果您想让事情进展得更快,请将IsAsync=True 添加到您的 ListBox 绑定中。这将启用异步绑定(我知道这太棒了)。
  2. 从 ListBox 中虚拟化废话(只需将以下内容添加到 ListBox):

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

最后一件事,尽管其他人建议使用 ObservableCollection,但在使用大数据时它也会带来性能损失。 即使您不打算拥有大数据,无论如何使用 BindingList 总是更安全。 事实上,在处理较小的数据集时,ObservableCollection 具有优势。

它们速度更快,并且与 OC 具有许多相似的属性。

【讨论】:

  • 使用 BindingList 并不安全。它比 ObservableCollection 有更多的开销。它们还打算与 WinForms 而不是 WPF 一起使用。这是一个很好的例子,说明它如何增加开销:biglittleendian.com/article/21 还有很多其他 SO 帖子也描述了它。
  • 感谢您的来信。我的知识来自这篇文章:stackoverflow.com/questions/3305383/…,这表明 BindingList 在处理大型数据集时比 ObservableCollection 表现更好(我同意,它适用于 Windows 窗体,应该在 WPF 中谨慎使用)。
  • 有趣..让我想拿出一个测试,比较两者。如果我这样做了,今晚我就把它扔到 CodeProject 上!
  • @ThyArtIsCode - 非常感谢您的回复。先前的评论首先回答了该问题,因此我将其标记为正确,但您的回答很棒,让我有很多东西要学习。
  • 别担心,很高兴我能帮上忙(:
【解决方案3】:

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

视图模型....

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 .....

设置窗口 x:Name....

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

数据上下文

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

你应该得到这样的结果......

更新 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 中生成一个新的(空行) ....

【讨论】:

  • 非常感谢您的评论和这个答案,蒙蒂!在解决原始问题以添加更优雅的解决方案后,我尝试实现此功能,但遇到了错误:ViewModel 的“RelayCommand”行上的命名空间或程序集错误;并在视图的 DataContext 声明中键入错误。我还没有为“RelayCommand”定义一个 ICommand,所以这可能是 ViewModel 错误的原因吗?另外,DataContext 无法识别 ViewModel 的原因可能是什么?
  • 是的,你必须添加 xmlns:ViewModels="clr-namespace:DbEntities" 并且可以在这里找到 RelayCommand 类...kellydun.com/wpf-relaycommand-with-parameter
  • Monty,乍一看这似乎可行,但只要在 UI 中更改选定的单元格值,就会将原始值发送到 OneditButtonClick。有没有办法在 UserViewModel 中绑定更改/新参数?这将解决我发布的其他几个问题。
  • 可能有(如果您编辑一个单元格,然后在按下编辑行的按钮之前选择另一行,您会看到数据已更改)。这将是完全合乎逻辑的,因为只有在用户选择另一行时才提交更改的数据(在 SQL Server 表中手动编辑数据是相同的)但是,该按钮允许用户编辑该行中的数据,它允许行\单元格编辑并具有编辑按钮几乎没有意义。您想要的是防止用户手动编辑单元格中的值....
  • 做得很好,并让按钮 Enabled....
猜你喜欢
  • 2014-06-11
  • 2011-10-06
  • 1970-01-01
  • 2011-01-13
  • 1970-01-01
  • 1970-01-01
  • 2012-06-20
  • 1970-01-01
  • 2015-09-15
相关资源
最近更新 更多