Persisting Data Between MVVM View Changes - wpf

I have an application which contains a hierarchy of View/Viewmodels.
ViewModelBase contains two ViewModels
private AViewModel _aViewModel = new AViewModel();
private BViewModel _bViewModel = new AViewModel();
My XAML binds a DataControl to
private ViewModelBase _currentView {get; set;}
public ViewModelBase CurrentView
{
get
{
return _currentView;
}
set
{
_currentView = value;
RaisePropertyChanged("CurrentView");
}
}
And decides which view to display based on DataTemplates
<DataTemplate DataType="{x:Type vm:AViewModel}">
<vw:AView />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:BViewModel}">
<vw:BView />
</DataTemplate>
All this works fine but I'm not sure how to persist data between View changes. Say for example that AViewModel contains a string called "Test" and has a two way binding in AView. By changing view using CurrentView = _bviewmodel then my data won't persist when I change back to _aviewmodel - What's the best way to make sure any data stays between view changes as opposed to creating a new blank viewmodel each time.
I have to get _currentView to _aViewModel and then back to _currentView

Should have posted my AView xaml - Inside it was
<UserControl.DataContext>
<vm:AViewModel></vm:AViewModel>
</UserControl.DataContext>
So it appears I was stupidly creating a new ViewModel inside the Xaml everytime I changed the view. Thanks to everyone for pointing me in the right direction. I deleted this from the Xaml and all is working fine now.

Data should be persistent. Make sure that you don't create new ViewModels every time you change the CurrentView.

Related

Removing UserControl code behind for use with MVVM

I am trying to create a user control using MVVM.
Basically I am trying to wrap a combobox that will pull data from a respository. This will allow me to use the same combobox in many different views in my application. There will be many of the wrapped comboboxes throughout the application.
I was easily able to create this control using a DependencyProperty and code-behind. I am now trying to convert this to MVVM and am having trouble figuring out how to get the value back to /from the ViewModel that in bound to the View where my combobox is located.
Any ideas or suggestions would be greatly appreciated at this point.
Thanks,
Eric
It is perfectly acceptable to use a UserControl that has code behind in it when using MVVM. If you really want to move the functionality out of the UserControl, then move it to whichever parent view models will require it. If you don't want to have the same code repeated in several places, you could encapsulate it in a class and add an instance of that class as a property to each of the relevant view models.
if you have a viewmodel that will pull data from a respository - you can use the same viewmodel in many different viewmodels in your application :)
and if you use a datatemplate your views know how to render this viewmodel
<DataTemplate DataType="{x:Type local:MyPullDataViewmodel}">
<view:MyCoolPullDataShowComboboxUserControl />
</DataTemplate>
It's pretty easy.
Let's say you have:
MyUserControlView.xaml
MYUserControlViewModel.cs
MyMainWindowView.xaml - For your MainWindow view (the one containing the UserControl)
MyMainWindowViewModel.cs - Your MainWindow ViewModel.
And you want to bind List<string> MyListToBind
And leave the code-behind completely empty.
MyUserControlViewModel.cs
public class MyUserControlViewModel
{
private List<string> _MyListToBind;
public List<string> MyListToBind { get; set;}
}
MyMainWindowViewModel.cs
public class MyUserControlViewModel
{
private MyUserControlViewModel _MyControlViewModel;
public MyUserControlViewModel MyControlViewModel { get; set;}
}
MyMainWindowView.xaml
<Window ...
xmlns:my="clr-namespace:NamespaceContainingYourUserControlView>
<my:MyUserControlView DataContext = "{Binding Path= MyControlViewModel}"/>
</Window>
MyUserControlView.xaml
<UserControl ...>
<DataGrid ItemsSource = "{Binding Path= MyListToBind}" .../>
...
</DataGrid>
</UserControl>
This doesn't support ViewModel updating View. To do that You have to use either DependencyProperties as you did instead of normal variables (as i did) or use INotifyPropertyChanged(google it, you'll get tons of examples) and OnPropertyChanged event.
You might read up on DataTemplates they are really useful in data binding.
You can find this usefeul:
http://www.youtube.com/watch?v=BClf7GZR0DQ
I sure as hell did !
Good luck.

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.

Binding a ContentControl to a deep path in WPF

The application I'm currently writing is using MVVM with the ViewModel-first pattern. I have XAML similar to the following:
<ContentControl Content="{Binding FooViewModel.BarViewModel.View, Mode=OneWay}"/>
Every VM is a DependencyObject. Every property is a DependencyProperty. Depending upon the state of the application, the value of the BarViewModel property of the FooViewModel can change, thus changing the value of the View property. Unfortunately when this happens, the new view is not displayed, and the old one remains.
This is extremely frustrating. I thought that if any part of a path expression changed, the binding would update, but that doesn't appear to be the case. When I've used shallower path expressions, such as FooViewModel.View and I've changed the value of the FooViewModel property, that has updated the ContentControl to which it's bound, but not in this case.
If your solution is that I abandon ViewModel-first, that is not an option, though I appreciate your advice. I must get this working as is.
CLARIFICATION
This is a question about data binding, and not about MVVM or how to implement it. You can safely ignore the MVVM aspects of this if it helps you to think about the problem, or if you have a different idea about how MVVM should be implemented. This is a large, existing project in which the MVVM design pattern cannot be changed. (It is far too late for that.)
So, with that said, the correct question to be answering is the following:
Given a binding path expression in which every element is a DependencyProperty and the final property is a view bound to a ContentControl, why does a change in a property in the middle of the path not cause the binding to update?
Although I would expect this to work, there are several problems with your approach.
Firstly, your view models should not use DependencyObject or DependencyProperty, this ties them in to WPF. They should instead implement INotifyPropertyChanged. This makes your view models reusable in other presentation technologies such as Silverlight.
Secondly, your view models shouldn't have references to your views, so you shouldn't require a View property on your view models.
I would seriously consider using an MVVM framework for view composition - Caliburn.Micro, for example, makes view model first development extremely straightforward, and already provides a view model base class which implements INotifyPropertyChanged, and a mechanism for building view compositions with conventions.
I.e. you can have a conductor view model which has an ActiveItem property, and you simply place a ContentControl on your view with the same name as the property:
<ContentControl x:Name="ActiveItem" />
You can use the ActivateItem() method to change the current active item.
Caliburn.Micro also has a host of other features, such as being able to place a Button control with x:Name="Save" on your view, and your Save method on your view model will automatically be invoked when the button is clicked.
Every VM is a DependencyObject. Every property is a
DependencyProperty.
why? a viewmodel should be a simple class with INotifyPropertyChanged and the Properties should be simple properties.
and if you want your different viewmodel be rendered in a different way - you should use DataTemplate.
<Window>
<Window.Resources>
<DataTemplate DataType="{x:Type local:MyViewModelA}>
<MyViewA/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:MyViewModelB}>
<MyViewB/>
</DataTemplate>
</Windows.Resources>
<Grid>
<ContentControl Content="{Binding MyActualVM}"/>
</Grid>
</Window>
EDIT: btw you always bind to the last Property: FooViewModel.BarViewModel.View --> so the INotifyPropertyChanged (if raised) just work for the .View
EDIT2: another approach could be to get the BindingExpression of your content control and call.
System.Windows.Data.BindingExpression expr = //get it from your contentcontrol
expr.UpdateTarget();
EDIT3: and a simple mvvm way - just use INotifyPropertyChanged
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.MyFooVM = new FooVM();
this.MyFooVM.MyBarVM = new BarVM(){View = "erster"};
this.DataContext = this;
}
public FooVM MyFooVM { get; set; }
private void Button_Click(object sender, RoutedEventArgs e)
{
this.MyFooVM.MyBarVM = new BarVM(){View = "zweiter"};
}
}
public class INPC : INotifyPropertyChanged
{
#region Implementation of INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropChanged(string property)
{
var handler = PropertyChanged;
if(handler != null)
handler(this, new PropertyChangedEventArgs(property));
}
#endregion
}
public class FooVM:INPC
{
private BarVM _myBarVm;
public BarVM MyBarVM
{
get { return _myBarVm; }
set { _myBarVm = value;OnPropChanged("MyBarVM"); }
}
}
public class BarVM : INPC
{
private string _view;
public string View
{
get { return _view; }
set { _view = value;OnPropChanged("View"); }
}
}

WPF TreeView bound to ObservableCollection not updating root nodes

Sorry - my question is almost identical to this one but since it didn't receive a viable answer, I am hoping that someone else has some fresh ideas.
I have a WPF TreeView that is bound to a hierarchy of a single type:
public class Entity
{
public string Title { get; set; }
public ObservableCollection<Entity> Children { get; set; }
}
The Entity class implements INotifyPropertyChanged, but I have omitted this code for clarity.
The TreeView is bound to an ObservableCollection<Entity> and each Entity instance exposes a set of contained Entity instances via its Children property:
<TreeView ItemsSource="{Binding Path=Entities}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:Entity}" ItemsSource="{Binding Path=Children}">
<TextBlock Text="{Binding Path=Title}" />
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
Initially the TreeView binds as expected and correctly displays a multi-level hierarchy. Also, when the membership of one of the Children collections is programmatically modified, the changes are correctly reflected in the TreeView.
However, changes to the membership of the root member level ObservableCollection<Entity> are not reflected in the TreeView.
Any suggestions would be appreciated.
Thanks,
Tim
My initial guess is that you have something like the following for the root node:
public ObservableCollection<Entity> Entities
{
get;
set;
}
Then, instead of doing something [good] like the following:
Entities.Clear();
foreach (var item in someSetOfItems)
Entities.Add(item);
You are doing something [bad] like this:
Entities = new ObservableCollection<Entity>(someSetOfItems);
You should be able to track down the issue by making the backing field of the Entities property readonly:
private readonly ObservableCollection<Entity> _entities
= new ObservableCollection<Entity>();
public ObservableCollection<Entity> Entities
{
get
{
return _entities;
}
}
Further explanation, long time for answer to come, but I believe that if you do the binding in XAML, and then in code assign a new object to the property you break the binding, so you would have to redo the binding in code for it to work. Hence the solution with the readonly backing field. If doing like that you will not be able to assign a new ObservableCollection and you won't break the binding by assigning a new object to the backing field.

How can I add an "IsDirty" property to a LINQ to SQL entity?

I am binding my entities to an edit form in WPF. Within a DataTemplate, I want to be able to set the background color of the root container within a DataTemplate to show it has been changed and these changes have not yet been submitted to the database.
Here's a very simple sample that demonstrates what I'm talking about (forgive errors):
<Page ...>
<Page.DataContext>
<vm:MyPageViewModel /> <!-- Holds reference to the DataContext -->
</Page.DataContext>
<ItemsControl
ItemsSource = {Binding Items}>
<ItemsControl.Resources>
<DataTemplate
DataType="Lol.Models.Item"> <!-- Item is L2S entity -->
<!-- In real life, I use styles to set the background color -->
<TextBlock Text="{Binding IsDirty, StringFormat='Am I dirty? /{0/}'}"/>
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
</Page>
The example just prints out "Am I dirty? yes" or "Am I dirty? no", but you get the idea.
To do this, I'll need to add a public property to my Item (partial class, simple) that can determine if the entity is dirty or not. This is the tough bit.
public partial class Item
{
public bool IsDirty
{
get
{
throw new NotImplementedException("hurf durf");
}
}
}
Outside of the entity, it's pretty simple (as long as you have the DataContext the entity is attached to). Inside, not so much.
What are my options here?
Edit: I don't think there's one good solution here, so suggestions for workarounds are welcome.
(Okay, similar questions exist, but they are all about how to determine this from outside of the entity itself and use the DataContext the entity is attached to.)
If you are using the dbml generated classes, you should be able to implement a couple of partial methods like this:
public partial class SampleEntity
{
partial void OnCreated()
{
this.IsDirty = true;
}
partial void OnLoaded()
{
this.PropertyChanged += (s, e) => this.IsDirty = true;
this.IsDirty = false;
}
public bool IsDirty { get; private set; }
}

Resources