How do I use the same ViewModel for two views? - c#

So I have my MainWindow.xaml which has the DataContext of BaseViewModel set like this.
<Window.DataContext>
<viewModel:BaseViewModel/>
</Window.DataContext>
it works fine, for instance when I select an item in my ListView it binds and updates my ImageView in my MainWindow.
<ListView Background="Transparent"
ItemsSource="{Binding ImageGridViewModel.Images}"
SelectedItem="{Binding ImageGridViewModel.SelectedImage}">
And then it updates like so
<Image Source="{Binding ImageGridViewModel.SelectedImage}"
Margin="20">
Perfect no issues.
However, I recently added a second view which is a Window called WatermarkWindow and I set the DataContext just like I did with the MainWindow, in the XAML like so.
<Window.DataContext>
<viewModel:BaseViewModel/>
</Window.DataContext>
And then the binding for the Image control on that new Window
<Image Source="{Binding ImageGridViewModel.SelectedImage}"
Margin="20">
However when I open that window, the Image control's source is not bound to the property, the property actually returns NULL and I think I know why that is, I think it's because in my BaseViewModel I am instantiating a new instance of that ViewModel evertime it gets called.
The reason to why I am doing it that way is because I wanted to instantiate a instance of it so I can actually use it to bind stuff. Rather than it being null.
If that's not the issue then I'm still really eager to learn and understand what the issue is.
What's the proper way of setting up a BaseViewModel that contains all the extra ViewModels?
public class BaseViewModel : ObservableObject
{
public ImageGridViewModel ImageGridViewModel { get; set; } = new ImageGridViewModel();
}
And the ObservableObject
public class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
And not that it matters because I know the properties work fine, here's the ViewModel.
public class ImageGridViewModel
{
public string ImagePath { get; set; }
public string SelectedImage { get; set; }
public ObservableCollection<string> Images { get; set; }
...

What I ended up doing was setting the DataContext property to this where I instantiate the new WawtermarkWindow
if (wmw == null)
{
wmw = new WatermarkWindow();
wmw.DataContext = this;
}

Related

WPF Databindings only updates when changed

So I've been sifting through similar questions on SO for a few days now. I just want to know why this problem occurs. I've got a Class with properties and a SeriesCollection used for Live Charts, which are binded to the UI. Since the properties need to be able to be Serialized, the SeriesCollection can not be part of the specific modelview (but need to binded to UI for plotting a chart). like so:
public class DLVOModel
{
public SeriesCollection table { get; set; }
public DLVOConfiguration DLVOConfiguration { get; set; }
}
public partial class DLVOModelizer : Window
{
public DLVOModel model { get; set; }
public DLVOModelizer()
{
InitializeComponent();
model = CreateModel();
DataContext = model; //Databinding
}
private DLVOModel CreateModel() => new DLVOModel()
{
DLVOConfiguration = new DLVOConfiguration(),
table = new SeriesCollection(),
};
public class DLVOConfiguration
{
public double HMax { get; set; }
public int Resolution { get; set; }
//... about 25 properties
}
`
XAML:
<window>
<lvc:CartesianChart Series="{Binding Path=table}"/>
<GroupBox DataContext="{Binding DLVOConfiguration}">
<Grid>
<TextBox Text="{Binding Path=HMax, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"/>
<TextBox Text="{Binding Path=Resolution, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</GroupBox>
So this all works well untill I try to deserialize an xml file. The model gets updated properly but the UI falls behind. The textboxxes updates to the models value when I try to change their text. This is odd because:
The Databinding works fine, so UI should update immediately.
When entering a new value in the UI the model property should change, not the UI.
(Also tried a version without UpdateSourceTrigger).
Before I binded directly to the DLVOConfiguration and everything worked fine.
I know there is a way in which your modelview inherits from INotifyPropertyChanged, but for some reason I run into the same problem.
EDIT:
I added code for the case in which I used INotifyPropertyChanged from this question:
WPF DataBinding not updating?
public class DLVOConfiguration : INotifyPropertyChanged
{
private double _HMax;
public double HMax
{
get { return _HMax; }
set
{
_HMax = value;
NotifyPropertyChanged("HMax");
}
}
private int _Resolution;
public int Resolution
{
get { return _Resolution; }
set
{
_Resolution = value;
NotifyPropertyChanged("Resolution");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
I guess you're replacing the instance being bound to somewhere. That breaks the data binding. As long as you're merely updating the properties with new values, it should work just fine.

Why does my application enter break mode when I bind to a collection?

I am currently trying to bind the ItemSource of my ItemsControl but for some reason it's throwing an issue saying that the application has entered break mode and I have no idea what the cause is, I really want to understand why it's entering breakmode, I tried debugging but it didn't really get me very far.
The goal was to create a custom UserControl and then being able to add them to a ObservableCollectionwith a button click. So creating a new one when the button has been clicked, unfortunatly I didn't get that far because this started happening.
So my question is, Why is it throwing that issue, is it something where it doesnt like the binding?
<ItemsControl ItemsSource="{Binding UserViewModel.Users}">
<controls:UserCard/>
</ItemsControl>
And I've setup the DataContext like so
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new BaseViewModel();
}
}
And for the BaseViewModel, it looks like this
public class BaseViewModel : ObservableObject
{
public UserViewModel UserViewModel { get; set; } = new UserViewModel();
}
And the UserViewModel looks like this
public class UserViewModel : ObservableObject
{
public ObservableCollection<User> Users { get; set; } = new ObservableCollection<User>();
public UserViewModel()
{
}
}
With an ObservableObject like so
public class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
The UserCard control goes in the ItemTemplate of the ItemsControl:
<ItemsControl ItemsSource="{Binding UserViewModel.Users}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<controls:UserCard/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

Moving behind code to a viewModel class binding

After learning about ObservableCollection and INotifyPropertyChanged, I'm trying use them to divide my code into MVVM.
But I'm having some trouble with binding outside of code-behind class.
My app have three boxes that let you input a person's name, income, age. Then it will display them on a DataGrid.
xaml:
<Window x:Class="myApp.MainWindow"
[...]
<Grid>
<DataGrid x:Name="peopleDisplay">
</DataGrid>
</Grid>
</Window>
in MainWindow.xaml.cs (no structure)
public partial class MainWindow : Window
{
private ObservableCollection<Person> peopleList = new ObservableCollection<Person>();
public MainWindow()
{
InitializeComponent();
peopleDisplay.ItemsSource = peopleList;
}
private void btnAddProduct_Click(object sender, RoutedEventArgs e)
{
peopleList.Add(new Person { personName = nameBox.text, income = incomebox.text, age = ageBox.text });
}
[...]
}
class People : INotifyPropertyChanged
{
private string personName;
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
public string PersonName {
get
{
return this.personName;
}
set
{
if( this.personName != value)
{
this.PersonName = value;
this.NotifyPropertyChanged("PersonName");
}
}
}
public int age { get; set; }
public double income { get; set; }
}
My main questions:
so now Im trying to do two things: add a new function that will calculate the total income of everyone, move the ObservableCollection above to a viewModel class
now in the new viewModel class I have the ObservableCollection personList (instead of inside behind code), but is it wrong to put the calculation method and the properties here too? If I put the calculation properties here this viewModel will be inheriting INotifyPropertyChanged, so when a the totalIncome properties changes it will change the UI automatically. it makes no sense to put it in the person model though, cause that class represent one person.
How do I bind this people List in viewModel to the xaml? If the list is in code-behind I can just do peopleDisplay.ItemsSource = peopleList;, but this viewModel is a class and not a ObservableCollection object, I cant set it to the dataGrid's ItemsSource. Is there a way to bind it in the viewModel class? Im in the progress of learning mvvm so I might be doing something wrong here too. Please advice
Your Model class is People. like below:
public class People : INotifyPropertyChanged
{
private string personName;
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public string PersonName
{
get
{
return this.personName;
}
set
{
if( this.personName != value)
{
this.PersonName = value;
this.NotifyPropertyChanged();
}
}
}
public int Age { get; set; }
public double Income { get; set; }
}
Your ViewModel like below:
public class PeopleViewModel
{
Public List<People> ListOfPeople { get; set; }
}
ViewModel can implement INotifyPropertyChanged interface to Notify the View.
Now you can set the data context as PeopleViewModel and bind your ListOfPeople to your DataGrid.
Set DataContext for your View you can do it from XAML or code behind.
Set ItemsSource for your DataGrid in your View .
XAML:
<Window x:Class="myApp.MainWindow" DataContext="{Binding PeopleViewModel }">
<Grid>
<DataGrid x:Name="peopleDisplay" ItemSource={Binding ListOfPeople}>
......
</DataGrid>
</Grid>
</Window>
Reference 1
Reference 2
1) I dont see any problem with your approach, but, what would happen if someday you want to test the method that calculate the "TotalIncome"? You could separate the calculation in an helper class.
2) First of all, you have to expose the collection in your ViewModel, using public properties. With that being said, you have to declare the binding in your xaml file.
<DataGrid x:Name="peopleDisplay"
ItemsSource="{Binding MyPropertyOnViewModel}">
</DataGrid>
Dont forget to set the DataContext of your window with your viewmodel.

WPF Application With Multiple Child Views - MVVM

My problem is quite simple, I want to have a MainView which in turn will have multiple Views which are dynamic and intractable, like in the diagram below:
But to do this you need multiple ViewModels, and I do not know how to organise them.
My original Idea is to have a MainViewModel, within which I will create properties that will return all my ChildViewModels as shown below, but It seems unprofessional to me and a bad practice.
public class MainViewModel : BaseViewModel
{
private EditPropertiesViewModel _editPropertiesViewModel;
public EditPropertiesViewModel EditPropertiesViewModel
{
get { return _editPropertiesViewModel; }
set
{
_editPropertiesViewModel = value;
base.OnPropertyChanged();
}
}
private UsersDetailsViewModel _usersDetailsViewModel;
public UsersDetailsViewModel UsersDetailViewModel
{
get { return _usersDetailsViewModel; }
set
{
_usersDetailsViewModel = value;
base.OnPropertyChanged();
}
}
//etc. etc..
}
Then from My MainView, I would set the Datacontext to the MainViewModel
Please help me I have no idea what to do, I am totally paused right now.
If you wish to achieve this without PRISM you can make use of ContentControl. For every region you create ContentControl and for every ContentControl you create its ViewModel property. Then you manipulate selected ViewModel associated with ContentControl and ContentControl adjusts view based on type of ViewModel assigned. For clarification take a look
XAML:
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Window.Resources>
<DataTemplate DataType="{x:Type viewModel:SubArticleViewModel}">
<view:SubArticleView/>
</DataTemplate>
</Window.Resources>
<ContentControl Content="{Binding ArticleViewModel}"/>
C#
class BaseViewModel : INotifyPropertyChanged
{
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
class MainViewModel
{
public BaseViewModel ArticleViewModel { get; set; }
}
class SubArticleViewModel : BaseViewModel
{
}
Whenever you assign
ArticleViewModel = new SubArticleViewModel();
DataTemplate defined as resource will be placed as Content of Control.
Above way out creates a lots of work and is more vulnerable for omission. PRISM would be a better choice anyway.
Create AppViewModel class with static ctor like this:
class AppViewModel : BaseViewModel
{
static AppViewModel()
{
_AppModel = new AppViewModel();
}
private static AppViewModel _AppModel;
public static AppViewModel Current
{
get { return _AppModel; }
}
private AppViewModel()
{
//Initialize view models here
MainPageModel = new MainPageViewModel();
}
//VIEW MODELS
public MainPageViewModel MainPageModel { get; private set; }
}
Create BaseViewModel class. All of your VM's should be inherited from it:
class BaseViewModel //implement INotifyPropertyChanged if needed
{
public AppViewModel AppModel
{
get { return AppViewModel.Current; }
}
}
So now you can create UserControl called "MainView":
public partial class MainView : UserControl
{
public MainView()
{
InitializeComponent();
//Prevent view updating in Designer
if (DesignerProperties.GetIsInDesignMode(this))
{
return;
}
var mainVM = AppViewModel.Current.MainPageModel;
DataContext = mainVM;
}
}
In the MainWindow.xaml:
<Window x:Class="MVVM_Test.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:views="clr-namespace:MVVM_Test.Views"
Title="MainWindow" Height="350" Width="525">
<views:MainView />
</Window>

Dependency Property vs INotifyPropertyChanged in ViewModel for Windows 8 application

I have created blank C#/XAML Windows 8 application. Add simple XAML code:
<Page
x:Class="Blank.MainPage"
IsTabStop="false"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<StackPanel
Margin="0,150"
HorizontalAlignment="Center">
<TextBlock
x:Name="xTitle"
Text="{Binding Title, Mode=TwoWay}"/>
<Button Content="Click me!" Click="OnClick" />
</StackPanel>
</Grid>
</Page>
And the simple code in C# part:
public sealed partial class MainPage
{
private readonly ViewModel m_viewModel;
public MainPage()
{
InitializeComponent();
m_viewModel = new ViewModel
{
Title = "Test1"
};
DataContext = m_viewModel;
}
private void OnClick(object sender, RoutedEventArgs e)
{
m_viewModel.Title = "Test2";
}
}
Now I want to implement ViewModel. I have two way:
Use Dependency Property
Implement INotifyPropertyChanged
For first approach it is:
public class ViewModel : DependencyObject
{
public string Title
{
get
{
return (string)GetValue(TitleProperty);
}
set
{
SetValue(TitleProperty, value);
}
}
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string)
, typeof(ViewModel)
, new PropertyMetadata(string.Empty));
}
For second it is:
public class ViewModel : INotifyPropertyChanged
{
private string m_title;
public string Title
{
get
{
return m_title;
}
set
{
m_title = value;
OnPropertyChanged("Title");
}
}
protected void OnPropertyChanged(string name)
{
if (null != PropertyChanged)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
I prefer the first way, because it allows use coerce (Silverlight for web and for WP7 doesn't have coerce functionality.. WinRT too.. but I'm still looking and hope) and looks more natural for me. But unfortunately, it works as OneTime for the first approach.
Could anybody explain to me why MS abandon using Dependency Property for implementing view model?
You should not be using a DependencyProperty in your ViewModel - you should only use them in your controls. You will never want to bind one ViewModel to another, also ViewModels do not need to persist their values nor provide default values, nor provide property metadata.
You should only use INotifyPropertyChanged in your ViewModels.

Categories