我的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();
}
}
答案 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;
}
}
}