一个相当冗长的答案,但此解决方案不需要任何额外的库、3rd 方工具等。您可以稍后根据需要扩展它,例如添加挂钩到鼠标移动/悬停/拖放/焦点,等等。首先是我在学习 WPF 的早期发现的子类化的前提。您不能子类化 xaml 文件,但可以通过 .cs 代码文件。在这种情况下,我将 DataGrid 子类化为 MyDataGrid。接下来,我为已知的控件类型创建了一个接口,以确保给定函数/方法/属性的联系。我已经精简了这个版本,以涵盖您需要获得的内容。
下面的接口只是为了暴露任何使用这个接口的类必须有一个名为 MyDataGridItemsChanged 的方法,并且需要一个 MyDataGrid 的参数.. 很简单
public interface IMyDataGridSource
{
void MyDataGridItemsChanged(MyDataGrid mdg);
}
现在,在代码中声明从 DataGrid 派生的 MyDataGrid。在这个类中,我添加了一个 IMyDataGridSource 类型的私有属性,以便在构建和绑定数据网格后在运行时获取。
public class MyDataGrid : DataGrid
{
// place-holder to keep if so needed to expand later
IMyDataGridSource boundToObject;
public MyDataGrid()
{
// Force this class to trigger itself after the control is completely loaded,
// bound to whatever control and is ready to go
Loaded += MyDataGrid_Loaded;
}
private void MyDataGrid_Loaded(object sender, RoutedEventArgs e)
{
// when the datacontext binding is assigned or updated, see if it is based on
// the IMyDataGridSource object. If so, try to type-cast it and save into the private property
// in case you want to add other hooks to it directly, such as mouseClick, grid row changed, etc...
boundToObject = DataContext as IMyDataGridSource;
}
// OVERRIDE the DataGrid base class when items changed and the ItemsSource
// list/binding has been updated with a new set of records
protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
{
// do whatever default behavior
base.OnItemsChanged(e);
// if the list is NOT bound to the data context of the IMyDataGridSource, get out
if (boundToObject == null)
return;
// the bound data context IS of expected type... call method to rebuild column headers
// since the "boundToObject" is known to be of IMyDataGridSource,
// we KNOW it has the method... Call it and pass this (MyDataGrid) to it
boundToObject.MyDataGridItemsChanged(this);
}
}
接下来进入您放置数据网格的表单。您将需要为您的项目添加一个“xmlns”引用,以便您可以添加一个“MyDataGrid”而不仅仅是“DataGrid”。就我而言,我的应用程序被称为“StackHelp”,因为这是我从提供的其他答案中进行各种测试的地方。 “xmlns:myApp”只是为设计者创建一个别名“myApp”,以便它可以访问我的应用程序中的类。然后,我可以添加
<Window x:Class="StackHelp.MyMainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:myApp="clr-namespace:StackHelp"
Title="Main Window" Height="700" Width="900">
<StackPanel>
<!-- adding button to the main window to show forced updated list only -->
<Button Content="Refresh Data" Width="100"
HorizontalAlignment="Left" Click="Button_Click" />
<myApp:MyDataGrid
ItemsSource="{Binding ItemsCollection, NotifyOnSourceUpdated=True}"
AutoGenerateColumns="True" />
</StackPanel>
</Window>
现在,进入 MyMainWindow.cs 代码隐藏
namespace StackHelp
{
public partial class MyMainWindow : Window
{
// you would have your own view model that all bindings really go to
MyViewModel VM;
public MyMainWindow()
{
// Create instance of the view model and set the window binding
// to this public object's DataContext
VM = new MyViewModel();
DataContext = VM;
// Now, draw the window and controls
InitializeComponent();
}
// for the form button, just to force a refresh of the data.
// you would obviously have your own method of querying data and refreshing.
// I am not obviously doing that, but you have your own way to do it.
private void Button_Click(object sender, RoutedEventArgs e)
{
// call my viewmodel object to refresh the data from whatever
// data origin .. sql, text, import, whatever
VM.Button_Refresh();
}
}
}
最后是包含 IMyDataGridSource 的示例 ViewModel
public class MyViewModel : IMyDataGridSource, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void RaisePropertyChanged(string propertyName)
{ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); }
public ObservableCollection<OneItem> ItemsCollection { get; set; }
= new ObservableCollection<OneItem>();
public void Button_Refresh()
{
ItemsCollection = new ObservableCollection<OneItem>
{
new OneItem{ DayName = "Sunday", DayOfWeek = 0},
new OneItem{ DayName = "Monday", DayOfWeek = 1},
new OneItem{ DayName = "Tuesday", DayOfWeek = 2},
new OneItem{ DayName = "Wednesday", DayOfWeek = 3},
new OneItem{ DayName = "Thursday", DayOfWeek = 4},
new OneItem{ DayName = "Friday", DayOfWeek = 5 },
new OneItem{ DayName = "Saturday", DayOfWeek = 6 }
};
RaisePropertyChanged("ItemsCollection");
}
// THIS is the magic hook exposed that will allow you to rebuild your
// grid column headers
public void MyDataGridItemsChanged(MyDataGrid mdg)
{
// if null or no column count, get out.
// column count will get set to zero if no previously set grid
// OR when the items grid is cleared out. don't crash if no columns
if (mdg == null)
return;
mdg.Columns[0].Header = "123";
}
}
现在,更进一步。我不知道您如何管理视图模型,并且您的表单等中可能有多个网格。您可以将上述 MyViewModel 类创建为较小的子集,例如 MyDataGridManager 类。所以每个数据网格都绑定到它自己的 MyDataGridManager 实例。它有自己的网格查询/填充列表,处理自己的重建列标题,鼠标点击(如果你想展开),选择记录更改等。
希望这对您有所帮助。同样,这不需要任何其他 3rd 方库,您可以根据需要进行扩展。我亲自对数据网格和其他几个用于特定模式处理的控件进行了此操作。