I have trouble understanding a problem that occured to me.
I Have a UserControl that is structured somwhat like this.
public class SomePage : Page
{
public static readonly DependencyProperty SomePropertyProperty =
DependencyProperty.Register("SomeProperty", typeof(IPropertyValue), typeof(SomeControl), new PropertyMetadata(null, new PropertyChangedCallback(OnSomePropertyChanged)));
private static void OnSomePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
//Do Some Stuff
}
}
And a ViewModel that looks like this
public class SomeViewModel : BindableBase
{
private IPropertyValue _prop;
public IPropertyValue Property
{
get
{
if (_prop== null)
_prop = new SomeConcreteValue();
return _prop;
}
}
}
And the whole Stuff is bound to a Page
<common:LayoutAwarePage>
<Page.DataContext>
<vm:SomeViewModel />
</Page.DataContext>
<ctrl:SomePage SomeProperty="{Binding Property}" />
</common:LayoutAwarePage>
In my understanding the PropertyChangedCallbacked is called whenever the Value of the DependencyProperty changes.
Allthough the value of ViewModel.Property does never change, the value of the DependencyProperty "SomeProperty" still does, as it changes from null to the inital bound value.
Is there any other possibility to get notified once the Property is initialized or am I simply missing something here?
Edit:
Maybe I was not clear about this. My problem is that the PropertyCahngedCallback is not fired when the initial value is set to SomeProperty.
I'm sorry I did'nt look at the log very closely.
The problem was'nt that the event i'nt fired but in fact that the Binding i'nt possible.
It seems that in Windows 8 Store Apps DependencyProperties with interface Types don't work as expected:
Error: Converter failed to convert value of type '#Namespace.ConcretePropertyType#, #Application#, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' to type 'IPropertyValue'; BindingExpression: Path='Property' DataItem=''Namespace.SomeViewModel, #Application#, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'; target element is '#Namespace.ConcretePropertyType#' (Name='null'); target property is '#ConcretePropertyType#' (type 'IPropertyValue').
Source:
http://social.msdn.microsoft.com/Forums/en-US/winappswithcsharp/thread/5ec0a4ba-b80a-4a8a-8e5a-f2fe776c45b5/
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.
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.
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).
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;