How to implement WPF ValueConverter that needs data from viewmodel? - c#

i am writing an application that has a viewmodel and a usercontrol that displays
data from this viewmodel. The viewmodel contains an entity "Appointment", and those
appointments have a property "UserName".
When I display the appointments, I want to use a value-converter to get a color for
the user (depending on "UserName"), but the colors are not contained in the entity "Appointment", so I wanted to create a value-converter that uses the entity "User" from the viewmodel.
What is the best way to use another entity from the viewmodel inside the converter?
Is it possible to access the viewmodel from the usercontrol? I tried to place the converter inside my viewmodel-class, but can I access this class from the usercontrol?
I figured out that the following possibilities might work:
Adjust the viewmodel so that each appointment also contains the color. But I don't want to do this because I don't want to mess with the viewmodel.
Set the converter-parameter from the class that also contains the viewmodel at startup. (Does this work?)
Use x:Reference to databind the converter parameter to the viewmodel that is unknown at compile-time.(Is this possible?)

Converter parameter is the way to go.
Why is the viewmodel unknown at compile time?
Bindings are not compile time checked anyway.
Is the UserControl.DataContext being set to an instance of Appointment, you should be able to set the parameter to {Binding UserName} or {Binding Appointment.UserName} depending on exactly what you are setting as the DataContext on the UserControl.

I would suggest that you re-examine your reluctance to modify the view model. The purpose of having a view model in the first place is so that everything that the view needs can be found in one place. Coming up with elaborate value converters to prevent modifying the view model is an approach that gets increasingly unmaintainable the more you do it.

Related

Mvvm - How to capture in the ViewModel, the UI's data-bound Control's Name/Id, using a Parameter Command? Is there a clear cut way I'm missing?

Mvvm, Wpf, VS2015
Hi All! I have googled this till my fingers bleed! But just hacks and work-arounds, or partial use of code behind.
My Wpf-Mvvm app uses Speech Synthesis.
Each button, (and in the next iteration, buttons using the selectedItem Index of Lists or ComboBoxes, to select the content(text)to be synthesized) specifies different content to be synthesized.
Sticking with Mvvm principles, in my viewModel, I need to capture which button, based on the List or ComboBoxes' SelectedItem/Value that is bound to the Parameter Command.
Why? So I can have the app synthesize the content(text/words)the user selects to hear. If not, I have to make separate commands and speech synthesizer methods for each.
There must be a simple clear-cut way. What am I missing here?
I thought of updating the Model's properties to reflect user's selection and use User Settings to persist,but have not tried yet.
Otherwise the Model's properties are accessible so far as encapsulation-wise to the ViewModel, but the property values from the Model still are not visible in viewModel.
I need the command bound control's ID/Name to route the content to be synthesized based on the model's class properties. But when I instantiate the model from the viewModel,
the model's properties are not there. I presume instantiating the Model class object from the viewModel, is in a different memory location, than the Model.
So I will try using User Setting's persistence for these properties, if no one else has a better way.
There has to be a clear solution to this, I hope :)
I know the View and ViewModel are supposed to not need to know what the other is doing. In this case though, I clearly need the List or ComboBoxes' Id or Name and their SelectedItem index.
I also need the button's Id or Name, because I have several groupings of content to choose from, and several buttons starting the synthesizing method.
Example: If the text content of an announcement to be synthesized, held in separate XML(SSML) files with identifier key values of 941, 23, 508, etc.,
I need to know which the User Selected to hear.
I imagine simply capturing in the viewModel, the Data-bound List/ComboBoxes' Selected ID index would suffice.
Thank you so very much for your help.
-Flazz

Binding single control to different Datacontext in wpf

I have a button button_extract. I want to bind it to two different data contexts (2 classes in different namespaces)
I want to set the Command and IsEnabled properties to different data context.
I have one datacontext set for Command property. But how to I specify the datacontext of IsEnable property which is in different class and namespace. Here is the scenario:-
I have a project named Environments with three namespaces:Viewmodel,Data and View
Viewmodel has class A
Data has class B
View has xaml C with button button_extract.
The data context for C is set to class A.The xaml is as follows
<UserControl x:Class="Enviornment.Views.C"
DataContext="Environment.Viewmodel.A">
<Button Name="button_extract" Command="{Binding ExtractButtonClick}" IsEnabled="{Enviornment.B.SelectedEnvionment}" >Extract</Button>
The above code does not work. The binding of IsEnabled throws error. How can I set the datacontext of IsEnabled to that of Enviornment.B???
You seem to have a common misconception about WPF regarding the need to set the DataContext. In fact, there is rarely any need to set a DataContext on any control as typically the Window has had its DataContext set and each control's DataContext will automatically inherit from that.
So, how to data bind to two different places? Generally, one place would use a normal Binding Path and the other would use a RelativeSource Binding Path. However, that would be more for the case where you wanted to data bind to properties of the set DataContext and properties of a control's code behind.
It is more common in your scenario to simply prepare a view model. That is a custom class that implements the INotifyPropertyChanged interface and provides all of the properties and functionality that your Window, UserControl, or 'view' requires. You would then set an instance of this single object as the DataContext.
Therefore, simply add properties of the relevant classes into your view model and then you will be able to access them all using the one single DataContext object. Please search online for MVVM for further information.

GetView() vs property in ViewModel

I'm currently in the need of setting the SelectedIndex property of my TabControl when a certain event (IEventAggregator) takes place and thought about how I'd implement that.
I came up with 2 possibilities:
Use GetView() provided by ViewAware in order to access my TabControl and set the SelectedIndex to my value
Use a property in my associated ViewModel and bind this property to my TabControl's SelectedIndex property via XAML
Both options are working fine but I personally want to get this question answered since this is not the first time I'm wondering where to implement the functionality in such cases.
I know that the first option won't enable the Notify support but besides that: What would be the proper way?
Having a GetView() method to manipulate the view directly from the viewmodel completely breaks MVVM. You might as well just put all your logic in codebehind. The whole point of MVVM is to abstract away the actual view so that it is decoupled from the logic, and the app can be unit tested.
What if you change your mind about the tabs in the future and decide to show your multiple views some other way? You've now got to start editing your viewmodel to edit the new view instead of just tweaking some XAML.
And for unit testing you're going to have no way to mock out your TabControl.

Which executes first Code Behind or View Model

Based on my previous question Accessing variables from XAML and object from ViewModel using Code Behind:
How would I know which executes first?
Is it the code behind or the ViewModel?
I just want to make sure that my code behind executes prior the ViewModel
The View and the ViewModel are both regular classes that get instantiated. This is done by calling the constructor as in any other class. So, as a simple answer to your question: Set a breakpoint in each constructor and see which one gets hit first.
There is no general answer to your question because it depends on your architecture and use case. Often, some control is bound to a property of the ViewModel of it's parent, which changes at some point. At that point your View already exists and you have no idea how long the value to which the property has been set is existing already. In other cases, your View is created for a specific ViewModel and takes it as constructor parameter.
The one way to make sure that the ViewModel exists before the View is to pass the ViewModel as a constructor parameter. The idea behind constructor parameters is to express: "This class needs existing instances of type xy to be created", which is what you are asking for. However, as you will set it as the Views DataContext in the constructor and as the DataContext can change after creation of the View, you cannot be sure that the View won't get a new ViewModel assigned after creation. Even worse, you will not be able to use your control in XAML anymore, because it doesn't have a default constructor anymore.
According to your first question, it is not really clear why the ViewModel should exist prior to the View. If you need to read a resource value from your View and assign it to a property on your ViewModel, I would expect it to be the other way around? Or are you accessing the View in your ViewModel (don't!)?
The question is, why you have to ask this question in the first place. There is something pretty wrong in your (or your bosses...) concept: View and ViewModel are two entities which should really work without knowing about each other. The idea is to build applications that could work perfectly without a single View existing by just getting/setting values on ViewModels and to have Views which would compile any run perfectly well without ViewModels, just without anything to show or do... If you try to hack this approach, you're better off not using MVVM at all.

How to forbid automatic databinding on validation error

I'm creating a simple database application in C# WPF using MVVM as Relay Commands and databinding. For in-memory storage of database content I use ObservableCollection, which is binded to the Datagrid as follows:
<DataGrid ItemsSource="{Binding Path=Softwares, Mode=OneWay}" SelectedItem="{Binding Path=SoftwareSelection, Mode=TwoWay}">
when the item is selected user can chose to edit it. For editation a form is opened with a bunch of textboxes with the data of given entity. All the fields are validated using IDataErrorInfo, unless all textboxes are valid, the ok button is not enabled, and therefore no changes can be saved to the collection and to the database.
Here is how the example textbox looks like:
<TextBox Text="{Binding Name, Mode=TwoWay, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged, ValidatesOnExceptions=True}"/>
But the tricky part is, in case I change some values in textboxes and then close the window, the new values are propagated to the ObservableCollection, which I don't want to. Do you have any idea, how to prevent such behaviour? I would like the databinding work only after clicking the button. Otherwise the databindng works well, so as the button (dis/en)abling and reflecting changes to the database and to the collection after clicking. Both views are serviced by different ViewModels, data between views are passed by firing events.
I tried to add to the DataGrid UpdateSourceTrigger=Explicit to the ItemsSource binding, but didn't help. Perhaps, I'm missing some application logic?
Thank you very much for your help.
This is where most WPF developers make mistakes of assumptions!
In MVVM dirty data can be stored in the ViewModel and that's what the layer of VM is for! It mimics the View from Model's perspective and because View is in error, the ViewModel would also be in the error. Thats perfectly valid.
So having said that, the question remains
How will you NOT allow the temporary / dirty data to flow to your
ObservableCollection?
Two ways...
If your ObservableCollection is specific to your model class (say MyItem) then if your Model class (MyItem) is an Entity class \ DAL class \ NHibernate class create a wrapper of MyItem class called ViewModelMyItem and then instead of ObservableCollection<MyItem> use ObservableCollection<ViewModelMyItem>.
This way dirty data from your View would be inside ViewModelMyItem and it can only be legitimately flown back to your model class (MyItem) ONLY when Save button is clicked. So that means in Save Command's Execute() delegate you can copy \ clone the ViewModelMyItem's properties into Item's properties, if validations in ViewModelMyItem are fine.
So if Item is an EntityType class / NHibernate class / WCF client model class, it would always only valid data as ViewModelMyItem is filtering the temporary / dirty information upfront.
You could use Explicit binding model. It stops the TwoWay data to flow back to the sorce Item unless BindingExpressions.UpdateSource() is explicitly called.
But according to me, this defeats MVVM in straightforward way because ViewModel will not have what UI is showing! Still however you can use *Attached Behavior * to govern explicit binding by staying in MVVM space!
Let me know if this helps!
You're better off putting the code into the domain object's property setter. Then synchronize with the visuals by triggering the NotifyPropertyChanged handler.
More on this topic:
http://msdn.microsoft.com/en-us/library/ms743695.aspx
http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.aspx
Setting the Binding Mode to Explicit should require you to call the binding expressions UpdateSource() method to send changes back to your model. Because you only mentioned that you set Explicit on the DataGrid's binding, I'm guessing you only need to make sure that you have that mode explicitly set on any property that is being bound directly back to your model. Such as your TextBox's Text Binding, in the case above. That will likely fix your problem but require you to call UpdateSource() on each target's BindingExpression one way or another.
If you're using one of the mainstream ORM's (EF, Linq to SQL, etc), then chances are your Entities automatically implement INotifyPropertyChanged and INotifyPropertyChanging. Because you are sharing a reference to your single instance, all changes in your edit will be reflected in your main view and anything else Binding to that instance. As a dirtier alternative, you can just create a separate instance of the same type and manually copy the values back over when the window's dialog result is true.
The first approach requires you to manually update the bindings, the second approach requires you to manually update the values from the Edit's instance.
With some more code, I can help with your specific approach.

Categories

Resources