在WPF的依赖属性模式中,自动将GetValue() 的返回值转换为预期的返回类型

后端开发 2026-07-12

(Edit Note): 我没把我的问题与一个更简单的示例结合起来,以聚焦于DependencyProperty的重复和冗长代码问题,而 LabeledConainerLabeledTextBox 实际上对DependencyProperty问题来说只是次要的。

我正在使用WPF和 .NET的 C#,并在用依赖属性来构建一些基本控件。我想让这种模式尽量可重复、尽量简单,以便在复制并粘贴一个依赖属性、编辑需要的部分来创建新属性时,尽量少改动。

我有一个 Textbox,在 DockPanel 中附带了一个标签。LabeledContainer 是通用控件,里面包含了标签和控件在 DockPanel 中的所有逻辑,以及用于快速指定标签应出现在控件的左侧、右侧,还是在上方或下方的更多选项,尽管这一切都能工作,我认为与问题无关。

下面是一个简化的示例文件:

namespace MyApp.Controls
{
    using ContentType = TextBox;
    using MyType = LabeledTextBox;

    public partial class LabeledTextBox : LabeledContainer
    {
        public new ContentType Content { get { return base.Content as ContentType; } set { base.Content = value; } }

        public static readonly DependencyProperty SetTextBoxTextProperty = DependencyProperty.Register(
            nameof(TextBoxText), typeof(string), typeof(MyType));

        public string TextBoxText
        {
            get { return (string)GetValue(SetTextBoxTextProperty); }
            set { SetValue(SetTextBoxTextProperty, value); }
        }
    }

    public LabeledLabel() : base()
    {
        InitializeComponent();
    }
}

我的问题在这行的 (string) 强制转换上

get { return (string)GetValue(SetTextBoxTextProperty); }

并不是因为它会导致错误,而是因为如果我拷贝/粘贴来创建一个新的依赖属性,以暴露诸如 HorizontalAlignmentTextBox 之类的东西——这正是我现在经常在这里做的事情——我就得修改它。

有没有某种通用逻辑,可以让我在这里自动将返回类型强制转换为所需类型,因为它已经从属性的类型中得知?是否有某种语法可以引用当前作用域的返回类型?最好是在编译时就能解析,以避免对性能造成任何影响,但我也愿意看看其他解决方案。

更广泛地说,这就是我为基本依赖属性定下的模式(注意使用 MyType 将所有这些属性的注册集中到一行)。我想知道它是否在某些方面不是最优,是否有其他方法可以解决我的问题、进一步减少重复代码,我是不是就这么做错了?

我在业余时间有时会用Rust编程,但我想到该语言中的宏——这本来是解决这些重复的基本依赖属性的显而易见的解决方案——不过对C#是否存在这类工具我并不熟悉。

(Edit):将 'ContentType' using声明改为TextBox,以代替在整理示例代码时错误地使用的标签。

解决方案

与其复制粘贴,不如使用代码生成器:

DependencyPropertyGenerator

依赖属性、路由事件和弱事件源生成器,适用于WPF/UWP/WinUI/Uno/Avalonia/MAUI平台。

用法

```cs using DependencyPropertyGenerator; using System.Windows.Controls;

nullable enable

namespace H.Generators.IntegrationTests;

[DependencyProperty("IsSpinning", DefaultValue = true, Category = "Category", Description = "Description")] public partial class MyControl : UserControl { // Optional partial void OnIsSpinningChanged(bool oldValue, bool newValue) { } } ```

将生成:

```cs //HintName: MyControl.Properties.IsSpinning.generated.cs

nullable enable

namespace H.Generators.IntegrationTests { public partial class MyControl { ///

/// Description
/// Default value: true ///
public static readonly global::System.Windows.DependencyProperty IsSpinningProperty = global::System.Windows.DependencyProperty.Register( name: "IsSpinning", propertyType: typeof(bool), ownerType: typeof(global::H.Generators.IntegrationTests.MyControl), typeMetadata: new global::System.Windows.FrameworkPropertyMetadata( defaultValue: (bool)true, flags: global::System.Windows.FrameworkPropertyMetadataOptions.None, propertyChangedCallback: static (sender, args) => { ((global::H.Generators.IntegrationTests.MyControl)sender).OnIsSpinningChanged( (bool)args.OldValue, (bool)args.NewValue); }, coerceValueCallback: null, isAnimationProhibited: false), validateValueCallback: null);

    /// <summary>
    /// Description<br/>
    /// Default value: true
    /// </summary>
    [global::System.ComponentModel.Category("Category")]
    [global::System.ComponentModel.Description("Description")]
    public bool IsSpinning
    {
        get => (bool)GetValue(IsSpinningProperty);
        set => SetValue(IsSpinningProperty, value);
    }

    partial void OnIsSpinningChanged();
    partial void OnIsSpinningChanged(bool newValue);
    partial void OnIsSpinningChanged(bool oldValue, bool newValue);
}

} ```

metalama的 [DependencyProperty]

WPF依赖属性

WPF的依赖属性是一个你可以从WPF标记语言中设置并通过一个Binding元素绑定到另一个对象(例如视图模型)的属性。与C#属性不同,你必须使用 DependencyProperty.Register 编程注册依赖属性。要把一个依赖属性暴露为C#属性,通常需要编写样板代码,如下例所示:

``` class MyClass { public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.Register( nameof(IsEnabled), typeof(bool), typeof(MyClass));

public bool IsEnabled
{
    get { return (bool)GetValue(IsEnabledProperty); }
    set { SetValue(IsEnabledProperty, value); }
}

} ```

与其编写这段样板代码,在C#自动属性上添加 [DependencyProperty] 方面,将其转换为WPF依赖属性:

class MyClass { [DependencyProperty] public bool IsEnabled { get; set; } }

[DependencyProperty] 方面提供:

  • 零样板代码。
  • 与Metalama.Patterns.Contracts集成,使用诸如 [NotNull] 或 [Url] 等方面来验证依赖属性(详情请参阅Metalama.Patterns.Contracts)。
  • 支持自定义的前置和后置赋值回调。
  • 根据属性访问器的可访问性检测可变或只读的依赖属性。
  • 处理默认值。
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章