luka
luka

Reputation: 637

Dependencyproperty strange behavior

I've created my usercontrol with a dependencyproperty. I noted that if the dependency property has the same name regitered of property, when I insert the control inside in a windows and write via xaml code the value , this control not show the labels, I need insert in codebehind the value and when start the application the control show the labels.

If I change the registered name of dependancypropety the controll Show the labels without pass the value via code behind, and show the lable each time I modify the value via xaml or via codebehind.

this is my usercontrol. xaml:

<UserControl x:Class="My.Controls.MyUserControl1"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">
<Grid x:Name="GridRoot">

</Grid>

and this is code behind:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
namespace My.Controls
{
   /// <summary>
/// Interaction logic for Plate.xaml
/// </summary>
public partial class MyUserControl1 : UserControl, INotifyPropertyChanged
{
    private Point pointOfOrigin;
    private Double labelsRad;
    //private Byte numOfChucks;
    private List<Label> labelNumber = new List<Label>();
    private List<Canvas> label = new List<Canvas>();

    #region - Dependency Properties mandatory for Binding -
    public static readonly DependencyProperty NumOfLabelsProperty =
        DependencyProperty.Register("NumOfLabels", typeof(byte), typeof(MyUserControl1), new PropertyMetadata(byte.MinValue));
    #endregion - Dependency Properties for Binding -

   [DefaultValue(byte.MinValue)]
    public byte NumOfLabels
    {
        get { return (byte)GetValue(MyUserControl1.NumOfLabelsProperty); }
        set
        {
            if (this.NumOfLabels != value)
            {
                SetValue(MyUserControl1.NumOfLabelsProperty, value);
                LabelsRender();
                OnPropertyChanged("NumOfLabels");
            }
        }
    }

    public MyUserControl1()
    {
        InitializeComponent();
        this.DataContext = this;

        //just to show when the control is designer mode
        pointOfOrigin = new Point(this.ActualWidth / 2, this.ActualHeight / 2);
        labelsRad = this.Width / 2;
    }

    private void PosLabels()
    {
        var indexLabel = Byte.MinValue;
        var pointLabel = new Point();

        for (; indexLabel < this.NumOfLabels; indexLabel++)
        {
            pointLabel = new Point((this.ActualWidth + (indexLabel * 50) )/2,(this.ActualHeight + (indexLabel *50))/2) ;
            Canvas.SetLeft(labelNumber[indexLabel], pointLabel.X);
            Canvas.SetTop(labelNumber[indexLabel], pointLabel.Y);
        }
    }
    private void RemoveLabels()
    {
        var indexLabel = Byte.MinValue;

        for (; indexLabel < labelNumber.Count; indexLabel++)
        {
            this.label[indexLabel].Children.Remove(labelNumber[indexLabel]);

            this.GridRoot.Children.Remove(label[indexLabel]);
        }

        //clean up 
        labelNumber.Clear();
        labelNumber.Clear();
        GC.Collect();
    }
    private void AddLabels()
    {
        var indexLabel = Byte.MinValue;

        for (; indexLabel < this.NumOfLabels; indexLabel++)
        {
            labelNumber.Add(
               new Label()
               {
                   Content = (indexLabel + 1).ToString("D2"),
                   Height = 23,
                   Width = 23,
                   Name = "labelNumber" + indexLabel,

               });
            label.Add(
                new Canvas()
                {
                    Name = "Label" + indexLabel,
                    Height = 23,
                    Width = 23,
                });

            label[indexLabel].Children.Add(labelNumber[indexLabel]);
            this.GridRoot.Children.Add(label[indexLabel]);

        }
    }

    private void LabelsRender()
    {
        RemoveLabels();
        AddLabels();
        PosLabels();
    }
    #region - INotifyPropertyChanged implementation -
    // Basically, the UI thread subscribes to this event and update the binding if the received Property Name correspond to the Binding Path element
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
            handler(this, new PropertyChangedEventArgs(propertyName));
    }
    #endregion - INotifyPropertyChanged implementation -
}

this is the windowtest xaml:

<UserControl x:Class="My.Controls.MyUserControl1"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">
<Grid x:Name="GridRoot">

</Grid>
</UserControl>

this is codeBehind:

enter cousing System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace XTesting
{
/// <summary>
/// Interaction logic for WindowTest.xaml
/// </summary>
public partial class WindowTest : Window
{
    public WindowTest()
    {
        InitializeComponent();
        this.MyUserControl.NumOfLabels = 7;
    }
}
}

if you want to try please delete the line after Initialize component and change de property dependency name in this form

public static readonly DependencyProperty NumOfLabelsProperty=DependencyProperty.Register("NumOfLabelsP",typeof(byte),typeof(MyUserControl1), new PropertyMetadata(byte.MinValue));

Upvotes: 0

Views: 154

Answers (3)

Clemens
Clemens

Reputation: 128013

Your NumOfLabels property should be declared like this:

public static readonly DependencyProperty NumOfLabelsProperty =
    DependencyProperty.Register(
        "NumOfLabels", typeof(int), typeof(MyUserControl1),
        new PropertyMetadata(0, NumOfLabelsPropertyChanged));

public int NumOfLabels
{
    get { return (int)GetValue(NumOfLabelsProperty); }
    set { SetValue(NumOfLabelsProperty, value); }
}

private static void NumOfLabelsPropertyChanged(
    DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
    ((MyUserControl1)obj).LabelsRender();
}

Note that you should not call anything else than GetValue and SetValue in the CLR wrapper of a dependency property, as explained in Checklist for Defining a Dependency Property:

Implementing the "Wrapper"

... In all but exceptional circumstances, your wrapper implementations should perform only the GetValue and SetValue actions, respectively. The reason for this is discussed in the topic XAML Loading and Dependency Properties.

You'll have to register a PropertyChangedCallback to react on property value changes.

Besides that it is not necessary to implement INotifyPropertyChanged for dependency properties, because they implement their own change notification mechanism.

Upvotes: 1

Franck Ngako
Franck Ngako

Reputation: 157

your xaml is bad. In fact the GridRoot used in your windows is different from the one used in your userControl. For a better behavior, your need to insert you usercontrol in a new windows and then set your property. your xaml should look something like this

<x:Windows>
   <local :MyUserControl1 NumOfLabels = 7/>
</x:Windows>

Upvotes: -1

Henk Holterman
Henk Holterman

Reputation: 273169

and write via xaml code the value , this control not show the labels, I need insert in codebehind the value

This is the correct, documented behaviour.

Your public byte NumOfLabels { get ... set ... } property is ony the 'front door' for code behind, the databindings won't use it.

Find another solution for the logic in set { }. You can add callbacks in the Register method for that.

And please, don't use byte here. It's a number, use int.

Upvotes: 2

Related Questions