I've run into an odd problem with attached properties where when I assign the property name in the call to RegisterAttached and name properly for the name of the attached property (say TranslateProperty and "Translate") the code for the attached property implementation doesn't fire. Just doesn't get called. If I change the string name to anything other than Translate (say "Translate_") the code gets called just fine.
Here's the implementation:
public class TranslateExtension : DependencyObject
{
public static readonly DependencyProperty TranslateProperty =
DependencyProperty.RegisterAttached("Translate_",
typeof(bool),
typeof(TranslateExtension),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender));
public static void SetTranslate(UIElement element, bool value)
{
AssignKeys(element);
element.SetValue(TranslateProperty, value);
}
public static bool GetTranslate(UIElement element)
{
return (bool)element.GetValue(TranslateProperty);
}
public bool Translate
{
set { base.SetValue( TranslateProperty, value); }
}
...
}
The above actually works because the property in the string is Translate_. If I change the string value to "Translate" it failed.
I have 2 other attached properties in the same class and they exhibit exactly the same behavior - same name as the AttachedProperty and they don't get called. Name it something else and it works.
I'm not sure what's going on here. My code is actually working with the invalid names, but I don't understand why, and more importantly I'm not sure if this wrong naming causes any side effects.
Can anybody see whether I'm overlooking something painfully obvious? I've revisited a few examples in articles of AttachedProperties and I don't see those implementations using special names - they always name the string property the same as the attached properties.
You shouldn't put extra code in your SetTranslate since it won't get called. From MSDN here:
Implications for Custom Dependency Properties
Because the current WPF implementation
of the XAML processor behavior for
property setting bypasses the wrappers
entirely, you should not put any
additional logic into the set
definitions of the wrapper for your
custom dependency property. If you put
such logic in the set definition, then
the logic will not be executed when
the property is set in XAML rather
than in code.
Similarly, other aspects of the XAML
processor that obtain property values
from XAML processing also use GetValue
rather than using the wrapper.
Therefore, you should also avoid any
additional implementation in the get
definition beyond the GetValue call.
Instead, add a PropertyChangedCallback to your FrameworkPropertyMetadata.
Related
In my WPF application, I made my own control and want to bind to a property in it. This is what I tried so far:
public partial class BreadcrumbContainer : Grid
{
public static readonly DependencyProperty TestProperty =
DependencyProperty.Register(nameof(Test), typeof(string), typeof(BreadcrumbContainer), new PropertyMetadata(string.Empty));
public string Test
{
get { return (string)GetValue(TestProperty); }
set { SetValue(TestProperty, value); Refresh(); }
}
public BreadcrumbContainer()
{
InitializeComponent();
}
private void Refresh()
{
// never gets called
}
}
And I try to bind to my Test property like this:
<controls:BreadcrumbContainer Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" Test="{Binding SearchMessage}"/>
And in my view model, I have a property SearchMessage. All my other bindings are working, so it must be something I'm doing wrong in my BreadcrumbContainer
Quoting from Adam Nathan's WPF Unleashed
.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!
The property wrapper in your case is your Test property. So basically, don't put any logic in there since it never gets called in runtime. The proper way to do this is to use a property changed callback. One example for you is in the answer found here. Notice the callback initialization in the DependencyProperty.Register call.
There is also the "official" documentation in Checklist for Defining a Dependency Property
- Implementing the "Wrapper":
In all but exceptional circumstances, your wrapper implementations should perform only the GetValue and SetValue actions, respectively. The reason for this is discussed in the topic XAML Loading and Dependency Properties.
From XAML Loading and Dependency Properties:
Because the current WPF implementation of the XAML processor behavior for property setting bypasses the wrappers entirely, you should not put any additional logic into the set definitions of the wrapper for your custom dependency property. If you put such logic in the set definition, then the logic will not be executed when the property is set in XAML rather than in code.
This is one example of a baffling behavior in WPF that will drive you mad if you don't know. It's not really your fault but I suggest reading WPF Unleashed by Adam Nathan - and all the online documentation - to learn more about these types of pitfalls.
I just discovered than I can do the following:
var button = new Button();
button.SetValue(TextBlock.TextProperty, "text");
var text = (string)button.GetValue(TextBlock.TextProperty); // text is "text"
While the above example is a bit unrealistic, it does show that I can attach a regular dependency property onto another object. It doesn't have to be a an attached property (TextBlock.TextProperty is not registerd with DependencyProperty.RegisterAttached().
This bares the questions why are there attached properties in the first place? The only difference I can see for now ist that I can't attach regular dependency properties in XAML. But that's about it. Are there any other differences?
Update:
To make it more clear, the below code works and looks pretty close to an attached property from the end users perspective:
public static class AttachedPropertyDeclarer
{
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text",
typeof(string),
typeof(Button),
new PropertyMetadata(default(string),OnTextChanged));
private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// do something when text changed
}
}
...
button.SetValue(AttachedPropertyDeclarer.TextProperty, "text");
var text = (string)button.GetValue(AttachedPropertyDeclarer.TextProperty);
Compare this to the attached property way:
public static class AttachedPropertyDeclarer
{
public static readonly DependencyProperty TextProperty = DependencyProperty.RegisterAttached(
"Text",
typeof(string),
typeof(AttachedPropertyDeclarer),
new PropertyMetadata(default(string),OnTextChanged));
private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// do something when text changed
}
}
The only effective differnce to an attached property here is that I have to declare the owner of type Button whereas in a attached property it would usually be AttachedPropertyDeclarer. But this only needs to be done if I need a changed event handler (i.e. OnTextChanged).
Regarding your example, you have not as you say, attached a regular dependency property onto another object. All your code has achieved is to store a string value in a Dictionary along with a reference to your object. That does not make it an Attached Property - importantly, you cannot access that string value from the Button directly, as there is no Text property on a Button.
What your code does is actually very similar to this:
Dictionary<object, object> values2 = new Dictionary<object, object>();
var button = new Button();
values2.Add(button, "text");
string text = values2[button].ToString();
Now to answer your question:
The main reason to declare an Attached Property is in order to add a property to a type that you didn't declare, thereby extending its functionality.
A great example of this would be to add a SelectedItems property to the ItemsControl or ListBox class. In doing so, we extend the current, or default functionality of the class. Another good example would be declaring an Attached Property that automatically brings added items into view (again in an ItemsControl or ListBox class).
UPDATE >>>
According to your comments, you seem to be refusing to accept the differences that I have outlined... you said:
There is literally no difference from the end users perspective except that I can't use it in XAML.
Firstly, do you not think that this is a huge difference?.. you won't be able to use it for data binding for a start. Furthermore, you keep saying that you can attach a property to a type that you haven't declared using a DependencyProperty, but you are 100% incorrect. You can reference an Attached Property directly in both code and XAML, while you can't reference what you are calling your attached property directly in either XAML or code.
All you are doing is storing a value in a Dictionary and you certainly don't need the overhead of a DependencyProperty to do that. There really is no comparison between doing that and declaring an Attached Property. From the Attached Properties Overview page on MSDN:
You might create an attached property when there is a reason to have a property setting mechanism available for classes other than the defining class.
Note the following part: a property setting mechanism
Adding values into a Dictionary is not a property setting mechanism. So again, you lose the ability to use your pretend Attached Property in Styles, Animations, Triggers, etc.
To clarify this situation for once and for all, you can develop a simple test project. Implement the IList SelectedItems Attached Property for a ListBox that I mentioned (you can find online tutorials for this) and then do the same using your pretend Attached Property (if it is even possible). The difference in the simplicity of development bewteen the two will clearly show you why you should use an Attached Property instead of a regular DependencyProperty.
If you look closely at dependency property identifier, all DP's are registered with class DependencyProperty and we pass the Owner class type and property name at time of registration.
Sample:
public static readonly DependencyProperty IsSpinningProperty =
DependencyProperty.Register(
"IsSpinning", typeof(Boolean), typeof(OwnerClass));
At time of registration it creates some unique hash code combining property name and owner class type to represent each DP uniquely.
So, when you set value for that DP on some object like in your case on Button, code flow is like this:
First it will get the unique value generated at time of registration of property and add the key value pair in private dictionary named _effectiveValues declared in class Dependency Object with Key set to unique hashcode at time of registration and value being the value set by user.
Note - No written documentation for this on MSDN but verified this by peeking into source code using reflector.
So, when you set the value from code behind it will work like I mentioned above because it does not validate before adding value in the dictionary if it belongs to that type or not and fetching value will get you the value from dictionary.
Not sure but might be constraint is there in XAML only where WPF guys enforced the type check. Sadly there is no written documentation for this on MSDN.
Attached properties are discovered, when you want to have control over an existing control, but dont want to extend it. A pretty good example is, there is no way to bind BlackOutDates property in XAML for WPF DatePicker. In that case you can use an Attached Property to attach a custom functionality to map the BlackOutDates. This suits good in MVVM, since attached properties provided way for binding in XAML.
public class BlackOutDatesAdapter
{
public static List<DateTime> GetBlackOutDates(DependencyObject obj)
{
return (List<DateTime>)obj.GetValue(BlackOutDatesProperty);
}
public static void SetBlackOutDates(DependencyObject obj, List<DateTime> value)
{
obj.SetValue(BlackOutDatesProperty, value);
}
// Using a DependencyProperty as the backing store for BlackOutDates. This enables animation, styling, binding, etc...
public static readonly DependencyProperty BlackOutDatesProperty =
DependencyProperty.RegisterAttached("BlackOutDates", typeof(List<DateTime>), typeof(BlackOutDatesAdapter), new PropertyMetadata(null, OnBlackOutDatesChanged));
private static void OnBlackOutDatesChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var control = sender as DatePicker;
var list = (List<DateTime>)e.NewValue;
foreach(var date in list)
{
control.BlackoutDates.Add(new CalendarDateRange(date));
}
}
}
Binding in XAML will look like this,
<DatePicker VerticalAlignment="Center"
Width="200"
local:BlackOutDatesAdapter.BlackOutDates="{Binding BlackOutDates}"
DisplayDate="{Binding DisplayDate}" />
In the callback of property, you can do your own mapping of adding the dates to DatePicker. For more information, please read this post.
I'm a little bit unclear with all this magic.
As I understood dependency properties get inherited from the DependencyObject, so values are stored:
in the instance itself if value is assigned (in the local dictionary)
or taken from the link to a parent element if value is not specified.
protected object GetValue(string propertyName)
{
if (LocalValues.ContainsKey(propertyName))
{
return LocalValues[propertyName];
}
return Parent.GetValue(propertyName);
}
Am I correct in this?
I also don't understand where are values for attached properties stored?
Control.FontSizeProperty = TextElement.FontSizeProperty.AddOwner(
typeof(Control), new FrameworkPropertyMetadata(SystemFonts.MessageFontSize,
FrameworkPropertyMetadataOptions.Inherits));
Does AddOwner method call on Attached property assigns value to the instance field? When does this happen and where does the value go?
Thanks!
Values for dependency properties are stored inside the objects (derived from DependencyObject) which we apply a property value to.
Let's take your TextElement.FontSizeProperty attached property for example:
<StackPanel TextElement.FontSize="20" ... >
...
</StackPanel>
XAML parser translates it to the following:
...
TextElement.SetFontSize(stackPanel, 20);
...
which is internally:
public static void SetFontSize(DependencyObject element, double value)
{
element.SetValue(TextElement.FontSizeProperty, value);
}
So, setting TextElement.FontSize on a stackPanel object is the same as calling
stackPanel.SetValue(TextElement.FontSizeProperty, value)
SetValue() is a method defined in the DependencyObject class. Inside the method many complex things happen, but in the end the effective value of a dependency property is wrapped in a structure called EffectiveValueEntry and stored in the following instance field inside DependencyObject:
private EffectiveValueEntry[] _effectiveValues;
The property system in WPF is pretty complex. MSDN really has a lot of information, but it is often hard to find. While there are many ways a DependencyProperty can be set, I'm not sure that you need to care where the values are stored.
For local values, you can assume that it is stored on the DependencyObject (again you shouldn't care where it is stored), with the caveat that they are not stored based on strings. It truly is associated with an instance of DependencyProperty. This is why you would want to add an owner to the property. If somebody sets TextElement.FontSize on your control, it is just like setting your local FontSize property.
As far as inheriting values for a property from a parent, this only happens with attached properties. From the MSDN entry for FrameworkPropertyMetadataOptions:
Although property value inheritance might appear to work for
nonattached dependency properties, the inheritance behavior for a
nonattached property through certain element boundaries in the runtime
tree is undefined. Always use RegisterAttached to register properties
where you specify Inherits in the metadata.
I would like to know when is actually what happening inside initalization process of controls when I start a WPF application?
When are DP initalized? When Binding? When does DataContext get set? Is DataContext avaialbe in constructor of a control? Is there any kind of order?
I realized I ran into a trap that once I set a value on getter/setter of a DP inside constructor of a control the DP value gets updated but immediately also the values gets rolled back to default value which was null.
So my guess is that contructors get initalized first and then dependency properties.
Can somebody help me out with this?
Edit: Just for Rachel. The dp receives the value 234 and immedialty rolls back to null. I think its because constructor gets called first and then subsequently the initalizing of dps happens which sets dp back to null because null is default value. Am i thinking wrong about this? What is the order of initalization steps of a control or dependency object.
class MySuperDuperCoolClass : ContentControl
{
public MySuperDuperCoolClass()
{
InitalizeComponents();
this.MySuperDuperProperty = "234";
}
public string MySuperDuperProperty
{
get { return (string)GetValue(MySuperDuperPropertyProperty);}
set { SetValue(MySuperDuperPropertyProperty, value);}
}
public static DependencyProperty MySuperDuperPropertyProperty =
DependencyProperty.Register("MySuperDuperProperty", typeof(string), typeof(MySuperDuperCoolClass),
new PropertyMetadata(null));
}
I find the DispatcherPriority Enum useful for recalling the exact event order:
Send
Normal - Constructors run here
DataBind
Render
Loaded
Background
ContextIdle
ApplicationIdle
SystemIdle
Inactive
Invalid
Input
As you can see, Constructors get run first, followed by data bindings.
DependencyProperties get initialized when the object gets created, just like any other property, so that would occur prior to the constructor being run so the property exists in the constructor.
Setting the DataContext property or other DependencyProperties works just like any other property you are setting. If you set them with a binding, they'll get evaluated after the constructor. If you set them in the XAML, they'll get set in the Constructor. If you set them in the Loaded event, they'll get set after everything has been constructed, bound, and rendered.
You also might find this SO answer useful:
Sequence of events when a Window is created and shown
As requested, here is the sequence of major events in WPF when a
window is created and shown:
Constructors and getters/setters are called as objects are created, including PropertyChangedCallback, ValidationCallback, etc on the
objects being updated and any objects that inherit from them
As each element gets added to a visual or logical tree its Intialized event is fired, which causes Styles and Triggers to be
found applied in addition to any element-specific initialization you
may define [note: Initialized event not fired for leaves in a logical
tree if there is no PresentationSource (eg Window) at its root]
The window and all non-collapsed Visuals on it are Measured, which causes an ApplyTemplate at each Control, which causes additional
object tree construction including more constructors and
getters/setters
The window and all non-collapsed Visuals on it are Arranged
The window and its descendants (both logical and visual) receive a Loaded event
Any data bindings that failed when they were first set are retried
The window and its descendants are given an opportunity to render their content visually
Steps 1-2 are done when the Window is created, whether or not it is
shown. The other steps generally don't happen until a Window is
shown, but they can happen earlier if triggered manually.
Edit based on code added to question
Your DependencyProperty.Register method looks funny to me. The signature of the method doesn't match any of the overloads for that method, and you're using what appears to be a custom UIProperty class to set the default value instead of the normal PropertyMetadata.
I can confirm that if your code runs as expected with a normal DependencyProperty.Register signature, so the likely cause of your problem is either somewhere within your custom code, or its with how you are using/setting the property.
The code I used for a quick sample test is this:
public partial class UserControl1 : ContentControl
{
public UserControl1()
{
InitializeComponent();
this.TestDependencyProperty = "234";
}
public string TestDependencyProperty
{
get { return (string)GetValue(TestDependencyPropertyProperty); }
set { SetValue(TestDependencyPropertyProperty, value); }
}
public static DependencyProperty TestDependencyPropertyProperty =
DependencyProperty.Register("TestDependencyProperty", typeof(string), typeof(UserControl1),
new PropertyMetadata(null));
}
and the XAML is
<ContentControl x:Class="WpfApplication1.UserControl1"
x:Name="TestPanel" ...>
<Label Content="{Binding ElementName=TestPanel, Path=TestDependencyProperty}"/>
</ContentControl>
In WPF you are setting default values for DP with PropertyMetaData not via constructor.
public partial class UserControl1 : ContentControl
{
public UserControl1()
{
InitializeComponent();
}
public string TestDependencyProperty
{
get { return (string)GetValue(TestDependencyPropertyProperty); }
set { SetValue(TestDependencyPropertyProperty, value); }
}
public static DependencyProperty TestDependencyPropertyProperty =
DependencyProperty.Register("TestDependencyProperty", typeof(string), typeof(UserControl1),
new PropertyMetadata("234"));
}
I have a DataTemplate that needs to set the IsSelected property on an ItemsControl's container (such as TreeViewItem, ListViewItem or ComboBoxItem). However, it doesn't know the type of the container until it's passed in to it. Since IsSelected isn't part of a common base class or interface, nor is it a common dependency property registered with AddOwner to the various classes (Duh, MS!!! WTF not?!!), I ended up with this mess...
if (container is TreeViewItem) {
(container as TreeViewItem).IsSelected = true;
return;
}
if (container is ListBoxItem) {
(container as ListBoxItem).IsSelected = true;
return;
}
if (container is ComboBoxItem) {
(container as ComboBoxItem).IsSelected = true;
return;
}
...which not only is verbose, but requires me to modify it if I ever use a different ItemsControl that uses different container types! Not good!
Sure I could enhance it a little by putting this logic in extension methods (damn C# for not having extension properties!!) called IsContainerSelected and SetContainerSelected and putting them on UIElement, then moving the above code inside there, but it's just making the outside neater. The inside is still a mess.
My only other thought is to use reflection and look for an IsSelected property and use that if found, but I'm always leery of doing things like that. However, since there isn't a common interface or base class, I'm not really sure I have a choice here.
For context, I'm sharing a complex data template between several different ItemsControls and the template itself has controls that can receive focus such as checkbox and textbox. However, when those controls receive focus via the mouse, the underlying container item doesn't get selected and whatever was selected before remains so.
My workaround is to use an attached behavior that utilizes the preview events to intercept the focus before it happens and set the underlying item accordingly, which works great when I've hard-coded TreeViewItem or ListBoxItem, etc., but I don't want to hard-code the type since the control shouldn't really care. So that's the part that breaks down.
Ugh!!! Why didn't MS just register the same attached property or at least create an ISelectableContainer interface?!!
I have read your answer, and it does make sense - in your case, IsSelected may obviously be part of the ViewModel, and that seems to be the best solution in your case.
But you asked for further explanation about C# dynamic features. C# 4.0 now has some dynamic functionalities, which allow us to create code that would only be possible in languages like Python, Ruby or JavaScript. This, of course, has its cost - a dynamic abuse would not only make code slower, but also more confusing - because you would lose compile-time errors and IntelliSense.
I have written a simple example so you may understand it better:
public class ClassOne
{
public int SameProperty { get; set; }
}
public class ClassTwo
{
public int SameProperty { get; set; }
}
public class ClassThree
{
public string SameProperty { get; set; }
}
public partial class Form1 : Form
{
public Form1() {
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) {
dynamic wrapper = new ClassOne();
wrapper.SameProperty = 5;
wrapper = new ClassTwo();
wrapper.SameProperty = 15;
wrapper = new ClassThree();
wrapper.SameProperty = "Now it is a string!";
// And now a run-time error...
wrapper.AnotherProperty = "And this won't work...";
}
}
As you can see, wrapper has no definite type whatsoever - a dynamic reference will allow any kind of method or property invocation, since the actual binding will only be made during run-time, not compile-time.
Of course, this example is very naive, but sometimes dynamic code may be useful - it is a good option to avoid explicit reflection, or to avoid long if...else statements based on type (like your snippet above).
I'm not sure that I fully understand your problem, but you could try adding an IsSelected boolean to your model and then binding that property against the Item control it's contained in. That way, you just have to worry about setting that property in the model, regardless of the container.
Per #mdm20's answer, he suggested modifying the ViewModel, which is of course normally what you want to do. However this is a purely view-related issue (keyboard navigation-related) and isn't reflected in the ViewModel at all, nor in this case should it be.
But that gave me an idea! Since I'm using a custom control to render the item in whichever items control (via its data template) it's being added to, that control naturally does have multiple instances (all of which are pointing to the same ViewModel instance), which is what I want!
Therefore, rather than adding the IsSelected to the ViewModel, I added it to the user control itself, then I just bind to that within the data template for the respective ItemsControl which I do know about. I can then set the IsSelected property in the code-behind for the user control as needed (i.e. during the preview mouse events, etc.) and the underlying ItemsControl responds appropriately! Works great and keeps the ViewModel clean since neither the model, nor the viewmodel need to know about it. The IsSelected remains purely in the UI which is where in this particular case it should be!