【问题标题】:Update Multiple DataGrids in WPF for Header Title为标题标题更新 WPF 中的多个 DataGrid
【发布时间】:2019-04-06 23:26:22
【问题描述】:

我有一个表单,其中包含动态数量的数据网格,这些数据网格以编程方式在新标签页上引入。

我的问题是我需要更改每列的标题。我已经尝试通过一种方法来做到这一点

DataGridForSupplier.Columns[0].Header = "123";

但它总是因错误而崩溃:

索引超出范围。必须是非负数且小于集合的大小

原来问题是网格没有完成加载。所以在等待所有标签页加载并向所有网格添加数据之后,即使是代码

DataGridForSupplier.Columns[0].Header = "123";

仍然会崩溃。如果选项卡自行加载而没有篡改标题,则数据网格显示正常。

我只是喜欢在 XAML 中执行此操作,问题是看到我不知道在运行时将加载多少个网格,我尝试在后面执行此操作。所以我现在对任何解决方案都持开放态度。我尝试找到一种解决方案,该解决方案将包含“主题”所有数据网格的内容。幸运的是,所有数据网格标题将在所有选项卡中重复。所以标签页 1 - 10 上的标题 1 将是相同的。标签页 1 - 10 上的标题 2 将是相同的

类似

  <DataGridTemplateColumn.Header> 
    <TextBlock Text="{Binding DataContext.HeaderNameText, RelativeSource=>> RelativeSource AncestorType={x:Type DataGrid}}}" /> 
  </DataGridTemplateColumn.Header> 

但这需要对每个网格重复。这似乎在此刻逃避了我。 欢迎任何帮助。

【问题讨论】:

  • 一种方法是在所有数据网格中禁用 AutogenerateColumns,当您在网格中找到数据项时,它将根据每列的对象属性名称进行填充。您也可以为属性名称添加别名。
  • 我尝试禁用 AutoGen,但并没有给我指明正确的方向。您似乎可以通过 XAML 将整体主题应用于所有数据网格,但我不确定这是如何完成的。以不同的方式问这个问题,如果我可以将数据网格名称传递给 XAML,我该如何调整每个数据网格标头的每个 col 1 的列名称,以通过 XAML 说“找到”
  • 我确实也尝试在数据表绑定到数据网格之前更改数据表中的标题,但这会由于某种原因造成另一个错误,似乎标题中不允许使用特殊字符,这很奇怪,因为它是类似于“this,name”的字符串中的第二个逗号,而且我认为在数据表中更改它并不是最好的方法。似乎最好的地方是在 XAML 中

标签: c# wpf


【解决方案1】:

一个相当冗长的答案,但此解决方案不需要任何额外的库、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 方库,您可以根据需要进行扩展。我亲自对数据网格和其他几个用于特定模式处理的控件进行了此操作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-03-20
    • 1970-01-01
    • 2013-07-20
    • 1970-01-01
    • 2014-01-14
    • 2011-12-16
    • 1970-01-01
    相关资源
    最近更新 更多