How can I create a simple bool dependency property IsInput. This value can only be set to true or false when the class is created in code. Seems rather simple but ive searched around online and haven't found a clear example.
I've seen examples like this one below online but I'm not quite clear on what I would duplicate to create my own bool dependency property correctly.
public static readonly DependencyProperty AncestorProperty =
DependencyProperty.Register("Ancestor", typeof(FrameworkElement), typeof(MyItem),
new FrameworkPropertyMetadata(Ancestor_PropertyChanged));
/// <summary>
/// Event raised when 'Ancestor' property has changed.
/// </summary>
private static void Ancestor_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MyItem c = (MyItem)d;
c.UpdateHotspot();
}
The second parameter of the Register method is the type of the property, i.e. bool, while the third parameter is the so-called owner type, which is the type that declares the property (MyControl in the example below).
For a complete dependency property declaration you also need to declare the "wrapper" property with a getter and a setter that call the dependency property's GetValue and SetValue methods.
public static readonly DependencyProperty IsInputProperty =
DependencyProperty.Register("IsInput", typeof(bool), typeof(MyControl),
new FrameworkPropertyMetadata(IsInputPropertyChanged));
/// <summary>
/// CLR wrapper for the 'IsInput' dependency property.
/// </summary>
public bool IsInput
{
get { return (bool)GetValue(IsInputProperty); }
set { SetValue(IsInputProperty, value); }
}
/// <summary>
/// Callback called when 'IsInput' property has changed.
/// </summary>
private static void IsInputPropertyChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
bool b = (bool)e.NewValue;
//TODO
}
Related
I am currently trying to add the following line of code to my XAML file:
<MouseBinding Command="helix:ViewportCommands.Rotate" Gesture="{Binding ViewportRotateGesture}" />
The problem is that this doesn't work. Whenever I try to bind a string to the "Gesture" property, I get the following exception:
System.Windows.Markup.XamlParseException: 'A 'Binding' cannot be set on the 'Gesture' property of type 'MouseBinding'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.'
Using the "MouseAction" property instead of the "Gesture" property does work, however this doesn’t allow you to add any modifiers. Does anybody know how to bind a mouse gesture in combination with a modifier (for example Shift+LeftClick)?
This is not a DependencyProperty, but a normal CLR property. You can't bind him. See public virtual InputGesture Gesture.
You can create a Behavior or an Attached Property for this purpose.
Example:
using System;
using System.Windows;
using System.Windows.Input;
namespace Core2022.SO.Chris.AttachedProperties
{
public static class InputBinding
{
/// <summary>Returns the value of the attached property Gesture for the <paramref name="inputBinding"/>.</summary>
/// <param name="inputBinding"><see cref="System.Windows.Input.InputBinding"/> whose property value will be returned.</param>
/// <returns>Property value <see cref="InputGesture"/>.</returns>
public static InputGesture GetGesture(System.Windows.Input.InputBinding inputBinding)
{
return (InputGesture)inputBinding.GetValue(GestureProperty);
}
/// <summary>Sets the value of the Gesture attached property to <paramref name="inputBinding"/>.</summary>
/// <param name="inputBinding"><see cref="System.Windows.Input.InputBinding"/> whose property is setting to a value..</param>
/// <param name="value"><see cref="InputGesture"/> value for property.</param>
public static void SetGesture(System.Windows.Input.InputBinding inputBinding, InputGesture value)
{
inputBinding.SetValue(GestureProperty, value);
}
/// <summary><see cref="DependencyProperty"/> for methods <see cref="GetGesture(System.Windows.Input.InputBinding)"/>
/// and <see cref="SetGesture(System.Windows.Input.InputBinding, InputGesture)"/>.</summary>
public static readonly DependencyProperty GestureProperty =
DependencyProperty.RegisterAttached(
nameof(GetGesture).Substring(3),
typeof(InputGesture),
typeof(InputBinding),
new PropertyMetadata(null, OnGestureChanged));
private static void OnGestureChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is not System.Windows.Input.InputBinding inputBinding)
throw new NotImplementedException($"Implemented only for the \"{typeof(System.Windows.Input.InputBinding).FullName}\" class");
inputBinding.Gesture = (InputGesture)e.NewValue;
}
}
}
<MouseBinding Command="helix:ViewportCommands.Rotate"
ap:InputBinding.Gesture="{Binding ViewportRotateGesture}"/>
If the property event will trigger I want a method that will be called
For example. If the name of person is changed the MethodOne() will be also called. How to implement this with INotifyPropertyChanged on another class for example on WPF MainWindow.xaml.cs? Thank You
public class ObservableObject : INotifyPropertyChanged
{
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler? PropertyChanged;
}
...
There a few ways that you can get notified of property changed.
Generally you should always define properties using DependencyProperty.Register and then add access methods.
In the example that follows change YourClass to be your class name.
Declare the property like this, note that the new PropertyMetadata specifies a static method to call when the property is changed. This can be useful but is optional and can be omitted.
/// <summary>
/// Backs the IsBusy dependency property.
/// </summary>
public static readonly DependencyProperty IsBusyProperty =
DependencyProperty.Register(
"IsBusy",
typeof( bool ),
typeof( YourClass ),
new PropertyMetadata( false, new PropertyChangedCallback( OnIsBusyChanged ) ) );
then provide an access method that will use the underlying backing property, so that any changes can be tracked.
/// <summary>
/// Gets or sets a value indicating whether the busy indicator should show.
/// </summary>
public bool IsBusy
{
get
{
return ( bool )GetValue( IsBusyProperty );
}
set
{
SetValue( IsBusyProperty, value );
}
}
The following is the static interface to the dependency property that is required to be static. In here we will receive the dependency object that will usually be an instance of your class and thus we can invoke an object method from that class.
/// <summary>
/// IsBusyProperty property changed handler.
/// </summary>
/// <remarks>
/// This is the static version that is invoked by the dependency property
/// with the appropriate object, so see if d is our class and if so then
/// we can invoke the method on the class
/// </remarks>
/// <param name="d">Instance of class that changed its IsBusy.</param>
/// <param name="e">Event arguments.</param>
private static void OnIsBusyChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
{
if (d is YourClass)
{
var bb = d as YourClass;
bb.OnIsBusyChanged(e);
}
}
and finally the method that will be called when the property is changed in the object
/// <summary>
/// IsBusyProperty property changed handler.
/// </summary>
/// <param name="e">Event arguments.</param>
protected virtual void OnIsBusyChanged(DependencyPropertyChangedEventArgs e)
{
// your code
// you could notify a parent or another event
// handler
}
As an alternative you can directly attach to the PropertyChanged event.
In your constructor (or somewhere)
PropertyChanged += somePropertyChanged;
this will then call the method somePropertyChanged which is defined as follows:
private void somePropertyChanged(object sender, PropertyChangedEventArgs e)
{
// sender is the object that caused the change
// and is usually your class
Console.WriteLine("Property {0} changed", e.PropertyName);
}
The first approach of specifying a callback in DependencyProperty.Register can be useful when you have a set of properties that can be considered as a group and when a similar action will be taken.
I am going over the differences between regular dependency properties and attached properties.
Using ILSpy, I had a look at how Register and RegisterAttached are implemented.
Register:
// System.Windows.DependencyProperty
/// <summary>Registers a dependency property with the specified property name, property type, owner type, property metadata, and a value validation callback for the property. </summary>
/// <returns>A dependency property identifier that should be used to set the value of a public static readonly field in your class. That identifier is then used to reference the dependency property later, for operations such as setting its value programmatically or obtaining metadata.</returns>
/// <param name="name">The name of the dependency property to register.</param>
/// <param name="propertyType">The type of the property.</param>
/// <param name="ownerType">The owner type that is registering the dependency property.</param>
/// <param name="typeMetadata">Property metadata for the dependency property.</param>
/// <param name="validateValueCallback">A reference to a callback that should perform any custom validation of the dependency property value beyond typical type validation.</param>
public static DependencyProperty Register(string name, Type propertyType, Type ownerType, PropertyMetadata typeMetadata, ValidateValueCallback validateValueCallback)
{
DependencyProperty.RegisterParameterValidation(name, propertyType, ownerType);
PropertyMetadata defaultMetadata = null;
if (typeMetadata != null && typeMetadata.DefaultValueWasSet())
{
defaultMetadata = new PropertyMetadata(typeMetadata.DefaultValue);
}
DependencyProperty dependencyProperty = DependencyProperty.RegisterCommon(name, propertyType, ownerType, defaultMetadata, validateValueCallback);
if (typeMetadata != null)
{
dependencyProperty.OverrideMetadata(ownerType, typeMetadata);
}
return dependencyProperty;
}
RegisterAttached:
// System.Windows.DependencyProperty
/// <summary>Registers an attached property with the specified property type, owner type, property metadata, and value validation callback for the property. </summary>
/// <returns>A dependency property identifier that should be used to set the value of a public static readonly field in your class. That identifier is then used to reference the dependency property later, for operations such as setting its value programmatically or obtaining metadata.</returns>
/// <param name="name">The name of the dependency property to register.</param>
/// <param name="propertyType">The type of the property.</param>
/// <param name="ownerType">The owner type that is registering the dependency property.</param>
/// <param name="defaultMetadata">Property metadata for the dependency property. This can include the default value as well as other characteristics.</param>
/// <param name="validateValueCallback">A reference to a callback that should perform any custom validation of the dependency property value beyond typical type validation.</param>
public static DependencyProperty RegisterAttached(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata, ValidateValueCallback validateValueCallback)
{
DependencyProperty.RegisterParameterValidation(name, propertyType, ownerType);
return DependencyProperty.RegisterCommon(name, propertyType, ownerType, defaultMetadata, validateValueCallback);
}
It looks like the only difference is how the PropertyMetaData is handled. Register has some extra logic using the passed in typeMetaData parameter while RegisterAttached just passes the PropertyMetaData along. Both methods then call RegisterCommon.
When it comes to implementing attached properties, is this the only difference? I looked at the Set/GetValue methods on DependencyObject. They are quite complicated (so I very easily could have missed some key bit of code that would explain things) but I couldn't find anything that would cause different code paths to be taken depending on what Register or RegisterAttached did.
Question:
Can someone explain to me what the difference I described above between Register and RegisterAttached is actually doing and how it allows attached properties to be used as a type of global property that is settable on any object? Maybe the place where the actual work is done is in the Set/GetValue methods on DependencyObject?
TLDR: the differences you see in your code example are indeed the only differences between "attached" and regular dependency properties, and after that code is run they are treated the same by WPF, there is no such concept as "attached" property internally at all (like some flag DependencyProperty.IsAttached or anything like that).
You are right that the difference between "attached" and regular dependency properties is just metadata handling.
First, it's perfectly possible to use regular property as attached one. For example suppose we have following property (note it's not "attached" in a sense RegisterAttached is not used):
public class TestProperties {
public static readonly DependencyProperty TestProperty = DependencyProperty.Register(
"Test",
typeof(Boolean),
typeof(TestProperties)
);
public static void SetTest(UIElement element, Boolean value) {
element.SetValue(TestProperty, value);
}
public static Boolean GetTest(UIElement element) {
return (Boolean) element.GetValue(TestProperty);
}
}
And suppose we have TextBlock like this:
<TextBlock x:Name="tb" local:TestProperties.Test="True" />
Because we have correct GetTest and SetTest methods - it's valid syntax. Now we can get the value of our properties as usual for attached properties:
// this returns true
var value = tb.GetValue(TestProperties.TestProperty);
Note that those GetTest and SetTest are not required either. They are needed only to be able to get\set "attached" property value from xaml. So far as you see regular and "attached" properties are exactly the same, there is no difference whatoever.
When we start to pass metadata - there are some differences though. When you register attached property, metadata you pass will be the default one and will apply to all types.
public class TestProperties {
public static readonly DependencyProperty TestProperty = DependencyProperty.RegisterAttached(
"Test",
typeof(Boolean),
typeof(TestProperties),
new PropertyMetadata(true, OnTestChanged)
);
private static void OnTestChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
}
public static void SetTest(UIElement element, Boolean value) {
element.SetValue(TestProperty, value);
}
public static Boolean GetTest(UIElement element) {
return (Boolean) element.GetValue(TestProperty);
}
}
If we now do:
var meta = TestProperties.TestProperty.GetMetadata(typeof(TextBlock));
// or any other type
We will see that metadata for any type is the default one you passed, with callback and other stuff. What that means, for example, is your value changed callback will be called whenever value of your attached property changes for any type (like TextBlock in our example above).
When we register property with metadata with regular Register - it behaves differently:
PropertyMetadata defaultMetadata = null;
if (typeMetadata != null && typeMetadata.DefaultValueWasSet())
{
defaultMetadata = new PropertyMetadata(typeMetadata.DefaultValue);
}
DependencyProperty dependencyProperty = DependencyProperty.RegisterCommon(name, propertyType, ownerType, defaultMetadata, validateValueCallback);
First it copies the default value (if any) and creates new metadata with only the default value. Then it creates property with that metadata but only default value is part of default metadata now. All callbacks are not part of default metadata. What that means is that now your callback will not be called when value of dependency property changes on arbitrary type (though default value is still preserved).
if (typeMetadata != null)
{
dependencyProperty.OverrideMetadata(ownerType, typeMetadata);
}
Now it takes full metadata you passed (with callbacks) and registers it only for the type you passed. In our example that means callbacks will be called only when value changes for type TestProperties itself (which must inherit DependencyObject from now on:
public class TestProperties : DependencyObject { // must inherit dependency object
public static readonly DependencyProperty TestProperty = DependencyProperty.Register(
"Test",
typeof(Boolean),
typeof(TestProperties),
new PropertyMetadata(true, OnTestChanged)
);
private static void OnTestChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
// this will be called ONLY when we do something like
// var prop = new TestProperties();
// prop.SetValue(TestProperty, true);
// but will NOT be called when we set value for TextBlock
}
public static void SetTest(UIElement element, Boolean value) {
element.SetValue(TestProperty, value);
}
public static Boolean GetTest(UIElement element) {
return (Boolean) element.GetValue(TestProperty);
}
}
If we want callback to be called for TextBlock - we can add that manually:
static TestProperties() {
TestProperties.TestProperty.OverrideMetadata(typeof(TextBlock), new PropertyMetadata(OnTestChanged));
}
But we have lost the (very useful) ability of "attached" properties to be able to track changes to values defined on any types (TextBlock, Button, whatever).
I hope this answers your question.
I am tring to extend an existing microsoft control called the PivotViewer.
This control has an existing property that I want to expose to my ViewModel.
public ICollection<string> InScopeItemIds { get; }
I have created an inherited class called CustomPivotViewer and I want to create a Dependency Property that I can bind to that will expose the values held in InScopeItemIds in the base class.
I have spent a fair while reading up about DependencyPropertys and am becomming quite disheartened.
Is this even possible?
You only need a DependencyProperty is you want it to be bindable, meaning: if you want to have, for example, a MyBindableProperty property in your control, with which you want to be able to do:
MyBindableProperty={Binding SomeProperty}
if, however, you want other DependencyProperties to bind to it, any property (either a DependencyProperty or a normal one) can be used.
I'm not sure what you really need, maybe you can clarify more, but if it's the first scenario that you want to implement, you can do it as follows:
create a DependencyProperty, let's call it BindableInScopeItemIds, like so:
/// <summary>
/// BindableInScopeItemIds Dependency Property
/// </summary>
public static readonly DependencyProperty BindableInScopeItemIdsProperty =
DependencyProperty.Register("BindableInScopeItemIds", typeof(ICollection<string>), typeof(CustomPivotViewer),
new PropertyMetadata(null,
new PropertyChangedCallback(OnBindableInScopeItemIdsChanged)));
/// <summary>
/// Gets or sets the BindableInScopeItemIds property. This dependency property
/// indicates ....
/// </summary>
public ICollection<string> BindableInScopeItemIds
{
get { return (ICollection<string>)GetValue(BindableInScopeItemIdsProperty); }
set { SetValue(BindableInScopeItemIdsProperty, value); }
}
/// <summary>
/// Handles changes to the BindableInScopeItemIds property.
/// </summary>
private static void OnBindableInScopeItemIdsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var target = (CustomPivotViewer)d;
ICollection<string> oldBindableInScopeItemIds = (ICollection<string>)e.OldValue;
ICollection<string> newBindableInScopeItemIds = target.BindableInScopeItemIds;
target.OnBindableInScopeItemIdsChanged(oldBindableInScopeItemIds, newBindableInScopeItemIds);
}
/// <summary>
/// Provides derived classes an opportunity to handle changes to the BindableInScopeItemIds property.
/// </summary>
protected virtual void OnBindableInScopeItemIdsChanged(ICollection<string> oldBindableInScopeItemIds, ICollection<string> newBindableInScopeItemIds)
{
}
in the OnBindableInScopeItemIdsChanged, you can update the inner collection (InScopeItemIds)
remember that the property you want to expose is read-only (it has no "setter"), so you might need to update it as so:
protected virtual void OnBindableInScopeItemIdsChanged(ICollection<string> oldBindableInScopeItemIds, ICollection<string> newBindableInScopeItemIds)
{
InScopeItemIds.Clear();
foreach (var itemId in newBindableInScopeItemIds)
{
InScopeItemIds.Add(itemId);
}
}
Hope this helps :)
EDIT:
I realized misunderstandings and here is a new version (in the context of the original question):
So, you can use the property you need for the binding, with following circumstances having in mind:
as this property is read-only, you will not be able to use it for 2-way binding.
as far as the containing type does not implement INotifyPropertyChanged, your target control used to display the data will not be notified about the changes to the property value.
as far as the returned by this property value does not implement INotifyCollectionChanged (one example is ObservableCollection<T>), the changes to the collection will not be affected on the target control which is used to display it.
Assuming I have an attached property defined like that :
public static string GetMyProperty(DependencyObject obj)
{
return (string)obj.GetValue(MyPropertyProperty);
}
public static void SetMyProperty(DependencyObject obj, string value)
{
obj.SetValue(MyPropertyProperty, value);
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.RegisterAttached("MyProperty", typeof(string), typeof(MyClass), new UIPropertyMetadata(0));
I can write the documentation for the property identifier (MyPropertyProperty) and for the accessors (GetMyProperty and SetMyProperty), but I have no idea where to put the documentation for MyClass.MyProperty attached property, since it is not an actual code element.
The MSDN library contains such documentation (see for instance Grid.Row), so it must be possible...
Where should I put the XML documentation comments for an attached property ?
There's an article about that for Sandcastle:
/// <summary>
/// This defines the <see cref="P:TestDoc.TestClass.IsBroughtIntoViewWhenSelected"/>
/// attached property.
/// </summary>
///
/// <AttachedPropertyComments>
/// <summary>This attached property indicates whether or not a tree view item is
/// brought into view when selected.
/// </summary>
/// <value>The default value is false</value>
/// </AttachedPropertyComments>
public static readonly DependencyProperty IsBroughtIntoViewWhenSelectedProperty =
DependencyProperty.RegisterAttached("IsBroughtIntoViewWhenSelected",
typeof(bool), typeof(TestClass),
new UIPropertyMetadata(false, OnIsBroughtIntoViewWhenSelectedChanged));
Even though the answer is a bit late I have found a solution for the appearance of the documentation during runtime of Visual Studio.
If you use ReSharper and press CTRLQ then the XML-Documentation that is added above the SetXXX-method is used to show for the Quick-Documentation.