简体   繁体   English

绑定到 WPF DataGrid 中的 DataGridTemplateColumn

[英]Binding to DataGridTemplateColumn in WPF DataGrid

I am customizing WPF DataGrid in a generic way.我正在以通用方式自定义 WPF DataGrid。 consider that I have defined an enum of Creatures like this:考虑一下我已经定义了一个这样的 Creatures 枚举:

public enum CreatureType
{
    Human,
    Animal,
    Insect,
    Virus
}

then I have an attached property of the above type so it can be attached to any control I want, that called CreatureTypeProeprty .然后我有一个上述类型的附加属性,因此它可以附加到我想要的任何控件,称为CreatureTypeProeprty then I want to attach this property to DataGridTemplateColumn and I want to access this inside the Template.然后我想将此属性附加到DataGridTemplateColumn并且我想在模板中访问它。 for example consider the following reproducible code of my problem:例如考虑我的问题的以下可重现代码:

 <DataGridTemplateColumn local:CreatureTypeProeprty.Value = "Human" Width="*">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <Grid>
                <TextBlock Text="{Binding Path=(local:CreatureTypeProeprty.Value), RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGridTemplateColumn }}"/>
            </Grid>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

This doesn't work and I know why because DataGrid column is not part of the visual tree.这不起作用,我知道为什么,因为DataGrid 列不是可视化树的一部分。 but I can't find any way to solve this issue.但我找不到任何方法来解决这个问题。 I also tried This but this solution can not be used in a generic way and ends up in a very dirty solution.我也试过这个但是这个解决方案不能以通用的方式使用并且最终会成为一个非常肮脏的解决方案。 I'm looking for a more sophisticated and clean way.我正在寻找一种更复杂、更干净的方式。 I also tried using the name for the column and specifying ElementName it works but also this can not be used when there is multiple columns and I have to repeat codes every time.我还尝试使用列的名称并指定ElementName它可以工作但是当有多个列并且我每次都必须重复代码时也不能使用它。

The datagrid column is more a conceptual thing. datagrid 列更多是一个概念性的东西。 It's not so much not in the visual tree as not there at all.与其说它不在可视化树中,不如说它根本不存在。

There is no spoon.没有勺子。

One way to do this sort of thing would be to use a list and index it.做这种事情的一种方法是使用列表并对其进行索引。 In your window viewmode在您的 window 视图模式中

public list<CreatureType> Creatures {get;set;}

Obviously, load that up with CreatureTypes in the same order as your columns.显然,按照与您的列相同的顺序使用 CreatureTypes 加载它。

You can then reference using something like然后你可以参考使用类似

  Text="{Binding DataContext.Creatures[4]} 
, RelativeSource={RelativeSource Mode=FindAncestor, 
  AncestorType=Datagrid}}

Unfortunately, you need a fixed number for indexing.不幸的是,您需要一个固定的数字来进行索引。

You could perhaps dynamically build your columns and add them to the datagrid.您也许可以动态构建您的列并将它们添加到数据网格。

I would do that by defining a string template and substituting values, then using xamlreader.parse() to build a datagrid column which you add to your datagrid.我会通过定义一个字符串模板并替换值,然后使用 xamlreader.parse() 构建一个数据网格列来实现这一点,您可以将其添加到数据网格中。

In my sample I have a col.txt file.在我的示例中,我有一个 col.txt 文件。 This is a sort of template for my datagrid columns.这是我的数据网格列的一种模板。

It is pretty much a piece of datagrid column markup.它几乎是一块数据网格列标记。

<?xml version="1.0" encoding="utf-8" ?>
<DataGridTemplateColumn>
    <DataGridTemplateColumn.Header>
    <Grid>
        <Grid.RowDefinitions>
        <RowDefinition Height="*" />
        <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <TextBlock Text="xxMMMxx" TextAlignment="Center" Grid.ColumnSpan="2"/>
        <TextBlock Text="Units" Margin="2,0,2,0" Grid.Row="1"/>
        <TextBlock Text="Value" Margin="2,0,2,0" Grid.Row="1" Grid.Column="2" />
    </Grid>
    </DataGridTemplateColumn.Header>
    <DataGridTemplateColumn.CellTemplate>
    <DataTemplate>
        <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <TextBlock Text="{Binding MonthTotals[xxNumxx].Products}" Margin="2,0,2,0" TextAlignment="Right"/>
        <TextBlock Text="{Binding MonthTotals[xxNumxx].Total}"    Margin="2,0,2,0" TextAlignment="Right" Grid.Column="1"/>
        </Grid>
    </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

It's showing totals for months so obviously 12 months and my array here is monthtotals[12].它显示了几个月的总计,显然是 12 个月,我这里的数组是 monthtotals[12]。 Obviously, this is a different thing and illustrating the approach.显然,这是另一回事,说明了方法。

I also have a dg.txt file which is of course something rather similar for my datagrid.我还有一个 dg.txt 文件,这当然与我的数据网格非常相似。 But that'd be repetitive.但那会是重复的。

And I have some code substitutes strings, parses the result and appends a built column:我有一些代码替换字符串,解析结果并附加一个构建的列:

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        // Get the datagrid shell
        XElement xdg = GetXElement(@"pack://application:,,,/dg.txt");  
        XElement cols = xdg.Descendants().First();     // Column list
        // Get the column template
        XElement col = GetXElement(@"pack://application:,,,/col.txt");  

        DateTime mnth = DateTime.Now.AddMonths(-6);

        for (int i = 0; i < 6; i++)
        {
            DateTime dat = mnth.AddMonths(i);
            XElement el = new XElement(col);
            // Month in mmm format in header
            var mnthEl = el.Descendants("TextBlock")
                        .Single(x => x.Attribute("Text").Value.ToString() == "xxMMMxx");
            mnthEl.SetAttributeValue("Text", dat.ToString("MMM"));

            string monthNo = dat.AddMonths(-1).Month.ToString();
            // Month as index for the product
            var prodEl = el.Descendants("TextBlock")
                        .Single(x => x.Attribute("Text").Value == "{Binding MonthTotals[xxNumxx].Products}");
            prodEl.SetAttributeValue("Text",
                "{Binding MonthTotals[" + monthNo + "].Products}");
            // Month as index for the total
            var prodTot = el.Descendants("TextBlock")
                        .Single(x => x.Attribute("Text").Value == "{Binding MonthTotals[xxNumxx].Total}");
            prodTot.SetAttributeValue("Text",
                "{Binding MonthTotals[" + monthNo + "].Total}");
            cols.Add(el);
        }

        string dgString = xdg.ToString();
        ParserContext context = new ParserContext();
        context.XmlnsDictionary.Add("", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
        context.XmlnsDictionary.Add("x", "http://schemas.microsoft.com/winfx/2006/xaml");
        DataGrid dg = (DataGrid)XamlReader.Parse(dgString, context);
        Root.Children.Add(dg);
    }
    private XElement GetXElement(string uri)
    {
        XDocument xmlDoc = new XDocument();
        var xmltxt = Application.GetContentStream(new Uri(uri));
        string elfull = new StreamReader(xmltxt.Stream).ReadToEnd();
        xmlDoc = XDocument.Parse(elfull);
        return xmlDoc.Root;
    }

Might be more complicated than you need but maybe it'll help someone else down the line.可能比您需要的更复杂,但也许它会帮助其他人。

BTW顺便提一句

I suggest an "unknown" or "none" as the first in any enum.我建议将“未知”或“无”作为任何枚举中的第一个。 This is standard practice in my experience so you can differentiate between a set and unset value.根据我的经验,这是标准做法,因此您可以区分设定值和未设定值。

Q1 How common is this? Q1 这有多普遍?

Not very.不是特别的。 It solves problems I would describe as "awkward" bindings and can simplify UI logic in templating.它解决了我称之为“笨拙”绑定的问题,并且可以简化模板中的 UI 逻辑。

Or a sort of super auto generator for columns.或者一种用于列的超级自动生成器。

A similar approach can be very useful for user configurable UI or dynamic UI.类似的方法对于用户可配置的 UI 或动态 UI 非常有用。

Imagine a dynamic UI where the user is working with xml and xml defines the UI.想象一个动态 UI,其中用户正在使用 xml 和 xml 定义 UI。 Variable stats or variable points calculation for a tabletop wargaming points calculator.桌面兵棋推演积分计算器的可变统计数据或可变积分计算。

Or where the user picks the columns they want and how to display them.或者用户选择他们想要的列的位置以及如何显示它们。 That can be built as strings and saved to their roaming profile.这可以构建为字符串并保存到他们的漫游配置文件中。 Loaded from there using xamlreader.load which gives you your entire datagrid definition.使用 xamlreader.load 从那里加载,它为您提供了整个数据网格定义。

q2 That's what the sample does. q2 这就是样本所做的。

dg.txt looks like dg.txt 看起来像

<?xml version="1.0" encoding="utf-8" ?>
<DataGrid
          ItemsSource="{Binding SalesMen}"
          AutoGenerateColumns="False"
          Grid.Column="1"
          CanUserAddRows="False"
          IsReadOnly="False"
    Name="dg">
  <DataGrid.Columns>
    <DataGridTextColumn Binding="{Binding FullName}"  Header="Name"/>
  </DataGrid.Columns>
</DataGrid>

This line gets Columns此行获取列

 XElement cols = xdg.Descendants().First();     // Column list

And this line append each column而这一行每列 append

 cols.Add(el);

It is so there are Xelement nodes you can find that the file is manipulated as xml rather than just as a string.正是因为有 Xelement 节点,您可以发现该文件被作为 xml 而不仅仅是字符串来处理。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM