3

我想在数据网格中显示数据,其中数据是

public class Thing
{
    public string Foo { get; set; }
    public string Bar { get; set; }
    public List<Candidate> Candidates { get; set; }
}

public class Candidate
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    ...
}

候选人列表中的候选人数量在运行时会有所不同。

所需的网格布局如下所示

Foo | Bar | Candidate 1 | Candidate 2 | ... | Candidate N

我希望DataTemplate每个候选人都有一个,因为我计划在运行时更改它 - 用户可以选择在不同列中显示有关候选人的哪些信息(候选人只是一个例子,我有不同的对象)。这意味着我还想在运行时更改列模板,尽管这可以通过一个大模板并折叠其部分来实现。

我知道如何实现我的目标的两种方法(两者都非常相似):

  1. 使用AutoGeneratingColumn事件并创建候选人
  2. 手动添加列

在这两种情况下,我都需要DataTemplate使用XamlReader. 在此之前,我必须编辑字符串以将绑定更改为想要的Candidate

有没有更好的方法来创建一个未知数量的 DataGridTemplateColumn 的 DataGrid?

注意:本题基于带 valueconverter 的动态数据模板

编辑:由于我需要同时支持 WPF 和 Silverlight,我创建了自己的DataGrid组件,该组件具有DependencyProperty用于绑定的列集合。当集合发生变化时,我会更新列。

4

3 回答 3

2

例如,我们创建 2 个 DataTemplate 和一个 ContentControl:

<DataTemplate DataType="{x:Type viewModel:VariantA}"> <dataGrid...> </DataTemplate>
<DataTemplate DataType="{x:Type viewModel:VariantB}"> <dataGrid...> </DataTemplate>

<ContentControl Content="{Binding Path=GridModel}" />

现在,如果您将 GridModel 属性(例如类型对象)设置为 VariantA 或 VariantB,它将切换 DataTemplate。

VariantA & B 示例实现:

public class VariantA
{
    public ObservableCollection<ViewModel1> DataList { get; set; }
}

public class VariantB
{
    public ObservableCollection<ViewModel2> DataList { get; set; }
}

希望这可以帮助。

于 2011-01-11T07:17:51.557 回答
0

我不知道这是否是一种“更好”的方式,因为这仍然很丑陋,但我个人确实喜欢这样:

  • 在 xaml 中制作模板
  • 使用采用当前绑定+绑定到列的多重绑定来获取“正确”的数据上下文(即:单元格而不是行)
  • 在此绑定上使用转换器来获取您喜欢的属性的值,如果您有许多要检索的属性,则可以选择添加一个参数。

例如:(对不起,我没有调整我的代码以适应你的项目,但你应该可以从那里自己做)

这是我的数据模板:

<DataTemplate x:Key="TreeCellTemplate">
    <Grid>
        <TextBlock HorizontalAlignment="Left" VerticalAlignment="Center" Margin="5,0,0,0">
            <TextBlock.Text>
                <MultiBinding Converter="{StaticResource RowColumnToCellConverter}" ConverterParameter="Text">
                    <Binding />
                    <Binding RelativeSource="{RelativeSource AncestorType=DataGridCell}" Path="Column" />
                </MultiBinding>
            </TextBlock.Text>
        </TextBlock>
    </Grid>
</DataTemplate>

这是我的转换器:

   public class RowColumnToCellConverter : MarkupExtension, IMultiValueConverter
   {
      public RowColumnToCellConverter() { }

      public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
      {
         XwpfRow row = values[0] as XwpfRow;
         XwpfTreeColumn column = values[1] as XwpfTreeColumn;

         if (row == null || column == null) return DependencyProperty.UnsetValue;

         TreeCell treeCell = (TreeCell)row[column.DataGrid.Columns.IndexOf(column)];
         switch ((string)parameter)
         {
            case "Text": return treeCell.Text;
            case "Expanded": return treeCell.Expanded;
            case "ShowExpandSymbol": return treeCell.ShowExpandSymbol;
            case "CurrentLevel": return new GridLength(treeCell.CurrentLevel * 14);

            default:
               throw new MissingMemberException("the property " + parameter.ToString() + " is not defined for the TreeCell object");
         }
      }

      public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
      {
         throw new NotSupportedException();
      }

      public override object ProvideValue(IServiceProvider serviceProvider)
      {
         return new RowColumnToCellConverter();
      }
   }

这节省了 MVVM 模型,我更喜欢这种做事方式,因为我真的不喜欢使用 xaml 解析器来制作“动态”数据模板,但从我的角度来看,它仍然是一个丑陋的 Hack。

我希望 MS 的人能给我们一种方法来获取单元格而不是行作为 dataContexts,以便能够动态生成模板列......

希望这可以帮助

编辑:在您的情况下,转换器实际上应该简单得多(如果我没记错的话,您可以直接返回单元格的实例,并且您不需要任何参数),但我仍然留下了更复杂的版本,只是如果其他人有类似的问题

于 2011-02-07T15:46:58.177 回答
0

我一直在研究类似的问题,但只发现了少数有用的模式。整个“动态列”问题在 Silverlight 中是一个有趣的问题。

昨天,我在搜索过程中在 Travis Pettijohn 的网站上发现了此页面Silverlight DataGrid with Dynamic Columns 。

以前我一直在使用Colin Eberhardt概述的“索引转换器”模式,它工作得非常好……只要你使用DataGridTextColumn. 一切都可以在后面的代码中完成,而且我在运行时应用样式没有问题。但是,我现在的要求是应用一些“单元格级别”格式 - 更改单元格的背景等 - 这意味着 aDataGridTemplateColumn是必需的。

aDataGridTemplateColumn对我来说最大的问题是我无法在代码中设置绑定。我知道我们可以通过解析 xaml 来构建它,但就像其他所有人一样,这似乎是一个巨大的 hack 并且无法维护到 nth。

Travis 概述的模式(上面的第一个链接)完全不同。在“运行时”(即页面加载时),在网格中创建您需要的列。这意味着遍历您的集合,并为每个项目添加一个具有适当标题等的列。然后为RowLoaded事件实现一个处理程序,并且在加载每一行时,只需将每个单元格的 DataContext 设置为适当的属性/属性索引父母。

private void MyGrid_RowLoaded(object sender, EventArgs e)
{
    var grid = sender as DataGrid;
    var myItem = grid.SelectedItem as MyClass;
    foreach (int i = 0; i < myItem.ColumnObjects.Count; i++)
    { 
        var column = grid.Columns[i]; 
        var cell = column.GetCellContent(e.Row)
        cell.DataContext = myItem.ColumnObjects[i];
    }
}

这消除了我使用索引转换器的需要。您可能可以Binding在设置时使用 acell.DataContext但对我来说更容易让模板直接绑定到底层对象。

我现在计划拥有多个模板(每个模板都可以绑定到我的单元格对象上的相同属性)并在页面加载时在它们之间切换。非常整洁的解决方案。

于 2011-11-21T20:43:53.480 回答