How do you pass a ViewModel to the constructor of a UserControl that is shown in a ContentPresenter? - wpf

I have several UserControls that should display the same data. Each UserControl has a different layout of the data that is to be presented. The ContentPresenter can bind to any one of the UserControls by using a DataTemplate in my Resources and by binding the Content to a StyleViewModel. Each UserControl is associated with a ViewModel as defined in the DataType of the DataTemplate. The ViewModels associated with any given UserControl all inherit from the StyleViewModel. The UserControls should get their data from a SettingsViewModel. The UserControls appear in the main Window.
The problem is that I can't figure out how to make the data from the SettingsViewModel accessible to the UserControls.
Is it possible to pass a reference to a SettingsViewModel to the constructor of one of these UserControls that are displayed using a ContentPresenter?
Is there another way to easily switch between different views of the data (i.e. my UserControls) without using a ContentPresenter? If so, how would I make the data accessible to the UserControls?
The following is code from my SingleLineViewModel.cs:
public class SingleLineViewModel : StyleViewModel
{
public SingleLineViewModel() { }
}
The other ViewModels are similar. They are essentially empty classes that inherit from StyleViewModel, so that I can bind to a Style property which is of type StyleViewModel in my SettingsViewModel. The StyleViewModel is also an essentially empty class that inherits from ViewModelBase.
The following is code from my Resources.xaml:
<ResourceDictionary <!--other code here-->
xmlns:vm="clr-namespace:MyProject.ViewModel"
<!--other code here-->
<DataTemplate DataType="{x:Type vm:SingleLineViewModel}">
<vw:ucSingleLine/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:SeparateLinesViewModel}">
<vw:ucSeparateLines/>
</DataTemplate>
<!--other code here-->
</ResourceDictionary>
The following is code from SettingsViewModel.cs:
public class SettingsViewModel : ViewModelBase
{
// other code here
private StyleViewModel _style;
public StyleViewModel Style
{
get { return _style; }
set
{
if (value != _style && value != null)
{
_style = value;
OnPropertyChanged("Style");
}
}
}
// other code here
public SettingsViewModel()
{
_style = new SingleLineViewModel();
}
// other code here
}
The following is code from my MainView.xaml:
<ContentPresenter Name="MainContent" Content="{Binding SettingsVM.Style}"/>

You might find that you are trying to do much at once. Consider how you might test this scenario? Or how would you walk the data in a Debugger to check its state? Good practice recommends that your data is separate from your UI elements. An MVVM pattern such as you are trying to use normally provides the view models to help transition the data from it's simple data into forms that the UI can use.
With that in mind, I would recommend that you try do develop a ViewModel tier that presents all the data without the UI holding it together, i.e. instead of trying to inject the additional SettingsViewModel into your controls you should make your viewmodels hold everything they need.
It looks like you are off to a good start, your SettingsViewModel lets you get hold of a Style, but your style doesn't seem to have any data. So why not pass it in the constructor.
public SettingsViewModel()
{
_style = new SingleLineViewModel(WhatINeedForStyle);
}

Related

Using base control in XAML, but loading a derived control

Here's a situation I am trying to solve:
I have a base UserControl from which I derive a number of other Controls that handle derivations of a base Object in a specific manner. (The purpose of this being to create a template for when additional derivations of the base control are needed later down the road.) What I would like to do is use the base control name as the tag in XAML, but when the control is actually rendered, show the derived control.
class BaseControl : UserControl { }
class DerivedControl1 : BaseControl { }
class DerivedControl2 : BaseControl { }
class BaseObject { }
class DerivedObject1 : BaseObject { // Requires DerivedControl1 to display }
class DerivedObject2 : BaseObject { // Requires DerivedControl2 to display }
class BaseContainerObject { }
class ContainerObject1 : BaseContainerObject
{
DerivedObject1 dObject0;
DerivedObject1 dObject1;
DerivedObject2 dObject2;
}
class ContainerObject2 : BaseContainerObject
{
DerivedObject2 dObject0;
DerivedObject2 dObject1;
DerivedObject1 dObject2;
}
window.xaml
<!-- Here is what I would like to do -->
<StackPanel>
<BaseControl Name="Object0" DependencyProperties="{Binding BaseContainerObject.dObject0}" />
<BaseControl Name="Object1" DependencyProperties="{Binding BaseContainerObject.dObject1}" />
<BaseControl Name="Object2" DependencyProperties="{Binding BaseContainerObject.dObject2}" />
</StackPanel>
I've played around with styles and data triggers to detect the specific type of ContainerObject, but I haven't found the right pattern to encapsulate a ContainerObject in a single template-able "package" yet.
I could dynamically add the controls from the code-behind, but I haven't had any luck with that so far. (The top-level of the control appears on VisualTree, but no children appear on the tree and none are rendered.)
Any thoughts?
EDIT:
I can't post a screenshot at the moment, but perhaps I can add a little more detail.
I have a data object (the DataContext for the window) that has up to nine attributes (the DerivedObjects) that the user will need to edit in my window. The meaning of those nine attributes, and, in turn, how they should be expressed in UI controls, changes based on the attributes of a second data object the user selects in a previous step. (That is the ContainerObject. The other data object is not referenced in the above code, although it contains a reference to the second data object.)
Those attributes can be expressed in four different ways: a text box (for continuous values), a combobox (for discrete values), a checkbox (for boolean values) and radio buttons (for a choice between two values).
I have created UserControls that package those controls in a horizontal Grid with 1) a label for the value's definition, 2) the value's units (if applicable) and, if applicable, 3) a checkbox to view the value in an alternate format (i.e. viewing a decimal number in hex). (Those are the DerivedControls that inherit from an XAML-less BaseControl that stores common properties and functions.) To maintain proper column alignment over the entire collection, I specify four column widths in a Style at the Window level and use a Converter to handle alignment for attributes that do not require the units and/or the alt-display checkbox.
When the user selects the second object in the previous step, the nine rows of the collection control should look to the second data object reference of the DataContext object to select the proper template and populate the other labels. Because I will need to use this collection in other programs, I am creating it in a separate assembly.
I know I am pigeon-holing myself in some fashion on this. I am trying to do this with as little code as possible, but I can't think of the right code pattern to use here. Every component is working fine, but I can't seem to get it all to come together in a simple way so I can work out the last few little bugs.
Thanks. I am just learning WPF, and I really like. I'm just at the point of trying to get my head wrapped around some of the finer details.
Here is a pretty good example from wpftutorial.net of what it sounds like you need. To summarize, you can use a DataTemplate to define how an object is displayed within a repeating control such as a ListBox, ComboBox or ListView. You can override the styles of those to make them appear as you want, or sometimes it's just easier to use ItemsControl (the control they inherit from) directly. They have a property named ItemsPanel that will allow you to specify a StackPanel as the ItemsPanelTemplate so you get the same desired layout of the objects as you showed above.
Setting how an object is dispalyed via a DataTemplate is great, but you want to dynamically change that template based on the type of the bound object if I understand correctly. This can be accomplished by creating a DataTemplateSelector.
public class PropertyDataTemplateSelector : DataTemplateSelector
{
public DataTemplate DefaultDataTemplate { get; set; }
public DataTemplate DerivedObject1Template { get; set; }
public DataTemplate DerivedObject2Template { get; set; }
public override DataTemplate SelectTemplate(object item,
DependencyObject container)
{
DataTemplate selectedTemplate = DefaultDataTemplate;
if (item is DerivedObject1)
{
selectedTemplate = DerivedObject1Template
}
else if (item is DerivedObject2)
{
selectedTemplate = DerivedObject2Template;
}
return selectedTemplate;
}
}
And then your XAML can use the template selector on the repeating control:
<Window x:Class="Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:..."
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<Window.Resources>
<!-- Default DataTemplate -->
<DataTemplate x:Key="DefaultDataTemplateResource">
...
</DataTemplate>
<!-- DataTemplate for Booleans -->
<DataTemplate x:Key="DerivedObject1TemplateResource">
<local:DerivedControl1 .../>
</DataTemplate>
<!-- DataTemplate for Enums -->
<DataTemplate x:Key="DerivedObject2TemplateResource">
<local:DerivedControl2 .../>
</DataTemplate>
<!-- DataTemplate Selector -->
<local:PropertyDataTemplateSelector x:Key="myCustomTemplateSelector"
DefaultnDataTemplate="{StaticResource DefaultDataTemplateResource}"
DerivedObject1Template = "{StaticResource DerivedObject1TemplateResource}"
DerivedObject2Template = "{StaticResource DerivedObject2TemplateResource}"/>
</Window.Resources>
<Grid>
<ItemsControl ItemsSource="{Binding}" ItemTemplateSelector="{StaticResource myCustomTemplateSelector}"/>
</Grid>
</Window>
Hopefully that will get you started!

WPF: right way to do a Tabcontrol with MVVM pattern

First of all, I'm newbie in WPF and specially in MVVM. I have a window with diferent tabs and a very large ViewModel with the business logic of the content of every tab. I know it is not right, so now I'm trying to do it more elegant:
As I see googling, an idea is to do a collection of a "base" viewmodel from wich inherit the sub-viewmodels of every tab, and a collection on this "base" viewmodel in the viewmodel of the window.
TabBaseViewModel
Tab1ViewModel inherits TabBaseViewModel
Tab2ViewModel inherits TabBaseViewModel
MainWindow ViewModel --> Collection of TabBaseViewModel
The contents the tabs do not have anything in common along each other.
How I have to proceed?
You should consider using an MVVM framework if you're using MVVM. With Caliburn.Micro for example, you can define your main view as:
<TabControl x:Name="Items">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding DisplayName}" />
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
Where the data context is a Conductor type that has a collection. The Items property will expose a collection of your view models:
public class MainViewModel : Conductor<IScreen>.Collection.OneActive
{
private OneOfMyViewModels oneOfMyViewModels;
private AnotherViewModel anotherViewModel;
protected override void OnInitialise()
{
// Better to use constructor injection here
this.oneOfMyViewModels = new OneOfMyViewModels();
this.anotherViewModel = new AnotherViewModel();
this.Items.Add(this.oneOfMyViewModels);
this.Items.Add(this.anotherViewModel);
}
protected override void OnActivate()
{
base.OnActivate();
this.ActivateItem(this.oneOfMyViewModels);
}
}
public class OneOfMyViewModels : Screen
{
public OneOfMyViewModels()
{
this.DisplayName = "My First Screen";
}
}
I posted an answer to a different question which shows how to do exactly this: How to Get a Reference to a ViewModel
It's a very simple example, but hopefully should get you started along the right track.

WPF Binding to UserControl Custom DependencyProperty

I have a custom UserControl called SongDescription:
<UserControl x:Class="DPTestAp.SongDescription" ...>
<Grid x:Name="LayoutRoot">
<DockPanel Height="50">
<TextBlock x:Name="title" Text="{Binding name}" Width="100" Height="30"/>
<TextBox x:Name="lyrics"/>
</DockPanel>
</Grid>
</UserControl>
I added DependencyProperty to it:
public partial class SongDescription : UserControl
{
public static readonly DependencyProperty SongProperty = DependencyProperty.Register("Song", typeof(Song), typeof(SongDescription));
public Song Song
{
get
{
return (Song)GetValue(SongProperty);
}
set
{
SetValue(SongProperty, value);
updateLyrics()
}
}
private void updateLyrics()
{
lyrics.Text = Song.lyrics;
}
public SongDescription()
{
InitializeComponent();
}
}
The question is: how to bind something to this SongProperty?
I use SongDescription in my main window like this:
<local:SongDescription x:Name="songDescription" Song="{Binding DataContext}"/>
I cannot make my TextBox lyrics show lyrics. In main window I tried to set DataContext to songDescription, like this:
songDescription.DataContext = new Song() { name="Home", lyrics="Hold on, to me as we go" };
or to window itself like this:
DataContext = new Song() { name="Home", lyrics="Hold on, to me as we go" };
I even tried to make Song a resource and bind it to SongProperty like this:
<Window.Resources>
<local:Song x:Key="res" name="Home" lyrics="Hold on, to me as we go"/>
</Window.Resources>
<Grid>
<local:SongDescription x:Name="songDescription" Song="{StaticResource res}"/>
</Grid>
Nothing helped. TextBlock title binds song name fine. But I can't make updateLyrics() method be called. (In real life this method is more complicated, so I can't use Binding like with name).
Thank you!
Yup, so that's a gotcha with dependency properties. You never ever put validation code inside of the accessor methods (get/set) because dependency properties are stored by WPF in a table that it itself manages. This is why you have to register dependency properties, it essentially creates entries on this table for storing the values associated with each property, and when you use 'GetValue' / 'SetValue' you are updating the entries on this table (which by the way relates to how WPF is able to manage data bindings in general).
The upshot of this though is that WPF can (and will) completely bypass your property accessors because it has direct access to the real data. Why should it use your accessors if it can just go to the data directly. Instead you need to implement a 'PropertyChanged' callback function or some WPF sanctioned method of doing validation, but never ever do it in your accessors.
See:
http://msdn.microsoft.com/en-us/library/ms752914.aspx
In addition to sircodesalot's answer, you are not bound on your lyrics textbox. Also, since the song your bound to is a class, you will need to specify the paths fully for the properties you want to show in the boxes such as "Path=Song.Lyrics".
Another thing to consider is that with dependency properties; your mode will be oneway by default so making the text field editable would be moot really unless you change it.
Third, if you're using MVVM you only need your main window context to be set to the view model and have a matching Song property to bind against.

GraphSharp library - binding layout

In our project we are using GraphSharp library. We have encountered some problems when we wanted to remove all edges and vertices from graph.
In every example there is, in xaml there is something like that
<zoom:ZoomControl Grid.Row="1" Zoom="0.2" ZoomBoxOpacity="0.5" Background="#ff656565">
<toProjectGraph:EntityGraphLayout x:Name="graphLayout" Margin="10"
Graph="{Binding Path=GraphViewModel.EntityGraph}"
LayoutAlgorithmType="{Binding Path=GraphViewModel.LayoutAlgorithmType, Mode=OneWay}"
OverlapRemovalAlgorithmType="FSA"
HighlightAlgorithmType="Simple"
/>
</zoom:ZoomControl>
xaml creates instance of our class EntityGraphLayout and uses it to visualize everything.
Is it possible in some way to "bind" this instance of EntityGraphLayout to some property in our view model so we can reference it in our view model code?
Or maybe there is a way that we can create instance of this class and tell xaml to get referebce to object from some path.
Sounds like what you want is to create the object in your viewmodel, expose it as a property, and bind it to the Content property of your zoom control, something like this:
viewmodel:
public class ViewModel {
private EntityGraphLayout _layout = new EntityGraphLayout();
public EntityGraphLayout EntityGraphLayoutProperty
{
get { return _layout; }
set { _layout = value; }
}
}
XAML:
<zoom:ZoomControl Content="{Binding EntityGraphLayoutProperty}" Grid.Row="1" Zoom="0.2" ZoomBoxOpacity="0.5" Background="#ff656565" >
</zoom:ZoomControl>
Note that you will need to make sure the DataContext for the zoom control is set to your viewmodel.
If you want it created in XAML, you could also access the object in your viewmodel by referring to it by the graphLayout name you defined in the XAML. This would require a reference to the view in your viewmodel, which may not be ideal.

Implementing a view-model-first approach inside a parent view/view model using MEFedMVVM

I am writing a WPF application using MEF and a third-party library called MEFedMVVM.
I am attempting to create a design whereby a parent view model has a collection of child view models, and I wish to use the view-model-first approach as this keeps the views outside of the view models thereby keeping the code more view model-centric and more unit testable.
I have read this discussion and this discussion regarding using DataTemplate for the view, and also Reed Copsy, Jr's suggestion here to use a generic view to view model mapping resource. But, I'm struggling to actually implement something that works.
My parent view is very simple:
<UserControl x:Class="MyParentView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:meffed="clr-namespace:MEFedMVVM.ViewModelLocator;assembly=MEFedMVVM.WPF"
meffed:ViewModelLocator.ViewModel="MyParentViewModel" />
The parent view model derives from a base type that implements IContextAware:
[ExportViewModel("MyParentViewModel")]
public class MyParentViewModel : ViewModelBase
{
[ImportingConstructor]
public MyParentViewModel()
{
var myChildVM = ServiceLocator.Current.GetInstance<MyChildViewModel>());
}
}
This is the child view model:
[Export(typeof(MyChildViewModel))]
[ExportViewModel("MyChildViewModel", true)]
public class MyChildViewModel : ViewModelBase
{
}
And this has a corresponding view:
<UserControl x:Class="MyChildView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:meffed="clr-namespace:MEFedMVVM.ViewModelLocator;assembly=MEFedMVVM.WPF"
meffed:ViewModelLocator.ViewModel="MyChildViewModel" />
Initially, I thought that specifying the second Boolean parameter on ExportViewModel attribute for MyChildViewModel would make everything work using a view-model-first approach as my views and view models are MEFed together in the views' XAML code. However, turns out this is not the case, and what I actually get passed in to IContextAware.InjectContext() when I instantiate a MyChildViewModel object in the MyParentViewModel constructor is a MyParentView object. Not a MyChildView object as I was expecting and hoping. Clearly, I need to add something to wire them up together. Could anyone provide an example on how to do this?
Thanks!
When you really want to use view-model-first then you should do:
[ExportViewModel("MyParentViewModel")]
public class MyParentViewModel : ViewModelBase
{
// Create property for your child vm
public MyChildViewModel Child {get; private set}
// If you do MEF use constructor injection instead of servicelocator
[ImportingConstructor]
public MyParentViewModel(MyChildViewModel child)
{
this.Child = child;
}
}
then define a datatemplate for your childvm
<DataTemplate DataType="{x:Type local:MyChildViewModel}">
<view:MyChildViewUserControl />
</DataTemplate>
in your MainView you know where you want to show the child data, otherwise you wouldn't need the child property ;) so simply put a ContentControl where the Child data should go and bind to your property.
e.g.
<TabControl>
<TabItem Header="MyChildData">
<ContentControl Content="{Binding Child}" />
</TabItem>
</TabControl>
PS: Code is written without IDE, so errors possible :)

Resources