I sometimes think I maybe using Dependency Properties unnecessarily. When do I need to use it? When I have a property that dependes on other properties? Say I have a Color property that I want it to be dependent on properties Hue, Saturation, Luminosity do I use a dependency property? Or what do I use? I controls thats bound to Color to update when properties Hue, Saturation, Luminosity are changed.
for now what I did was
public byte Hue {
get { return _hue; }
set
{
if (_hue == value)
return;
_hue = value;
NotifyPropertyChanged("Hue");
NotifyPropertyChanged("Color"); // to update controls bound to color
}
}
But I think this is not the right way of doing things? If I have more properties that affect color, I will have 1 extra line in all those properties?
You should only use a DependencyProperty when you want to be able to bind its value to something through XAML, e.g.
<local:MyObject MyDependencyProperty="{Binding ...}" />
Update: as mentioned by Ian below, dependency properties are also required if you want to be able to animate your property or set it through a style
If you do not need to work in this way then it is unnecessary. e.g. If you just want to be able to set the value to a constant through XAML (as below) this will work without using a DependencyProperty
<local:MyObject MyRegularProperty="Some Value" />
Similarly, if you want to bind to the value of a property on (for example) your view model:
<TextBlock Text="{Binding MyViewModelProperty}" />
then you do not need to use a DependencyProperty. Provided that you implement INotifyPropertyChanged then the Text will still be updated when the property changes.
Edit: on re-reading your question, I am not sure whether or not your situation will be affected by whether or not you use a DependencyProperty - if I'm reading it correctly, all you want to do is cause a number of properties to be updated on the UI when any one of those properties changes, right?
I don't think there is anything wrong with how you are implementing things at the moment (i.e. raising a lot of PropertyChanged events in each setter), but if you aren't keen on in then you could try having a single property that exposes relevant child properties to bind to that are all calculated:
class ColorWrapper
{
public Color Color { get; set; }
public byte Hue
{
get { return this.Color.Hue; } //or however this is calculated
}
Then have a Color property on your ViewModel that raises the PropertyChanged event and bind to that through the View:
<TextBlock Text="{Binding Color.Hue}" />
As I said, I wouldn't say that this is particularly an improvement on what you have already though.
The general rules are:
For XAML controls, use dependency properties;
For data (which you bind to in the interface), use INotifyPropertyChanged.
There are exceptions, but they are rare.
Another use of dependency properties is with navigation journal. Custom dependency properties on a Page with Juornal flag in the meta-data are included in the state that WPF saves for the page.
Remember that Dependency Properties, although they allow Binding either as a source or a target, are also Thread-Sensitive, and when serializing you will have to use a surrogate, serialization as DependencyObject isn't serializable.
Oh, and Equals and GetHashCode are sealed :(
Another usage of DP is Attached Property. Attached property is a type of DependencyProperty where the property can be used in any other controls.
For example, you can declare AttachedProperty in class MyAttachedProperties and used it for TextBlock, Button, Label,...
Some examples of Attached Properties: Grid.Row, Grid.Column. A customized one:
public static readonly DependencyProperty DarkThemeProperty =
DependencyProperty.RegisterAttached("DarkTheme", typeof(Theme), typeof(Page));
public static Theme GetDarkTheme(DependencyObject obj)
{
return (Theme)obj.GetValue(DarkThemeProperty);
}
public static void SetDarkTheme(DependencyObject obj, Theme value)
{
obj.SetValue(DarkThemeProperty, value);
}
I was stepping into the "trap" of DependencyProperty often.
At some point I realized that every single Change of any value is triggered somehow and somewhere, that can be an algorithm, a process, a user-input... and a lot of things, but NEVER it just changes by itself.
Get control about that triggers, hook directly at source and you never need DependencyProperty and even no INotifyPropertyChanged. I mean maybe I do something wrong or right, but all problems, and they are sometimes of serious difficulty, I solve with only RoutedEvents and Properties { get , set } -> And inside of get, set you can do SO much. Everthing that cannot be solved here, can be done with AddHandler and don't forget to RemoveHandler when not needed anymore.
Also needed is a solid understanding of Threading and Dispatching in some cases.
Also needed is a good knowledge how to work with Lambda expressions and Actions.
(Probably not useable for windows phone etc. applications, guessing)
Respectively: A serious drawback is coding control and effort. For that approach I need to write more Code and have better documentation, the big advantage is better understanding what is actually happen within your application and what could happen, what are the extends. There is NO drawback in performance, it is even faster, because when you hook directly into your triggers, you are using the lightest and most appropriate form of an Approach - Executing Code ONLY when it is necessary and only that heavy that it is necessary.
Finally: DependencyProperties could make sense in very responsive applications where you actually DON't KNOW the possible relations and reactions. That could be over a million possible cases -> You don't want to have control over those, you would never finish your product.
Caveats: Not everything that looks like it is producing a ton of unknown cases are unknown. You can work with RANGES (from x to y) and write algorithms to regain control over thousands of possible cases with just one method.
So that are my two cents: DependencyProperties and INotifyPropertyChanged is not needed in most of the cases and just a way of having an easier coding life at the cost of actually understanding your product and (sometimes) performance.
(Notification to myself: I write this because I just came into a situation where I was thinking I need DependencyProperty, this time for real.. but no.. one hour of thinking and the solution was easy, found the trigger.)
Related
Right. So moving from WPF to UWP, I'm trying to use x:Bind to get compile-time benefits. Simple scenarios work fine; however I have found a number of issues that I was not able to solve. They are all related, so I thought I'd post them in one place:
I haven't been able to make Intellisense work with x:Bind. I have set DataContext (as well as d:DataContext just as we do in WPF) both in XAML and in the constructor, but it won't show members no matter what. Has anyone done this successfully?
Then I read somewhere that in UWP, DataContext is always set to Page's code-behind (really??) and that I need to define a ViewModel type property in the code-behind and then use that property in x:Bind. Is this correct? I tried it and it works but gives rise to the next question.
If I define a property of ViewModel type in Page's code-behind, Any sub-properties that raise PropertyChanged notifications do not update the UI. For example, if the code-behind property is named Game (of type GameVM) and there is a public property in GameVM named Player (of type GamePlayer), and in turn GamePlayer contains a property named Name, the x:Bind path will look like {x:Bind Path=Game.Player.Name}. But if I do this, any change notifications raised from within Name property do not update Page's UI.
One alternate I tried was to listen to PropertyChanged at each level and then bubble it up the hierarchy, but that hasn't worked. Even if it does, doing this seems a bit too much work. In WPF sub-properties like Game.Player.Name work properly without having to doing property change bubbling. Or am I missing something?
Right. After playing with it for a few days and searching numerous references, here are my findings:
{x:Bind} lacks design-time support. The feature is on the wishlist though. You may want to upvote it there.
(The new version of Visual Studio 15.4.4 does support Intellisense in {x:Bind}in the required way.)
{x:Bind} uses code-behind as its DataContext. So you need to define a public property of your ViewModel type in the code-behind and then use it in your {x:Bind} path.
As pointed out by IInspectable, the default mode for {x:Bind} is OneTime, unlike {Binding} which uses OneWay or TwoWay in almost all cases. So you need to explicitly specify Mode in your binding. People coming from WPF should take special care of it.
Sub-properties that implement notification change work perfectly fine in {x:Bind}. There is no need of bubbling these notifications upwards in the property hierarchy. The problem I was facing (#3 in the question) was because my sub-property was of type List<T>. I changed it to ObservableCollection<T> and it started working.
Hope this works somebody down the road.
Well as a beginner, the only question I can answer for you is the first one. Intellisense does not work inside the {x:Bind}. The members are never shown there in UWP for some unknown reasons. As for the next two questions of yours, I am still working on them.
I ran into the same challenge that you have seen. In my experience, in order to create the compile-time binding and have it update with custom objects as properties, the Page class seems to need to know about the data context and custom objects... all you need to do is reference them in the code behind, and then bind to them in the XAML. This creates the code generation objects it needs.
For example, I have a viewmodel, CustomerViewModel that is bound in XAML. That viewmodel also has a property of type IGuest. In order to use the guest object and have it update properly, I came up with this in the code behind...
CustomerViewModel vm
{
get
{
return (CustomerViewModel)DataContext;
}
}
IGuest g
{
get
{
return vm.CurrentGuest;
}
}
public CartGuestControl()
{
this.InitializeComponent();
}
You don't need to assign any of the UI data contexts from the code behind... simply reference the datacontext that is bound in XAML. When binding to any straight viewmodel properties, I use {x:Bind Path=vm.IsEditing, Mode=OneWay}. For binding to any of the guest properties, it looks like this, {x:Bind Path=g.FirstName, Mode=TwoWay}. You could do something like this for your Player object.
I have run into times where x:Bind simply won't do what I expect it to do no matter what I try. This can usually be solved by breaking things out into smaller user controls with more specific data contexts or by using "regular" Binding.
I'm using the Telerik WPF RadGanttView control to display a bunch of data. Since the data can be arbitrarily weird - events that are instantaneous, or that last days, or have a week between them - it's not possible to set a PixelLength (i.e. a scale factor) that's guaranteed to be useful. I've been asked to make it possible to change this scale factor with a slider. Unfortunately, just changing the scale factor with the slider has a usability issue, which I need to fix by manually adjusting where the view is scrolled. I'm at a loss as to how exactly.
To outline the usability problem I'm fixing: the view works by providing a "viewport", located at an "offset", over the whole "extent" of the data set. (Terms lifted from the Telerik API. These seem to simply represent pixels in the canvas underlying the scrollable view.) When the scale factor goes from, say, 100% to 200% (which corresponds to halving the PixelLength), what happens is that the width of the extent is doubled, but the horizontal offset remains the same. The consequence is that after zooming in, it's quite likely you'll see entirely different data than before, since the events that were there before got "pulled out" of the viewport to the right.
The way I intend to fix this is: grab the offset/viewport/extent etc before zooming in, zoom, then do some maths I haven't figured out yet with that and the new offset/viewport/extent. The problem is: the properties of RadGanttView that describe the scrolling stuff are not DependencyPropertys, and I cannot simply bind them to properties on my ViewModel. (In fact, they're not even accessible in XAML to begin with, RadGanttView implements IScrollingInfo explicitly.)
So, my question is: how do I, in my ViewModel, or wherever else in reaction to the ViewModel's scale factor changing, access properties of a control in the corresponding View, that cannot be data-bound? Every search I tried tells me that accessing the view from the viewmodel is "not MVVM", but since Telerik is a third-party library, I can't really refactor how this works on their side.
A fill-in-the-blanks outline of my code:
FooViewModel.cs
class FooViewModel
{
// A slider in the view pushes its value into this property
double ScaleFactor
{
get { /*...*/ }
set
{
PixelLength = GetNewPixelLength(value);
// ...
}
}
// The RadGanttView pulls its scale from this property
double PixelLength
{
get { ... }
set
{
// How do I get these values?
var oldOffset = ???;
var oldExtent - ???;
// Trigger the actual change in the view.
PropertyChanged("PixelLength", ...);
var newExtent = ???;
???.HorizontalOffset = GetNewOffset(...);
}
}
}
FooView.xaml
<UserControl ... d:DataContext="{d:DesignInstance my:FooViewModel}">
<telerik:RadGanttView x:Name="Gantt">
<!-- ... -->
</telerik:RadGanttView>
</UserControl>
Things I've looked into:
Making a bunch of DependencyProperty wrappers in the FooView code-behind that just access corresponding properties in the RadGanttView. This seems like it both is a horrible abuse of the system - i.e. it doesn't seem to make sense to have a dependency property not backed by a DependencyObject. And also that it plain wouldn't work - in WPF, the view seems to "push" data into the view model, and I'd still have no way to actually get the current values, since the values of the wrapper properties would never get updated.
Uh, Commands, maybe? I'm fairly new at WPF, I have no clue how those work at all, merely a vague impression that they might be a loosely coupled way for the view model to talk to the view.
Attached properties? Custom bindings? Way above my pay grade, if they help I don't know how myself. It seems like they could accomplish the "dirty" solution of just binding a control to a view model property. Since the type of that property would be IScrollingInfo, not the whole view, I could live with that.
Attached Behaviors may solve your issue. They are basically Attached Properties with a callback.
Check out my answer here. Just instead of KeyDown event, you register to the Changed event (or whatever your control is actually calling it) and then assign the value you get from the Changed event to your Attached Property, and you have two way binding on a non-bindable property
Well, i must admit, still sometimes XAML seems a bit mysterious to me. The thing is, i always liked to debug through the C# code (setting lots of breakpoints in them) to get the idea of "what is happening" and "how is it happening". But with declarative XAML syntax that's not an option. I think you'll agree that to work with XAML, or to be precise, to work with/understand some existing XAML code you got to "already know" how things work with XAML declaration. There is just no way you can know/learn things investigating the execution of your application code. So i'm more than interested to take a look through XAML inside-out, as detailed as possible. I'm NOT talking about "learning" XAML, I know the basic stuff. May be i can provide some examples to clarify the sort of things i'm looking for -
Compared to C# code how an object gets instantiated when we use them in XMAL? Are they stored in managed heap? Same way as C# code-instantiated objects?
How the properties get set while using Mark-Up Extension syntax for Data/Command Binding?
When any property of an INotifyPropertyChanged type gets updated, how the Binding instatnce inside the XAML syntax updates the itself? How exactly it gets notified it at the first place, & by whom?
A viewmodel can be set as the DataContext of a view at runtime by defining Typed DataTemplate, like -
<DataTemplate DataType="{x:Type viewmodels:AccountsViewModel}">
<views:Accounts/>
</DataTemplate>
How does it happen actually? What are the rules for setting DataContext other than searching for the DataContext property upward the logical tree?
How the whole template things (DataTemplate, ControlTemplate & ItemsPanelTemplate) are treated/resolved at run time.
etc. etc. etc.
So if you are good/experienced/expert in XAML what would you suggest (links, articles, blogposts, books whatever) as reference that helps getting clear & deeper understanding about how XAML works "under-the-hood"? Thanks in advance.
Most can be explained by don't thinking of XAML as a real programming language, more like a declarative language. Everything you do in xaml, can be made in C# aswell, and in fact this is whats happening.
Compared to C# code how an object gets instantiated when we use them
in XMAL? Are they stored in managed heap? Same way as C#
code-instantiated objects?
Yes because they are just c# objects. Most resources are stored in a hibernated state, i rememberd the word inflated somewhere. Converter or other "direct" c# objects are created when they are needed. Important here is that these resources are usually shared, so they will get created only once.
How the properties get set while using Mark-Up Extension syntax for Data/Command Binding?
This again depends on where you use the markup extension. In a Style? In a Template? In a instanced user control like a window? Usually they are evaluated when you actually need them. It wouldn't make sense to evaluate them, when the inflated style is stored in the actual resource dictionary. They get evaluated when you actually use the style on an object.
When any property of an INotifyPropertyChanged type gets updated, how
the Binding instatnce inside the XAML syntax updates the itself? How
exactly it gets notified it at the first place, & by whom?
By the binding engine. WPF checks if your DataContext inherits the INotifyPropertyChanged interface, attaches to the event provided by the interface and listens to any changes. If such an event is raised, the binding engine will just call the getter again.
How does it happen actually? What are the rules for setting DataContext
other than searching for the DataContext property upward
the logical tree?
In short: None other. Datacontext is simply an inherited attached property. If you don't re set it on a child control, it will take the value the parent has until it reached the root. The only exception to this are ContentControls and ContentPresenter they will not inherit the DataContext but will change them depending on the content. So these controls always have by default the Content as their DataContext.
How the whole template things (DataTemplate, ControlTemplate & ItemsPanelTemplate) are treated/resolved at run time.
Simply spoken: Everytime WPF finds a non ui object, it tries to find a DataTemplate for the given type. In an ItemsControl for example: You can bind a list of MyClass; unless you provide an explicit DataTemplate or DataTemplateSelector it will search the resource tree upwards for an implicit style. Again remember that this already does not happen in XAML, but on the C# objects that was generated out of the xaml.
And is it by any means possible (at present or near future) to debug
through XAML code?
How do you think you can debug something that is not executed, but evaluated on compile time?
Please don't take this as 100% correct. Over the Years this is what i gathered of informations about XAML and the usage. If you have any corrections or find something that is clearly wrong. Please tell me, we are all here to learn and i always learn new things about the stuff i use :)
I'm currently writing a WPF progress bar that includes a rate (see Windows 8 - Fancy Progress Bars API?).
The screenshot below shows what I've got so far (left) and a badly done all in code as part of my learning exercise (right). I'm now trying to convert the code version to use as much XAML as possible.
I've got most of the way there by creating a new class called RateBase and implementing it in a similar way to RangeBase. I've then added a new instance and provided a template file RangeGraph. I'm attempting to do this as by the book as possible, but I'm not sure how to tackle the final stage.
I now wish to add a graph, this graph is to display the rate as it has changed historically as the progress has progressed. I have 'Rate' as a value I can bind to, but I believe somwhere I need a Double[] containing my historical rate values. My question is where should this be placed (I don't really want to pollute RateBase) and how do I bind to it from my template (I don't believe I can bind to RangeGraph.cs if I add properties on there or am I wrong?)
You are right, you will need historical Data. In my opinion, whenever the bound Dependency Property Rate changes, you should move the old value into an IEnumerable that's defined on the graph control itself (The same place that has the DP) and use that to draw the lines. I personally would create a class named MyControlData and add an instance of that to the control.
You might also want to add a Timer and move the current Rate to the IEnumerable when it Elapses, so longer streaks of the same rate will appear as multiple bars. Depends on how you actually determine progress. You might get into the following dilemma here : The Rate changes at a different interval than the Percentage in most cases - what floats your boat?
Keeping the history in your control's scope leaves your application agnostic to the history of your Rate, but lets your control display it as required.
To use DataBinding in a UserControl, edit the <UserControl x:Name="myControl"> node in Control.xaml and add a name like shown here. Wherever you want to bind, refer to ElementName=myControl. Please note that you will have to implement INotifyPropertyChanged on the Control (or on MyControlData) if you want to achieve this - or, and that would be advisable, directly implement it as a dependency property.
And BTW, if you have no idea how to achieve what you intend to have a look into ItemsControl. I think what you want to do can easily be achieved by means as simple as using ItemsControl and ItemsTemplate, where the ItemsSource is your historical data and the ItemTemplate depicts your current rate in comparison to your MaxRate. MaxRate is another property you can set from the DependencyProperty Rate's changed handler.
I am trying to come up with a solution for the following problem:
I have a DateTimeRange class which contains 3 properties:
Start: DateTime
End: DateTime
Length: Length (=End-Start)
This class is used throughout my project as dependency property on various controls. This values are mutable, for example, if the Start changes, the End will be moved with the same offset (keeping the same Length).
My problem is that in code that listens for updates of the range, does not recieve an event when any of the properties have changed, only when the complete object is replaced.
I want to let the DateTimeRange class notify the user that the whole object has changed when a property is changed, not just one property.
I have tried to make DateTimeRange an immutable struct. But this makes two way DataBinding to any of it's properties impossible.
Does anybody have a suggestion how to do this? IMO this problem is very frustrating, as the same problem happens with Margins for example (you cannot bind to Margin.Left of a control).
The framework does not really support this requirement. The approach I would take would be to make DateTimeRange implement INotifyPropertyChanged, raising the event whenever any of its properties change. On a class that has DateTimeRange as a dependency property add / remove handlers for the INotifyPropertyChanged as the property changes. Within your handler for this event you can then perform the action you require.
Note, you could make DateTimeRange a DependencyObject, which would allow you to create two way bindings to its properties.