能否创建一个 GridViewColumn 的模板,而不需要固定的数据绑定?

3
这是否有可能实现呢?
我有一个ListView,想为每一列创建一个模板,标记为“cellTextBox”的每个列都将显示一个文本框(并在失去焦点事件时调用TextBox_LostFocus())。我真的很想使用一个单一模板而不是为每一列都定义DockPanel和TextBox。但是,每一列将绑定到数据源中的不同列。
换句话说,我希望有一个“替身”GridViewColumn的模板,可以让我为每个列指定不同的绑定路径。
我尝试过类似于以下内容的东西(除其他事项外),但它不起作用。
您有什么想法吗?
<Page.Resources>
            <DataTemplate x:Key="cellTextBox">
                <DockPanel>
                <TextBox
                        LostFocus="TextBox_LostFocus"
                        Width="100"
                        Text="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=DisplayMemberBinding}"
                />
                </DockPanel>
            </DataTemplate>
</Page.Resources>
<StackPanel>

        <ListView ScrollViewer.HorizontalScrollBarVisibility="Disabled" 
                  HorizontalAlignment="Stretch" 
                  ItemsSource="{Binding Tables[0]}" 
                  Width="Auto"
                  x:Name="Service_Tasks">

            <ListView.View>
                <GridView>
                    <GridViewColumn Width="120" CellTemplate="{StaticResource cellTextBox}" DisplayMemberBinding={Binding Path=Field1} Header="Field1" />
                    <GridViewColumn Width="120" CellTemplate="{StaticResource cellTextBox}" DisplayMemberBinding={Binding Path=Field2} Header="Field2" />
                    <GridViewColumn Width="120" CellTemplate="{StaticResource cellTextBox}" DisplayMemberBinding={Binding Path=Field3} Header="Field3" />
                    <GridViewColumn Width="120" CellTemplate="{StaticResource cellTextBox}" DisplayMemberBinding={Binding Path=FieldN} Header="FieldN" />
                    <!-- ... other columns ... -->
                </GridView>
            </ListView.View>

        </ListView>
</StackPanel>

编辑- 我认为我应该分享一下@H.B.解决方案的Visual Basic翻译,这对我很有用。也许这会帮助其他人。

Imports System.Windows.Markup

Public Class TemplateBuilderExtension : Inherits StaticExtension
    Dim _path As String
    Shared _tagpath As String

    'Private TagPath As String

    Shared Property TagPath As String
        Get
            TagPath = _tagpath
        End Get
        Private Set(ByVal value As String)
            _tagpath = value
        End Set
    End Property

    Property Path As String
        Get
            Path = _path
        End Get
        Set(ByVal value As String)
            _path = value
        End Set
    End Property

    Public Overrides Function ProvideValue(ByVal serviceProvider As System.IServiceProvider) As Object
        TagPath = Path
        Dim resourceExt = New StaticResourceExtension("TemplateBuilder_BaseTemplate")
        'Dim baseTemplate As DataTemplate = DirectCast(resourceExt.ProvideValue(serviceProvider), DataTemplate)
        ProvideValue = DirectCast(resourceExt.ProvideValue(serviceProvider), DataTemplate)
    End Function

    Public Sub New()
        MyBase.new()
    End Sub

    Public Sub New(ByVal Path As String)
        Me.Path = Path
    End Sub
End Class

Public Class TemplateBuilderTagExtension : Inherits MarkupExtension
    Public Sub New()
        MyBase.New()
    End Sub
    Public Overrides Function ProvideValue(ByVal serviceProvider As System.IServiceProvider) As Object
        ProvideValue = New Binding(TemplateBuilderExtension.TagPath)
    End Function
End Class

显然,无论如何,XAML都是相同的。

不要忘记在根级标签中添加以下命名空间引用:

xmlns:me="clr-namespace:WpfApplication1",将WpfApplication替换为您的应用程序的根命名空间(可以在应用程序属性中找到)。

再次感谢 @H.B.!

1个回答

4
我目前能想到的唯一内联方法是使用标记扩展,当您尝试将多个值传递给一个属性时需要使用它。
然而,在模板中获取变量数据似乎并不容易,以下使用静态属性的方法相当繁琐,也许您可以想出更好的方法:
<ListView ItemsSource="{Binding Data}">
    <ListView.Resources>
        <!-- x:Shared="False" because otherwise the Tag extension is only evaluated the first time the resource is accessed -->
        <DataTemplate x:Shared="false" x:Key="TemplateBuilder_BaseTemplate">
            <TextBox Text="{me:TemplateBuilderTag}" Width="100" LostFocus="TextBox_LostFocus" />
        </DataTemplate>
    </ListView.Resources>
    <ListView.View>
        <GridView>
            <GridViewColumn CellTemplate="{me:TemplateBuilder Name}"  />
            <GridViewColumn CellTemplate="{me:TemplateBuilder Occupation}" />
        </GridView>
    </ListView.View>
</ListView>

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("TemplateBuilder_BaseTemplate");
        // This line causes the evaluation of the Tag as the resource is loaded.
        var baseTemplate = resourceExt.ProvideValue(serviceProvider) as DataTemplate;
        return baseTemplate;
    }
}

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

我尝试在获取资源时使用自定义服务提供程序,但不幸的是它没有到达标签的 ProvideValue,那将是一个更好的方式来获得路径。

你当然可以从头开始动态创建模板,在 TemplateBuilderExtension 中完成,这样你就不会遇到这样的问题。


+1 这看起来非常有前途。由于我此时没有我的代码,所以今晚我会尝试它。我有点惊讶的是,XAML 提供了控件模板的方法,但不包括像 GridViewColumn 这样的非可视元素,尽管这些元素似乎经常被使用。我是否遗漏了一些明显的东西?在这些类型的列表中,我应该使用除 ListView 之外的其他东西吗(请记住,此项目是在 3.5 中)? - transistor1
1
@transistor1:嗯,ListView 是一个视图,可能不适合编辑,你可以使用 WPF 工具包中的 DataGrid。 - H.B.
快速问题:public string Path { get; set; } 这一行的意思是 Path 属性不会被内部存储(即 get 和 set 没有被实现)吗?还是这是默认属性实现?我更习惯 VB,虽然我做过一些基本的 C# 编程,所以我正在翻译它。 - transistor1
...我相当确定这是一个默认的属性实现。 - transistor1
1
@transistor1:这被称为自动实现属性,它只是一个私有后备字段和公共属性的简写形式,该属性公开了它。当然,不能直接访问该字段... - H.B.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接