标记扩展在设计时不起作用

时间:2014-12-02 09:41:02

标签: c# wpf xaml design-time markup-extensions

我有标记扩展,允许我同时使用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的问题。

3 个答案:

答案 0 :(得分:3)

我也是最近才遇到这个问题的。 原来,我以某种方式在WPF设计器中禁用了项目代码。 启用项目代码使标记扩展名能够按需工作。

enter image description here

摘自docs.microsoft.com

答案 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); }
    }
}