我有标记扩展,允许我同时使用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 :(得分:3)
答案 1 :(得分: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>
绑定本身也是一种扩展。你有点想扩展一个扩展......
你离开它并使用正常的方法呢? :)
答案 2 :(得分: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); }
}
}