I have a read-only dependency property which is shared between two WPF classes. So I made this property shared property. While binding this property on XAML, It takes its default value and can't be updated. But If I use the main owner of this property, its value can be updated.
So what is your advice?
public class A
{
private static readonly DependencyPropertyKey XPropertyKey =
DependencyProperty.RegisterReadOnly("X",
typeof(Point), typeof(A),
new FrameworkPropertyMetadata(new Point(0, 0),
FrameworkPropertyMetadataOptions.Inherits |
FrameworkPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty XProperty =
RelativeMousePointPropertyKey.DependencyProperty;
public Point X
{
get { return (Point)GetValue(XProperty); }
set { SetValue(XProperty, value); }
}
public void SetRelativeMousePoint(Point point)
{
SetValue(XPropertyKey, point);
}
}
public class B
{
//I use this class for binding
public static readonly DependencyProperty XProperty =
A.XProperty.AddOwner(typeof(B));
public Point X
{
get { return (Point)GetValue(X); }
}
}
Related
I am creating an application based on one of the example applications in the HelixToolkit (SurfacePlot) found in the ExampleBrwoser.
The SurfacePlotVisual3D class has three dependency properties, I tried to add another one simply by copying/renaming an existing one, but it does not work.
XAML
<h:HelixViewport3D ZoomExtentsWhenLoaded="True" ShowCoordinateSystem="True">
<local:SurfacePlotVisual3D CurrentX="{Binding CurrentX}" Points="{Binding Data}" ColorValues="{Binding ColorValues}" SurfaceBrush="{Binding SurfaceBrush}" />
</h:HelixViewport3D>
The "CurrentX" property is the one I tried to add.
SurfacePlotVisual3D.cs
public class SurfacePlotVisual3D : ModelVisual3D
{
public static readonly DependencyProperty CurrentXProperty =
DependencyProperty.Register("CurrentX", typeof(double), typeof(SurfacePlotVisual3D),
new UIPropertyMetadata(ModelChanged));
public static readonly DependencyProperty PointsProperty =
DependencyProperty.Register("Points", typeof(Point3D[,]), typeof(SurfacePlotVisual3D),
new UIPropertyMetadata(null, ModelChanged));
public static readonly DependencyProperty ColorValuesProperty =
DependencyProperty.Register("ColorValues", typeof(double[,]), typeof(SurfacePlotVisual3D),
new UIPropertyMetadata(null, ModelChanged));
public static readonly DependencyProperty SurfaceBrushProperty =
DependencyProperty.Register("SurfaceBrush", typeof(Brush), typeof(SurfacePlotVisual3D),
new UIPropertyMetadata(null, ModelChanged));
...
...
public Brush SurfaceBrush
{
get { return (Brush)GetValue(SurfaceBrushProperty); }
set { SetValue(SurfaceBrushProperty, value); }
}
public double CurrentX
{
get { return (double)GetValue(CurrentXProperty); }
set { SetValue(CurrentXProperty, value); }
}
private static void ModelChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((SurfacePlotVisual3D)d).UpdateModel();
}
MainWindow.cs
public partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
this.DataContext = new MainViewModel();
}
}
MainViewModel.cs
public class MainViewModel : INotifyPropertyChanged
{
...
...
public Func<double, double, double> Function { get; set; }
public Point3D[,] Data { get; set; }
public double[,] ColorValues { get; set; }
public double CurrentX { get; set; }
public MainViewModel()
{
CurrentX = 6;
MinX = 0;
MaxX = 3;
MinY = -2;
MaxY = 2;
Rows = 100;
Columns = 50;
Function = (x, y) => Math.Pow(Math.E, (-1) * Math.Pow(y, 2)) * 3*Math.Sin(2*x) + 2;
ColorCoding = ColorCoding.ByGradientX;
UpdateModel();
}
...
...
}
I can change "Data", "ColorValues" and "Function" in the MainViewModel and the effect is immediately visible in the plot, but the CurrentX property wont do anything.
As the comment of Anton suggested the missing default value was the problem.
public static readonly DependencyProperty CurrentXProperty =
DependencyProperty.Register("CurrentX", typeof(double), typeof(SurfacePlotVisual3D),
new UIPropertyMetadata((double)0, ModelChanged));
This fixed the problem. The (double)0 cast is needed because without it you will get an exception because "object" is not "double" (in my case). So thanks a lot Anton, made my day!
In the following example of my custom control, why I can't change a TransparentColor property in a setter of a SelectedColor property? A solution with callback method works fine, what's the difference between them in the case of change another property?
public class MyColorPicker : Control
{
static MyColorPicker()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyColorPicker), new FrameworkPropertyMetadata(typeof(MyColorPicker)));
}
public static readonly DependencyProperty SelectedColorProperty =
DependencyProperty.Register("SelectedColor", typeof(Color), typeof(MyColorPicker), new PropertyMetadata(Color.FromRgb(0, 201, 201), OnSelectedColorChange));
private static void OnSelectedColorChange(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var colorPicker = (MyColorPicker)sender;
var selectedColor = colorPicker.SelectedColor;
colorPicker.TransparentColor = Color.FromArgb(0, selectedColor.R, selectedColor.G, selectedColor.B);
}
public Color SelectedColor
{
get { return (Color)GetValue(SelectedColorProperty); }
set
{
// Why this not working? BorderStopColor = Color.FromArgb(0, value.R, value.G, value.B);
SetValue(SelectedColorProperty, value);
}
}
private static readonly DependencyPropertyKey TransparentColorPropertyKey =
DependencyProperty.RegisterReadOnly("TransparentColor", typeof(Color), typeof(MyColorPicker), new PropertyMetadata(Color.FromArgb(0, 0, 201, 201)));
public static readonly DependencyProperty TransparentColorProperty = TransparentColorPropertyKey.DependencyProperty;
public Color TransparentColor
{
get { return (Color)GetValue(TransparentColorProperty); }
protected set { SetValue(TransparentColorPropertyKey, value); }
}
}
The setter of a CLR wrapper for a dependency property should only call the SetValue method to set the actual value of the dependency property.
Any other logic should be implemented in the callback.
Also note that the CLR property wrappers are bypassed at runtime when setting dependency properties in XAML (but the callbacks are not):
Why are .NET property wrappers bypassed at runtime when setting dependency properties in XAML?
I'm creating a simple User Control with three properties. For simplicity let's assume these are A, B and C. Moreover C = A + B. I want to display all of them in TextBoxes (A, B - User Editable, C - read only). Whenever a user modifies A or B, the value of C should be updated.
I've already created Dependency Properties for A and B in MyControl.xaml.cs file.
public static readonly DependencyProperty AProperty =
DependencyProperty.Register("A", typeof(double),
typeof(MyControl), new FrameworkPropertyMetadata(0.0));
public double A
{
get { return (double)GetValue(AProperty); }
set { SetValue(AProperty, value); }
}
public static readonly DependencyProperty BProperty =
DependencyProperty.Register("B", typeof(double),
typeof(MyControl), new FrameworkPropertyMetadata(0.0));
public double B
{
get { return (double)GetValue(BProperty); }
set { SetValue(BProperty, value); }
}
My question is: what shall I do with C and where its definition should be kept ? Should the logic be coded inside a control or maybe it's a user's responsibility to remember about the relationship between the properties ?
You should declare another DependencyProperty for C in the UserControl and add property change handlers to your A and B properties to update the value of C when their values change. This should do the job:
public static readonly DependencyProperty AProperty =
DependencyProperty.Register("A", typeof(double),
typeof(MyControl), new UIPropertyMetadata(0.0, OnAOrBPropertyChanged));
public static readonly DependencyProperty BProperty =
DependencyProperty.Register("B", typeof(double),
typeof(MyControl), new UIPropertyMetadata(0.0, OnAOrBPropertyChanged));
public static void OnAOrBPropertyChanged(DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs e)
{
dependencyObject.SetValue(CProperty, (double)dependencyObject.GetValue(AProperty) +
(double)dependencyObject.GetValue(BProperty));
}
You might like to view the Read-Only Dependency Properties page on MSDN for help with declaring a read-only DependencyProperty.
You need to make another dependency property for C also
You can try this:
public static readonly DependencyProperty AProperty =
DependencyProperty.Register("A", typeof(double),
typeof(MyControl), new FrameworkPropertyMetadata(OnAorBChange));
public double A
{
get { return (double)GetValue(AProperty); }
set { SetValue(AProperty, value); }
}
public static readonly DependencyProperty BProperty =
DependencyProperty.Register("B", typeof(double),
typeof(MyControl), new FrameworkPropertyMetadata(OnAorBChange));
public double B
{
get { return (double)GetValue(BProperty); }
set { SetValue(BProperty, value); }
}
public static readonly DependencyProperty CProperty =
DependencyProperty.Register("C", typeof(double),
typeof(MyControl), new FrameworkPropertyMetadata(null));
public double C
{
get { return (double)GetValue(CProperty); }
set { SetValue(CProperty, value); }
}
private static void OnAorBChange(DependencyObject obj,
DependencyPropertyChangedEventArgs args)
{
var obj1 = obj as MyControl;
obj1.C = obj1.A + obj1.B;
}
I assume you did the proper binding :)
I basically have the same problem as this guy here: Error: " 'Subjects' property was already registered by 'Period' " is raised when more than one control is placed on the form
The main difference between us is that I want to subscribe to an event that has access to the local instance when the xaml changes the property.
So I have my UserControl:
public partial class BoardSquare : UserControl
{
public BoardSquare()
{
InitializeComponent();
Location = new BoardLocation(Int32.MinValue, Int32.MinValue);
XPositionProperty =
DependencyProperty.Register("XPosition", typeof(int),
typeof(BoardSquare), new PropertyMetadata(
new PropertyChangedCallback((value, args) =>
{
Location.X = (int)args.NewValue;
resetBackgroundColorToPosition();
})));
YPositionProperty=
DependencyProperty.Register("YPosition", typeof(int),
typeof(BoardSquare), new PropertyMetadata(
new PropertyChangedCallback((value, args)=>
{
Location.Y = (int)args.NewValue;
resetBackgroundColorToPosition();
})));
}
private void resetBackgroundColorToPosition()
{
this.Background = (Brush)(new ColorEnumToBrushesConverter()).Convert(Location.GetSquareColor(), typeof(BlackWhiteColor), null, null);
}
public readonly DependencyProperty XPositionProperty;
public readonly DependencyProperty YPositionProperty;
public int XPosition
{
get
{
return (int)GetValue(XPositionProperty);
}
set
{
SetValue(XPositionProperty, value);
}
}
public int YPosition
{
get
{
return (int)GetValue(YPositionProperty);
}
set
{
SetValue(YPositionProperty, value);
}
}
public BoardLocation Location { get; set; }
}
Here is my XAML:
<local:BoardSquare Grid.Column="3" Grid.Row="0" XPosition="3" YPosition="0"/>
<local:BoardSquare Grid.Column="4" Grid.Row="0" XPosition="4" YPosition="0"/>
From what I understand, the solution is to make XPositionProperty static and then register it in a static constructor. My problem then is I can't access the local instance of the class when my PropertyChangeCallback event happens.
How can I set the property in the XAML and still get an on property changed event in the C# code?
Is there a better solution than dependency properties?
Below is the working code of BoardSquare after I implemented the answer.
public partial class BoardSquare : UserControl
{
static BoardSquare()
{
XPositionProperty =
DependencyProperty.Register("XPosition", typeof(int),
typeof(BoardSquare), new PropertyMetadata(
new PropertyChangedCallback((objectInstance, args) =>
{
BoardSquare boardSquare = (BoardSquare)objectInstance;
boardSquare.Location.X = (int)args.NewValue;
boardSquare.resetBackgroundColorToPosition();
})));
YPositionProperty =
DependencyProperty.Register("YPosition", typeof(int),
typeof(BoardSquare), new PropertyMetadata(
new PropertyChangedCallback((objectInstance, args) =>
{
BoardSquare boardSquare = (BoardSquare)objectInstance;
boardSquare.Location.Y = (int)args.NewValue;
boardSquare.resetBackgroundColorToPosition();
})));
}
public BoardSquare()
{
InitializeComponent();
Location = new BoardLocation(Int32.MinValue, Int32.MinValue);
}
private void resetBackgroundColorToPosition()
{
this.Background = (Brush)(new ColorEnumToBrushesConverter()).Convert(Location.GetSquareColor(), typeof(BlackWhiteColor), null, null);
}
public static readonly DependencyProperty XPositionProperty;
public static readonly DependencyProperty YPositionProperty;
public int XPosition
{
get
{
return (int)GetValue(XPositionProperty);
}
set
{
SetValue(XPositionProperty, value);
}
}
public int YPosition
{
get
{
return (int)GetValue(YPositionProperty);
}
set
{
SetValue(YPositionProperty, value);
}
}
public BoardLocation Location { get; set; }
}
The first argument of PropertyChangedCallback is the local instance (btw. you better name it obj than value to avoid confusion). You have to cast this DependencyObject to BoardSquare and that's all.
public static readonly DependencyProperty XPositionProperty =
DependencyProperty.Register("XPosition", typeof(int), typeof(BoardSquare),
new PropertyMetadata(new PropertyChangedCallback((obj, args) => {
BoardSquare bs = obj as BoardSquare;
bs.Location.X = (int)args.NewValue;
bs.resetBackgroundColorToPosition();
})));
If I understand your question correctly, you are trying to override already WPF certificate property with your own. I don't think that's necessary here. If XPosition is already WPF certificate property, all you have to do is create a property in your usercontrol with getter/setter with setter calling INotifypropertychanged. you don't need dependency property in this case unless you want only same name but behave differently, and why do you want that?.
How can I create a custom read-only Boolean dependency property that returns And operation between two custom Boolean dependency properties, for example (A, B),
And when A or B changes, I want the result property to trigger.
Any help to achieve that!
Part 1: dependencies.
public static readonly DependencyProperty Source1Property =
DependencyProperty.Register(
"Source1",
typeof(bool),
typeof(MyControl),
new FrameworkPropertyMetadata(false, new PropertyChangedCallback(UpdateTarget)));
public bool Source1
{
get { return (bool)GetValue(Source1Property); }
set { SetValue(Source1Property, value); }
}
void UpdateTarget(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MyControl self = (MyControl)d;
d.Target = d.Source1 && d.Source2;
}
Part 2: read-only
internal static readonly DependencyPropertyKey TargetPropertyKey =
DependencyProperty.RegisterReadOnly(
"Target",
typeof(bool),
typeof(MyControl),
new PropertyMetadata(false));
public static readonly DependencyProperty TargetProperty =
TargetPropertyKey.DependencyProperty;
public bool Target
{
get { return (bool)GetValue(TargetProperty); }
protected set { SetValue(TargetPropertyKey, value); }
}
Disclaimer: I didn't try the part 2.
Part 3:
if the source dependency properties are not defined by you, you can do the following trick:
DependencyPropertyDescriptor dpd = DependencyPropertyDescriptor.FromProperty(
MyControl.Source1Property,
typeof(MyControl)));
if (dpd != null)
dpd.AddValueChanged(this, UpdateTarget);
You can do this by defining your two dependency properties A and B (for the sake of the example, I guess), and define a callback to be executed whenever these changes, using PropertyMetaData in the DependencyProperty constructor. In this callback, simply perform the calculation you want and set the Result depdendency property to that value. Here is a working example:
public class Data : DependencyObject
{
public static readonly DependencyProperty AProperty = DependencyProperty.Register("A", typeof(Boolean), typeof(Data), new PropertyMetadata(false,HandleValueChanged));
public static readonly DependencyProperty BProperty = DependencyProperty.Register("B", typeof(Boolean), typeof(Data), new PropertyMetadata(false, HandleValueChanged));
public static readonly DependencyProperty ResultProperty = DependencyProperty.Register("Result",typeof (Boolean), typeof (Data));
private static void HandleValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
d.SetValue(ResultProperty, ((Data)d).Result);
Debug.WriteLine("Value change");
}
public bool Result
{
get { return A & B; }
}
public bool A
{
get { return (bool) GetValue(AProperty); }
set
{
if ( A != value )
SetValue(AProperty, value);
}
}
public bool B
{
get
{
return (bool) GetValue(BProperty);
}
set
{
if (B != value)
SetValue(BProperty, value);
}
}
}