首页 > 解决方案 > 数据网格控件不显示 ObservableCollection 数据

问题描述

一般来说,我对 WPF 和 C# 比较陌生。我正在玩它,遇到了一个问题,我觉得这对专家来说是小菜一碟,但我不知道我做错了什么。我正在尝试创建一个简单的 DataGrid 控件(在 TabControl 内)并将其绑定到 ObservableCollection 对象。我使用微软数据绑定概述中提供的数据绑定演示作为我的代码的基础。

主窗口 XAML:

   <Window x:Class="PetProject.MainWindow"
        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:PetProject"
        mc:Ignorable="d"
        Title="PetProject" Height="350" Width="525">
    <Window.Resources>
        <CollectionViewSource 
              Source="{Binding Source={x:Static Application.Current}, Path=Dogs}"   
              x:Key="DogsDataView" />
    </Window.Resources>



    <Grid Margin="8,8,8,8">
        <TabControl>
            <TabItem Header="Dogs">
                <DataGrid ItemsSource="{Binding Source={StaticResource DogsDataView}}">
                </DataGrid>
            </TabItem>
        </TabControl>
    </Grid>
</Window>

代码隐藏:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;

    namespace PetProject
    {
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        /// 


        public partial class MainWindow : Window
        {
            CollectionViewSource DogsDataView;

            public MainWindow()
            {
                InitializeComponent();
                DogsDataView = (CollectionViewSource)(this.Resources["DogsDataView"]);
            }
        }
    }

应用 XAML 是

   <Application x:Class="PetProject.App"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:local="clr-namespace:PetProject"
                 Startup="AppStartup">
                 <!--StartupUri="MainWindow.xaml"-->
    </Application>

代码隐藏:

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using System.ComponentModel;

namespace PetProject
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        private ObservableCollection<Dog> dogs = new ObservableCollection<Dog>();

        void AppStartup(object sender, StartupEventArgs args)
        {
            LoadData();
            MainWindow mainWindow = new MainWindow();
            mainWindow.Show();

        }

        public ObservableCollection<Dog> Dogs
        {
            get { return this.dogs; }
            set { this.dogs = value; }
        }

        private void LoadData() {
            Dog Johnny = new Dog("Johnny",1325);
            Dog Diamond = new Dog("Diamond",1327);
            this.Dogs.Add(Johnny);
            this.Dogs.Add(Diamond);
        }


    }
}

Dog 只是一个实现 INotifyPropertyChanged 接口的类(它现在什么都不做):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Collections.ObjectModel;

namespace PetProject
{
    public class Dog : INotifyPropertyChanged
    {
        private string name;
        private string number;

        public event PropertyChangedEventHandler PropertyChanged;

        public Dog(string name, int number)
        {
            this.name = name;
            this.number = number.ToString("D4");
        }


        protected void NotifyPropertyChanged(string propName)
        {
            if (this.PropertyChanged != null)
                this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
        }
    }
}

对于理解为什么没有填充 DataGrid 的任何帮助,我将不胜感激。此外,任何关于不良编码习惯或改进代码的建议都将非常受欢迎,因为我正处于一个非常初始的经验学习阶段。谢谢!

标签: c#wpfdata-binding

解决方案


首先,我建议您阅读有关 MVVM 原理的信息,然后可能选择一个 MVVM 框架与 WPF 一起使用。例如MVVM 轻量级工具包是开始和理解 MVVM 的不错选择。

对于您的示例,这里只是对您的代码的一些评论:

  • 我建议您将所有“业务”数据分组到一个 viewModel 类中(请参阅网络上的 MVVM 实践)-App类中没有任何内容......
  • 此 ViewModel 将实现“INotifyPropertyChanged”接口
  • 因此,该Dogs属性将位于此 ViewModel 中,并将在其设置器中引发“PropertyChanged”事件(您的示例中当前不是这种情况)
  • 有几个 MVVM 框架可以自动将您的视图“绑定”到您的视图模型,但要理解,主要目标是为您设置Window.DataContext适当的 ViewModel。
  • 这就是您可以在 App.xaml 中恢复的原因:StartupUri="MainWindow.xaml"
  • 然后加载您的 ViewModel,您可以执行类似的操作来加载您的 Dogs 集合:

    public partial class MainWindow : Window
    {
        public MainWindow()
             {
                InitializeComponent();
                Loaded += MainWindow_Loaded;
             }
    
            private void MainWindow_Loaded(object sender, RoutedEventArgs e)
            {
                // For test: LOAD & SET your DataContext here
                //
                var myDogViewmodel = new DogViewModel();
                myDogViewModel.LoadData();
                this.DataContext = myDogViewmodel;
            }
        }
    
  • 你的 ViewModel 应该是这样的:

    public class DogViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private ObservableCollection<Dog> _dogs;
        public ObservableCollection<Dog> Dogs
        {
            get { return _dogs; }
            set
            {
                _dogs = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Dogs"));
            }
        }
        public void LoadData()
        {
            // ....
        }
    }
    
  • 那么你的Dog类还必须实现 INotifuPropertyChanged 接口:

        public class Dog : INotifyPropertyChanged
        {
            public event PropertyChangedEventHandler PropertyChanged;
    
            private string _name;
            private int _number;     
    
            public string Name
            {
                get => _name;
                set
                {
                     if (_name != value)
                     { 
                        _name = value;
                        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Name"));
                     }
                }
            }
    
            public int Number
            {
                get => _number;
                set
                {
                    if (_number != value)
                    {
                        _number = value;
                        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Number"));
                    }
                }
            }
        }
    
  • 最后,在您的 MainWindow.xaml 中:

>

<Window x:Class="PetProject.MainWindow"
    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:PetProject"
    mc:Ignorable="d"
    Title="PetProject" Height="350" Width="525">

<Grid Margin="8,8,8,8">
    <TabControl>
        <TabItem Header="Dogs">
            <DataGrid ItemsSource="{Binding Dogs}" />
        </TabItem>
    </TabControl>
</Grid>

它现在应该可以工作了;)告诉我是否清楚。熟悉 MVVM...


推荐阅读