【问题标题】:WPF: How to implement a generic property available at all GUI controls?WPF:如何实现所有 GUI 控件都可用的通用属性?
【发布时间】:2011-09-14 16:10:25
【问题描述】:

是否可以实现可以在每个 GUI 控件(即 FrameworkElement)中指定的属性? 我想象的是像Tag这样的属性,但是,我宁愿不使用Tag,而是我自己的属性。

编辑: 由于 Felice 和 CodeNaked 的回复,我再次尝试使用附加依赖属性进行第一次实验。 尽管仔细检查了所有内容,但我的附加属性 (SetAdsName()) 的设置器永远不会被调用,尽管附加属性的注册成功且没有错误。 没有错误消息 - 与 WPF 一样。

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:ads="clr-namespace:WAL.UI.Lib.Ads.Binding;assembly=WAL.UI.Lib"
        Title="Demo Page" Height="350" Width="525"
>
    <Grid>
        <Label ads:AdsNameRegistry.AdsName="SpecialLabelName" Content="Some Button Caption" />
    </Grid>
</Window>


public class AdsNameRegistry : DependencyObject
{
    public static readonly DependencyProperty AdsNameProperty = DependencyProperty.RegisterAttached( "AdsName", typeof(String), typeof(AdsNameRegistry), new FrameworkPropertyMetadata( "", FrameworkPropertyMetadataOptions.AffectsRender ) );

    public static void SetAdsName( UIElement element, String value )
    {
        try
        {
            MessageBox.Show( "SetAdsName(value=" + value + ")" );
            element.SetValue( AdsNameProperty, value );
        }
        catch (Exception ex)
        {
            Debug.Assert( false, "Exception in SetAdsName(): " + ex.Message + "\n\n" + ex );
            throw;
        }
    }

    public static String GetAdsName( UIElement element )
    {
        try
        {
            return (String) element.GetValue( AdsNameProperty );
        }
        catch (Exception ex)
        {
            Debug.Assert( false, "Exception in GetAdsName(): " + ex.Message + "\n\n" + ex );
            throw;
        }
    }
}

}

我的第一个猜测是,永远不会调用 SetAdsName(),因为 XAML 编码中没有作为父元素的元素。 但这似乎是一个错误的结论。

问候, 七

【问题讨论】:

  • 感谢所有响应者。你让我开心:-)

标签: wpf data-binding c#-4.0 properties


【解决方案1】:

这可以通过使用Attached Property 来完成。 您可以通过以下示例中的注册方式在附加属性时收到通知:

public static readonly DependencyProperty XYZProperty =
            DependencyProperty.RegisterAttached(
                "XYZ",
                typeof(object),
                typeof(TargetClass),
                new PropertyMetadata(OnXYZChanged)
                );

并实现 OnXYZChanged:

private static void OnXYZChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ...     
}

d 将是您要附加的对象,在e.NewValue 中您将找到该属性的新值。

【讨论】:

  • 嗨 Felice,我也尝试过。但是,似乎在提供此附加属性的同一个 XAML 文件中必须存在一个父元素,对吗?例如。必须有一个周围的网格才能说
  • 不,没必要,附加属性是自包含的,看链接搜索一些示例。你只需要写YourExtClass.Yourproperty=""
  • @Seven 对于你所说的例子,为了让事情正常工作你需要你所说的,但你实际上可以指定 Grid.Row 到处都没有得到任何有趣的东西。属性的效果是由你定义属性时定义的函数来驱动的。
  • 抱歉,我遗漏了一些东西,因为永远不会调用方法 SetAdsName()。虽然我在运行时没有编译器错误或失败。
  • @Seven 没问题,你必须在属性元数据中提供通知功能。我会找一个例子并在回复中发布。
【解决方案2】:

您可以使用attached properties 添加您想要的任何自定义属性。示例包括 Canvas.Left 属性。

【讨论】:

  • 好的,谢谢。如果你们都确定这一点,我会再试一次,如果我成功了,我会报告。感谢你们两位的迅速回复。
【解决方案3】:

首先,是的,您需要一个附加属性。但是您没有正确实现该模式。

永远不要在您的 GetXxxx() 或 SetXxxx() 方法中添加任何逻辑。 GetXxxx 和 SetXxxx 应分别包含对 GetValue 和 SetValue 的调用:

public static String GetAdsName(UIElement element)
{
    return (string) element.GetValue(AdsNameProperty);
}
public static void SetAdsName(UIElement element, String value)
{
    element.SetValue(AdsNameProperty, value);
}

为什么它们不应该包含任何其他逻辑?因为在加载 XAML 时,不会调用您的 SetXxxx 方法。 WPF 团队有一个选择:他们可以让 XAML 加载器使用反射来查找您的 SetXxxx 方法,并在运行时动态调用它,以便您的方法可以调用 SetValue;或者他们可以删除中间人,直接调用 SetValue。他们选择了后者,因为它更简单、更高效。

如果您希望在设置属性时产生副作用,则需要在构造 PropertyMetadata 时指定 propertyChangedCallback。在你的情况下:

public static readonly DependencyProperty AdsNameProperty =
    DependencyProperty.RegisterAttached("AdsName",
        typeof(String), typeof(AdsNameRegistry),
        new FrameworkPropertyMetadata("",
            FrameworkPropertyMetadataOptions.AffectsRender, OnAdsNameChanged));

private static void OnAdsNameChanged(DependencyObject sender,
    DependencyPropertyChangedEventArgs e)
{
    // do something with the new value
}

【讨论】:

  • 谢谢。这也是我几秒钟前发现的。这是缺失的部分。但是,我发现实现永远不会被调用的方法很奇怪,但是在丢失时会导致编译器错误......
  • 当您想以编程方式设置属性时,它们很有用。此外,它们告诉编译器该属性适用于哪些类型的DependencyObjects。如果您的 SetXxxxListBox 作为其第一个参数,并且您的 XAML 尝试将其设置为 Grid,则 Visual Studio 会给您一个错误。所以实际上使用了方法签名,至少用于验证。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-02
相关资源
最近更新 更多