2014-12-02 45 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); } 
    } 
}