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。

4

3 回答 3

4

我最近也遇到了这个。结果,不知何故,我在 WPF 设计器中禁用了项目代码。 启用项目代码使标记扩展按需要工作。

在此处输入图像描述

取自docs.microsoft.com

于 2018-08-01T12:08:25.350 回答
0

So I've decided to use following approach:

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

So, wrapping template into ContentPresenter allows to use binding I wish and reuse template.

Following this, it is possible to go further, I have working concept, but decided not to use it (at least yet). Idea is to automatically generate columns, by getting public properties of ItemsSource, which might be further improved by creating attribute, which would define header description and width:

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); }
    }
}
于 2014-12-03T10:04:28.480 回答
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>

绑定本身也是一种扩展。您有点试图扩展扩展名...

您如何不使用它并使用正常的方法呢?:)

于 2014-12-02T11:07:14.517 回答