实现自定义“路径”用户控件内的属性

发布于 2025-01-05 22:38:36 字数 3179 浏览 2 评论 0原文

您可能注意到,ComboBox、ListBox 和此类“选择器”控件使用一些带有后缀“path”的属性,这些属性允许将具有不同属性名称的对象绑定到其数据模板,而无需更改模板本身。

当然,我不知道这项技术的确切名称,但基本上,我想要得到的是这样的:

<my:ACustomControl HeadingPath="SomePropertyOfModelA" ContentPath="OtherPropertyFromModelA" ItemsSource="{Binding ModelA}"... />

...并且在不更改控件或其数据模板中的任何内容的情况下能够做到这一点,

<my:ACustomControl HeadingPath="DifferentPropertyFromModelB" ContentPath="NewPropertyFromB" ItemsSource="{Binding ModelB}" ... />

我不这样 做真的很喜欢使用字符串连接动态构建数据模板的方法......

有什么想法吗?

编辑:

好的,

现在可以了,但是我的方法有一些黑点。由于我使用 ValueConverters 来获取属性的实际值,并且该值转换器在 Xaml 中实例化为静态资源,因此我发现显示用户控件时涉及一些优先级顺序,因为 ItemsControl 在 ValueConverters 之前接收项目数据获取属性的名称。结果什么都没有被转换。

我的解决方法是在设置 HeadingPath 的值后立即从后面的代码强制设置 ValueConverter 的 PathProperty 值。这就是静态资源具有 x:Name 和 x:Key 的原因。

这是我到目前为止所做的。 (请将此视为沙箱,很抱歉没有考虑命名约定等)

1.- 创建一个自定义控件,其中包含 ItemsControl,如下所示:

<ItemsControl x:Name="InternalItemsControl" ItemsSource="{Binding Items, Mode=TwoWay}">

其中 Items 是后面代码中的依赖属性

2.- 设置CodeBehind 作为类构造函数中用户控件 LayoutRoot Grid 的 DataContext

Public Sub New()
    InitializeComponent()
    LayoutRoot.DataContext = Me 
End Sub

3.- 在代码隐藏中定义名为 Items 的依赖属性

4.- 定义字符串类型的名为 HeadingPath 的依赖属性来保存业务对象属性名称

5.- 创建IVaueConverter 实现,它也继承自 DependencyObject 并实现 INotifyPropertyChanged,如下所示

Imports System.Windows.Data
Imports System.ComponentModel

Public Class PathPropertiesValueConverter
    Inherits DependencyObject
    Implements IValueConverter
    Implements INotifyPropertyChanged
    ...

6.- 在 IValueConverter 实现中定义一个名为 PathProperty 的字符串类型的依赖属性,以保存属性的名称,该属性的值将在转换数据时用作返回值。

7.- 在 Convert 方法...:

    Public Function Convert(value As Object, targetType As System.Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert
        If value IsNot Nothing AndAlso Not String.IsNullOrEmpty(PathProperty) Then
            Return value.GetType.GetProperty(PathProperty).GetValue(value, Nothing)
        Else
            Return Nothing
        End If
    End Function

8.- 将 IValueConverter 实现实例化为用户控件中的静态资源

<local:PathPropertiesValueConverter x:Key="PathConverter" x:Name="HeadingPathConverter" 
    PathProperty="{Binding ElementName=LayoutRoot, Path=DataContext.HeadingPath, Mode=TwoWay}" />

9.- 此后,您可以使用属性名称作为 HeadingPath 属性中的字符串,从任何 silverlight 页面将 ChildItems 绑定到 ItemsControl 上:

    <StackPanel Orientation="Vertical" >
        <local:MyCustomControl HeadingPath="PropiedadA" Items="{Binding Listado, Mode=TwoWay}" />
        <local:MyCustomControl HeadingPath="PropiedadB" Items="{Binding Listado2, Mode=TwoWay}" />
    </StackPanel>

10.-作为 Listado 和 Listado 2 ObservableCollections,加载了数据和此级别设置的所有绑定内容...这是结果:

http://dl.dropbox.com/u/6450273/PathProperties.JPG

As you might noticed, ComboBox, ListBox and that kind of "selectors" controls uses some properties with the suffix "path" which allows the binding of objects with different property names to its datatemplate without changing the template itself.

Certainly I don't know exactly the name of this technique, but Basically, what I'm trying to get is something like this:

<my:ACustomControl HeadingPath="SomePropertyOfModelA" ContentPath="OtherPropertyFromModelA" ItemsSource="{Binding ModelA}"... />

... and without changing anything in the control or its data template being able to do this

<my:ACustomControl HeadingPath="DifferentPropertyFromModelB" ContentPath="NewPropertyFromB" ItemsSource="{Binding ModelB}" ... />

I' dont really like the approach of building dynamically the datatemplate using strings concatenation...

Any ideas?

EDIT:

OK,

It's working now, but there is some dark spot on my approach. Since I'm using ValueConverters to get the actual value of the property, and this value converter is instantiated in Xaml as Static Resource, I've found that there's some priority order involved when the usercontrol is being shown since ItemsControl Receives Items data before ValueConverters gets the name of the property. As result nothing gets converted.

My workaround was to force setting ValueConverter's PathProperty Value from code behind right after setting HeadingPath's value. That's why the static resource has x:Name and x:Key.

Here is what I've done so far. (Please consider this as a sandbox, I'm sorry for not taking care of naming conventions and so on)

1.- Create a Custom Control With an ItemsControl inside like this:

<ItemsControl x:Name="InternalItemsControl" ItemsSource="{Binding Items, Mode=TwoWay}">

Where Items is a dependency property in code behind

2.- Set CodeBehind as DataContext of usercontrol LayoutRoot Grid at the class constructor

Public Sub New()
    InitializeComponent()
    LayoutRoot.DataContext = Me 
End Sub

3.- Define a dependency property named Items in code Behind

4.- Define a dependency property named HeadingPath of type string to hold business object property name

5.- Create a IVaueConverter Implementation which also inherits from DependencyObject and implements INotifyPropertyChanged like this

Imports System.Windows.Data
Imports System.ComponentModel

Public Class PathPropertiesValueConverter
    Inherits DependencyObject
    Implements IValueConverter
    Implements INotifyPropertyChanged
    ...

6.- Define a dependency property inside IValueConverter implementation named PathProperty of type string to hold the name of the property which value will be used as return value when converting data.

7.- At Convert method...:

    Public Function Convert(value As Object, targetType As System.Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert
        If value IsNot Nothing AndAlso Not String.IsNullOrEmpty(PathProperty) Then
            Return value.GetType.GetProperty(PathProperty).GetValue(value, Nothing)
        Else
            Return Nothing
        End If
    End Function

8.- Instantiate the IValueConverter implementation as static resource in usercontrol

<local:PathPropertiesValueConverter x:Key="PathConverter" x:Name="HeadingPathConverter" 
    PathProperty="{Binding ElementName=LayoutRoot, Path=DataContext.HeadingPath, Mode=TwoWay}" />

9.- After this you can use Property Name as string in HeadingPath property to bind ChildItems on ItemsControl from any silverlight Page:

    <StackPanel Orientation="Vertical" >
        <local:MyCustomControl HeadingPath="PropiedadA" Items="{Binding Listado, Mode=TwoWay}" />
        <local:MyCustomControl HeadingPath="PropiedadB" Items="{Binding Listado2, Mode=TwoWay}" />
    </StackPanel>

10.- Being Listado and Listado 2 ObservableCollections, loaded with data and all the binding stuff at this level set... this is the result:

http://dl.dropbox.com/u/6450273/PathProperties.JPG

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

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

发布评论

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

评论(1

瞳孔里扚悲伤 2025-01-12 22:38:36

我认为最好的选择是自己创建 DataTemplate。在幕后,DisplayMemberPath 基本上是创建一个 DataTemplate,并将 TextBlock 绑定到该属性。 SelectedValuePath 只是一个带有 PropertyChangedCallback 的 DependencyProperty,用于设置所选值绑定路径。最终,您创建的任何自定义属性都只是您在其他地方定义的某些功能的快捷方式。

I think your best option will be to create the DataTemplate yourself. Behind the scenes DisplayMemberPath is basically creating a DataTemplate with a TextBlock bound to the property. SelectedValuePath is just a DependencyProperty with a PropertyChangedCallback that sets the selected value binding path. Ultimately any custom property you create is just going to be a shortcut to some functionality you've defined elsewhere.

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