Multiple DataContext in a View vs MVVM? Is that right? - c#

I have the following problem: I'm building a chat WPF application on which I want the user to be connected with different accounts to GTalk. I've made a ViewModel that permits handling the communication but I don't know which is the best way to handle DataContext. I think that I need different DataContexts for every connection instance but I don't know if this is the right way and don't know how to define multiple DataContexts in a View.

A DataContext provides the default binding source for an element in the view. It is inherited from parent to child within the visual tree. You can change the DataContext for any element by binding it to some property of its parent DataContext. This is a common way of creating 'islands' that bind to a child view model.
For example, if you have a User view model that has an Address Property, you can render this in an AddressUserControl as follows:
<StackPanel>
... elements bound to properties of User ...
<AddressUserControl DataContext="{Binding Address}"/>
</StackPanel>

If you have a variable number of accounts, you can use the ItemsControl.
The rough idea behind this: You can bind the ItemsControl to a list of "Sub-ViewModels" in your main ViewModel and define a DataTemplate (= a View) for each of them. The DataContext of each DataTemplate is automatically assigned to a corresponding item in your ViewModel's list.

Related

how to get TextBlock value inside resource dictionary

I'm new to WPF and I have following text-block in resource dictionary
<TextBlock x:Name="textSampleVal" Text="{Binding textSamVal}" />
How to get this textSampleVal value from ViewModel class
How to get this textSampleVal value from ViewModel class
Simply by accessing the textSampleVal property that the TextBlock binds to.
It doesn't make any difference whether you define the binding in a ResourceDictionary or a view, the TextBlock will still bind to the textSamVal property of its DataContext and this is supposed to be the view model.
A view model never "gets" anything from the view, i.e. the view model doesn't know anything about any TextBlock elements. The view updates the view model using data bindings.

UWP - How to show different View (UserControl) in each PivotItem when Pivot bound to list of ViewModels?

I'm building a UWP app using the Template10 Minimal template. I have a list of ViewModels that share a common base class. They are bound to a Pivot as follows:
ItemsSource="{Binding EnabledModels}"
I've setup multiple data templates to map each ViewModel concrete type to the View (UserControl) created for that particular ViewModel as follows:
<Pivot.Resources>
<DataTemplate x:Key="gettingStarted" x:DataType="vm:GettingStartedViewModel">
<v:GettingStartedPart DataContext="{Binding}"></v:GettingStartedPart>
</DataTemplate>
<DataTemplate x:Key="packageSelection" x:DataType="vm:PackageSelectionViewModel">
<v:PackageSelectionPart DataContext="{Binding}"></v:PackageSelectionPart>
</DataTemplate>
</Pivot.Resources>
I've not been able to determine how to get the View to actually display. Currently it will only display the type name of the ViewModel. I'm sure I've messed up the bindings somehow.
My ultimate goal is to present a Pivot with a series of data collection screens that all share a common base ViewModel, but each screen has it's own data needs. I'd like to keep the screens as separate UserControl views and dedicated ViewModels to make them easier to maintain independently.
I've looked for other patterns for multi-screen data capture in UWP that don't require separate pages but haven't had any luck.
Thanks for any guidance you can provide!
I was able to get the DataTemplateSelector to work so the Pivot would display a PivotItem containing the appropriate View (UserControl) for each ViewModel in my list of EnabledModels. Thank you to commenters Will and AVK Naidu.
Good resource for this situation available here

When to use FindAncestor?

In the image below, why is FindAncestor needed and why is AncestorType not UserControl?
If the UserControl was inside a StackPanel, would AncestorType be StackPanel?
In other words, what does the parent control have to do with binding to the viewmodel.Message? Why is all the other xaml needed in the Binding?
It wouldn't need to specify AncestorType=UserControl, since that's the default context. If you omit the "RelativeSource" parameter, it will bind to the local DataContext.
In this case, the UserControl and the Window it is contained in must have different DataContexts. Since a UserControl generally doesn't know the name of its parent at design time, you can at the least usually assume it's only going to be in a single Window and create a binding to the Window's DataContext using RelativeSource as shown.
This can be common because in an application, the Window might have a DataContext for information about the application as a whole, but the current UserControl might be focused on a particular piece of data (such as an Employee). In this case all the default bindings point to the Employee, but the UserControl might want to bind to something in the parent DataContext too.
Why is FindAncestor needed?
Here FindAncestor is used to access to the Window object to get an access to the DataContext of Window object.
Why is AncestorType not UserControl?
Because the DataContext of the UserControl object may be not the same as the DataContext of Window object.
Normally it's the same but you can redefine it.
If the UserControl was inside a StackPanel, would AncestorType be StackPanel?
No if the StackPanel is itself in your Window.
Don't forget that you target the Type of the graphical object that contain the DataContext you want.
In this case, the developer just knew that the object that had the datacontext they were looking for was a Window. If, for example, a data context was set on a stackpanel and you wanted something from that datacontext, then you would use the AncestorType = Window. Since there are two datacontexts, and the user wanted to bind something to the outside one, FindAncestor was used.

Loading XAML at runtime using the MVVM pattern in WPF

This is a question that extends from the originally posted here:
Link to loading-xaml through runtime
I'm working on a WPF MVVM application that loads XAML content dynamically from an external source, very similar as the answer in the post above.
Here is what I got so far:
My View declares an instance of the ViewModel as a resource and creates an instance of that ViewModel
In my ViewModel constructor I'm loading a XamlString property coming from an external source (file or db..)
In my view I have a button that user clicks after ViewModel finishes loading and in the click-event code-behind I'm deserializing the dynamically loaded XAML and add it to my grid.
My question is, how can I eliminate code-behind and automate the logic so the View can render the new xaml section dynamically right after the ViewModel is done getting the XAML content and initializing the string property?
Should I use some kind of Messaging Bus so the ViewModel notifies once the property has been set so the View can add the new content?
What troubles me is the fact that ViewModels do have a reference to Views and should not be in charge of generating UI elements.
Thanks in advance!
Edit:
Just to clarify: in my particular case I am not trying to bind a Business Object or Collection (Model) to a UI element (e.g. Grid) which obviously could be accomplished through templates and binding. My ViewModel is retrieving a whole XAML Form from an external source and setting it as a string property available to the View. My question is: Who should be in charge of deserializing this XAML string property into a UI element and add it programmatically to the my grid once my Xaml string property in the VM is set?
This sounds to me more of like a View responsibility, not ViewModel. But the pattern as i understand it enforces to replace any code-behind logic with V-VM bindings.
I have a working solution now and I'd like to share it. Unfortunately I did not get rid of code-behind completely but it works as I expect it to. Here is how it works(simplified):
I have my simplified ViewModel:
public class MyViewModel : ViewModelBase
{
//This property implements INPC and triggers notification on Set
public string XamlViewData {get;set;}
public ViewModel()
{
GetXamlFormData();
}
//Gets the XAML Form from an external source (e.g. Database, File System)
public void GetXamlFormData()
{
//Set the Xaml String property
XamlViewData = //Logic to get XAML string from external source
}
}
Now my View:
<UserControl.Resources>
<ViewModel:MyViewModel x:Key="Model"></ViewModel:MyViewModel>
</UserControl.Resources>
<Grid DataContext="{StaticResource Model}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel>
<!-- This is the Grid used as a Place Holder to populate the dynamic content!-->
<Grid x:Name="content" Grid.Row="1" Margin="2"/>
<!-- Then create a Hidden TextBlock bound to my XamlString property. Right after binding happens I will trigger an event handled in the code-behind -->
<TextBlock Name="tb_XamlString" Text="{Binding Path=XamlViewData, Mode=TwoWay, UpdateSourceTrigger=LostFocus, NotifyOnValidationError=True, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}" Visibility="Hidden" Loaded="tb_XamlString_Loaded" />
</StackPanel>
</Grid>
Basically I created a hidden TextBlock bound to my XAML String property in the ViewModel and I hooked its Loaded event to an event handler in the code behind of the View:
private void tb_XamlString_Loaded(object sender, RoutedEventArgs routedEventArgs)
{
//First get the ViewModel from DataContext
MyViewModel vm = content.DataContext as MyViewModel;
FrameworkElement rootObject = XamlReader.Parse(vm.XamlViewData) as FrameworkElement;
//Add the XAML portion to the Grid content to render the XAML form dynamically!
content.Children.Add(rootObject);
}
This may not be the most elegant but gets the job done. Like some people say, in MVVM there are some cases like this where little code-behind code is needed. It doesn't hurt and also part of this solution still uses the V-VM Binding principles when using the VM to retrieve and populate the XamlString property and exposing it to the View. If we would like to Unit Test the XAML parsing and loading functionality we could delegate it to a separate class.
I hope someone finds this useful!
I'm having trouble understanding what you're saying, so my answer will be based on my interpretation. You should consider posting a sample (simplified) of what you're trying to do.
1) I think you're misunderstanding what MVVM does. MVVM is mostly a binding-based pattern. Your view model should be exposing properties containing business objects and your view should just be binding to those properties. If I am misunderstanding you, and that's what you are doing, then your problem is that your view needs to be aware of when the properties get updated (after you deserialize your xaml, etc). There are two ways to do this: INotifyPropertyChanged interface on your viewmodel, or make your view model inherit from DependencyObject, and make the properties dependency properties. I won't go into details here, because this is a large subject that you should research on Google before making a decision.
2) Generally speaking, you shouldn't use click events inside your view if you're using MVVM. Instead, create properties on the view model of type ICommand (and create ICommand implementations to match, or use an implementation of DelegateCommand (google it) which will allow you to use delegates to implement the interface. The idea is, your view binds to the property and executes the handler directly inside the viewmodel.
3) If you want to push information from the viewmodel to the view, then you should create an event on the viewmodel and subscribe to it in the view, but this is a last resort, only to be used in cases like displaying a new window, etc. Generally, you should be using binding.
4) To be more specific about what you're doing, you should be binding your Grid's ItemsSource property to some property on the view model. Note, the property on the view model should be of type ObservableCollection<T> if you want to be able to add items and get instant updates.
Hope this helps.

MVVM DataTemplate and non-empty view model constructor

I have the below DataTemplate:
<DataTemplate DataType="{x:Type vm:MyViewModel}">
<views:MyView/>
</DataTemplate>
The thing is, my view model has a constructor that takes parameters which are automatically injected by the container (unity). In order for the DataTemplate to work though, MyViewModel needs to have a paramaterless constructor.
Is there any other way I can inject the appropriate values into my view model if I am using a DataTemplate to create it?
I don't think Views should be creating ViewModels.
ViewModels should be creating other ViewModels, and the View simply defines how to draw the ViewModel.
For example, a ParentViewModel might have a property called ChildViewModel. The ParentView will contain a ContentControl which has its Content bound to ChildViewModel, and a DataTemplate would be used to tell the application to draw ChildViewModel as a ChildView.
With that being said, how is your View currently creating your ViewModel? You could always add DependencyProperties to your View and build your ViewModel in the View's loaded event using these properties.
You might want to consider using an MVVM framework such as Caliburn.Micro, and take a ViewModel first approach.

Categories