2014-12-02 37 views
0

我有標記擴展允許我同時使用GridView中的綁定和單元格模板。它在運行時正常工作,但它在設計時不起作用,想知道是否有任何事情可以解決。我已經測試過返回簡單的字符串,而不是DataTemplate,只是爲了確保定製標記擴展在設計時能夠正常工作 - 並且工作正常,所以它應該與某個事實相關,即返回DataTemplate標記擴展在設計時不起作用

[MarkupExtensionReturnType(typeof(DataTemplate))] 
public class TemplateBuilderExtension : MarkupExtension 
{ 
    public string Path { get; set; } 

    public TemplateBuilderExtension() { } 
    public TemplateBuilderExtension(string path) 
    { 
     Path = path; 
    } 

    // Here be dirty hack. 
    internal static string TagPath { get; private set; } 

    public override object ProvideValue(IServiceProvider serviceProvider) 
    { 
     TagPath = Path; 
     var resourceExt = new StaticResourceExtension("GridViewTextCell"); 

     // This line causes the evaluation of the Tag as the resource is loaded.   
     var baseTemplate = (DataTemplate)resourceExt.ProvideValue(serviceProvider); 

     return baseTemplate; 
    } 
} 

[MarkupExtensionReturnType(typeof(BindingExpression))] 
public class TemplateBuilderTagExtension : MarkupExtension 
{ 
    public TemplateBuilderTagExtension() 
    {   
    } 

    public override object ProvideValue(IServiceProvider serviceProvider) 
    { 
     return new Binding(TemplateBuilderExtension.TagPath); 
    } 
} 

<Window.Resources> 
    <DataTemplate x:Shared="false" x:Key="GridViewTextCell"> 
     <Border BorderBrush="Blue" BorderThickness="1"> 
      <TextBlock Text="{markupExtensions:TemplateBuilderTag}"></TextBlock> 
     </Border> 
    </DataTemplate> 
</Window.Resources> 
<Grid>  
    <ListView SelectedIndex="5">   
     <ListView.View> 
      <GridView> 
       <GridViewColumn Header="Id" CellTemplate="{markupExtensions:TemplateBuilder Id}" Width="300"/> 
      </GridView> 
     </ListView.View> 
    </ListView>   
</Grid> 

更新:我已經簡化代碼,以儘可能短,在現實情況下有多個GridView的申請通過,每個網格包含多個列,並且這些列應該重新使用相同的模板,也無法使用DataGrid因性能問題。

+0

我沒有得到這樣的想法呢。你是什​​麼意思「使用綁定和單元格模板」?你想在你的細胞模板中使用視覺父母的datacontext嗎?然後,您可以採用更簡單的方法 – deafjeff 2014-12-02 10:19:17

+0

事情是,如果您希望使用單元格模板,則需要爲每列定義它,因爲您無法將綁定路徑傳遞給單元格模板。有問題的代碼解決了這個問題。 – Giedrius 2014-12-02 10:59:30

+0

這不是DataTemplate Selector的候選人嗎? – deafjeff 2014-12-02 13:10:53

回答

0

您的擴展沒有多大意義。這一切都可以這樣寫:

<Window.Resources> 
    <sys:String x:Key="path">thatPath<sys:String/> 

    <DataTemplate x:Shared="false" x:Key="GridViewTextCell"> 
     <Border BorderBrush="Blue" BorderThickness="1"> 
      <TextBlock Text="{Binding Path={StaticResource path}}"></TextBlock> 
     </Border> 
    </DataTemplate> 
</Window.Resources> 
<Grid>  
    <ListView SelectedIndex="5">   
     <ListView.View> 
      <GridView> 
       <GridViewColumn Header="Id" CellTemplate="{StaticResource GridViewTextCell}" Width="300"/> 
      </GridView> 
     </ListView.View> 
    </ListView>   
</Grid> 

綁定本身也是一個擴展。你有點試圖擴展擴展...

你如何離開它,並使用正常的方法呢? :)

+0

好的,如果單元格模板足夠複雜,那麼您不希望通過應用程序中的所有GridViews爲GridView中的每個列重複此操作?假設你需要在網格中有10列,並且所有應該使用相同的單元格模板,但不同的綁定(綁定是簡單的,相同的視圖模型,不同的屬性)? – Giedrius 2014-12-02 11:18:32

+0

看看我的編輯 – 2014-12-02 11:34:25

+0

不幸的是,它不能這樣工作,只要你設置了'DisplayMemberBinding','CellTemplate'屬性值就被忽略了。 – Giedrius 2014-12-02 11:46:29

0

所以我決定使用以下方法:

<GridViewColumn Header="TestColumn"> 
    <GridViewColumn.CellTemplate> 
     <DataTemplate> 
      <ContentPresenter Content="{Binding TestProperty}" ContentTemplate="{StaticResource GridViewTextCell}" /> 
     </DataTemplate> 
    </GridViewColumn.CellTemplate> 
</GridViewColumn> 

所以,包裝模板到ContentPresenter允許使用綁定我想和重用模板。

接下來,有可能走得更遠,我有工作理念,但決定不使用它(至少尚未)。想法是自動生成列,通過的ItemsSource的獲取公共屬性,這可能是由創建的屬性有待進一步提高,這將定義頭描述和寬度:

public class ExtendedListView : ListView 
{ 
    public static readonly DependencyProperty AutoColumnsProperty = 
       DependencyProperty.Register("AutoColumns", typeof(bool), typeof(ExtendedListView), new FrameworkPropertyMetadata(true, OnAutoColumnsPropertyChanged)); 

    protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue) 
    { 
     base.OnItemsSourceChanged(oldValue, newValue); 

     OnAutoColumnsPropertyChanged(this, new DependencyPropertyChangedEventArgs(AutoColumnsProperty, false, true)); 
    } 

    private static void OnAutoColumnsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     var newValue = (bool)e.NewValue; 

     var dataGrid = (ExtendedListView)d; 

     if (newValue) 
     { 
      dataGrid.AddAutoColumns(); 
     } 
     else 
     { 
      dataGrid.DeleteAutoColumns(); 
     }   
    } 

    Type GetBaseTypeOfEnumerable(IEnumerable enumerable) 
    { 
     if (enumerable == null) 
     { 
      return null;     
     } 

     var genericEnumerableInterface = enumerable 
      .GetType() 
      .GetInterfaces().FirstOrDefault(i => i.GetGenericTypeDefinition() == typeof(IEnumerable<>)); 

     if (genericEnumerableInterface == null) 
     { 
      return null; 
     } 

     var elementType = genericEnumerableInterface.GetGenericArguments()[0]; 

     if (!elementType.IsGenericType) 
     { 
      return elementType; 
     } 

     return elementType.GetGenericTypeDefinition() == typeof(Nullable<>) 
      ? elementType.GetGenericArguments()[0] 
      : elementType; 
    } 

    private readonly HashSet<GridViewColumn> autoGeneratedColumns = new HashSet<GridViewColumn>(); 

    private void AddAutoColumns() 
    { 
     var gridView = View as GridView; 

     if (gridView == null) 
     { 
      throw new Exception("Not a grid view"); 
     } 

     var itemType = GetBaseTypeOfEnumerable(ItemsSource); 

     if (itemType == null) 
     { 
      throw new Exception("Could not resolve item type"); 
     } 

     var properties = itemType.GetProperties(); 
     foreach (var property in properties) 
     { 
      var gridViewColumn = new GridViewColumn 
      { 
       CellTemplate = CreateTemplate(property.Name), 
       Header = property.Name, 
       Width = 100 
      }; 

      gridView.Columns.Add(gridViewColumn); 

      autoGeneratedColumns.Add(gridViewColumn); 
     }   
    } 

    private DataTemplate CreateTemplate(string path) 
    { 
     string xamlTemplate = string.Format("<DataTemplate><ContentPresenter Content=\"{{Binding {0}}}\" ContentTemplate=\"{{StaticResource GridViewTextCell}}\" /></DataTemplate>", path); 

     var context = new ParserContext 
     { 
      XamlTypeMapper = new XamlTypeMapper(new string[0]) 
     }; 

     context.XmlnsDictionary.Add("", "http://schemas.microsoft.com/winfx/2006/xaml/presentation"); 
     context.XmlnsDictionary.Add("x", "http://schemas.microsoft.com/winfx/2006/xaml"); 

     var template = (DataTemplate)XamlReader.Parse(xamlTemplate, context); 

     return template; 
    } 

    private void DeleteAutoColumns() 
    { 
     var gridView = View as GridView; 

     if (gridView == null) 
     { 
      throw new Exception("Not a grid view"); 
     } 

     for (int columnIndex = gridView.Columns.Count - 1; columnIndex >= 0; --columnIndex) 
     { 
      if (autoGeneratedColumns.Contains(gridView.Columns[columnIndex])) 
      { 
       gridView.Columns.RemoveAt(columnIndex); 
      } 
     }  
    } 

    public bool AutoColumns 
    { 
     get { return (bool)GetValue(AutoColumnsProperty); } 
     set { SetValue(AutoColumnsProperty, value); } 
    } 
}