我有一个表格,其中包含动态数量的数据网格,这些数据网格以编程方式引入到新标签页中。
我的问题是我需要更改每列的标题。我尝试通过一种方法来做
DataGridForSupplier.Columns[0].Header = "123";
但始终会因错误而崩溃:
索引超出范围。必须为非负数并且小于集合的大小
原来的问题是网格尚未完成加载。因此,在等待所有标签页加载并将数据添加到所有网格之后,即使是代码
DataGridForSupplier.Columns[0].Header = "123";
仍然会崩溃。如果让选项卡自行加载而没有标题篡改,则数据网格将正常显示。
我只是希望在XAML问题中这样做,因为我不知道在运行时会在后台加载多少个网格。因此,我现在可以接受任何解决方案。我试图找到一种解决方案,该解决方案将以“主题”所有数据网格为主题。幸运的是,所有datagrids标头都将在所有选项卡上重复。因此,选项卡1-10上的标题1将相同。标签页1-10的标题2相同
类似
<DataGridTemplateColumn.Header>
<TextBlock Text="{Binding DataContext.HeaderNameText, RelativeSource=>> RelativeSource AncestorType={x:Type DataGrid}}}" />
</DataGridTemplateColumn.Header>
,但这需要对每个网格重复。目前,这似乎使我逃脱了。 任何帮助都将受到欢迎。
答案 0 :(得分:0)
一个相当冗长的答案,但是此解决方案不需要任何其他库,第三者工具等。您可以在以后根据需要进行扩展,例如将钩子添加到鼠标移动/悬停/拖放/放下/焦点,等。首先,我在学习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”只是向设计者制作了一个ALIAS“ 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();
}
}
}
最后是我的示例ViewModel,其中包含IMyDataGridSource
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实例。它具有自己的网格查询/填充列表,处理自己的重建列标题,鼠标单击(如果要扩展),记录所选更改等。
希望这对您有所帮助。同样,这不需要任何其他第三方库,您可以根据需要扩展。我已经亲自完成了此操作,并且对数据网格和其他一些用于某些特定模式处理的控件进行了更多操作。