如何在运行时动态更改DataGrid String列格式?

时间:2018-06-19 10:36:42

标签: c# wpf datagrid

我的WPF桌面应用程序提供了一个UI来搜索人员并在DataGrid中显示结果。 此外,用户可以在运行时更改语言(当前(UI)文化)。

XAML中的DataGrid定义是

<DataGrid Name="SearchResultTable" AutoGenerateColumns="False" MinHeight="200" CanUserSortColumns="False">
    <DataGrid.Columns>
        <DataGridTextColumn Header="lastName" Binding="{Binding LastName}" SortDirection="Ascending" IsReadOnly="True" DisplayIndex="0"/>
        <DataGridTextColumn Header="firstName" Binding="{Binding FirstName}"   IsReadOnly="True" DisplayIndex="1"/>
        <DataGridTextColumn Header="dateOfBirth" Binding="{Binding DateOfBirth }"   IsReadOnly="True" DisplayIndex="2"/>
    </DataGrid.Columns>
</DataGrid>

DateOfBirth列内容应根据所选语言进行格式化。 c#代码部分是:

DataGridTextColumn col = (DataGridTextColumn)SearchResultTable.Columns[2];
col.Binding.StringFormat = CultureInfo.CurrentUICulture.DateTimeFormat.ShortDatePattern;

此代码部分在创建UI类之后以及每次用户选择其他语言时执行。 只要用户没有执行填充DataGrid的搜索,这就会产生罚款。

但是,每次

首次填充DataGrid时
IList<Person> searchResult = // read data from database
SearchResultTable.ItemsSource = searchResult;

并且用户选择不同的语言,抛出以下异常:

Binding cannot be changed after it has been used

那么,如何在运行时动态更改DataGrid String列格式?

修改

在mm8的答案中,我可以实现如下解决方案: 因为我可能不会更改Person类(业务域!),所以我创建了一个以Person为参考的辅助类

public class PersonHelper : INotifyPropertyChanged
{
    // ...
    private Person person = null;
    public String LastName { get => person.getLastName(); set => person.setLastName(LastName); }
    // ... 
    public string FormattedDateOfBirth => DateOfBirth.ToShortDateString();
    public void OnPropertyChanged(string name)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }
    // ...
}
UI类中的

搜索方法:

PersonObsColl = new ObservableCollection<PersonHelper>();
foreach (Person p in searchResult)
{
    PersonHelper ph = new PersonHelper(p);
    ph.PropertyChanged += new PropertyChangedEventHandler(PropertyFormattedDateOfBirthChanged);
    PersonObsColl.Add(ph);
}
在CurrentUICulture更改时调用的UI类方法中的

foreach (PersonHelper ph in PersonObsColl)
    ph.OnPropertyChanged("FormattedDateOfBirth");

和UI类中的Handler

private void PropertyFormattedDateOfBirthChanged(object sender, PropertyChangedEventArgs e)
{
    if (sender is PersonHelper && e.PropertyName.Equals("FormattedDateOfBirth"))
    {
        // avoid to call invalidation for every search result line; can this be improved ?
        if(PersonObsColl.IndexOf((PersonHelper)sender) == PersonObsColl.Count -1)
            SearchResultTable.InvalidateVisual();
    }
}

2 个答案:

答案 0 :(得分:2)

  

如何在运行时动态更改DataGrid String列格式?

不能。您无法更改任何现有绑定的StringFormat

您应该绑定到一个属性,该属性返回已经格式化的字符串,并在格式更改时引发此事件StringFormat,而不是设置列的PropertyChanged属性:

public string FormattedDateOfBirth => DateOfBirth.ToShortDateString();

XAML:

<DataGridTextColumn Header="dateOfBirth" Binding="{Binding FormattedDateOfBirth}" IsReadOnly="True" DisplayIndex="2"/>
  

有了这个答案,就失去了允许用户按日期对各个列进行排序的功能(而是由字符串规则进行了排序)。

只需将SortMemberPath属性设置为原始属性的名称即可。

<DataGridTextColumn ... SortMemberPath="DateOfBirth" Binding="{Binding FormattedDateOfBirth}" />

答案 1 :(得分:-1)

有了第一个答案,就失去了允许用户按日期对各个列进行排序的功能(而是由字符串规则进行了排序)。

为了保持按日期排序的功能,我最终在每当CurrentUICulture发生更改时就在运行时替换了列:

foreach (DataGridColumn dgc in SearchResultTable.Columns)
{
    if (dgc is DataGridTextColumn)
    {
        DataGridTextColumn dgtc = (DataGridTextColumn)dgc;
        Binding bo = (Binding)dgtc.Binding;
        string pps = bo.Path.Path;
        if (pps.Equals("DateOfBirth"))
        {
            int idx = SearchResultTable.Columns.IndexOf(dgtc);
            DataGridTextColumn dgtcn = new DataGridTextColumn();
            dgtcn.IsReadOnly = true;
            Binding b = new Binding("DateOfBirth");
            b.StringFormat = CultureInfo.CurrentUICulture.DateTimeFormat.ShortDatePattern;
            dgtcn.Binding = b;
            SearchResultTable.Columns[idx] = dgtcn;
            break;
        }
    }
}
相关问题