带格式化文本单元格的

时间:2016-07-14 12:09:23

标签: c# wpf xaml mvvm datagrid

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.TextFieldValue_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;
    }
  }

1 个答案:

答案 0 :(得分:0)

事实证明,我一直非常接近解决方案;它可以在不诉诸DataGrid_Calibs_LoadingRowTemplateSelector的情况下实现。我们仍然需要DataGrid_Calibs_AutoGeneratingColumn事件处理程序,但只是为了使用两个TextBlock正确应用自定义单元格模板,我们仍然需要自定义DataGridTemplateColumn

新用户控件XAML代码

诀窍是扩展用户控件的资源定义。

<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()方法,即单元格包含对象名称,而不是字段值。但为什么呢?

致谢

非常感谢在StackOverflow上发布到thisthis问题的答案。

相关问题