I change the layout dynamically at runtime.
There was a task to rewrite the Attached Property values from the element being removed to the embedding element.
I get all the assigned properties using the GetLocalValueEnumerator() method.
But now I need to somehow select only the Attached property from the general DependecyProperty list.
I read a good explanation about the differences between them here: What's the difference between a dependency property and an attached property in WPF?
But I did not understand how to use this information for my purpose.
Updating
According to the comments from #Clemens, I created such a method.
So far it works acceptable for my solution.
Perhaps in the future there will be some nuances.
And then I will think about how to eliminate them.
private static bool IsDependecyProperty(Type ownerTYpe, DependencyProperty property)
{
if (typeof(DependencyObject).IsAssignableFrom(property.OwnerType) &&
property.OwnerType.IsAssignableFrom(ownerTYpe))
{
string propName = property.Name;
return ownerTYpe.GetProperty(propName) != null;
}
return false;
}
Related
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've built a custom control in WPF that inherits from ListBox. In this I have implementet my own property that is a BindingList. To make this property bindable I've implemeneted it as a DependencyProperty:
public BindingList<CheckableListItem> CheckedItems
{
get
{
return (BindingList<CheckableListItem>)GetValue(MultiComboBox.CheckedItemsProperty);
}
set
{
SetValue(MultiComboBox.CheckedItemsProperty, value);
}
}
public static readonly DependencyProperty CheckedItemsProperty;
I register this DependencyProperty in a static constructor inside my custom control:
CheckedItemsProperty = DependencyProperty.Register("CheckedItems",
typeof(BindingList<CheckableListItem>),
typeof(MultiComboBox),
new FrameworkPropertyMetadata(new BindingList<CheckableListItem>()));
(MultiComboBox is the name of my custom control. CheckableListItem is a simple class I've written just for this purpose).
This BindingList is then updated inside the custom control (never outside) as the user interacts with it.
When I use my custom control in XAML I bind to the CheckItems property with the mode "OneWayToSource". I'm using the MVVM pattern and the property in the ViewModel that I'm binding to is also a BindingList. The ViewModel never affects this list, it just reacts at the changes that the custom control make to the list. The property in the ViewModel looks like this:
private BindingList<CheckableListItem> _selectedItems;
public BindingList<CheckableListItem> SelectedItems
{
get
{
return _selectedItems;
}
set
{
if (value != _selectedItems)
{
if (_selectedItems != null)
{
_selectedItems.ListChanged -= SelectedItemsChanged;
}
_selectedItems = value;
if (_selectedItems != null)
{
_selectedItems.ListChanged += SelectedItemsChanged;
}
OnPropertyChanged("SelectedItems");
}
}
}
As you can see I'm listening to changes made to the list (these changes always occur inside my custom control), and in the "SelectedItemsChanged"-method I update my Model accordingly.
Now...this works great when I have one of these controls in my View. However, if I put two (or more) of them in the same View strange things start to happen. This will of course mean that I'll have two lists with selected items in my ViewModel. But if do something in the View that changes one of the lists, both lists are affected! That is, the event handlers for the event ListChanged is triggered for both list if changes are made to any one of them!
Does anyone recognize this problem and/or have a solution to it? What is wrong with my implementation?
My first though is that the DependencyProperty is static. Normally that means shared between all instances. But I guess DependencyProperties work in some other "magical" way so that might not be the problem.
Any tips or hints is appreciated!
I had a similar problem with a collection-type dependency property. My solution was taken from the MSDN article on Collection-Type Dependency Properties. It was adding the following line
SetValue(OperatorsPropertyKey, new List<ListBoxItem>()); //replace key and type
in the constructor of my control because it seems that a collection-type dependency property constructor is being called only once no matter how many instances your control containing this collection has (static eh).
This sounds like you bound both/all the Views to the same ViewModel. That would explain that changes to one cause changes in the other.
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!
I have a complex WPF control that for some reasons (ie. performance) is not using dependency properties but simple C# properties (at least at the top level these are exposed as properties).
The goal is to make it possible to bind to some of those top level properties -> I guess I should declare them as DPs.(right? or is there some other way to achieve this? )
I started reading on MSDN about DependencyProperties and DependencyObjects and found an example:
public class MyStateControl : ButtonBase
{
public MyStateControl() : base() { }
public Boolean State
{
get { return (Boolean)this.GetValue(StateProperty); }
set { this.SetValue(StateProperty, value); }
}
public static readonly DependencyProperty StateProperty = DependencyProperty.Register(
"State", typeof(Boolean), typeof(MyStateControl),new PropertyMetadata(false));
}
If I'm right - this code enforces the property to be backed up by DependencyProperty which restricts it to be a simple property with a store(from functional point of view, not technically) instead of being able to calculate the property value each time getter is called and setting other properties/fields each time setter is called.
What can I do about that? Is there any way I could make those two worlds meet at some point?
//edit
I guess I have to tell you a little more about what I want to do and what my limitations are. So:
I have TabControl that is bound to a collection of ViewModel(I'm using MVVM pattern) objects. Every tab is meant to be an editor for one object of that collection. Objects can be of different types so I have multiple definitions each with a different DataType property. Now I have that complex WPF Control that I want to use as a part of one of those DataTemplates. If I use usual TextBox I can simply bind to its Text property, but I can't do the same with Text property of that custom control simply because its Text property is not a dependency property.
In this scenario I have :
no direct access to the control itself nor to its events
no code behind file that I can use to do that kind of thing
I can see however a dirty solution -
In the Window class I would have to subscribe to CollectionChanged event of the collection that is bound to the TabControl.
Whenever an item is added to that collection use ItemContainerGenerator to obtain a copy of I suppose TabItem and use it to find the right copy of 'complex control'
Regiter items handlers to 'complex controls' events to do the job
This is wrong because:
this is agains MVVM - I have to play with tab control to do the job instead of doing it in the ViewModel class
this couples in an unwanted way the view and viewmodel
I think you are mixing up Dependency Properties and implementing INotifyPropertyChanged on your classes.
You don't need your property to be a dependency property, you just need your class to implement INotifyPropertyChanged and call OnPropertyChanged whenever the state of your object changes in a way that would affect the value you want to expose to binding.
So let's say you have a property Sum that you want to bind to. The Sum property simple adds two other properties (or fields, or whatever) together. When anything happens that affects the Sum calculation, you want to notify that the Sum value has changed, so the any controls bound to Sum get updated.
public int Sum => Value1 + Value2;
public int Value1
{
set
{
// changing this affects "Sum", so I need to notify that the binding should update
_value1 = value;
OnPropertyChanged("Sum");
}
}
public int Value2
{
set
{
// changing this affects "Sum", so I need to notify that the binding should update
_value2 = value;
OnPropertyChanged("Sum");
}
}
It seems to me that you've been saddled with a WPF user control that was built by someone who didn't intend it to be used with data binding. I would assume that this is for one of two reasons: a) there's some logical reason that you shouldn't be able to bind to this property, or b) the original author of this control didn't know what he was doing. You haven't provided enough information for me to know which of those two conditions is the one you're really working under.
But in general, the reason you can't expose calculated properties for binding is that calculated properties generally don't have a setter. It doesn't make sense to set a property whose value is calculated.
If there are other properties whose values need to be updated when this one changes, the right approach (or at least the one consonant with the design of dependency properties) is to handle those updates in the dependency property's callback function, which is kind of what the callback function is for.
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.