views:

52

answers:

2

I did a test on custom dependency properties on WPF userControls, and does not work... Please help.

Task:

Having an UserControl - MyCircle, build the CenterX and CenterY bindable properties.

Here is my code:

MyTestCircle.xaml

<Canvas x:Name="mainCanvas" Width="100" Height="100">
    <Ellipse x:Name="myCircle" Fill="Red" 
             Width="{Binding ElementName=mainCanvas, Path=ActualWidth}"
             Height="{Binding ElementName=mainCanvas, Path=ActualHeight}"/>     
</Canvas>

MyTestCircle.xaml.cs

public partial class MyTestCircle : UserControl
{
    public MyTestCircle()
    {
        InitializeComponent();
    }

    public double CenterX
    {
        get { return (double)GetValue(CenterXProperty); }
        set { SetValue(CenterXProperty, value); }
    }

    public static readonly DependencyProperty CenterXProperty =
        DependencyProperty.Register("CenterX", typeof(double), typeof(MyTestCircle),
        new UIPropertyMetadata(double.NaN, 
            new PropertyChangedCallback(OnCenterYChanged), 
            new CoerceValueCallback(OnCenterXCoerceValue)));

    public static void OnCenterXChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
    {
        MyTestCircle circle = (MyTestCircle)obj;
        Canvas.SetLeft(circle, (double)args.NewValue - circle.Width / 2);
    }

    public static object OnCenterXCoerceValue(DependencyObject source, object obj)
    {
        MyTestCircle circle = (MyTestCircle)obj;
        return Canvas.GetLeft(circle) + circle.Width / 2;            
    }




    public double CenterY
    {
        get { return (double)GetValue(CenterYProperty); }
        set { SetValue(CenterYProperty, value); }
    }

    public static readonly DependencyProperty CenterYProperty =
        DependencyProperty.Register("CenterY", typeof(double), typeof(MyTestCircle),
        new UIPropertyMetadata(double.NaN,
            new PropertyChangedCallback(OnCenterYChanged),
            new CoerceValueCallback(OnCenterYCoerceValue)));

    public static void OnCenterYChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
    {
        MyTestCircle circle = (MyTestCircle)obj;
        Canvas.SetTop(circle, (double)args.NewValue - circle.Height / 2);
    }

    public static object OnCenterYCoerceValue(DependencyObject source, object obj)
    {
        MyTestCircle circle = (MyTestCircle)obj;
        return Canvas.GetTop(circle) + circle.Height / 2;
    }

}

Testing Canvas:

<Canvas x:Name="mainCanvas">
    <my:MyTestCircle Canvas.Left="104" Canvas.Top="132" x:Name="myTestCircle1" />
    <Line Stroke="Black"
        X1="0" Y1="0" 
        X2="{Binding ElementName=myTestCircle1, Path=CenterX}"
        Y2="{Binding ElementName=myTestCircle1, Path=CenterY}"
        />
</Canvas>

So, the Line in the Testing canvas does not follow the center of the circle... Why?

EDIT:

Remark:
In the code or Designer, I could never chanage the CenterX and CenterY properties. I need that that properties be "linked" with the Left/Top canvas properties... Is that possible?

A: 

You need such a result?

exmpale

public Circle()
    {
        InitializeComponent();


    }
    public double CenterX
    {
        get { return (double)GetValue(CenterXProperty); }
        set { SetValue(CenterXProperty, value); }
    }

    public static readonly DependencyProperty CenterXProperty =
        DependencyProperty.Register("CenterX", typeof(double), typeof(Circle),
        new UIPropertyMetadata(double.NaN,
            new PropertyChangedCallback(OnCenterXChanged),
            new CoerceValueCallback(OnCenterXCoerceValue)));


    public static void OnCenterXChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
    {
        Circle circle = (Circle)obj;
        Canvas.SetLeft(circle, (double)args.NewValue - circle.Width / 2);
    }

    public static object OnCenterXCoerceValue(DependencyObject source, object obj)
    {
        return double.Parse(obj.ToString());
    }




    public double CenterY
    {
        get { return (double)GetValue(CenterYProperty); }
        set { SetValue(CenterYProperty, value); }
    }

    public static readonly DependencyProperty CenterYProperty =
        DependencyProperty.Register("CenterY", typeof(double), typeof(Circle),
        new UIPropertyMetadata(double.NaN,
            new PropertyChangedCallback(OnCenterYChanged),
            new CoerceValueCallback(OnCenterYCoerceValue)));

    public static void OnCenterYChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
    {
        Circle circle = (Circle)obj;
        Canvas.SetTop(circle, (double)args.NewValue - circle.ActualHeight / 2);
    }

    public static object OnCenterYCoerceValue(DependencyObject source, object obj)
    {
          return double.Parse(obj.ToString());
    }

<Canvas x:Name="mainCanvas" Width="100" Height="100">
        <Ellipse x:Name="myCircle" Fill="Red" 
             Width="{Binding ElementName=mainCanvas, Path=Width}"
             Height="{Binding ElementName=mainCanvas, Path=Height}"/>
    </Canvas>

for testing create in MainWindows^

private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            myTestCircle1.CenterY = 200;
            myTestCircle1.CenterX = 300;

        }

EDIT:

private void Window_KeyDown(object sender, KeyEventArgs e)
{
    if (e.Key == Key.Left)
    {

        myTestCircle1.CenterX -= 1.0;
    }
    else if (e.Key == Key.Right)
    {

        myTestCircle1.CenterX += 1.0;
    }
}

binding is work...or you need something else?

simply denis
no, the **binding** does not work for you r example. Test on the TestCanvas from My example...
serhio
binding is work...or you need something else?
simply denis
infact, as Kris mentionned, I never change CenterX and CenterY, I change only Left and Top, but expected that the CenterX and CenterY properties chnages too...
serhio
So, I need that CenterX and CenterY changes when I change Left/Top...
serhio
+1  A: 

You are never actually setting CenterX and CenterY so they remain at their default but there are other issues. The question seems homeworkey so I'll just point out the problems.

Coerce handlers are used when you want to adjust a value before setting it e.g. limiting a value. In this situation they're not needed plus you incorrectly cast obj which is a double to a MyTestCircle.

Your change handlers are using the Width and Height of the MyTestCircle UserControl which haven't been set, you should either set them or access size values that have been set. You could just have an Ellipse in the user control and set the size of the UserControl. You also have a typo calling the Y handler when X changes.

If this is Homework try to fix the problems mentioned and we can have another look. If not and you just want a line to the Center I would remove the properties completely use the built-in Left,Top properties and add a RenderTransform that translates -50,-50.

Do you need the extra properties? Do they need to update if you set Left and Top? If so custom Attached Properties would be a nice solution.

Kris
I move my circle by changing the Left and Right properties.... But I want bin the Line to the center of the circle. It's all... I really don't change CenterX nor CenterY
serhio
"custom Attached Properties".. I read the article you linked... but... could you just bring an little light example of that kind of properties. Say, When I change Left that CenterX changes too... or something like this.
serhio