Keeping track of dependency property value changes at global level - wpf

I am having a number of controls in my application(which user can add to canvas), each having various properties(mostly dependency properties). User can change its properties through property grid(like color, text etc.).
I have save functionality implemented, so if user makes any change in canvas we ask him to save the document before leaving. At present I am keeping track of Add/Delete/Resize like operations(changing IsChanged flag to true). I also want to keep track of any property changes done by user, say if he changes the color of control through propertygrid.
One straightforward solution is to handle PropertyChangedCallback for each proeprty and set the flag in that. Problem with this is that I will have to write PropertyChangedCallback for each proeprty in each control, and at the same time I will have to make sure that every new proeprty added do the same.
My question: Is there any other better way of tracking property changes, say at some global place?

If your objects/classes are DependencyObjects, you could create your own 'base class', deriving from DependencyObject which overrides OnPropertyChanged:
http://msdn.microsoft.com/en-us/library/system.windows.dependencyobject.onpropertychanged.aspx
In the implementation you could register that the object had changed in some singleton or associated change-tracking manager class, then call base.OnPropertyChanged.
Then change all your objects to implement this new class rather than DependencyObject and when any properties change your code will get called.
Hope that helps!

Related

how to bind button is enable property to a enum in wpf

i am using listView grouping based on an enum which {Updated and not Updated}. In my case, their is a button to call update I just want to disable if rows are in an updated group. See in the images.
The list that is present in updated section I want that the button should be disabled. I am using an example found in Here
IsEnabled={Binding ProperyNameOnYourViewModel, Mode=OneWay}
That will work assuming 1) your VM implements INotifyPropertyChanged and triggers appropriate event at the appropriate moment (typically, property's setter is responsible for that); 2) your control itself knows how to react on IsEnabled=false visually, i.e. changes color or whatever you like it to do.
Since you got an enum rather than bool, the easiest way to stick to the solution I've suggest is by extending your VM with yet another property, which recomputes boolean flag all the time. Then you might invoke PropertyChanged event not from your bool property (to which IsEnabled is actually bound), but from the enum property such that whenever it changes, WPF gets notified that boolean property has changed as well.
Otherwise, solve the problem with help of converters, which requires a lot more code.

Using dependency property default value as binding source

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}}"/>

How can I add additional bindable visual state groups to a button?

I need to extend a button control to add some additional, bindable, visual states.
What I would like to create is an additional boolean property, to bind to, that will create a simple visual state change on the button. For this example, it could just be an additional border which changes colour according to the boolean value.
All the existing button behaviour should be entirely seperate from this new set of states.
Can this be done?
This can certainly be done. Here's a post that walks you through the steps you'll need to take, and includes additional information for extending the control with properties (besides just those to hold state) that show up in the final control. The pieces you'll need are:
A class that derives from Button (your custom control class)
A default style for this control (which goes in /themes/generic.xaml). You can start off with Button's generic style and add your states to it.
A dependency property that holds your boolean value
A new VisualStateGroup that holds your two new states
Some code in your class that glues together changing states with the boolean value you've defined, presumably resulting from user interaction, etc.
You may find it's easier to edit the visual state in Blend, depending on how sophisticated your transitions will be, etc.

Detect changes to user input controls in Silverlight?

I have a childwindow with a number of Textboxes, Comboboxes, and DatePickers. I want to know if a user has changed any value in these (to know if I need to save to db)
One way I could think of doing this are in the 'on chg' event handlers and set bool. But if a user changes the value, in say a combobox, then changes back to the original this would still be seen as a change.
Are there other alternatives?
(note the project is not set up as MVVM)
If you don't use mvvm but still bind to an object then:
before the window is shown create a copy of the object, save it, and bind it to DataContext
whenever you need to know if user made any changes you can compare the saved object to DataContext (property by property)
I you don't use binding at all then:
before the window is shown save all fields that can be modified to a Dictionary
whenever you need to know if user made any changes you can compare the dictionary values to values of the fields

How to call a user control method using MVVM?

I'm working in a WPF project, I'm using the MVVM patter in my project.
I created a user control (also in WPF) and I want to use it in my project, now, my problem is that I have a method in my user control that I need to call from my View Model, but I don't know how to do that, how to bind to the method inside my control from the view model.
If I use code behind, obviously there is no problem since I have a direct reference to my control, so I can do "mycontrol.MyMethod();"m, but of course, doing in this way will go against the logic of the MVVM pattern.
I tried to use a Dependency Property in my user control, and use that Dependency Property to bind to it in the xaml of my project but it didn't worked, the compiler says that the property was not found or is not serializable.
So I will appreciate if someone can share some light about how can I accomplish this.
Edited
As far as I understand you have the view, which is all the GUI, then you have the model, which is all the logic, and them you have the view-model which is like an intermediate layer used to bind the view with the model, right?
In this way I have developed my project, however I came to the problem that I need a custom control, a TextBox that remember what the user entered, and when he start typing, if there are words that start with that letter, those words are shown as a suggestion, as Google does it.
This TextBox is used as a search filter; so I created a user control to do this, I added a method to my user control to allow whatever application that uses my control to add items to an internal array that holds all the strings that the user has entered.
I created a user control because I couldn't find any control that behaves the way I want.
So my problem is when I add my user control to the main project, because I need to someway be able to call the method that add the items to the internal array, but maybe I'm doing things the wrong way, so if any of you has a better idea, I will appreciate if you shared it with me.
You should never call View methods from ViewModel, and vice versa.
Make a property (ObservableCollection?) on your ViewModel, it will have CollectionChanged event, subscribe to it to monitor changes (if needed).
When you add an item to the collection in your ViewModel, GUI will be updated accordingly (you have to perform the Add() operation on GUI thread, btw).
If you need to change the current position in your list, there are colections for that (CollectionViewSource, etc).
If you really really need to pass a string to your control, make a DependencyProperty and bind it OneWay to your ViewModel's property. When you set the value, it will call PropertyChangedCallback on your DependencyProperty.
Why does the consumer of the user control need to maintain the control's internal array? That seems like you've exposed an implementation detail that you don't need to.
Why not simply make that array a dependency property (and an IEnumerable<string> or ObservableCollection<string> besides)? Then you can simply create the corresponding property in your view model and bind it to the control. It also makes the control considerably more versatile.
You shouldn't call something in the View from the ViewModel since that breaks the model.
If the reason you want to call the method in the user control is to do with UI only, I don't see anything wrong with doing it from the view - the view's cs and the view's xaml are in the same "space" in the model. You can be overly-purist in wanting to have lean and mean view cs files.

Resources