Changing which DependencyProperty I bind to at run time - wpf

I'm using WPF and have a data class which I bind to a control's DependencyProperties. I need to change the binding at run time under the control of a user. Ideally I'd like to be able to do something like this
myControl.SetBinding(UserControl.GetDependencyProperty("HeightProperty")
, myBinding);
Of course GetDependencyProperty taking a string doesn't work, I've got around this by creating my own static class
public static DependencyProperty GetDP(string Name)
{
switch (Name)
{
case "Height": return UserControl.HeightProperty;
case "Width": return UserControl.WidthProperty;
....
}
Is there a better way?

You haven't described how the user changes the target dependency property. Can you just store the DependencyPropertys themselves rather than strings? That way you don't have to do any conversion at all. Pseudo-code:
//just an array of all allowable properties
public DependencyProperty[] AllowedProperties { get; }
//the property the user has chosen
public DependencyProperty ChosenProperty { get; set; }
//called whenever ChosenProperty changes
private void OnChosenPropertyChanged()
{
//redo binding here, using ChosenProperty as the target
}
Edit after comments: You can use DependencyPropertyDescriptor.FromName to get a DependencyProperty from its name, assuming you know the type of the owner:
var descriptor = DepedencyPropertyDescriptor.FromName(nameFromExcel, typeof(YourUserControl), typeof(YourUserControl));
var dependencyProperty = descriptor.DependencyProperty;

Related

Dependency Property register names

public string City
{
get { return GetValue(CityProperty).ToString(); }
set { SetValue(CityProperty, value); }
}
public static readonly DependencyProperty CityProperty =
DependencyProperty.Register("City", typeof(string), typeof(Object1));
If I make a mistake, here:
...Register("City"), typeof...
like for instance I forget to capitalize "city", or i.e "vity"
Is the dependency property subsystem just totally blown away? The string City property is still there right? I can set it in various ways and the static CityProperty thingamabob is still there, I can do something somewhere with that. I can do like Object1.CityProperty and such, But where is exactly is the breakdown/link? If that "City" literal in the register method and the City property don't match, then the string City property is just not a dependency property?
I guess also I mean, if the string City property is calling GetValue, then what is the difference? Will the subsystem 'find' everything and be able to support using string City property as the: a) target of binding b) animation c) styling
EDIT
In a somewhat disturbing development, the following 'works' . When you set the City property from XAML, it apparently stores it somewhere. What part(s) of a) using as a target for binding, b) styling, c) animation don't is a mystery to me.
namespace util
{
public class foo : FrameworkElement
{
public String City
{
get { return (String)GetValue(CityProperty); }
set { SetValue(CityProperty, value); }
}
// Using a DependencyProperty as the backing store for City. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CityProperty =
DependencyProperty.Register("city", typeof(String), typeof(foo), null);
}
}
namespace screwing_up_dependencies
{
public partial class MainPage : PhoneApplicationPage
{
public MainPage()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var aFoo = this.FindName("bar");
if (aFoo is util.foo)
{
util.foo theFoo = (util.foo)aFoo;
((Button)sender).Content = theFoo.City;
}
}
}
}
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"
xmlns:util="clr-namespace:util"
>
<util:foo x:Name="bar" City="blah"></util:foo>
<Button Content="Button" Grid.Row="1" Click="Button_Click" Height="105"/>
</Grid>
EDIT 2
Furthermore, you can also do this. It apparently doesn't make any difference what the "Name" parameter to the register command is. And also it doesn't seem to matter what the OwnerType parameter is either.
<util:foo x:Name="bar2" City="{Binding ElementName=ContentPanel, Path=Width}"></util:foo>
public static readonly DependencyProperty CityProperty =
DependencyProperty.Register(Guid.NewGuid().ToString(), typeof(String), typeof(object), null);
var aFoo = this.FindName("bar2");
if (aFoo is util.foo)
{
util.foo theFoo = (util.foo)aFoo;
((Button)sender).Content = theFoo.City;
}
Will the subsystem 'find' everything and be able to support using string City property as the: a) target of binding b) animation c) styling
No.
Reflection is used by other systems to divine information about the instance. When the property city does not exist no operations can occur. For in C# one can have overloaded property names (i.e. City and city) and those are two separate entities in the eyes of the compiler and the runtime process reflecting off of an unknown instance.
is there any Lint tool available that will detect this kind of thing automagically
To avoid errors when creating dependency properties I use Jeff Wilcox's (Helpful Silverlight Snippets - Jeff Wilcox). Via using snippets which are geared towards the 6 types of dependency properties, it avoids the errors. Note that even though it is Silverlight, I use them without change in WPF.

Does it make sense to directly use a business object's DependencyProperty's in the model?

With the following business object:
public class ItemsRow : BusinessObject<ItemsRow>
{
public static readonly DependencyProperty ItemIdProperty = DependencyProperty.Register("ItemId", typeof(int), typeof(ItemsRow));
public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register("Description", typeof(string), typeof(ItemsRow));
public int ItemId
{
get { return (int)this.GetValue(ItemIdProperty); }
set { this.SetValue(ItemIdProperty, value); }
}
public string Description
{
get { return (string)this.GetValue(DescriptionProperty); }
set { this.SetValue(DescriptionProperty, value); }
}
}
How would you go about exposing the properties in a model, seeing as how the properties are already DependencyProperty's?
I was wondering if it would make any sense to do this:
public class ItemModel: DependencyObject
{
Item _item;
public ItemModel(Item item)
{
_item = item;
}
public static readonly DependencyProperty DescriptionProperty = Item.DescriptionProperty;
public string Description
{
get { return _item.Description; }
set { _item.Description = value; }
}
}
Would that work as intended or would the model necessarily have to have its own set of DependencyProperty's that are backed by the business object's DependencyProperty's? Or could this be modified slightly to work correctly?
That won't work because the dependency property registration needs to know on which type the property is being defined; that's why you pass the third argument to the register method. So far that reason alone, it won't work properly. But from a theoretical MVVM design stand point, having a separate object in your model that closely resembles your business object is a trade-off that you chose to have another layer of abstraction. You are essentially buying the redundancy to allow yourself to have another layer of abstraction allowing you to swap the business object without changing your model. However, if you make your model object dependent on specifics of your business object implementation, you are defeating that purpose. In that case, I would just directly use the "business object" as your model object.

WPF/Attached Properties - Please explain why this works

Please help me understand where the value "ABC" gets stored. When I run memory profilers I don't see any instance of MyClass, and in fact the binding works and the GroupBox.Header gets the value ABC...
Thanks for your help.
<GroupBox Header="{Binding Path=(local:MyClass.Tag1), RelativeSource={RelativeSource Self}}"
local:MyClass.Tag1="ABC" />
public class MyClass
{
public static readonly DependencyProperty Tag1Property = DependencyProperty.RegisterAttached("Tag1", typeof(object), typeof(MyClass), new UIPropertyMetadata(null));
public static object GetTag1(DependencyObject obj)
{
return obj.GetValue(Tag1Property);
}
public static void SetTag1(DependencyObject obj, object value)
{
obj.SetValue(Tag1Property, value);
}
}
Dependency properties maintain a dictionary internally. Values are stored using sparse storage mechanism. These properties are associated at the class level - being static. The value ABC is stored in the dictionary as key value pairs
Here is a pretty straight forward explanation of how it works: http://nirajrules.wordpress.com/2009/01/19/inside-dependencyobject-dependencyproperty/
Essentially as Hasan Fahim said, the dependency properties are stored in an internal Hashtable based on the property name and the owner of the property. By storing the property as associated with the owner, you can actually have unique entires in the HashTable for different objects of the same type. This means that the Get and Set methods do not need to be static.
Example:
public class Something
{
public static readonly DependencyProperty IsEditableProperty = DependencyProperty.Register("IsEditable", typeof(Boolean), typeof(ResourceCanvas), new PropertyMetadata(true));
public Boolean IsEditable
{
get { return (Boolean)this.GetValue(IsEditableProperty); }
set { this.SetValue(IsEditableProperty, value); }
}
}
With version I can instantiate many instances of type Something each containing a "different" value of IsEditable.

WPF: instances of usercontrol share dependency properties

I made an usercontrol and it works great, but when I put two instances of this control to one window, only the last of them works. I tried to find solution and I realized, that dependency properties are shared, but I dont know how to get it work.
Here is my dependency property:
public double AnimatingVerticalOffset
{
get { return (double)GetValue(AnimatingVerticalOffsetProperty); }
set { SetValue(AnimatingVerticalOffsetProperty, value); }
}
public static readonly DependencyProperty AnimatingVerticalOffsetProperty;
static ListChooser()
{
ListChooser.AnimatingVerticalOffsetProperty =
DependencyProperty.Register("AnimatingVerticalOffset", typeof(double), typeof(ListChooser), new UIPropertyMetadata(OnAnimationVerticalOffsetChanged));
}
The dependency property itself must be static with no ties to one single instance. And that applies for its callbacks too (OnAnimationVerticalOffsetChanged in your case) - these must be static methods (don't worry, the object instance is passed via its parameter, you just have to do some type casting to ensure the object is the type you are working with).
You should use static initializer to initialize DP, the method you used (initializing in constructor) works, but the DP will overwrite for each instance.
See this question for deeper explanation.
EDIT:
Corrected code:
public double AnimatingVerticalOffset
{
get { return (double)GetValue(AnimatingVerticalOffsetProperty); }
set { SetValue(AnimatingVerticalOffsetProperty, value); }
}
public static readonly DependencyProperty AnimatingVerticalOffsetProperty =
DependencyProperty.Register("AnimatingVerticalOffset", typeof(double), typeof(ListChooser), new UIPropertyMetadata(OnAnimationVerticalOffsetChanged));
static ListChooser()
{
}
If the callback is not static, you will get compile error (=> you have to make it static).
EDIT:
Remember, the DP definition is static, not the property's value itself! DPs work exactly just like any other property, it just has some extra features: value inhertiance, bidnings, animation...

DependencyProperty.Register() usage?

I have 2 controls A and B that need to share a dependency property.
A has the property defined as:
public static readonly DependencyProperty PathProperty= DependencyProperty.Register("PathProperty", typeof(string), typeof(A),
new PropertyMetadata(string.Empty, OnPathChanged));
public string Path
{
get { return (string)GetValue(PathProperty); }
private set { SetValue(PathProperty, value); }
}
private static void OnPathChanged(DependencyObject dobj, DependencyPropertyChangedEventArgs args)
{
//Dos something
}
Inside of class B,
I have
public static readonly DependencyProperty Path = A.PathProperty.AddOwner(typeof(B));
public string Path
{
get { return (string)GetValue(Path); }
set { SetValue(Path, value); }
}
Now, if I set the Dependency property Path on B explictly...(from code like Binstance.Path = "value" )
I would expect the OnPathChangedmethod to fire inside of A control?
Isnt that the expected behavior or am I missing something? How do I get this to work? ... i.e changing path property on B should fire OnPAthChanged on A
Thanks!
I think you've misunderstood the concept of DependencyProperties... Two separate controls do not receive updates of each other's events - nor does two Dependency-derived objects receive notifications of other objects' changes (E.g. if you have two textboxes - changing one's TextProperty, does nothing to the other). If you really want your second Control type to fire the static validation-callback - you need to make it public and call it in your registration of the DependencyProperty on class B. I wouldn't recommend it though - it gives you very tight coupling between the two classes that otherwise have nothing in common (as I understand your example).

Resources