开发者

WPF: How to implement a generic property available at all GUI controls?

is it possible to implement a property which could be specified at every GUI control (i.e. FrameworkElement)? What I imagine is a property like Tag, however, I would prefere not开发者_如何学Python to use Tag, but my own property.

EDIT: Due to the replys of Felice and CodeNaked I gave my first experiments with Attached Dependency Properties a second try. Although double-checking everything, the setter of my attached property (SetAdsName()) never will be called, although the registering of the Attached Property succeeds with no errors. There are no error messaged -- as always with 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;
        }
    }
}

}

My fist guess was, that SetAdsName() will never be called because there is no element as a parent within the XAML coding. But this seems to be a false conclusion.

Regards, Seven


First of all, yes, you want an attached property. But you're not implementing the pattern correctly.

Never put any logic in your GetXxxx() or SetXxxx() methods. GetXxxx and SetXxxx should contain only calls to GetValue and SetValue, respectively:

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

Why shouldn't they contain any other logic? Because when the XAML is loaded, your SetXxxx method won't be called. The WPF team had a choice: they could make the XAML loader use Reflection to find your SetXxxx method, and invoke it dynamically at runtime, so that your method could call SetValue; or they could remove the middleman and just call SetValue directly. They opted for the latter, because it's much simpler and much more efficient.

If you want to have side effects when your property is set, you need to specify a propertyChangedCallback when you construct your PropertyMetadata. In your case:

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
}


This can be done by using an Attached Property. You can be notified when the property is attached by registering as in the following example:

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

and implementing OnXYZChanged:

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

d will be the object you are attaching to and in e.NewValue you will find the new value of the property.


You can use attached properties to add any custom property you want. Examples include the Canvas.Left property.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜