WPF PropertyPath on Custom control - wpf

I have a problem, I create a control named Tile (like the Tile on Windows 10).
This tile simulate rotation 3D by using a projection class, like the projection class in Silverlight.
We have a base projection class like this :
abstract public class Projection
: FrameworkElement
{
static public readonly DependencyProperty RotationZProperty = DependencyProperty.Register(
nameof(RotationZ),
typeof(double),
typeof(Projection),
new UIPropertyMetadata(0.0, OnRotationChanged));
static public readonly DependencyProperty RotationYProperty = DependencyProperty.Register(
nameof(RotationY),
typeof(double),
typeof(Projection),
new UIPropertyMetadata(0.0, OnRotationChanged));
static public readonly DependencyProperty RotationXProperty = DependencyProperty.Register(
nameof(RotationX),
typeof(double),
typeof(Projection),
new UIPropertyMetadata(0.0, OnRotationChanged));
public double RotationZ
{
get { return this.GetValue<double>(RotationZProperty); }
set { SetValue(RotationZProperty, value); }
}
public double RotationY
{
get { return this.GetValue<double>(RotationYProperty); }
set { SetValue(RotationYProperty, value); }
}
public double RotationX
{
get { return this.GetValue<double>(RotationXProperty); }
set { SetValue(RotationXProperty, value); }
}
public FrameworkElement Child
{
get;
set;
}
static private void OnRotationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is Projection pl)
{
pl.OnRotationChanged();
}
}
private void OnRotationChanged()
{
// Some code
}}
After that, we have the PlaneProjectionClass :
[ContentProperty("Child")]
sealed public class PlaneProjection
: Projection
{
}
The Tile class use a dependency property of type Projection :
public class Tile
{
static public readonly DependencyProperty PlaneProjectionProperty = DependencyProperty.Register(
nameof(Projection),
typeof(Projection),
typeof(Tile),
new UIPropertyMetadata());
public Projection Projection
{
get { return (Projection)GetValue(PlaneProjectionProperty); }
private set { SetValue(PlaneProjectionProperty, value); }
}
override public void OnApplyTemplate()
{
Projection = GetTemplateChild("PART_Projection") as Projection;
}
}
So for the XAML, we have this in ControlTemplate :
<controls:PlaneProjection x:Name="PART_PlaneProjection">
<Border>
<Grid>
<!-- Some design -->
</Grid>
</Border>
</controls:PlaneProjection>
Now I would like to animate the planeprojection.
So I create the storyboard and animate the projection with rotationX :
static public void CreateAnimation(Tile tile)
{
Storyboard.SetTarget(anim, tile);
Storyboard.SetTargetProperty(doubleAnim, new PropertyPath("(Tile.Projection).(PlaneProjection.RotationX"));
}
But at debug, I have this error : Cannot resolve all references of the property on the path of the property '(Tile.Projection).(PlaneProjection.RotationX)
I don't understand the mistake :( Any ideas on using PropertyPath on custom control ?

The Projection property in class Tile does not follow the naming conventions for dependency properties.
It should e.g. look like this:
public static readonly DependencyProperty PlaneProjectionProperty =
DependencyProperty.Register(nameof(PlaneProjection), typeof(Projection), typeof(Tile));
public Projection PlaneProjection
{
get { return (Projection)GetValue(PlaneProjectionProperty); }
private set { SetValue(PlaneProjectionProperty, value); }
}
The property path would simply be this:
Storyboard.SetTargetProperty(anim, new PropertyPath("PlaneProjection.RotationX"));
You wouldn't even need a Storyboard. Just call
tile.PlaneProjection.BeginAnimation(Projection.RotationXProperty, anim);
As a note, a private setter does not make the dependency property read-only. See Read-Only Dependency Properties for details.

Related

doing some mathematical calculation on custom dependency property values

I have three dependency property. TestControlHeight, HalfValue1 and HalfValue2. Now, depending on these three value I have to calculate
a third value which will be assigned as the height of the inner control which will also be a dependency property.
height of inner control = TestControlHeight/ (HalfValue1 - HalfValue2);
Where can I write this code to calculate the height of the inner control (which si also a dependency property)
public static readonly DependencyProperty TestControlHeightProperty =
DependencyProperty.Register("TestControlHeight", typeof (double),
typeof (TestControl), new PropertyMetadata(default(double)));
public double TestControlHeight
{
get { return (double) GetValue(TestControlHeightProperty); }
set { SetValue(TestControlHeightProperty, value); }
}
public static readonly DependencyProperty HalfValue1Property =
DependencyProperty.Register("HalfValue1", typeof (double), typeof
(TestControl), new PropertyMetadata(default(double)));
public double HalfValue1
{
get { return (double) GetValue(HalfValue1Property); }
set { SetValue(HalfValue1Property, value); }
}
public static readonly DependencyProperty HalfValue2Property =
DependencyProperty.Register("HalfValue2", typeof (double), typeof
(TestControl), new PropertyMetadata(default(double)));
public double HalfValue2
{
get { return (double) GetValue(HalfValue2Property); }
set { SetValue(HalfValue2Property, value); }
}
Thanks & Regards,
From the WPF Unleashed book:
.NET property wrappers are bypassed at runtime when setting dependency properties
in XAML!
Although the XAML compiler depends on the property wrapper at compile time, WPF calls the underlying GetValue and SetValue methods directly at runtime! Therefore, to maintain parity between setting a property in XAML and procedural code, it’s crucial that property wrappers not contain any logic in addition to the GetValue/SetValue calls.
If you want to add custom logic, that’s what the registered callbacks are for. All of WPF’s built-in property wrappers abide by this rule, so this warning is for anyone writing a custom class with its own dependency properties.
So, your code could look like this (not tested):
public static readonly DependencyProperty TestControlHeightProperty =
DependencyProperty.Register("TestControlHeight", typeof(double), typeof(TestControl),
new PropertyMetadata(false, new PropertyChangedCallback(OnHeightChanged)));
public double TestControlHeight
{
get { return (double)GetValue(TestControlHeightProperty); }
set { SetValue(TestControlHeightProperty, value); }
}
public static readonly DependencyProperty HalfValue1Property =
DependencyProperty.Register("HalfValue1", typeof(double), typeof(TestControl),
new PropertyMetadata(false, new PropertyChangedCallback(OnHeightChanged)));
public double HalfValue1
{
get { return (double)GetValue(HalfValue1Property); }
set { SetValue(HalfValue1Property, value); }
}
public static readonly DependencyProperty HalfValue2Property =
DependencyProperty.Register("HalfValue2", typeof(double), typeof(TestControl),
new PropertyMetadata(false, new PropertyChangedCallback(OnHeightChanged)));
public double HalfValue2
{
get { return (double)GetValue(HalfValue2Property); }
set { SetValue(HalfValue2Property, value); }
}
public double MyInnerControlHeight
{
get { return (double)GetValue(MyInnerControlHeightPropertyProperty); }
set { SetValue(MyInnerControlHeightPropertyProperty, value); }
}
public static readonly DependencyProperty MyInnerControlHeightPropertyProperty =
DependencyProperty.Register("MyInnerControlHeightProperty", typeof(double), typeof(TestControl),
new PropertyMetadata(0));
private static void OnHeightChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var testControl = d as TestControl;
if (testControl != null)
{
testControl.MyInnerControlHeight = testControl.TestControlHeight / (testControl.HalfValue1 - testControl.HalfValue2);
}
}

Dependent DependencyProperty

I want to make a dependency property whose value depend on other dependency property but still settable/writable. For example, ‘DateOfBirth’ is an independent DependencyProperty. Now I want to make another DependencyProperty with name YearOfBirth. When DateOfBirth with Change it will Coerce the value of YearOfBirth property. i.e. d.CoerceValue(YearOfBirthProperty).
But How can I make dependent dependency property (e.g. YearOfBirth) writeable/settable?
If I code using CLR Properties, it works fine but how can do by using Dependency Properties!
What about this:
public partial class MainWindow : Window
{
public static DependencyProperty DateofBirthProperty = DependencyProperty.Register("DateofBirth", typeof(DateTime), typeof(MainWindow), new FrameworkPropertyMetadata(new PropertyChangedCallback(DateofBirth_Changed)));
public DateTime DateofBirth
{
get { return (DateTime)GetValue(DateofBirthProperty); }
set { SetValue(DateofBirthProperty, value); }
}
private static void DateofBirth_Changed(DependencyObject o, DependencyPropertyChangedEventArgs args)
{
MainWindow thisClass = (MainWindow)o;
thisClass.SetDateofBirth();
}
private void SetDateofBirth()
{
DOBYear = DateofBirth.Year;
}
public static DependencyProperty DOBYearProperty = DependencyProperty.Register("DOBYear", typeof(int), typeof(MainWindow), new FrameworkPropertyMetadata(new PropertyChangedCallback(DOBYear_Changed)));
public int DOBYear
{
get { return (int)GetValue(DOBYearProperty); }
set { SetValue(DOBYearProperty, value); }
}
private static void DOBYear_Changed(DependencyObject o, DependencyPropertyChangedEventArgs args)
{
MainWindow thisClass = (MainWindow)o;
thisClass.SetDOBYear();
}
private void SetDOBYear()
{
//Put Instance DOBYear Property Changed code here
}
public MainWindow()
{
InitializeComponent();
}
}

How to Expose a DependencyProperty of a Control nested in a UserControl?

I'm trying to bind an Image down from a Window into a UserControl 'Display' thats inside a UserControl 'DisplayHandler'. Display has a DependancyProperty 'DisplayImage'.
This is Similar to this, but their answers didn't help with my problem.
DisplayHandler also should have the Property 'DisplayImage' and pass down the Binding to Display. But Visual Studio doesn't allow me to register a DependancyProperty with the same name twice. So I tried to not register it twice but only to reuse it:
window
<my:DisplayHandler DisplayImage=
"{Binding ElementName=ImageList, Path=SelectedItem.Image}" />
DisplayHandler
xaml
<my:Display x:Name="display1"/>
cs
public static readonly DependencyProperty DisplayImageProperty =
myHWindowCtrl.DisplayImageProperty.AddOwner(typeof(DisplayHandler));
public HImage DisplayImage {
get { return (HImage)GetValue(DisplayImageProperty); }
set { SetValue(DisplayImageProperty, value); }
}
public HImage DisplayImage /*alternative*/ {
get { return (HImage)display1.GetValue(Display.DisplayImageProperty); }
set { display1.SetValue(Display.DisplayImageProperty, value); }
}
**neither of the 2 properties worked out.*
Display
public HImage DisplayImage {
get { return (HImage)GetValue(DisplayImageProperty); }
set { SetValue(DisplayImageProperty, value); }
}
public static readonly DependencyProperty DisplayImageProperty =
DependencyProperty.Register("DisplayImage", typeof(HImage), typeof(Display));
I have been thinking a Control goes up the Tree and looks for its Property if its own Value is not defined. ->reference
So it should work somehow...
I made some attempts with Templating and A ContentPresenter because that worked for the ImageList(ImageList also contains the Display), but I couldn't get it to bind the value like for an ListBoxItem.
The AddOwner solution should be working, but you have to add a PropertyChangedCallback that updates the embedded control.
public partial class DisplayHandler : UserControl
{
public static readonly DependencyProperty DisplayImageProperty =
Display.DisplayImageProperty.AddOwner(typeof(DisplayHandler),
new FrameworkPropertyMetadata(DisplayImagePropertyChanged));
public HImage DisplayImage
{
get { return (Image)GetValue(DisplayImageProperty); }
set { SetValue(DisplayImageProperty, value); }
}
private static void DisplayImagePropertyChanged(
DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var dh = obj as DisplayHandler;
dh.display1.DisplayImage = e.NewValue as HImage;
}
}

WPF Binding Expression and Dependency property

I am trying to bind a text box to a dependency property to change its width. The following code is not working. Any help?
public class ToolbarManager : DependencyObject
{
public static readonly DependencyProperty toolbarButtonWidth = DependencyProperty.Register("toolbarButtonWidth", typeof(Double), typeof(ToolbarManager), new FrameworkPropertyMetadata(32.0, FrameworkPropertyMetadataOptions.AffectsMeasure ));
public static readonly DependencyProperty toolbarButtonHeight = DependencyProperty.Register("toolbarButtonHeight", typeof(Double), typeof(ToolbarManager), new FrameworkPropertyMetadata(32.0, FrameworkPropertyMetadataOptions.AffectsMeasure));
public double ButtonWidth
{
get { return (double)GetValue(toolbarButtonWidth); }
set { SetValue(toolbarButtonWidth, value); }
}
public double ButtonHeight
{
get { return (double)GetValue(toolbarButtonHeight); }
set { SetValue(toolbarButtonHeight, value); }
}
public static ToolbarManager Instance { get; private set; }
static ToolbarManager()
{
Instance = new ToolbarManager();
}
}
Here is the markup code:
<TextBox Width="{Binding Source={x:Static local:ToolbarManager.Instance}, Path=ButtonWidth, Mode=OneWay}" />
The default value works, but if I modify the value in code nothing happens ?!!!
rename your dependency property should solve your problem
public class ToolbarManager : DependencyObject
{
public static readonly DependencyProperty ButtonWidthProperty =
DependencyProperty.Register("ButtonWidth", typeof(Double), typeof(ToolbarManager), new FrameworkPropertyMetadata(32.0, FrameworkPropertyMetadataOptions.AffectsMeasure ));
public static readonly DependencyProperty ButtonHeightProperty =
DependencyProperty.Register("ButtonHeight", typeof(Double), typeof(ToolbarManager), new FrameworkPropertyMetadata(32.0, FrameworkPropertyMetadataOptions.AffectsMeasure));
public double ButtonWidth
{
get { return (double)GetValue(ButtonWidthProperty); }
set { SetValue(ButtonWidthProperty, value); }
}
public double ButtonHeight
{
get { return (double)GetValue(ButtonHeightProperty); }
set { SetValue(ButtonHeightProperty, value); }
}
public static ToolbarManager Instance { get; private set; }
static ToolbarManager()
{
Instance = new ToolbarManager();
}
}
hope this helps

DependencyProperty and WPF Designer

class MyLine : Shape {
public static readonly DependencyProperty X11Property;
public static readonly DependencyProperty X22Property;
public static readonly DependencyProperty Y11Property;
public static readonly DependencyProperty Y22Property;
static MyLine() {
X11Property = DependencyProperty.Register("X11", typeof(double), typeof(MyLine), new UIPropertyMetadata(double.NaN));
X22Property = DependencyProperty.Register("X22", typeof(double), typeof(MyLine), new UIPropertyMetadata(double.NaN));
Y11Property = DependencyProperty.Register("Y11", typeof(double), typeof(MyLine), new UIPropertyMetadata(double.NaN));
Y22Property = DependencyProperty.Register("Y22", typeof(double), typeof(MyLine), new UIPropertyMetadata(double.NaN));
}
[TypeConverter(typeof(LengthConverter))]
public double X11 { get { return (double)GetValue(X11Property); } set { SetValue(X11Property, value); } }
[TypeConverter(typeof(LengthConverter))]
public double X22 { get { return (double)GetValue(X22Property); } set { SetValue(X22Property, value); } }
[TypeConverter(typeof(LengthConverter))]
public double Y11 { get { return (double)GetValue(Y11Property); } set { SetValue(Y11Property, value); } }
[TypeConverter(typeof(LengthConverter))]
public double Y22 { get { return (double)GetValue(Y22Property); } set { SetValue(Y22Property, value); } }
protected override System.Windows.Media.Geometry DefiningGeometry {
get {
var geometryGroup = new GeometryGroup();
// Add line
geometryGroup.Children.Add(new LineGeometry(new Point(X11, Y11), new Point(X22, Y22)));
return geometryGroup;
}
}
}
Why when I update the "myLine" coordinates in WPF designer(VS 2010), it does not update it automatically(live)?
When using default "Line" WPF objects, they are automatically updated when the XAML code is changed(edited) in XAML/Design view.
Since these properties affect rendering, you should specify it in the metadata:
X11Property = DependencyProperty.Register("X11", typeof(double), typeof(DirectionLine), new FrameworkPropertyMetadata(double.NaN, FrameworkPropertyMetadataOptions.AffectsRender));
I'm not sure it will be enough for the designer to take it into account, but it's worth a try...

Resources