簡體   English   中英

綁定到 WPF DataGrid 中的 DataGridTemplateColumn

[英]Binding to DataGridTemplateColumn in WPF DataGrid

我正在以通用方式自定義 WPF DataGrid。 考慮一下我已經定義了一個這樣的 Creatures 枚舉:

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

然后我有一個上述類型的附加屬性,因此它可以附加到我想要的任何控件,稱為CreatureTypeProeprty 然后我想將此屬性附加到DataGridTemplateColumn並且我想在模板中訪問它。 例如考慮我的問題的以下可重現代碼:

 <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>

這不起作用,我知道為什么,因為DataGrid 列不是可視化樹的一部分。 但我找不到任何方法來解決這個問題。 我也試過這個但是這個解決方案不能以通用的方式使用並且最終會成為一個非常骯臟的解決方案。 我正在尋找一種更復雜、更干凈的方式。 我還嘗試使用列的名稱並指定ElementName它可以工作但是當有多個列並且我每次都必須重復代碼時也不能使用它。

datagrid 列更多是一個概念性的東西。 與其說它不在可視化樹中,不如說它根本不存在。

沒有勺子。

做這種事情的一種方法是使用列表並對其進行索引。 在您的 window 視圖模式中

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

顯然,按照與您的列相同的順序使用 CreatureTypes 加載它。

然后你可以參考使用類似

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

不幸的是,您需要一個固定的數字來進行索引。

您也許可以動態構建您的列並將它們添加到數據網格。

我會通過定義一個字符串模板並替換值,然后使用 xamlreader.parse() 構建一個數據網格列來實現這一點,您可以將其添加到數據網格中。

在我的示例中,我有一個 col.txt 文件。 這是我的數據網格列的一種模板。

它幾乎是一塊數據網格列標記。

<?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>

它顯示了幾個月的總計,顯然是 12 個月,我這里的數組是 monthtotals[12]。 顯然,這是另一回事,說明了方法。

我還有一個 dg.txt 文件,這當然與我的數據網格非常相似。 但那會是重復的。

我有一些代碼替換字符串,解析結果並附加一個構建的列:

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

可能比您需要的更復雜,但也許它會幫助其他人。

順便提一句

我建議將“未知”或“無”作為任何枚舉中的第一個。 根據我的經驗,這是標准做法,因此您可以區分設定值和未設定值。

Q1 這有多普遍?

不是特別的。 它解決了我稱之為“笨拙”綁定的問題,並且可以簡化模板中的 UI 邏輯。

或者一種用於列的超級自動生成器。

類似的方法對於用戶可配置的 UI 或動態 UI 非常有用。

想象一個動態 UI,其中用戶正在使用 xml 和 xml 定義 UI。 桌面兵棋推演積分計算器的可變統計數據或可變積分計算。

或者用戶選擇他們想要的列的位置以及如何顯示它們。 這可以構建為字符串並保存到他們的漫游配置文件中。 使用 xamlreader.load 從那里加載,它為您提供了整個數據網格定義。

q2 這就是樣本所做的。

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>

此行獲取列

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

而這一行每列 append

 cols.Add(el);

正是因為有 Xelement 節點,您可以發現該文件被作為 xml 而不僅僅是字符串來處理。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM