【问题标题】:WPF DataGrid DataBinding is not displayedWPF DataGrid DataBinding 不显示
【发布时间】:2017-12-06 14:52:34
【问题描述】:

我当前的 WPF 应用程序中有一个 DataGrid,我想将它绑定到一个包含 ObservableCollection 的 ViewModel。用户可以在某些 TextBoxes 中输入搜索值,并且在输入被击中后,我正在对我们的数据库执行查询,以返回记录表。从这些记录中,我为 ObservableCollection 填充数据。我现在正在苦苦挣扎,因为数据网格没有显示数据。

我已经阅读了一堆关于绑定的帖子,但我仍然错过了一些我认为的东西。

Product.cs

public class Product : InotifyPropertyChanged, IEditableObject
{
    public string Title { get; set; } = "";

    //public Product()
    //{

    //}
    private ProductViewModel _productViewModel = new ProductViewModel();
    public ProductViewModel productViewModel { get { return _productViewModel; } set { _productViewModel = value; } }

    public DataTable ProductsTable { get; set; }

    public void GetProducts(string filter)
    {
        //< --doing some stuff to fill the table-->

        foreach (DataRow row in ProductsTable.Rows)
        {
            productViewModel.Products.Add(new Product
            {

                Title = (string)row["TITLE"],

            });
        }
    }
}

ProductViewModel.cs

public class ProductViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    private Product _SelectedProduct;
    private ObservableCollection<Product> _Products = new ObservableCollection<Product>();
    public ObservableCollection<Product> Products { get { return _Products; } set { _Products = value; } }

    public ProductViewModel()
    {
    }

    public void NotifyPropertyChanged(string propertyName)
    {

        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

ProductWindow.xaml

<DataGrid 
    Name="ProductsGrid" 
    AutoGenerateColumns="False" 
    ItemsSource="{Binding Products, Mode=TwoWay, NotifyOnSourceUpdated=True}" 
    SelectedItem="{Binding SelectedProduct, Mode=TwoWay}"  
    CanUserAddRows="False" SelectionUnit="FullRow"  
    VerticalAlignment="Stretch" 
    Grid.Row="0" 
    Margin="10,10,10,10" 
    >
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding Title}" Header="Title"></DataGridTextColumn>
    </DataGrid.Columns>
</DataGrid>

ProductWindow.xaml.cs

    public partial class ProductWindow : Page
{
    public object DialogResult { get; private set; }
    //public ProductViewModel ProductViewModel;
    public ProductWindow()
    {

        InitializeComponent();

        DataContext = new ProductViewModel();//stackflow
        //var ProductViewModel = products.ProductViewModel;
        //ProductsGrid.DataContext = new ProductViewModel();

    }

    public ProductViewModel ViewModel => DataContext as ProductViewModel;

    private void OnKeydownHandler(object sender, KeyEventArgs e)
    {
        if (e.Key == Key.Enter)
        {
            var tb = sender as TextBox;
            Product products = new Product();
            string filter = "";//performing some ifelse to create filter
            products.GetProducts(filter);
            //ProductsGrid.DataContext = products.ProductsTable;
            //ProductsGrid.DataContext = products.productViewModel;

        }
        else if (e.Key == Key.Escape)
        {
            ProductsGrid.DataContext = null;
            foreach (TextBox tb in FindVisualChildren<TextBox>(this))
            {
                // do something with tb here
                tb.Text = "";
            }
        }
    }

}

【问题讨论】:

  • 修复set { _Products = value; }添加NotifyPropertyChanged(nameof(Products));
  • @CelsoLívero 仍然一无所有。集合中充满了项目,但它们没有显示。我已将其更改为: public ObservableCollection Products { get { return _Products; } 设置 { _Products = 价值; NotifyPropertyChanged(nameof(Products)); } }
  • 看看here,也许对你有帮助
  • here another help,在没有任何工具包的情况下,如何在 MVVM 中使用 WPF
  • @CelsoLívero 非常容易,如果您根本不想学习如何使用 WPF。

标签: c# wpf xaml datagrid


【解决方案1】:

如果DataContextProductViewModel,并且填充了该ProductViewModelProducts 集合,您将在DataGrid 中看到行。我已经测试过了。您提供的视图模型似乎没有任何行。

也就是说,你的设计有问题:

Product 创建一个ProductViewModelProductViewModel 创建Product 的集合。正如我刚才所说,每个Product 创建一个ProductViewModel。它创建了Product 的集合。他们不断创造彼此,直到您获得StackOverflowException。如果您没有看到,您一定是从其他地方调用 GetProducts()。

但是Product 没有必要拥有ProductViewModel 的副本。这就像在汽车的每个车轮上添加一辆车。

所以让我们改为这样做:ProductViewModel 拥有Product 的集合。只是。我们将调用GetProducts() 以确保我们在网格中获得了一些项目。你的绑定没问题。你只是没有填充集合。

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        DataContext = new ProductViewModel();
    }

    //  Now you can call ViewModel.GetProducts(filterString) from an event handler. 
    //  It would be more "correct" to use a Command, but let's take one step at a time. 
    public ProductViewModel ViewModel => DataContext as ProductViewModel;
}

视图模型

//  You didn't include any implementation of IEditableObject. I presume 
//  you can add that back in to this version of the class. 
public class Product : INotifyPropertyChanged, IEditableObject
{
    //  You weren't raising PropertyChanged here, or anywhere at all. 
    //  In every setter on a viewmodel, you need to do that. 
    private string _title = "";
    public string Title {
        get => _title;
        set
        {
            if (_title != value)
            {
                _title = value;
                NotifyPropertyChanged(nameof(Title));
            }
        }
    }

    public Product()
    {
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public void NotifyPropertyChanged(string propertyName)
    {

        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

public class ProductViewModel : INotifyPropertyChanged
{
    public ProductViewModel()
    {
        GetProducts("");
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private Product _SelectedProduct;

    public Product SelectedProduct
    {
        get { return _SelectedProduct; }
        set
        {
            if (value != _SelectedProduct)
            {
                _SelectedProduct = value;
                NotifyPropertyChanged(nameof(SelectedProduct));
            }
        }
    }


    public DataTable ProductsTable { get; set; }

    public void GetProducts(string filter)
    {
        //< --doing some stuff to fill the table-->

        Products.Clear();

        foreach (DataRow row in ProductsTable.Rows)
        {
            Products.Add(new Product
            {
                Title = (string)row["TITLE"],
            });
        }
    }

    private ObservableCollection<Product> _Products = new ObservableCollection<Product>();
    //  This setter MUST raise PropertyChanged. See the Title property above for example. 
    public ObservableCollection<Product> Products { get { return _Products; } private set { _Products = value; } }

    public void NotifyPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

更新

问题出在:您创建了一个新的Product,它创建了自己的ProductsViewModel没有任何东西绑定到该视图模型的任何属性。您填充了它的集合,而 DataGrid 不知道也不关心,因为您将它的 ItemsSource 绑定到了不同对象的属性。

所以请使用我上面的建议,尤其是窗口的ViewModel 属性。 我刚刚在 ProductsViewModel.GetProducts() 中进行了更改,您需要复制该更改:现在它在填充集合之前调用 Products.Clear()

    if (e.Key == Key.Enter)
    {
        var tb = sender as TextBox;

        //  Don't create this
        //Product products = new Product();

        string filter = "";//performing some ifelse to create filter
        ViewModel.GetProducts(filter);

    }
    else if (e.Key == Key.Escape)
    {
        //  Setting the DataContext to null breaks everything. Never do that. 
        //ProductsGrid.DataContext = null;

        //  Instead, just clear the collection. It's an ObservableCollection so it will 
        //  notify the DataGrid that it was cleared. 
        ViewModel.Products.Clear();

        foreach (TextBox tb in FindVisualChildren<TextBox>(this))
        {
            // do something with tb here
            tb.Text = "";
        }
    }

【讨论】:

  • 对不起,我错过了一部分。调用 GetProducts() 并在我的情况下填充了 137 个不同的对象。如果我将 DataGrid 设置为 AutoGenerateColumns="True" 并在 ProductWindow.xaml.cs ProductsGrid.DataContext = products.productViewModel; Datagrid 正在填充集合。但我想切换到 AutoGenerateColumns="False" 并在 xaml 中添加列,并且数据网格没有填充行。现在,我将尝试遵循您的回答并尝试使事情正常进行
  • 我正在写一个与 Ed Plunkett 写的相同的回复(嗯,几乎),如果你做出他所做的更改将起作用,使用 GetProducts 方法时确实存在问题,以及其他问题在您的代码中,Ed Plunkett 详细解释了它们......
  • @josef_skywalker 请显示“未填充行”版本的实际完整代码。不要遗漏任何重要的部分。我需要能够重现您在屏幕上看到的内容。在任何情况下,您都应该按照我的建议进行更改。
  • @josef_skywalker 您是否在事件处理程序中创建新产品?
  • @EdPlunkett 哇,感谢这实际上帮助我解决了问题。我现在也更好地理解了这种模式。感谢您解释清楚的解决方案。
猜你喜欢
  • 1970-01-01
  • 2018-08-04
  • 2019-09-02
  • 1970-01-01
  • 1970-01-01
  • 2013-06-16
  • 2013-03-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多