Dependency Property in WPF - c#

I am working on a DependencyProperty for my Avalon dock controller. Here is some sample code which i have currently working on.
Requirement is: Create all dependency properties in one single class and access the property in View. Something like this.
<Button isPaneVisible="true"> or <Button isPaneVisible="{Staticresource path=name, Mode=twoway">
Could you please help me to reslove this issue?
namespace Avatar.UI.ViewModel
{
internal class DependencyPropertyClass : DependencyObject
{
public static readonly DependencyProperty IsPaneVisibleProperty =
DependencyProperty.RegisterAttached("IsPaneVisible", typeof(bool), typeof(DependencyPropertyClass),
new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, IsPaneVisible_PropertyChanged));
private static void IsPaneVisible_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
/// <summary>
/// Sets the IsPaneVisible for an element.
/// </summary>
public bool IsPaneVisible
{
get { return (bool)GetValue(IsPaneVisibleProperty); }
set
{
SetValue(IsPaneVisibleProperty, value);
}
}
}
}
<UserControl x:Class="Avatar.UI.View.ContentView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
xmlns:avalonDock="http://avalondock.codeplex.com"
xmlns:local="clr-namespace:Avatar.UI.ViewModel"
d:DesignHeight="300" d:DesignWidth="300">
<Button IsPaneVisible="true"></Button
</UserControl>

Defining an attached dependency property also requires the definition of static get and set accessor methods. See Custom Attached Properties for more information. Note also that your class does not necessarily need to be derived from DependencyObject as long as it only defines attached properties. But it is always a good idea to define such properties in a public class.
public class DependencyPropertyClass
{
public static readonly DependencyProperty IsPaneVisibleProperty =
DependencyProperty.RegisterAttached("IsPaneVisible", typeof(bool), typeof(DependencyPropertyClass),
new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, IsPaneVisible_PropertyChanged));
private static void IsPaneVisible_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
public static bool GetIsPaneVisible(DependencyObject obj)
{
return (bool)obj.GetValue(IsPaneVisibleProperty);
}
public static void SetIsPaneVisible(DependencyObject obj, bool value)
{
obj.SetValue(IsPaneVisibleProperty, value);
}
}
And as Cyborgx37 has pointed out, you would use an attached property in XAML like this:
<Button local:DependencyPropertyClass.IsPaneVisible="True" />

I could be wrong, but I think you are looking for this:
<Button local:DependencyPropertyClass.IsPaneVisible="true"></Button>
You have to specify the namespace, since IsPaneVisible is not part of the "http://schemas.microsoft.com/winfx/2006/xaml/presentation" namespace.
See: Attached Properties Overview
EDIT
It's been a while since I've done this, so things are slowly coming back to me as I scan your code. For an attached property, you cannot use an instance property to get/set the property. You must create static Get<PropertyName> and Set<PropertyName> functions:
public static void SetIsPaneVisible(DependenyObject target, Boolean value)
{
target.SetValue(IsPaneVisibleProperty, value);
}
public static bool GetIsPaneVisible(DependenyObject target)
{
return (bool)target.GetValue(IsPaneVisibleProperty);
}
Seriously... please read the linked article. It's all explained there.

The dependency property should derive the base calss for which you are going to create dependency property. for example, if you are going to create dependency property for button, then derive the base class button to your class.
This is how I have resolved my issue.

Related

Access Indirect property from XAML - WPF

I have a Control class named MyControl and it has direct bool property AccessDirectProperty and an Object MyControlSettings
public class MyControl : Control
{
public bool AccessDirectProperty
{
get; set;
}
public MyControlSettings ControlSettings
{
get;set;
}
}
Please find the MyControlSettings class details
public class MyControlSettings
{
public bool AccessIndirectProperty
{
get;set;
}
}
Direct property AccessDirectProperty can be accessible from XAML without any error.
<Window>
<Grid>
<local:MyControl AccessDirectProperty="True"/>
</Grid>
</Window>
But I cannot access the property AccessIndirectProperty from the object ControlSettings in XAML. The below code fails to do that.
<Window>
<Grid>
<local:MyControl AccessDirectProperty="True" ControlSettings.AccessIndirectProperty=""/>
</Grid>
</Window>
Can anyone help me on this?
I'm afraid that XAML does not support accessing "nested" properties.
You could, however, make ControlSettings an independent class with attached properties:
public class ControlSettings : DependencyObject
{
public static readonly DependencyProperty AccessIndirectPropertyProperty =
DependencyProperty.RegisterAttached(
"AccessIndirectProperty", typeof(bool), typeof(ControlSettings),
new PropertyMetadata(false));
public static bool GetAccessIndirectProperty(DependencyObject d)
{
return (bool) d.GetValue(AccessIndirectPropertyProperty);
}
public static void SetAccessIndirectProperty(DependencyObject d, bool value)
{
d.SetValue(AccessIndirectPropertyProperty, value);
}
}
Then,
<local:MyControl x:Name="myControl"
AccessDirectProperty="True"
ControlSettings.AccessIndirectProperty="True" />
would set a value which could be accessed via
var p = ControlSettings.GetAccessIndirectProperty(myControl); // yields True
Now, on a technical level, the following is not useful to modify a property of an existing MyControlSettings instance provided through MyControl.ControlSettings. However, if your use case allows creating and assigning an entirely new MyControlSettings instance to MyControl.ControlSettings, you can do so in XAML:
<local:MyControl>
<ControlSettings>
<local:MyControlSettings AccessIndirectProperty="true" />
</ControlSettings>
</local:MyControl>
A side note: The term "ControlSettings" suggest to me that you want to kind of "package" control settings/properties in some kind of MyControlSettings "container". Now, i don't know why and what the motivation for this is, but keep in mind that choosing this approach can make it very hard or even impossible to use data bindings in a meaningful way where such a settings property is supposed to be the binding target. If you want to be able to use individual settings as binding target (like AccessIndirectProperty="{Binding Path=Source}"), i would rather suggest your MyControl exposes those settings individually as DependencyProperties.

nested property propertychanged event only fired once in usercontrol in silverlight

There are two Viewmodels, both of them had implemented the INotifyPropertyChanged interface (I have called the OnpropertyChanged("propertyname") in my actual code).
Public Class A{
public B BProperty
{
get
{
return _BProperty;
}
set
{
if (_BProperty != null)
_BProperty.PropertyChanged -= _BProperty_PropertyChanged;
_BProperty = value;
OnPropertyChanged("BProperty");
if (_BProperty != null)
_BProperty.PropertyChanged += _BProperty_PropertyChanged;
}
}
void _BProperty_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "status")
{
OnPropertyChanged("BProperty");
}
}
B _BProperty;
}
Public Class B
{
public int status{get;set;}
}
I also had a userControl:
<MyUserControl ...
... >
<Grid>
</Grid>
</MyUserControl>
And I had a dependencyProperty:
/// <summary>
/// File status
/// </summary>
public int Filestatus
{
get { return (int)GetValue(FilestatusProperty); }
set { SetValue(FilestatusProperty, value); }
}
public static readonly DependencyProperty FilestatusProperty =
DependencyProperty.Register(
"Filestatus",
typeof(int),
typeof(MyUserControl),
new PropertyMetadata(0, OnFilestatusPropertyChanged));
private static void OnFilestatusPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MyUserControl control = (MyUserControl)d;
if (e.NewValue != null)
{
}
}
edit:2015/09/21
Add the get/set methods:
public static readonly DependencyProperty FileStatusProperty = DependencyProperty.RegisterAttached(
"FileStatus", typeof(int), typeof(FileStatusIconControl), new PropertyMetadata(0, PropertyChangedCallback));
public static int GetFileStatus(DependencyObject source)
{
return (int)source.GetValue(FileStatusProperty);
}
public static void SetFileStatus(DependencyObject target, int value)
{
target.SetValue(FileStatusProperty, value);
}
private static void PropertyChangedCallback(
DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
FileStatusIconControl fsic = dependencyObject as FileStatusIconControl;
if(fsic != null)
wahahahahaha;
}
edit end
I used this userControl in my mainPage like this:
<mainPage ...
...>
<Grid>
<MyUserControl Filestatus={Binding Bproperty.status} />
<TextBox Text={Binding Bproperty.status} />
</Grid>
</mainPage>
The datacontext of mainPage is an instance of Class A.
My question is:
When status is changed, the text of textbox is changed, but the OnFilestatusPropertyChanged method only was called once when Usercontrol is loaded. Why?
Thanks.
I will start by saying that while I was looking into your question I ran into some problems with the code you have provided. I appreciate that somewhere you have some real code which has a problem and you cannot share this code with us so have tried to reproduce the problem in a smaller set of files.
However, if you do do this, please at least verify that the code you have provided runs and exhibits the problem. It is evident that you haven't tried to run your sample code (particularly the XAML) as there are problems with it:
attribute values should be surrounded in double-quotes,
the binding path Bproperty.status should be BProperty.status (first P capitalised).
All these things slow down someone trying to help you. Worse still, when I do find a problem with your code I can't be sure whether it's a genuine problem that your real code also has or whether it's something you introduced putting together your sample code. So all I can do is point out all the problems I find in the hope that one of them is the problem you have in your real code.
Firstly, your TextBox's Text property binding doesn't contain Mode=TwoWay. In WPF, this binding is TwoWay by default, but in Silverlight all bindings are OneWay by default. If you are familiar with WPF, this may be why you omitted Mode=TwoWay.
Secondly, I don't see why you have implemented class B as you have, apparently leaving it up to class A to fire property-changed events on its behalf. This approach doesn't work: when Silverlight updates the value in the status property of a B instance, it does so by calling the status setter of the B instance. Your B class has only an autogenerated property setter, which certainly doesn't fire the PropertyChanged event. Because this event wasn't fired, Silverlight doesn't know that is has some updates to do, and furthermore your A class isn't aware that it has changed either.
I would implement INotifyPropertyChanged in the usual way in class B, by calling OnPropertyChanged in the status setter. I would also remove the BProperty_PropertyChanged event handler in class A as I don't think it does you any favours.

Adding my own properties to any control

Is it possible to add my own custom property to any control?
I want to do something similar to e.g. x:Name="aname".
So I could write in xaml:
<TextBox local:MyCustomProperty="myValue" />
Maybe someone can provide a small sample or a link showing how to do this.
In google I only find markup extensions, which doesn't seem to be what I am looking for.
You can use an Attached Property.
public class MyCustomProperty
{
#region Color dependency property
public static readonly DependencyProperty ColorProperty;
public static Color GetColor(DependencyObject obj)
{
return (Color)obj.GetValue(ColorProperty);
}
public static void SetColor(DependencyObject obj, Color value)
{
obj.SetValue(ColorProperty, value);
}
#endregion
static MyCustomProperty()
{
//register attached dependency property
var metadata = new FrameworkPropertyMetadata(Colors.Transparent);
ColorProperty = DependencyProperty.RegisterAttached("Color",
typeof(Color),
typeof(MyCustomProperty), metadata);
}
}
And in XAML
<DockPanel custom:MyCustomProperty.Color="{StaticResource MyColor}" >

Global Scope Attached Dependency Property

I would like to do something like a static variable in normal programming, only in XAML using Dependency Properties.
Meaning I would like the property to :
Be one instance
Be visible by every element
Be bindable
How do I do that ?
It sounds like you want an attached property that always applies to every element. I think the easiest way to make that work would be through the CoerceValueCallback of the dependency property, where you could force it to always return the static value regardless of the element's local value (you would update the static value in the PropertyChangedCallback).
This seems like an odd way to use the dependency property system, though. Maybe you just need a central binding source? You can bind to a static instance by assigning Binding.Source using x:Static:
{Binding Source={x:Static Member=global:GlobalObject.SharedInstance},
Path=SharedValue}
Note that SharedValue isn't a static property; it's a property of an instance accessed from the static SharedInstance property:
public class GlobalObject {
private static readonly GlobalObject _instance = new GlobalObject();
public static GlobalObject SharedInstance { get { return _instance; } }
public object SharedValue { get; set; }
}
Easy.
Create an attached DependencyProperty on the DependencyObject Type.
public static readonly DependencyProperty DerpProperty =
DependencyProperty.RegisterAttached(
"Derp",
typeof(DependencyObject),
typeof(Herp),
new FrameworkPropertyMetadata());
public static void SetDerp(DependencyObject element, Herp value)
{
element.SetValue(DerpProperty, value);
}
public static Herp GetDerp(DependencyObject element)
{
return (Herp)element.GetValue(DerpProperty);
}
Defined on any type, it can be used on any type as well. In this example, it creates a new property called Derp on all DependencyObject instances that gets/sets an associated Herp value.
Assuming this is defined in a type called LolKThx in the namespace WpfFtw, you might use it in this way...
<Textblock
xmlns:lol="clr-namespace:WpfFtw"
lol:LolKThx.Derp="There's an implicit conversion for string -> Herp, btw" />
You can specify callbacks in your FrameworkPropertyMetadata to perform any action you need on setting/getting values.

FreezableCollection not providing change notification when sub properties change

I have a FreezableCollection for which I want to monitor the changes to sub properties. Here is a subsection of the code:
public class FieldHeading : DependencyObject
{
public static readonly DependencyProperty LayoutProperty = DependencyProperty.Register("Layout", typeof(FieldHeadingLayout), typeof(FieldHeading),
new FrameworkPropertyMetadata(FieldHeadingLayout.Above,
FrameworkPropertyMetadataOptions.AffectsRender |
FrameworkPropertyMetadataOptions.AffectsMeasure |
FrameworkPropertyMetadataOptions.AffectsParentMeasure));
public FieldHeadingLayout Layout
{
get { return (FieldHeadingLayout) GetValue(LayoutProperty); }
set { SetValue(LayoutProperty, value); }
}
}
public class FieldPanel : FrameworkElement
{
private static readonly DependencyProperty FieldHeadingProperty = DependencyProperty.Register("FieldHeading", typeof(FreezableCollection<FieldHeading>), typeof(FieldPanel),
new FrameworkPropertyMetadata(null,
FrameworkPropertyMetadataOptions.AffectsMeasure |
FrameworkPropertyMetadataOptions.AffectsParentMeasure |
FrameworkPropertyMetadataOptions.AffectsRender, HeadingChanged));
private static void HeadingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Debug.WriteLine("Hello");
}
public FreezableCollection<FieldHeading> FieldHeadings
{
get
{ return (FreezableCollection<FieldHeading>) GetValue(FieldHeadingProperty); }
set { SetValue(FieldHeadingProperty, value);}
}
public FieldPanel()
{
AddVisual(_contentVisual = new DrawingVisual());
FieldHeadings = new FreezableCollection<FieldHeading>();
}
}
Then we assign a new value to Layout for one of the FieldHeadings, no change notification is generated. Obviously I'm missing something important. HeadingChanged is never called.
The MSDN help on FreezableCollection, which can be found here: FreezableCollection, states:
Event changed... Occurs when the Freezable or an object it contains is modified. (Inherited from Freezable.)
Thanks in advance for any help.
~ Cameron
Actually, you can do what you're trying to do. This is exactly why FreezableCollection<T> exists! All you need to do is change FieldHeading to derive from Freezable instead of DependencyObject and changes to items in the collection will give the same change notification as if the entire item had been replaced.
This is an incredibly useful and little-known WPF feature.
From Charles Petzold himself,
These freezable collection classes fire change notifications whenever items are added to or removed from the collection, of course, but also when a dependency property of any item within the collection changes. This is an extremely powerful mechanism.
Here is a small sample that demonstrates how to use FreezableCollection<>. I made a new WPF project in Visual Studio. Here is the XAML for MainWindow.xaml.cs and its C# code behind:
<Window x:Class="FreezableCollection.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:FreezableCollection"
x:Name="Root"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<DataTemplate DataType="{x:Type local:MyFreezable}">
<CheckBox IsChecked="{Binding IsNice}" Content="Check me!"/>
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox ItemsSource="{Binding ElementName=Root, Path=MyFreezables}" />
</Grid>
</Window>
using System.Windows;
namespace FreezableCollection
{
public partial class MainWindow : Window
{
public static readonly DependencyProperty MyFreezablesProperty =
DependencyProperty.Register("MyFreezables", typeof(MyFreezableCollection), typeof(MainWindow), new FrameworkPropertyMetadata(null, FreezablesChangedCallback));
public MyFreezableCollection MyFreezables
{
get => (MyFreezableCollection)GetValue(MyFreezablesProperty);
set => SetValue(MyFreezablesProperty, value);
}
public MainWindow()
{
InitializeComponent();
MyFreezables = new MyFreezableCollection { new MyFreezable() };
}
private static void FreezablesChangedCallback(object sender, DependencyPropertyChangedEventArgs args)
{
MessageBox.Show("Changed!");
}
}
public class MyFreezableCollection : FreezableCollection<MyFreezable>
{
protected override Freezable CreateInstanceCore() => new MyFreezableCollection();
}
public class MyFreezable : Freezable
{
public static readonly DependencyProperty IsNiceProperty =
DependencyProperty.Register("IsNice", typeof(bool), typeof(MyFreezable), new PropertyMetadata(false));
public bool IsNice
{
get => (bool)GetValue(IsNiceProperty);
set => SetValue(IsNiceProperty, value);
}
protected override Freezable CreateInstanceCore() => new MyFreezable();
}
}
The sample displays a list containing a single CheckBox. Click on it to toggle the IsNice property on an item in the data-bound MyFreezableCollection. Even though nothing is added to or removed from the list, the dependency property change callback is invoked.
The change notification handler will only notify you when the value of the property changes, so in this case if the freezable collection changes to a new collection. In your property changed handler you need to subscribe to the CollectionChanged event and in that event you need to subscribe to the PropertyChanged event on the new item. Now, finally, you have an event that will allow you to react to changes in properties of items belonging to a freezable collection that is a dependency property. Remember to unsubscribe to the old collection's and old item's events.
The thing is that you subscribe only for the FieldHeadings property changes, meaning you will receive notifications only if someone assigns a new instance of the collection itself, using, for example, the FeildHedings property setter.
In order to receive notifications when the Layout property changes, you have to subscribe to it on each individual instance of FieldHeading.

Categories

Resources