2011-05-02 30 views
2

在這個假設的例子中,假設我有一個FooSet對象,它有五個屬性Foo1,Foo2,Foo3 Foo4和Foo5,它們都是Foo類型,它本身有幾個屬性。最後,我有一個名爲FooTemplate的DataTemplate,它知道如何以圖形方式顯示Foo類型的對象。在內置的WPF DataGrid中,我可以爲DataGridTemplateColumn設置數據源嗎?

現在,當使用內置的DataGrid時,ItemsSource是FooSet對象的集合。我想要做的是設置五個使用FooTemplate數據模板的模板列。但是,DataGrid的模板列類型不允許我爲該列設置數據源(例如Foo1,Foo2等),因此我最終會複製模板,每個列只需更改一次,只需將Foo1.SomeProp更改爲Foo2.SomeProp在模板的綁定中,這當然是荒謬的。但是我對於我的生活無法找到如何說'B列使用Foo2作爲數據源。'

下面是一些僞XAML顯示我想要的東西......

<Resources> 
    <DataTemplate TargetType="Foo"> 
     <StackPanel> 
      <local:FooPropAControl Value="{Binding FooPropA}" /> 
      <local:FooPropBControl Value="{Binding FooPropB}" /> 
      <local:FooPropCControl Value="{Binding FooPropC}" /> 
     </StackPanel> 
    </DataTemplate> 
</Resources> 

<DataGrid ItemsSource="{Binding MyItems}" AutoGenerateColumns="false"> 
    <DataGrid.Columns> 
     <DataGridTemplateColumn DataSource="{Binding Foo1}" /> 
     <DataGridTemplateColumn DataSource="{Binding Foo2}" /> 
     <DataGridTemplateColumn DataSource="{Binding Foo3}" /> 
     <DataGridTemplateColumn DataSource="{Binding Foo4}" /> 
     <DataGridTemplateColumn DataSource="{Binding Foo5}" /> 
    </DataGrid.Columns> 
</DataGrid> 

即使我必須明確指定該列中的模板,這仍然很好。它將該列的數據源設置爲FooSet的屬性,這樣我就可以使用一個DataTemplate。所有其他列讓你設置一些綁定,這樣做。我甚至嘗試子類化DataGridTemplateColumn添加DataSource,但沒有太多(我的猜測是因爲沒有列本身,而是規定如何生成行中的單元格,但這只是一個猜測。)

現在我知道第三方的Xceed網格可以讓你明確指出,但我希望有一個本地解決方案。

那麼,howzyadoodat?或者你能嗎?

中號

回答

2

好問題,我會使用ContentControl接近它,代碼仍然會有點誇大,但它不是複製整個模板更好,如:

<DataGrid ItemsSource="{Binding EmpSets}"> 
    <DataGrid.Resources> 
     <DataTemplate DataType="{x:Type obj:Employee}"> 
      <TextBlock> 
       <Run Text="{Binding Name}"/> 
       <Run Name="RunChan" Text=" - "/> 
       <Run Text="{Binding Occupation}"/> 
      </TextBlock> 
     </DataTemplate> 
    </DataGrid.Resources> 
    <DataGrid.Columns> 
     <DataGridTemplateColumn Header="Emp1"> 
      <DataGridTemplateColumn.CellTemplate> 
       <DataTemplate> 
        <ContentControl Content="{Binding Emp1}"/> 
       </DataTemplate> 
      </DataGridTemplateColumn.CellTemplate> 
     </DataGridTemplateColumn> 
     <DataGridTemplateColumn Header="Emp2"> 
      <DataGridTemplateColumn.CellTemplate> 
       <DataTemplate> 
        <ContentControl Content="{Binding Emp2}"/> 
       </DataTemplate> 
      </DataGridTemplateColumn.CellTemplate> 
     </DataGridTemplateColumn> 
     <!-- ... --> 
    </DataGrid.Columns> 
</DataGrid> 

這裏我用一個隱式DataTemplate在資源中,但您也可以通過定義&引用密鑰來顯式將其應用爲每個ContentControl的ContentTemplate,但無論如何您都知道。


準系統子類化的方法:

public class DataGridTemplateMemberColumn : DataGridTemplateColumn 
{ 
    public static readonly DependencyProperty MemberPathProperty = 
      DependencyProperty.Register("MemberPath", typeof(string), typeof(DataGridTemplateMemberColumn), new UIPropertyMetadata(null)); 
    public string MemberPath 
    { 
     get { return (string)GetValue(MemberPathProperty); } 
     set { SetValue(MemberPathProperty, value); } 
    } 

    protected override System.Windows.FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem) 
    { 
     return GenerateContent(CellEditingTemplate, dataItem); 
    } 

    protected override System.Windows.FrameworkElement GenerateElement(DataGridCell cell, object dataItem) 
    { 
     return GenerateContent(CellTemplate, dataItem); 
    } 

    private FrameworkElement GenerateContent(DataTemplate template, object dataItem) 
    { 
     var contentControl = new ContentControl(); 
     contentControl.ContentTemplate = template; 
     if (MemberPath != null) 
     { 
      Binding binding = new Binding(MemberPath); 
      binding.Source = dataItem; 
      contentControl.SetBinding(ContentControl.ContentProperty, binding); 
     } 
     else 
     { 
      contentControl.Content = dataItem; 
     } 
     return contentControl; 
    } 
} 
<DataGrid.Columns> 
    <cex:DataGridTemplateMemberColumn MemberPath="Emp1" /> 
    <cex:DataGridTemplateMemberColumn MemberPath="Emp2" /> 
    <cex:DataGridTemplateMemberColumn MemberPath="Emp3" /> 
    <cex:DataGridTemplateMemberColumn MemberPath="Emp4" /> 
    <cex:DataGridTemplateMemberColumn MemberPath="Emp5" /> 
</DataGrid.Columns> 
+0

啊! @ H.B。我的朋友!大聲笑!!我這次甚至只爲你提供代碼! ;)至於你的答案,是不是把ContentControl放在另一個ContentControl中,因爲這是DataGridTemplateColumn在內部已經生成的用於保存模板的內容?儘管如此,這總比沒有好。我仍然傾向於繼承DataGridTemplateColumn以在其中添加DataSource。 – MarqueIV 2011-05-02 06:55:47

+0

@MarquelIV:實際上,子類的正確類只是DataGridColumn,因爲你不需要所有這些模板屬性等,我一直在試圖做到這一點,**我相信這是最好的解決方案**,只是不完全那裏呢。不知道DataGridCells的可視化樹是什麼樣的,我個人只考慮將ContentPresenter用於ControlTemplates。 – 2011-05-02 07:05:25

+0

@ H.W。其實你想要所有其他模板相關的東西。你只需要一個額外的方式來設置源。其實,看看這裏... stackoverflow.com/questions/1104164/...。請特別注意LoadTemplateContent代碼中的「SetBinding」行。我們只需要爲DataGridTemplateColumn的子類添加一個'BindingPath'DP(我們甚至可以有單獨的顯示/編輯路徑),然後重寫該方法(我們不能因爲它是私有的而不是可重寫的)並初始化與路徑。時間去擊中反射器贊成! :) – MarqueIV 2011-05-02 07:27:16

1

你可以每列中使用一個ContentControl中提供所需的結合:

<DataGrid ItemsSource="{Binding MyItems}" AutoGenerateColumns="false"> 
    <DataGrid.Columns> 
     <DataGridTemplateColumn> 
      <DataGridTemplateColumn.CellTemplate> 
       <DataTemplate> 
        <ContentControl Content="{Binding Foo1}"/> 
       </DataTemplate> 
      </DataGridTemplateColumn.CellTemplate>      
     </DataGridTemplateColumn> 
     ... 
    </DataGrid.Columns> 
</DataGrid> 

一個ContentControl中是一個無外觀的控制呈現其內容,(默認值到它的DataContext)使用模板。因此應該使用隱式的DataTemplate。

+0

是@ColinE,@ H.B。只是說了同樣的事情。只是出於對你們的好奇心,爲什麼不使用ContentPresenter?我的意思是ContentControl並沒有在內部使用它,所以爲什麼不切掉中間人,因爲ContentPresenter本來就是爲了這個目的而在模板中使用的呢? – MarqueIV 2011-05-02 06:59:31

1

這是一個與@ H.B非常相似的東西的清理版本。建議。然而,即使這是我正在使用的那個,我的禮節仍然表示要爲其他人投票,但我仍然認爲他已被接受。

public class DataGridTemplateMemberColumn : DataGridTemplateColumn 
{ 

    public static readonly DependencyProperty MemberPathProperty = DependencyProperty.Register(
     "MemberPath", 
     typeof(PropertyPath), 
     typeof(DataGridTemplateMemberColumn), 
     new UIPropertyMetadata(null) 
    ); 

    public PropertyPath MemberPath 
    { 
     get { return (PropertyPath)GetValue(MemberPathProperty); } 
     set { SetValue(MemberPathProperty, value); } 
    } 

    [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] 
    protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem) 
    { 
     return LoadTemplateContent(CellEditingTemplate ?? CellTemplate, CellEditingTemplateSelector ?? CellTemplateSelector); 
    } 

    [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] 
    protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem) 
    { 
     return LoadTemplateContent(CellTemplate, CellTemplateSelector); 
    } 

    private FrameworkElement LoadTemplateContent(DataTemplate template, DataTemplateSelector selector) 
    { 
     ContentPresenter target = new ContentPresenter(); 

     target.ContentTemplate   = template; 
     target.ContentTemplateSelector = selector; 

     BindingOperations.SetBinding(
      target, 
      ContentPresenter.ContentProperty, 
      new Binding(){Path = MemberPath} 
     ); 

     return target; 

    } 

} 

...以下是您如何使用它...

<DataGrid AutoGenerateColumns="False"> 
    <DataGrid.Columns> 
     <foo:DataGridTemplateMemberColumn Header="Input" MemberPath="Input" /> 
     <foo:DataGridTemplateMemberColumn Header="Output" MemberPath="Output" /> 
    </DataGrid.Columns> 
</DataGrid> 
+0

我想如果你打算把屬性設置爲PropertyPath,你應該先創建綁定,然後設置Binding.Path屬性,否則你可能會使用一個由字符串的構造函數解析的字符串捆綁。幾乎你無緣無故地將它來回轉換一次。 – 2011-05-02 08:57:23

+0

@ H.B。好點子。更新!但是,這絕對比直接使用字符串作爲後臺設置對象更好。這樣,在XAML中或者在C#中都不需要轉換器。 – MarqueIV 2011-05-02 12:22:13

相關問題