One of the first things you learn when you working with WPF is the BindingMode enum:
OneWay: Updates the binding target (target) property when the binding source (source) changes.
OneWayToSource: Updates the source property when the target property changes.
TwoWay: Causes changes to either the source property or the target property to automatically update the other.
OneTime: Updates the binding target when the application starts or when the data context changes.
I wonder why there is no OneTimeToSource mode. Something like:
OneTimeToSource: Updates the binding source when the target changes.
I thought that this mode is not available for some particular reason (such as not breaking a specific pattern) but at the same time I also find this mode extremely useful, like establish the properties of the source from target (like config, for example) at the moment of coupling.
At least, I see it as a way to keep weakly coupled two structures and allow source to adapt to the target according to the properties of the target.
So, why is not there a 'OneTimeToSource' option in binding modes?
I wonder why there is no OneTimeToSource mode. Something like:
OneTimeToSource: Updates the binding source when the target changes.
The existig OneWayToSource works exactly like this, i.e. it updates the source property each time the target property is set to a new value.
Using a OneTimeToSource mode wouldn't make much sense as this would just set the source property to the default value of the target property once when the view was loaded for the first time.
Then it wouldn't be much of a point setting up the binding in the first place. This mode would for example set the string source property of a view model that was bound to a TextBox in the view to an empty string or a null reference which already is the default value for a string.
Related
Having to work with a legacy silverlight application I ran into a strange piece of code. The viewmodel has a List dependency property as binding source for the grid. This DP has a default value, an other List that is used globally in the app. This is used to easily share entity data between different parts of the application.
DependencyProperty MyEntitiesProperty = DependencyProperty.Register("MyEntities", typeof(List<Entity>), typeof(...), new PropertyMetadata(Global.Entities));
Now, when the list is changed (on user actions), the global list is repopulated from database but MyEntities is never set explicitly. This does not work: the grid (the binding target) never changes. So its a wrong solution.
I speculate that the idea behind all this could have been been the following: if you have a DP with a given value and you never set a local value for it then the effective value of the DP will be the default value. If the 'underlying' default value is changed, the changes are reflected in the effective value.
If it worked, it was a nice way of sharing data between independent viewmodels without fiddling with property change events and such.
What is wrong here? Is it a big misunderstanding of how DPs work or the idea was ok and some implementation details were missed?
Please comment if something is not clear.
Well, taking also your comment into account, it is a big misunderstanding of how DPs work. Let me explain:
Setting a globally known list as the default value of MyEntities might not be a pattern I recommend, but is technically not faulty and can be done to share a list. MyEntities now holds a reference to this very list.
If you now replace the global list with a new list instance, the old instance does not cease to exist. Your property MyEntities still holds a reference to the old list. The value of a DP is only updated automatically if it is bound via Binding to either an ordinary property that is wired with the INotifyPropertyChanged mechanism or another DP.
Setting a default value happens neither via a Binding to an ordinary property nor via a Binding to another DP, it is just a plain old object reference.
I can think of several ways to correct the situation:
First solution
If the global list implements INotifyCollectionChanged (e.g. ObservableCollection, DependencyObjectCollection) you can - instead of creating a new list instance - just delete the old items from the list and add the new items. The views that have a reference to the list will perform an update as soon as they receive the associated CollectionChanged event.
Second solution
Make sure the Global.Entities list is available and always up-to-date as a public property (wired with INotifyPropertyChanged) on the DataContext of the root view. Now when you want a nested view somewhere deep down inside the UI tree to be connected to this Global.Entities list you can bind it to the root view's DataContext' public list property.
<MyRootView>
... nested views spread across multiple files ...
<MyNestedEntitiesListDisplay
MyEntities="{Binding
Path=DataConext.GlobalEntities,
RelativeSource={RelativeSource AncestorType=MyRootView}}"/>
I read on many places that a DataTrigger is not limited to dependency properties only (like a Trigger), but it can also respond to any .NET property.
So is the DataTrigger a superset of the Trigger in its capability?
Why does a Trigger exist when we have the DataTrigger?
There are three types of triggers in WPF.
Property Triggers (Simply Trigger)
Data Triggers
Event Triggers
Trigger in WPF are actually property triggers. So, they only work with Dependency property. They are mainly for depedency property values to performs actions conditionally. (When Dependency Property meets a specified condition)
Whereas DataTriggers are more powerful. They can bind to normal .net property to monitor for changes or any dependency property or another control or StaticResources and so on. They perform action through bindings. (When bound data meets a specified condition)
So, you could say that Triggers exists to provide a more specific action to Dependency Properties, instead of using more generic Data Triggers which is for any .net data ( of course mainly through change notification)
So, to answer your question, AFAIK, No, there is nothing that property trigger can do that data-trigger cannot.
I have a WPFToolkit DataGrid with at least one column bound (via a proxy object as columns are not part of the visual tree) to a property. I wish to toggle all columns to Visible so that I can perform a calculation based on the DataGridColumnHeader (which is only created when its column is visible for the first time). Having done the calculation I want to reset the column to use the binding that was previously set.
I've attempted to get and store the Binding Expression etc, but with no joy. I have also attempted to use the DependencyObject.SetValue() method to change the property value non-destructively, but this doesn't event correctly change the value, let alone retain the original binding.
Any ideas?
You need to call SetCurrentValue() so that it won't clear the binding. SetValue destroys the old binding.
From MSDN:
This method is used by a component that programmatically sets the value of one of its own properties without disabling an application's declared use of the property. The SetCurrentValue method changes the effective value of the property, but existing triggers, data bindings, and styles will continue to work.
Given you have this
<TextBox Text="{Binding TestProperty}"/>
The SetValue you will overwrite the binding with whatever you provide. If you call SetCurrentValue, however, will ensure that the property takes on the given value, but won't destroy any bindings.
Be aware that you should not use SetCurrentValue in your dependency properties' setter/getter.
SetCurrentValue is more useful in scenarios where you need a property to take on a given value but don't want to overwrite any bindings, triggers, or styles that have been configured against your property.
I have a strange problem: I have a WPF screen on which I have an extended TextBox that makes some conversions in some cases and that property is bound to a property on view model. When the data context is changed the conversions are reevaluated. When reevaluated my extended TextBox has the new data context but the BindingOperations.GetBindingExpression(textBox, dpproperty).DataItem is still the old data context.
Maybe the conversion that I make is too early after changind the data context? Usually the data context is not changed into the whole screen so the bindings to be reevaluated?
Thanks!
Well the problem is, that the bindings of the view do not notice that the DataContext is changed. So they are still 'looking' on the VM which was assigned first.
So the best way, to solve the issue is to change the data in the VM assigned as DataContext instead of assigning another instance. Since the VM should implement INotifyPropertyChanged the bindings will update automatically.
I have a Window that uses DataTemplates to display a different UserControl (view) in a ContentPresenter based on the type of its Content property, which is bound to a property that holds the current viewmodel. In this way, by changing the viewmodel property with an event, I can facilitate the basic back/forward navigation I need.
When creating a new viewmodel, it is passed a reference to the current one. Going back to the old viewmodel instance works fine for a CheckBox control, but not for a UserControl I made that contains a TextBlock and a ComboBox.
The problem is that, when the view containing the ComboBox gets unloaded, the ComboBox's ItemsSource gets nulled, which triggers it to clear its SelectedItem/Text properties, which are for some reason still bound to my viewmodel--thus clearing the data it stores. I don't know how to manually unbind them at the appropriate time. (Again, the CheckBox works just fine.)
I have read that other users have had this exact same problem. For them, changing the declaration order of the ItemsSource and SelectedItem/Text bindings so that the attributes for the latter are placed before the former fixes the issue. However, in my case, it does not. Others have also fixed the issue by ignoring null/empty values, but this won't work in my case.
I could work around the issue by copying the interesting data to a separate object, and reloading it from that, but I would need to add code to trigger reloading the data = more data linkage code to maintain.
I could also avoid using DataTemplates and manually add the UserControls in the codebehind, which would allow me to break the data binding before removing the UserControl. But this runs counter to the point of MVVM.
I'm not above modifying my very non-MVVM UserControl to handle any events on the ComboBox it contains to work around this issue.
UPDATE:
I have narrowed down the issue a little bit. I refactored the code so that it manually creates and adds the view UserControl based on which viewmodel has been set. The issue now only occurs when I set the DataContext of the view UserControl to null. If I simply replace the view without removing the reference, it no longer erases the values in question. Is this a usable workaround, or does it create issues like memory leaks?
Maybe something that would "open mind" for a simpler solution... If I understand your problem, it's similar to a past problem we had. In our case, we simply made the assumption that it's not possible to set a specific value to null when accessed by the bound property, so we tweaked the appropriate ViewModel Properties a bit:
public MyItem SelectedItem {
get {
return Model.MyItem;
}
set {
if (value != null) {
// Set and notify if not null
Model.MyItem = value;
OnPropertyChanged("SelectedItem");
}
else // just notify when trying to set to null
OnPropertyChanged("SelectedItem");
}
}
Using such tweaked properties we were able to block any try to set the value to null, by calling OnPropertyChanged(..) insead, the existing value was recalled by the UI. If there is a need to be able to set a value to null, you have to provide a seperate property allowing that.
Not sure if this applies to your problem.
Good luck.
UPDATE
oh, I see probably this describes same method as "Others have also fixed the issue by ignoring null/empty values" which seems not to work in your case. But I dont unterstand why it shouldn't.
This is a known bug in the early versions of WPF caused by event leapfrogging. It was fixed for the Selector-derived controls in .NET 4.0.
See this blog post for more details: http://blogs.interknowlogy.com/2011/03/09/event-leapfrogging/
I have worked around the issue by adding a property Active and corresponding Activate()/Deactivate() methods to my base viewmodel class, and calling these as appropriate when swapping out viewmodels. This fits into my application pretty well. I'm still open to other suggestions, of course.