kemarofangs
Topic Author
Posts: 32
Joined: 13 Jun 2014, 21:30

How To Implement Attached Property?

04 Aug 2016, 07:02

Another question, let's say I've made a control and want to customize sub-components of that control using xaml. Would I use attached properties to do this? Below I'm trying to assign a border's corner radius in this way. Where have I gone wrong and what needs to change for this to work?

Assigning the dependency property to readonly doesn't seem to be allowed (compile time error).

I could do this differently if everything wasn't required to be a usercontrol.
Maybe change this
[Noesis.UserControlSource("Assets/Darren/Code/QuickSwap.xaml")]
to something like this
[Noesis.Source(Noesis.Border,"Assets/Darren/Code/QuickSwap.xaml")]
Just a thought though. Admittedly, I don't know how difficult it is to pair xaml with a code behind in noesis.
<darren:QuickSwap x:Name="m_quickSwap_leftHand" Canvas.Top="75" Canvas.Left="0" Height="150" Width="100" CornerRadius="25" />
<UserControl
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  mc:Ignorable="d"
  xmlns:noesis="clr-namespace:NoesisGUIExtensions"
  x:Class="Darren.QuickSwap" Opacity=".5">
  <Border x:Name="m_border" Background="#FF595959" BorderBrush="Black" BorderThickness="5" />
</UserControl>
namespace Darren
{
    [Noesis.UserControlSource("Assets/Darren/Code/QuickSwap.xaml")]
	public class QuickSwap : Noesis.UserControl
	{
        #region Variables & Properties

        private Noesis.Border m_border = null;

        #region Attached Properties

        /// <summary>
        /// EX) http://www.codemag.com/article/1405061
        /// </summary>
        public Noesis.CornerRadius CornerRadius
        {
            get
            {
                // Retrieve Value
                return (Noesis.CornerRadius)GetValue(CornerRadiusProperty);
            }
            set
            {
                // Mirror value to subcomponent
                if (null == m_border)
                {
                    m_border = FindName("m_border") as Noesis.Border;
                }
                if (null != m_border)
                {
                    m_border.CornerRadius = value;
                }

                // Assign Value
                SetValue(CornerRadiusProperty, value);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public static Noesis.DependencyProperty
            CornerRadiusProperty =
            Noesis.DependencyProperty.Register(
                "CornerRadius",
                typeof(Noesis.CornerRadius), typeof(QuickSwap),
                new Noesis.PropertyMetadata(""));

        #endregion Attached Properties
        #endregion Variables & Properties

        /// <summary>
        /// Initialization.
        /// </summary>
        public void OnPostInit()
		{

        }
	}
}
 
User avatar
sfernandez
Site Admin
Posts: 1915
Joined: 22 Dec 2011, 19:20

Re: How To Implement Attached Property?

04 Aug 2016, 17:51

Hi,

Attached properties are used to add custom behaviors to other classes. For example, the Panel.ZIndex that you can set on any Panel child, is only useful and only has sense in the Panel logic. Child elements don't know what to do with that value.

If you define a new dependency property in your QuickSwap user control, and you set the property in the same control, this could be considered as a normal dependency property, not an attached one.

The readonly keyword can't be used on the DependencyProperty definition because some limitations with mono and how we initialize our plugin.

About the UserControlSource attribute, it is required to pair the XAML file with the UserControl code. You cannot set that attribute to any other type of control.

Your example should look like this if defining an Attached Property:
[UserControlSource("Assets/Darren/Code/QuickSwap.xaml")]
public class QuickSwap: UserControl
{
    public static DependencyProperty CornerRadiusProperty = DependencyProperty.RegisterAttached(
        "CornerRadius", typeof(CornerRadius), typeof(QuickSwap),
        new PropertyMetadata(new CornerRadius(), OnCornerRadiusChanged));

    private static void OnCornerRadiusChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        //CornerRadius cornerRadius = (CornerRadius)e.NewValue;
        // do something with the new corner radius and the sender element
    }

    public static CornerRadius GetCornerRadius(DependencyObject element)
    {
        return (CornerRadius)element.GetValue(CornerRadiusProperty);
    }

    public static void SetCornerRadius(DependencyObject element, CornerRadius cornerRadius)
    {
        element.SetValue(CornerRadiusProperty, cornerRadius);
    }
}
<UserControl
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  mc:Ignorable="d"
  xmlns:darren="clr-namespace:Darren"
  x:Class="Darren.QuickSwap">
  <Rectangle darren:QuickSwap.CornerRadius="4,2,4,2"/>
</UserControl>
And if it is going to be used as a normal property, something like this:
[UserControlSource("Assets/Darren/Code/QuickSwap.xaml")]
public class QuickSwap: UserControl
{
    public static DependencyProperty CornerRadiusProperty = DependencyProperty.Register(
        "CornerRadius", typeof(CornerRadius), typeof(QuickSwap),
        new PropertyMetadata(new CornerRadius()));

    public CornerRadius CornerRadius
    {
        get
        {
            return (CornerRadius)GetValue(CornerRadiusProperty);
        }
        set
        {
            SetValue(CornerRadiusProperty, cornerRadius);
        }
    }
}
<darren:QuickSwap CornerRadius="10,10,20,20" />
 
User avatar
jsantos
Site Admin
Posts: 2905
Joined: 20 Jan 2012, 17:18
Contact:

Re: How To Implement Attached Property?

04 Aug 2016, 21:37

The readonly keyword can't be used on the DependencyProperty definition because some limitations with mono and how we initialize our plugin.
This will be solved in v1.3
About the UserControlSource attribute, it is required to pair the XAML file with the UserControl code. You cannot set that attribute to any other type of control.
This attribute will also disappear in v1.3 and you will load the XAML manually. Normally this code will be automatically generated (similar to the automatic hidden code generated by Visual Studio in WPF).

More details, soon. : )
 
kemarofangs
Topic Author
Posts: 32
Joined: 13 Jun 2014, 21:30

Re: How To Implement Attached Property?

05 Aug 2016, 10:50

v1.3 sounds quite promising. Thanks for both examples regarding attached and normal properties. Definitely going to make use of both of them. There's nothing special I need to do in order to guard against memory leaks using these basic patterns correct?

Here's my working code thanks to your examples / explanations.
namespace Darren
{
    [Noesis.UserControlSource("Assets/Darren/Code/QuickSwap.xaml")]
	public class QuickSwap : Noesis.UserControl
	{
        #region Variables & Properties
        #region Dependency Properties

        /// <summary>
        /// EX) http://www.codemag.com/article/1405061
        /// </summary>
        public static Noesis.DependencyProperty CornerRadiusProperty = Noesis.DependencyProperty.Register(
            "CornerRadius",
            typeof(Noesis.CornerRadius),
            typeof(QuickSwap),
            new Noesis.PropertyMetadata(new Noesis.CornerRadius(), OnCornerRadiusChanged));

        /// <summary>
        /// 
        /// </summary>
        public Noesis.CornerRadius CornerRadius
        {
            get
            {
                return (Noesis.CornerRadius)GetValue(CornerRadiusProperty);
            }
            set
            {
                SetValue(CornerRadiusProperty, value);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void OnCornerRadiusChanged(object sender, Noesis.DependencyPropertyChangedEventArgs e)
        {
            Darren.QuickSwap quickSwap = sender as Darren.QuickSwap;
            if (null != quickSwap)
            {
                Noesis.Border border = quickSwap.FindName("m_border") as Noesis.Border;
                if (null != border)
                {
                    Noesis.CornerRadius cornerRadius = (Noesis.CornerRadius)e.NewValue;
                    if (null != cornerRadius)
                    {
                        border.CornerRadius = cornerRadius;
                    }
                }
            }
        }

        #endregion Dependency Properties
        #endregion Variables & Properties

        /// <summary>
        /// Initialization.
        /// </summary>
        public void OnPostInit()
		{

        }
	}
}
 
User avatar
sfernandez
Site Admin
Posts: 1915
Joined: 22 Dec 2011, 19:20

Re: How To Implement Attached Property?

05 Aug 2016, 12:35

You are welcome.

Your code seems correct and you don't have to worry about memory leaks there, you are just doing normal casts of already created objects.

Who is online

Users browsing this forum: Bing [Bot] and 1 guest