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).
Related
My object has one true property VideoDimension and one property VideoRatio which is basically a "reduced" version of the former.
I am looking for a way to connect the two in the most elegant way possible.
public static readonly DependencyProperty VideoDimensionProperty =
DependencyProperty.Register(
nameof(VideoDimension),
typeof(Point),
typeof(MyControl),
new PropertyMetadata(new Point(0, 0))
);
public Point VideoDimension
{
get { return (Point)GetValue(VideoDimensionProperty); }
set { SetValue(VideoDimensionProperty, value); }
}
public static readonly DependencyProperty VideoRatioProperty =
MysteryFunction(VideoDimensionProperty, (value) =>
{
Point point = (Point)value;
return point.X / point.Y;
});
public double VideoRatio
{
get { return (double)GetValue(VideoRatioProperty); }
}
What could this MysteryFunction above be?
I would like to have VideoRatio calculated in a lazy fashion.
The working workarounds I found so far are:
Make VideoRatioProperty its own DependencyProperty, and manually update it when VideoDimension is Changed. This is not ideal because:
VideoRatioProperty may be calculated for nothing, if no one is listening to it.
It creates a second source of truth. It makes one able to modify VideoRatio and not VideoDimension. This can be alleviated using RegisterReadOnly to protect it from public callers, but the owner object can still modify it.
Only use VideoRatio as a lightweight getter that would compute the value on the fly. But then it becomes hard to make other elements bind to it, since they should listen to the VideoDimensionProperty notifier, but take the value from VideoRatio. This is too much to know from an external caller.
Dont use VideoRatio at all, but make the callers listen to VideoDimension with a binding Converter that would calculate the ratio. This is also too much to know from an external caller, and this is definitely not scalable.
"Calculated for nothing, if no one is listening to it"
The source object shouldn't care if anyone "listens" to it. It should update its state regardless so this isn't an issue.
"But then it becomes hard to make other elements bind to it..."
You could implement INotifyPropertyChanged in your class and raise the PropertyChanged event for the VideoDimension whenever you want to notify the subscribers.
"But the owner object can still modify it"
Not only it can - it should. That's its responsibility. Whether it sets a read-only dependency property explicitly using the key or raise a PropertyChanged event for a read-only CLR property is just a matter of taste.
You shouldn't force the external callers to use a converter to get the value. The other two options are perfectly fine and should be seen as solutions rather than workarounds.
With properties it can be done like this, using PropertyChangedCallback and a readonly VideoRatio property:
public Point VideoDimension
{
get { return (Point)GetValue(VideoDimensionProperty); }
set { SetValue(VideoDimensionProperty, value); }
}
public static readonly DependencyProperty VideoDimensionProperty =
DependencyProperty.Register(
nameof(VideoDimension),
typeof(Point),
typeof(MainWindow),
new PropertyMetadata(new Point(0,0), VideoDimensionChangedCallback));
private static void VideoDimensionChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Point p = (Point)e.NewValue;
d.SetValue(VideoRatioPropertyKey, p.X / p.Y);
}
public double VideoRatio
{
get { return (double)GetValue(VideoRatioProperty); }
}
public static readonly DependencyProperty VideoRatioProperty = VideoRatioPropertyKey.DependencyProperty;
private static readonly DependencyPropertyKey VideoRatioPropertyKey =
DependencyProperty.RegisterReadOnly(
nameof(VideoRatio),
typeof(double),
typeof(MainWindow),
new PropertyMetadata(0));
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.
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.
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...
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;