I am struggling to find information about how to add some extra functionality in Visual Studio for dependency properties.
Basically, I want to create a similar function as the Effect property on most controls. If I press the Effects' new button, a list of possible effect objects are shown.
This is what I am used to for DP
public MyClass MyClass
{
get { return (MyClass)GetValue(MyClassProperty); }
set { SetValue(MyClassProperty, value); }
}
public static readonly DependencyProperty MyClassProperty =
DependencyProperty.Register("MyClass", typeof(MyClass),
typeof(MyView),
new FrameworkPropertyMetadata(new MyClass(), FrameworkPropertyMetadataOptions.AffectsRender, MyClassPropertyChangedCallback));
private static void MyClassPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as MyView;
control.MyClass = (MyClass)e.NewValue;
}
Related
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?.
I have class that is derived from FrameworkElement, and I want WPF to update its Location property by using DoubleAnimation. I register the property as DependendencyProperty:
public class TimeCursor : FrameworkElement
{
public static readonly DependencyProperty LocationProperty;
public double Location
{
get { return (double)GetValue(LocationProperty); }
set
{
SetValue(LocationProperty, value);
}
}
static TimeCursor()
{
LocationProperty = DependencyProperty.Register("Location", typeof(double), typeof(TimeCursor));
}
}
Following code sets up the storyboard.
TimeCursor timeCursor;
private void SetCursorAnimation()
{
timeCursor = new TimeCursor();
NameScope.SetNameScope(this, new NameScope());
RegisterName("TimeCursor", timeCursor);
storyboard.Children.Clear();
DoubleAnimation animation = new DoubleAnimation(LeftOffset, LeftOffset + (VerticalLineCount - 1) * HorizontalGap + VerticalLineThickness,
new Duration(TimeSpan.FromMilliseconds(musicDuration)), FillBehavior.HoldEnd);
Storyboard.SetTargetName(animation, "TimeCursor");
Storyboard.SetTargetProperty(animation, new PropertyPath(TimeCursor.LocationProperty));
storyboard.Children.Add(animation);
}
Then I call storyboard.Begin(this) from another method of the object which contains the above SetCursorAnimation() method and this object is derived from Canvas. However the Location property is never updated(set accessor of Location is never called) and no exception is thrown. What am I doing wrong?
When a dependency property is animated (or set in XAML, or set by a Style Setter, etc.), WPF does not call the CLR wrapper, but instead directly accesses the underlying DependencyObject and DependencyProperty objects. See the Implementing the "Wrapper" section in Checklist for Defining a Dependency Property and also Implications for Custom Dependency Properties.
In order to get notified about property changes, you have to register a PropertyChangedCallback:
public class TimeCursor : FrameworkElement
{
public static readonly DependencyProperty LocationProperty =
DependencyProperty.Register(
"Location", typeof(double), typeof(TimeCursor),
new FrameworkPropertyMetadata(LocationPropertyChanged)); // register callback here
public double Location
{
get { return (double)GetValue(LocationProperty); }
set { SetValue(LocationProperty, value); }
}
private static void LocationPropertyChanged(
DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var timeCursor = obj as TimeCursor;
// handle Location property changes here
...
}
}
And note also that animating a dependency property does not necessarily require a Storyboard. You could simply call the BeginAnimation method on your TimeCursor instance:
var animation = new DoubleAnimation(LeftOffset,
LeftOffset + (VerticalLineCount - 1) * HorizontalGap + VerticalLineThickness,
new Duration(TimeSpan.FromMilliseconds(musicDuration)),
FillBehavior.HoldEnd);
timeCursor.BeginAnimation(TimeCursor.LocationProperty, animation);
Is it possible to detect when a Xaml binding is set on a DependencyProperty in Silverlight?
E.g. if I had a custom user-control with single dependency property and a binding declared like this:
public class MyControl : UserControl
{
public static readonly DependencyProperty TestProperty =
DependencyProperty.Register("Test",
typeof(object), typeof(MyControl),
new PropertyMetadata(null));
public object Test
{
get { return GetValue(TestProperty); }
set { SetValue(TestProperty, value); }
}
}
<MyControl Test="{Binding APropInViewModel}>
</MyControl>
Can I in the MyControl code to something like this?
// Ctor
public MyControl()
{
TestProperty.BindingChanged += new EventHandler(...)
}
e.g. can I get a notification of binding?
NOTE:
This is to solve a tricky order of precedence problem, described here so just checking for new values in the DependencyPropertyChanged handler won't work - because the property changed handler doesn't fire!!
It is possible for value changes in this binding. You can detect changes using propertychanged callback method which is static for Dependency properties.
public static readonly DependencyProperty TestProperty =
DependencyProperty.Register("Test",
typeof(object), typeof(MyControl),
new PropertyMetadata(null, TestChangedCallbackHandler));
private static void TestChangedCallbackHandler(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
MyControl obj = sender as MyControl;
Test = args.NewValue;
}
However, this might cause following event listening cases. If you want to listen changes on this dependency property is explained in this link :
Listen DepencenyProperty value changes
public void RegisterForNotification(string propertyName, FrameworkElement element, PropertyChangedCallback callback)
{
Binding b = new Binding(propertyName) { Source = element };
var prop = System.Windows.DependencyProperty.RegisterAttached(
"ListenAttached" + propertyName,
typeof(object),
typeof(UserControl),
new System.Windows.PropertyMetadata(callback));
element.SetBinding(prop, b);
}
and call like this
this.RegisterForNotification("Test", this, TestChangedCallback);
I have created a very Simple attached Property:
public static class ToolBarEx
{
public static readonly DependencyProperty FocusedExProperty =
DependencyProperty.RegisterAttached(
"FocusedEx", typeof(bool?), typeof(FrameworkElement),
new FrameworkPropertyMetadata(false, FocusedExChanged));
private static void FocusedExChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is ToolBar)
{
if (e.NewValue is bool)
{
if ((bool)e.NewValue)
{
(d as ToolBar).Focus();
}
}
}
}
public static bool? GetFocusedEx(DependencyObject obj)
{
return (bool)obj.GetValue(FocusedExProperty);
}
public static void SetFocusedEx(DependencyObject obj, bool? value)
{
obj.SetValue(FocusedExProperty, value);
}
}
Setting this in Xaml works perfectly fine, but if I try setting it within a Style:
I receive an ArguemntNullException during the Runtime (saying: "Value cannot be null.
Parameter name: property").
I cannot figure what is wrong here. Any hint is appriciated!
A common mistake made when registering attached dependency properties is to incorrectly specify the ownerType argument. This must always be the registering class, ToolBarEx here:
public static readonly DependencyProperty FocusedExProperty =
DependencyProperty.RegisterAttached(
"FocusedEx", typeof(bool?), typeof(ToolBarEx),
new FrameworkPropertyMetadata(false, FocusedExChanged));
And just for avoiding unnecessary code in the property changed handler you could safely cast NewValue to bool:
private static void FocusedExChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var toolBar = d as ToolBar;
if (toolBar != null && (bool)e.NewValue)
{
toolBar.Focus();
}
}
I have next dependecy property:
public static DependencyProperty IsInReadModeProperty =
DependencyProperty.Register("IsInReadMode", typeof(bool),
typeof(RegCardSearchForm), new PropertyMetadata(false, ReadModeChanged));
and I have next method which handle Property changed event:
public static void ReadModeChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue)
{
btnSearch.Visibility = Visibility.Collapsed;
btnExport.Visibility = Visibility.Collapsed;
cbExportWay.Visibility = Visibility.Collapsed;
}
}
But compiler give me error that i cannot acces non static buttons (btnSearch and e.t.c ) in static context.
I want change visibility of buttons when property value changed. How can I resolve this situation?
Since (non-attached) DependencyProperties are restricted to being set on their owner type you can just create an instance method to hold your logic and call that from the static method by casting the DependencyObject:
public static readonly DependencyProperty IsInReadModeProperty = DependencyProperty.Register(
"IsInReadMode",
typeof(bool),
typeof(RegCardSearchForm),
new UIPropertyMetadata(false, ReadModeChanged));
private static void ReadModeChanged(DependencyObject dObj, DependencyPropertyChangedEventArgs e)
{
RegCardSearchForm form = dObj as RegCardSearchForm;
if (form != null)
form.ReadModeChanged((bool)e.OldValue, (bool)e.NewValue);
}
protected virtual void ReadModeChanged(bool oldValue, bool newValue)
{
// TODO: Add your instance logic.
}
One way could be to extend a class from DependencyObject that would contain the set/get of the controls that you want to manipulate. And process it in ReadModeChanged event by accessing DependencyObject.
This example might help.
... example derives from DependencyObject to create a new abstract class. The class then registers an attached property and includes support members for that attached property.
If ReadModeChanged is a static method of the container for your buttons, then just make it an instance method of the container.
These things do have to be static in order for a DependencyProperty to work properly However, the parameter to your PropertyChanged handler is probably the thing you need: it's the instance whose property has just changed. I suspect this would work for you:
public static void ReadModeChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue)
{
RegCardSearchForm c = (RegCardSearchForm)d;
c.btnSearch.Visibility = Visibility.Collapsed;
c.btnExport.Visibility = Visibility.Collapsed;
c.cbExportWay.Visibility = Visibility.Collapsed;
}
}