是否可以在没有固定数据绑定的情况下为 GridViewColumn 创建模板?

发布于 2024-12-01 02:45:09 字数 3705 浏览 0 评论 0原文

这可能吗?

我有一个 ListView,我想为列创建一个模板,以便我标记为“cellTextBox”的每一列都显示一个文本框(并且还在 LostFocus 事件上调用 TextBox_LostFocus())。我真的很想使用单个模板,而不是为每个列定义 DockPanel 和 TextBox。但是,每一列都将绑定到数据源中的不同列。

换句话说,我想要 GridViewColumns 的“千篇一律”,它允许我为每个列指定不同的绑定路径。

我尝试过类似的事情(除其他外),但它不起作用。

有什么想法吗?

<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>

编辑 - 我想我应该分享 @HB 解决方案的 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 替换为应用程序的根命名空间是(这可以在您的应用程序的属性中找到)。

再次感谢@HB!

Is this possible?

I have a ListView and I want to create a template for the columns, so that each column that I mark as a 'cellTextBox' displays with a textbox in it (and also calls the TextBox_LostFocus() on the LostFocus event). I'd really like to use a single template rather than defining a DockPanel and TextBox for every single column. However, each column is going to be bound to a different column in the data source.

In other words, I'd like a "cookie cutter" for GridViewColumns that allows me to specify a different binding path for each one.

I tried something like this (among other things), but it doesn't work.

Any ideas?

<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>

EDIT - I thought I would share the Visual Basic translation of @H.B.'s solution, which worked for me. Perhaps this will help out someone else.

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

Obviously, the XAML is the same, regardless.

Do not forget to add the following namespace reference to your root-level tag:

xmlns:me="clr-namespace:WpfApplication1", replacing WpfApplication with whatever your application's root namespace is (this can be found in your application's properties).

Thanks again @H.B.!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

微凉徒眸意 2024-12-08 02:45:09

我现在能想到的内联的唯一方法是使用 markup-extensions 当您尝试将多个值传递给一个属性时。

不过,将变量数据放入模板似乎并不简单,并且以下使用静态属性的方法相当麻烦,也许您可​​以想到更好的方法:

<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 中从头开始动态创建模板,这样您就不会遇到此类问题。

The only method i can think of right now to inline this is using markup-extensions as you try to pass more than one value to one property.

Getting variable data into the template seems to be non-trivial though and the following approach using a static property is quite the hack, maybe you can think of something better:

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

I tried to use a custom service provider when getting the resource but unfortunately it did not arrive in ProvideValue of the tag, that would have been a better way to get the path accross.

You could of course create the template dynamically from scratch in the TemplateBuilderExtension, that way you will not run into such issues.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文