在AutoGenerateColmns=True
的设置中,我试图找到一种方法,允许我在WPF中的C#DataGrid
的单个单元格中显示多色文本。具有自定义单元格模板的解决方案或单元格内的HTML解释器都可以;由于技术原因,我更喜欢第一种选择,但我花了很多时间试图让这些工作起作用,但没有用。
另一个问题是我在这里处理几个表,我必须使用相同的用户控件来显示。只有一个表需要文本格式。我已经弄清楚如何根据我选择的标准动态地改变表的性质,但我不知道如何根据那里的实际文本填充这些修改过的单元格。文本本身包含一个标记("|||"
- 三个管道),用于分割字符串;一个用蓝色显示,另一个用红色显示。
让我告诉你我的代码。我将从App.xaml
的摘录开始,以便明确我如何设置DataGrid
样式。
App.xaml
正如您将看到的,我需要让DataGrid
自动生成列,因为我正在编写一个数据库,在编译时我不知道它的模式。由于表格标题可能包含下划线,因此我创建了一个自定义模板,它不会以通常的方式处理下划线,即与Alt的短手键组合。
<Style x:Key="DataGridStyle" TargetType="DataGrid">
<Setter Property="AutoGenerateColumns" Value="True" />
<Setter Property="CanUserAddRows" Value="False" />
<Setter Property="ColumnHeaderStyle">
<Setter.Value>
<Style TargetType="DataGridColumnHeader">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{TemplateBinding Content}"
HorizontalAlignment="Center"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</Setter.Value>
</Setter>
<Setter Property="CanUserSortColumns" Value="True" />
</Style>
DataGrid
本身这有一些修改,其中一个是我使用基于另一个用户控件的自定义RowDetailsTemplate
。这是无关紧要的(我希望),但为了完整起见,我把它留在这里(如果有一些副作用我不知道)。 ExpView
是我正在工作的命名空间; “Exp”本身就是调用该软件的原因(这就是DataGrid
的祖先项被称为“ExpView:ExpResultsTable
”的原因。
你看我在这里订阅了两个活动。我尽可能地尝试将我的代码隐藏为空(如MSDN文档中推荐的干净MVVM实现),但是只有XAML才能做到这么多,我担心......随意纠正我,虽然。我使用这些事件来确定DataGrid
是针对特殊表格还是普通表格。
<DataGrid Name="DataGrid_Calibs"
Style="{StaticResource DataGridStyle}"
SelectedItem="{Binding Path=SelectedCalib,
RelativeSource={RelativeSource AncestorType=ExpView:ExpResultsTable}}"
AutoGeneratingColumn="DataGrid_Calibs_AutoGeneratingColumn"
LoadingRow="DataGrid_Calibs_LoadingRow">
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<ExpView:CalibrationDetails/>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
我在这个XAML文件的开头还有以下资源声明进入单元格,以防我必须显示“特殊”表。
<UserControl.Resources>
<ResourceDictionary>
<DataTemplate x:Key="DifferenceDataTemplate">
<StackPanel Orientation="Vertical">
<TextBlock x:Name="FieldValue_Old" Foreground="Blue"/>
<TextBlock x:Name="FieldValue_New" Foreground="Red"/>
</StackPanel>
</DataTemplate>
</ResourceDictionary>
</UserControl.Resources>
如果必须显示正常数据,计划是使用普通数据绑定填充表。 (我没有使用DataGrid
填充RowView
;而是将DataGrid
绑定到正确显示公共属性的对象。您将在我的活动中看到此效果处理程序代码如下。)如果数据是“特殊的”,我想用上面资源中定义的StackPanel
替换单元格,并设置TextBlock
s(或Label
的内容对于应该进入单元格的字符串的第一部分和第二部分,如果这些结果更好地工作,则在"|||"
分割。
用于自动生成DataGrid
列的WPF事件处理程序的优点在于,除非您明确覆盖该列,否则它将执行其正常工作。这就是为什么以下代码在使用上述DataGrid
替换StackPanel
的单元格时工作正常,以防我发现“特殊表”。
private void DataGrid_Calibs_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e) {
ParameterDetails parameterDetailsControl = ((ScrollViewer)((DockPanel)((StackPanel)((DataGrid)sender).Parent).Parent).Parent).Parent as ParameterDetails;
ExpUserInterface parent = Window.GetWindow((DependencyObject)parameterDetailsControl) as ExpUserInterface;
ExpResultsTable resultsTableControl = parent.ResultsTable.Content as ExpResultsTable;
string selector = resultsTableControl.SelectedTabItem.Header.ToString();
if (selector.Equals("special")) {
DifferenceDataTemplateColumn col = new DifferenceDataTemplateColumn();
col.Header = e.PropertyName;
col.CellValue = e.PropertyName;
col.CellTemplate = (DataTemplate)FindResource("DifferenceDataTemplate");
col.CellTemplate.DataType = typeof(MyDatabaseObject);
col.IsReadOnly = true;
e.Column = col;
}
}
private void DataGrid_Calibs_LoadingRow(object sender, DataGridRowEventArgs e) {
MyDatabaseObject calib = e.Row.DataContext as MyDatabaseObject;
// anything I can do about the problem in here?
}
第二个事件处理程序主要是为了向您显示我确实绑定到MyDatabaseObject
而不是RowView
。
我欣然承认前四行是丑陋的,但至少它们现在起作用了。不过,欢迎提出任何建议。
其余的工作正常:“普通”表按计划填充,“特殊”表格在每个单元格中使用我的自定义StackPanels
格式化。但是,我没有成功填充这些细胞。
以下是我几天来一直在思考的问题,而且我无法想象查询通常的嫌疑人(Stackoverflow,MSDN等):
LoadingRow
事件是我应该访问字符串,拆分字符串,设置FieldValue_Old.Text
和FieldValue_New.Text
的地方。我可以通过calib
访问该对象(见上文) - 调试器显示我就在那里 - 但我不能为我的生活找出如何访问TextBlock
的单个(“特殊”)单元格,更不用说如何对当前行的所有列执行此操作。CellTemplateSelector
来解决这个问题吗?怎么样?DataGrid
解释其单元格中的HTML吗? (我不需要担心代码注入,因为我只是在填充DataGrid
时生成HTML代码。)任何帮助都受到高度赞赏,并且会让我免于快速接近秃顶,因为我正在撕裂我的头发。谢谢!
我忘了在前面提到的DataGridTemplateColumn
事件处理程序中添加我一直在引用的自定义AutoGeneratingColumn
的代码。
public class DifferenceDataTemplateColumn : DataGridTemplateColumn {
public string CellValue { get; set; }
protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem) {
ContentPresenter presenter = (ContentPresenter)base.GenerateElement(cell, dataItem);
BindingOperations.SetBinding(presenter, ContentPresenter.ContentProperty, new Binding(this.CellValue));
return presenter;
}
}
答案 0 :(得分:0)
事实证明,我一直非常接近解决方案;它可以在不诉诸DataGrid_Calibs_LoadingRow
或TemplateSelector
的情况下实现。我们仍然需要DataGrid_Calibs_AutoGeneratingColumn
事件处理程序,但只是为了使用两个TextBlock
正确应用自定义单元格模板,我们仍然需要自定义DataGridTemplateColumn
。
诀窍是扩展用户控件的资源定义。
<UserControl.Resources>
<ResourceDictionary>
<DataTemplate x:Key="DifferenceDataTemplate">
<StackPanel Orientation="Vertical">
<TextBlock x:Name="FieldValue_Old" Foreground="Blue" Text="{Binding Converter={StaticResource fieldValueConverter}, ConverterParameter={StaticResource True}}"/>
<TextBlock x:Name="FieldValue_New" Foreground="Red" Text="{Binding Converter={StaticResource fieldValueConverter}, ConverterParameter={StaticResource False}}"/>
</StackPanel>
</DataTemplate>
</ResourceDictionary>
</UserControl.Resources>
App.xaml
我已经以允许它用于TextBlock
的方式定义了转换器,但为了这样做,我需要传递bool
。由于XAML默认情况下将ConverterParameter
作为string
传递,因此除了App.xaml
之外,我在fieldValueConverter
中定义了两个新资源。
<Application ...
xmlns:s="clr-namespace:System;assembly=mscorlib"
xmlns:MyNamespace="clr-namespace:MyNamespace"
... >
<Application.Resources>
<MyNamespace:FieldValueConverter x:Key="fieldValueConverter" />
<s:Boolean x:Key="True">True</s:Boolean>
<s:Boolean x:Key="False">False</s:Boolean>
...
</Application.Resources>
</Application>
App.xaml.cs
剩下的工作就是在App.xaml
代码隐藏中定义实际值转换器。
[ValueConversion(typeof(string), typeof(string))]
public class FieldValueConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
bool isOld = (bool)parameter;
string[] fieldValues = value.ToString().Split(new string[] {"|||"}, System.StringSplitOptions.None);
string fieldValue = "";
if (isOld)
fieldValue = fieldValues[0];
else if (!isOld && fieldValues.Length == 2)
fieldValue = fieldValues[1];
return fieldValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
throw new NotImplementedException("Back conversion of comparison fields is not supported.");
}
}
fieldValues.Length == 2
检查的唯一原因是,对于不包含字符串"|||"
的字段,我不希望第二个字段显示任何内容。此外,由于默认情况下不包含差异的字段为空,因此我默认返回空字符串。
这就是缺少的一切!它现在就像一个魅力。
我不明白为什么需要自定义DataGridTemplateColumn
。它提供了另一个属性来访问我上面称为CellValue
的内容,因为我的数据绑定只会在绑定对象上调用ToString()
方法,即单元格包含对象名称,而不是字段值。但为什么呢?