总的来说,我对WPF和C#相当陌生。我正在玩弄它,遇到了一个问题,我觉得这对专家来说是小菜一碟,但我不知道我做错了什么。 我正在尝试创建一个简单的 DataGrid 控件(在 TabControl 中)并将其绑定到 ObservableCollection 对象。 我使用 Microsoft 的数据绑定概述中提供的数据绑定演示作为代码的基础。
主窗口 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));
}
}
}
我将不胜感激任何帮助,以了解为什么没有填充数据网格。 此外,任何关于不良编码习惯或改进代码的建议都将非常受欢迎,因为我正处于非常初始的经验学习阶段。 谢谢!
不能绑定到私有字段。只能绑定到公共属性。就数据网格而言,Dog
没有要显示的信息。
public class Dog : INotifyPropertyChanged
{
private string _name;
private string _number;
public Dog(string name, int number)
{
Name = name;
Number = number.ToString("D4");
}
public String Name
{
get { return _name; }
set
{
if (value != _name)
{
_name = value;
NotifyPropertyChanged(nameof(Name));
}
}
}
public String Number
{
get { return _number; }
set
{
if (value != _number)
{
_number = value;
NotifyPropertyChanged(nameof(Number));
}
}
}
我在您的私人字段前面加上下划线,因为这是标准做法。这是标准做法,因为有两个仅大小写不同的标识符会导致混淆和错误。
首先,我建议您阅读MVVM原则,然后选择一个MVVM框架与WPF一起使用。例如,MVVM 轻量级工具包是启动和理解 MVVM 的不错选择。
对于您的示例,下面仅提供有关代码的一些注释:
- 我建议你把所有的"业务"数据分组到一个视图模型类中(参见网络上的 MVVM 实践) -
App
类中没有任何内容...... - 此视图模型将实现"INotifyPropertyChanged"接口
- 因此,
Dogs
属性将位于此 ViewModel 中,并将在其资源库中引发"属性已更改">事件(示例中当前未出现的情况) - 有几个 MVVM 框架会自动将您的视图"绑定"到您的视图模型,但要理解,主要目标是使用适当的 ViewModel 设置您的
Window.DataContext
。 - 这就是为什么您可以在 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; } }
-
您的视图模型应如下所示:
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...