Mvvm model ViewModel - c#

It can be named MVVM model or not? Because View interacts with DataModel through
ViewModelData. Does View should interact only with ViewModelData? I did read somewhere that right MVVM model should implement INotify in ViewModel not in Model. Is it right?
namespace WpfApplication135
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModelData();
}
}
public class ViewModelData
{
public DataModel DM { get; set; }
public ViewModelData()
{
DM = new DataModel();
}
}
public class DataModel : INotifyPropertyChanged
{
public int label;
public int Label
{
get
{
return label;
}
set
{
label = value;
RaisePropertyChanged("Label");
}
}
public DataModel()
{
Action Update = new Action(Run);
IAsyncResult result = Update.BeginInvoke(null, null);
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string info)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
public void Run()
{
int i=0;
while(true)
{
System.Threading.Thread.Sleep(2000);
Label = ++i;
}
}
}
}
xaml
<Grid>
<Label Content="{Binding DM.Label}" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top"/>
</Grid>

The initial thought for MVVM was indeed that the View should not know (not depend on) the Model.
In practice this meant re-implementing all those Model properties in the ViewModel (see the light-yellow box in the picture below), a lot of work. And extra painful when your Model can easily implement INPC, for example when it is generated from a database schema. Entity Framework in database-first mode lets you inject the INPC code through the T4 templates.
The consensus quickly became that it is OK to forward a ViewModel.Model property and bind to that, just like your DM property. See the light-blue box in the picture.
The issue is visualized well in this picture, note the large number of arrows in the top right corner. They depict the various solutions for databinding and you can use any combination of them.

One point of MVVM is that your view should not know your data model and vice versa. That's why there is a ViewModel. Right now, your ViewModel is wasted code. It does nothing.
You may want to read up on MVVM a bit more, because explaining it is way too broad for the Q&A format here.

Related

How to pass a ref to a viewmodel correctly

I've created a Textbox with placeholder text and a clear button. I've implemented it using a view model for the data context, and using a style with target type TextBox. In xaml, using it is pretty simple.
<TextBox DataContext="{Binding NameBox}" Style="{StaticResource placeholder}"/>
The way I've implemented the view model, though, smells funny to me:
public class PlaceholderTextBoxViewModel : NotifiableViewModelBase {
private string text;
public string Text {
get => text;
set {
text = value;
OnTextChange(text);
}
}
public string PlaceholderText { get; set; }
public RelayCommand ClearCommand => new RelayCommand(() => Text = "");
private event Action<string> OnTextChange;
public PlaceholderTextBoxViewModel(ref string text, string placeholderText, Action<string> changeHandler = null) {
OnTextChange = changeHandler ?? (_ => { });
Text = text;
PlaceholderText = placeholderText;
}
}
In case it doesn't smell too bad to you yet, check out how it's used
private string _name;
public string Name {
get => _name;
set {
_name = value;
System.Console.WriteLine(_name); // needed to silence auto prop error
}
}
public PlaceholderTextBoxViewModel NameBox { get; }
// in the constructor...
NameBox = new PlaceholderTextBoxViewModel(ref _name, "Exam Name", t => Name = t);
It definitely doesn't seem right that I need to pass an explicit setter (the changeHandler) to the PlaceholderTextBoxViewModel. It seems, indeed, that the ref I'm passing is never really used (and is only necessary at all -- though not as a ref -- if there is to be pre-existing text in the box).
I've never used refs before and I must be doing something wrong. I have also tried pointing everything that uses the Name property (in the final code excerpt) to the _name field but that doesn't work, the field isn't properly updated, or at least isn't "communicating" its updates (in various uses, CanExecutes are not updated, SearchPredicates are not refreshed, etc). I'm using MVVMLight, and I imagine that changing a field's value doesn't trigger OnPropertyChanged -- if the field's value is even changing at all.
How do I get the ref to work correctly? Am I doing this completely wrong?
I understand that there are other ways to implement this TextBox with its clear command, even in pure MVVM (namely, if I put the ClearCommand in the consuming VM instead of the textbox's VM itself, then the textbox doesn't need to have a VM at all). But I'd really like to know how to make sense of my attempted solution, if only for a better understanding of C# and of refs.
Problem here seems to be an architectural one. MVVM is a tiered architecture that looks like this:
Model -> View Model -> View
Model is the lowest tier, view is the highest. More importantly, each tier does not have any direct visibility into any of the tiers above it.
The problem in your example is that you've broken separation of concerns. You have a parent class creating the PlaceholderTextBoxViewModel, which implies it's in the view model. However, that same class contains a "Name" property, which is actually data that should be in your model layer. Your existing architecture is such that your view model cannot see the data layer, so you've effectively had to set up your own property change notification mechanism, using the ref and delegate, to keep your model and view model synchronized.
Throw it all out, and start again. Your model should contain POCOs, so start with something like this:
public class MyModel
{
public string Name {get; set;}
}
Then, when you create your view model, pass an instance of this class into its constructor so that it has visibility:
public class MyViewModel : NotifiableViewModelBase
{
private MyModel Model;
public MyViewModel(MyModel model) => this.Model = model;
public string Text
{
get => this.Model.Name;
set
{
this.Model.Name = value;
RaisePropertyChanged(() => this.Text);
}
}
// ... etc ....
}
I've stuck to your nomenclature of using "Name" in the model and "Text" in the view model, in practice they're usually the same, but that's up to you. Either way, you still have property change notification in your view model, and it's the view model layer updating the model layer.
Obviously there are lots of variations on this. If you don't want changes to propagate through to your model layer immediately (and there are plenty of cases where you may not want that) then give Text a backing field (_Text) and only do the synchronization at the points your want it to occur. And of course, if you want to go one step further then your model classes could instead implement interfaces, and you can use dependency injection to inject those interfaces into the view model classes instead of giving them access to the actual implementations themselves.
Above all else, keep in mind that the sole purpose of the view model is to prepare the model layer data for consumption by the view. Anything else...data, domain, business logic etc...all of that belongs in your model layer, which shouldn't have any visibility into the view model layer at all.
The ref would only make sense, if you are going to change the value.
In this case you can use the OnTextChange event.
public PlaceholderTextBoxViewModel(ref string text, string placeholderText, Action<string> changeHandler = null)
{
OnTextChange = newValue =>
{
text = newValue; // <- value back to the ref
changeHandler?.Invoke(newValue);
}
Text = text;
PlaceholderText = placeholderText;
}
BTW your solution is somehow much too complicated. Keep the ViewModel as simple and abstract as possible. In this case a simple Name property is enough.
Leave it up the the UI developers which control they use and how they will implement a clear the control logic.
Here an example
ViewModel
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
namespace WpfApp1
{
public class MainViewModel : ReactiveObject
{
[Reactive] public string Name { get; set; }
}
}
View
<Window
x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApp1"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="800"
Height="450"
mc:Ignorable="d">
<Window.DataContext>
<local:MainViewModel />
</Window.DataContext>
<Grid>
<StackPanel
Width="200"
VerticalAlignment="Center">
<Label Content="{Binding Name}" />
<DockPanel>
<Button
DockPanel.Dock="Right"
Content="x"
Width="20"
Click="NameTextBoxClearButton_Click"/>
<TextBox x:Name="NameTextBox"
Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
TextWrapping="Wrap" />
</DockPanel>
</StackPanel>
</Grid>
</Window>
View CodeBehind
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void NameTextBoxClearButton_Click( object sender, RoutedEventArgs e )
{
NameTextBox.Text = string.Empty;
}
}
The simplest solution would be to correct your binding. You don't have to manually forward the data by trying to implement your own change notification system.
Just make sure that your data source always implements INotifyPropertyChanged and properly raises the INotifyPropertyChanged.PropertyChanged event from each property set method. Then bind directly to this properties:
class PlaceholderTextBoxViewModel : INotifyPropertyChanged
{
private string text;
public string Text
{
get => this.text;
set
{
this.text = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
The binding syntax allows to reference nested properties.
In your special case, the {Binding} expression must be:
<TextBox DataContext="{Binding NameBox.Text}" />
Now the TextBox.Text value is automatically propagated to the PlaceholderTextBoxViewModel.Text property and the constructor becomes parameterless.

Wpf Binding Confusion

I'm trying to learn on MVVM. I've understand the concept, however, i'm confused about the binding. I'm not sure where to bind my Fill property. Please help. Tqvm in advanced.
View - name: MainScreen.xaml
<Path Fill="{Binding mainScreenClass, Converter={StaticResource colorConverter}}"/>
inCodeBehind
DataContext = new vmMainScreen();
ViewModel - name:vmMainScreen
public ICommand cmdMouseEnterNav { get; private set; }
public mMainScreen mainScreenClass { get; set; }
public vmMainScreen()
{
mainScreenClass = new mMainScreen();
mainScreenClass.propNaviconFill = new SolidColorBrush(Colors.White);
naviconMouseEventChecker();
}
private void naviconMouseEventChecker()
{
cmdMouseEnterNav = new SimpleCommand
{
ExecuteDelegate = x => mainScreenClass.propNaviconFill = (SolidColorBrush)(new BrushConverter().ConvertFrom("#c5a02b"))
};
}
Model - name:mMainScreen
public class mMainScreen : INotifyPropertyChanged
{
private Brush _NaviconFill = new SolidColorBrush(Colors.White);
public Brush propNaviconFill
{
get
{
return this._NaviconFill;
}
set
{
this._NaviconFill = value;
NotifyPropertyChanged("propNaviconFill");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
I understand that when i breakpoint on my colorConverter, I'm getting the class. Not the property of propNaviconFill. If i create another property with the Brush class on my ViewModel and bind it to Fill, there is no problem. But that means I'm not following the correct structure of MVVM. Thanks again.
You should bind to the property of your view model.
<Path Fill="{Binding propNaviconFill, Converter={StaticResource colorConverter}}"/>
Use the view model implementing the INotifyPropertyChanged as the data context of your view.
DataContext = new mMainScreen();
If you really want to use vmMainScreen as your data context, then vmMainScreen should implement INotifyPropertyChanged there and you should study how NotifyPropertyChanged was used to notify the view that the view model property has changed.
Keep in mind there are two basic types of MVVM:
1. View First
2. View Model First
Based on your example you are trying to do View First. This is easier to implement but comes with drawbacks on larger projects since the view controls the creation of the ViewModel it's harder to inject data or state into the ViewModel.
For all MVVM patterns you have the three parts:
Model - Basically a state bag. This thing is just like a customer class which most of the time implements INotifyProperty changed.
ViewModel - This is like the controller class in MVC. It has all the real logic and does the work.
View - This is your XAML and it only holds presentation logic. The code-behind class ie: MyWindow.xaml.cs should not be used except to setup the ViewModel if your going View First. (there are exceptions of course but generally it should basically be empty)
For View First
Your Window (or control) should create the ViewModel in the constructor and assign it to the DataContext.
Your ViewModel will have ICommand's, ObservableCollections and such that can be bound to controls in the View. So when your constructor fires you fill up your data and put it into the necessary structures; because of databinding this gets related to the View and shown.
Your Model (you usually have more than one, can have Customer, Order, StockTicker or whatever.) These are created by the ViewModel and put into things such as ObservableCollections for the View to databind to.

Sharing data between different ViewModels

I'm trying to develop an easy MVVM project that it has two windows:
The first window is a text editor, where I bind some properties such as FontSize or BackgroundColor:
<TextBlock FontSize="{Binding EditorFontSize}"></TextBlock>
its DataContext is MainWindowViewModel:
public class MainWindowViewModel : BindableBase
{
public int EditorFontSize
{
get { return _editorFontSize; }
set { SetProperty(ref _editorFontSize, value); }
}
.....
The second window is the option window, where I have an slider for changing the font size:
<Slider Maximum="30" Minimum="10" Value="{Binding EditorFontSize }" ></Slider>
its DataContext is OptionViewModel:
public class OptionViewModel: BindableBase
{
public int EditorFontSize
{
get { return _editorFontSize; }
set { SetProperty(ref _editorFontSize, value); }
}
.....
My problem is that I have to get the value of the slider in the option window and then I have to modify the FontSize property of my TextBlock with this value. But I don't know how to send the font size from OptionViewModel to MainViewModel.
I think that I should use:
A shared model
A model in MainWindowViewModel and a ref of this model in OptionViewModel
Other systems like notifications, messages ...
I hope that you can help me. It's my first MVVM project and English isn't my main language :S
Thanks
Another option is to store such "shared" variables in a SessionContext-class of some kind:
public interface ISessionContext: INotifyPropertyChanged
{
int EditorFontSize { get;set; }
}
Then, inject this into your viewmodels (you are using Dependency Injection, right?) and register to the PropertyChanged event:
public class MainWindowViewModel
{
public MainWindowViewModel(ISessionContext sessionContext)
{
sessionContext.PropertyChanged += OnSessionContextPropertyChanged;
}
private void OnSessionContextPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "EditorFontSize")
{
this.EditorFontSize = sessionContext.EditorFontSize;
}
}
}
There are many ways to communicate between view models and a lot of points what the point is the best. You can see how it is done:
using MVVMLight
in Prism
by Caliburn
In my view, the best approach is using EventAggregator pattern of Prism framework. The Prism simplifies MVVM pattern. However, if you have not used Prism, you can use Rachel Lim's tutorial - simplified version of EventAggregator pattern by Rachel Lim.. I highly recommend you Rachel Lim's approach.
If you use Rachel Lim's tutorial, then you should create a common class:
public static class EventSystem
{...Here Publish and Subscribe methods to event...}
And publish an event into your OptionViewModel:
eventAggregator.GetEvent<ChangeStockEvent>().Publish(
new TickerSymbolSelectedMessage{ StockSymbol = “STOCK0” });
then you subscribe in constructor of another your MainViewModel to an event:
eventAggregator.GetEvent<ChangeStockEvent>().Subscribe(ShowNews);
public void ShowNews(TickerSymbolSelectedMessage msg)
{
// Handle Event
}
The Rachel Lim's simplified approach is the best approach that I've ever seen. However, if you want to create a big application, then you should read this article by Magnus Montin and at CSharpcorner with an example.
Update: For versions of Prism later than 5 CompositePresentationEvent is depreciated and completely removed in version 6, so you will need to change it to PubSubEvent everything else can stay the same.
I have done a big MVVM application with WPF. I have a lot of windows and I had the same problem. My solution maybe isn't very elegant, but it works perfectly.
First solution: I have done one unique ViewModel, splitting it in various file using a partial class.
All these files start with:
namespace MyVMNameSpace
{
public partial class MainWindowViewModel : DevExpress.Mvvm.ViewModelBase
{
...
}
}
I'm using DevExpress, but, looking your code you have to try:
namespace MyVMNameSpace
{
public partial class MainWindowViewModel : BindableBase
{
...
}
}
Second solution: Anyway, I have also a couple of different ViewModel to manage some of these windows. In this case, if I have some variables to read from one ViewModel to another, I set these variables as static.
Example:
public static event EventHandler ListCOMChanged;
private static List<string> p_ListCOM;
public static List<string> ListCOM
{
get { return p_ListCOM; }
set
{
p_ListCOM = value;
if (ListCOMChanged != null)
ListCOMChanged(null, EventArgs.Empty);
}
}
Maybe the second solution is simplier and still ok for your need.
I hope this is clear. Ask me more details, if you want.
I'm not a MVVM pro myself, but what I've worked around with problems like this is,
having a main class that has all other view models as properties, and setting this class as data context of all the windows, I don't know if its good or bad but for your case it seems enough.
For a more sophisticated solution see this
For the simpler one,
You can do something like this,
public class MainViewModel : BindableBase
{
FirstViewModel firstViewModel;
public FirstViewModel FirstViewModel
{
get
{
return firstViewModel;
}
set
{
firstViewModel = value;
}
}
public SecondViewModel SecondViewModel
{
get
{
return secondViewModel;
}
set
{
secondViewModel = value;
}
}
SecondViewModel secondViewModel;
public MainViewModel()
{
firstViewModel = new FirstViewModel();
secondViewModel = new SecondViewModel();
}
}
now you have to make another constructor for your OptionWindow passing a view model.
public SecondWindow(BindableBase viewModel)
{
InitializeComponent();
this.DataContext = viewModel;
}
this is to make sure that both windows work on the same instance of a view model.
Now, just wherever you're opening the second window use these two lines
var window = new SecondWindow((ViewModelBase)this.DataContext);
window.Show();
now you're passing the First Window's view model to the Second window, so that they work on the same instance of the MainViewModel.
Everything is done, just you've to address to binding as
<TextBlock FontSize="{Binding FirstViewModel.EditorFontSize}"></TextBlock>
<TextBlock FontSize="{Binding SecondViewModel.EditorFontSize}"></TextBlock>
and no need to say that the data context of First window is MainViewModel
In MVVM, models are the shared data store. I would persist the font size in the OptionsModel, which implements INotifyPropertyChanged. Any viewmodel interested in font size subscribes to PropertyChanged.
class OptionsModel : BindableBase
{
public int FontSize {get; set;} // Assuming that BindableBase makes this setter invokes NotifyPropertyChanged
}
In the ViewModels that need to be updated when FontSize changes:
internal void Initialize(OptionsModel model)
{
this.model = model;
model.PropertyChanged += ModelPropertyChanged;
// Initialize properties with data from the model
}
private void ModelPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(OptionsModel.FontSize))
{
// Update properties with data from the model
}
}
I'm new to WPF and I've come up with a solution to this and I'm curious of more knowledgeable people's thoughts about what's right and wrong with it.
I have an Exams tab and a Templates tab. In my simple proof of concept, I want each tab to "own" an Exam object, and to be able to access the other tab's Exam.
I define the ViewModel for each tab as static because if it's a normal instance property, I don't know how one tab would get the actual instance of the other tab. It feels wrong to me, though it's working.
namespace Gui.Tabs.ExamsTab {
public class GuiExam: INotifyPropertyChanged {
private string _name = "Default exam name";
public string Name {
get => _name;
set {
_name = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName="") {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public partial class ExamsHome : Page {
public ExamsHome() {
InitializeComponent();
DataContext = ViewModel;
}
public static readonly ExamsTabViewModel ViewModel = new ExamsTabViewModel();
}
public class ExamsTabViewModel {
public GuiExam ExamsTabExam { get; set; } = new GuiExam() { Name = "Exam from Exams Tab" };
public GuiExam FromTemplatesTab { get => TemplatesHome.ViewModel.TemplatesTabExam; }
}
}
namespace Gui.Tabs.TemplatesTab {
public partial class TemplatesHome : Page {
public TemplatesHome() {
InitializeComponent();
DataContext = ViewModel;
}
public static readonly TemplatesTabViewModel ViewModel = new TemplatesTabViewModel();
}
public class TemplatesTabViewModel {
public GuiExam TemplatesTabExam { get; set; } = new GuiExam() { Name = "Exam from Templates Tab" };
public GuiExam FromExamTab { get => ExamsHome.ViewModel.ExamsTabExam; }
}
}
And then everything is accessible in the xaml:
TemplatesHome.xaml (excerpt)
<StackPanel Grid.Row="0">
<Label Content="From Exams Tab:"/>
<Label FontWeight="Bold" Content="{Binding FromExamTab.Name}"/>
</StackPanel>
<StackPanel Grid.Row="1">
<Label Content="Local Content:"/>
<TextBox Text="{Binding TemplatesTabExam.Name, UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Center" Width="200" FontSize="16"/>
</StackPanel>
ExamsHome.xaml (excerpt)
<StackPanel Grid.Row="0">
<Label Content="Local Content:"/>
<TextBox Text="{Binding ExamsTabExam.Name, UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Center" Width="200" FontSize="16"/>
</StackPanel>
<StackPanel Grid.Row="1">
<Label Content="From Templates Tab:"/>
<Label FontWeight="Bold" Content="{Binding FromTemplatesTab.Name}"/>
</StackPanel>

How To Create MessageBox or Dialog Box in design MVVM pattern application

To those who are MVVM Purist, my question is, Is there a more simplistic, user readable,and unit testable code solution to the problem "How to create message box or dialog box in MVVM design pattern application" then what I come up with here? Disclaimer, I'm not a MVVM Purist and I will add a few lines of code in the View's code-behind if it means more simplistic, user readable and unit testable code. My solution builds upon what awardcoder.blogspot suggested. The first thing on notice in the solution is there is View's code-behind for handling MessageBox. From this point, I realize the fact that adding code in the View's code-behind is already heading down a not MVVM Purist path. Therefore, my solution take full advantage of this single rule breakage without additional rule breaking.
BaseModel.cs
public class BaseModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, e);
}
}
}
MessageBoxModel.cs
public class MessageBoxModel : BaseModel
{
private string msgboxcontent;
public string MsgBoxContent
{
get
{
return msgboxcontent;
}
set
{
this.msgboxcontent = value;
OnPropertyChanged("MsgBoxContent");
}
}
}
MessageBoxViewModel.cs // Child View-Model
public class MessageBoxViewModel
{
private MessageBoxModel MB;
public MessageBoxViewModel()
{
MB = new MessageBoxModel();
MB.msgboxcontent = "My Message Box Content";
}
public MessageBoxModel MessageBoxModel
{
get
{
return MB;
}
}
MainWindowViewModel.cs // Parent View-Model
public class MainWindowViewModel
{
private MessageBoxViewModel child_MsgBoxviewmodel;
public MainWindowViewModel()
{
child_MsgBoxviewmodel = new MessageBoxViewModel();
}
public MessageBoxViewModel MsgBoxViewModel
{
get
{
return child_MsgBoxviewmodel;
}
}
}
MainWindow.xaml.cs //Parent View
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
private void MessageBoxButton_Clicked(object sender, RoutedEventArgs e)
{
// Instantiate the dialog box
MessageBoxView dlgView = new MessageBoxView();
// Call parent view model to get child view model
MainWindowViewModel mvm = this.DataContext as MainWindowViewModel;
// Configure the dialog box
dlgView.DataContext = mvm.MsgBoxViewModel ;
// Open the dialog/message box
dlgView.ShowDialog();
}
}
MessageBoxView.xaml.cs //Child View
public partial class MessageBoxView : Window
{
public MessageBoxView()
{ //DialogBox
InitializeComponent();
}
}
The xmal files are not shown here because that one of the advantage of using MVVM. View styling is all up to the UI designer.
The message box will appear once someone click on the messageboxbutton.
Unit testing can then be done as usual on the model and viewmodel classes without worrying about popup windows during the test.
JP
Let me say first that I am also not a MVVM Purist but I can already spot one thing that from my point of view I would change in your code.
The button click event you have there should change to a binding in xaml to a ICommand of you MainWindowViewModel.
This will allow you to completely remove that code behind code, in the future you may also apply contracts (interfaces) with this approach to make your solution even more extensible :)
Cheers!

Implement the windows phone app in mvvm model

I am trying to implement MVVM for one of my Windows Phone app that i am developing and its growing to be big.
I have tried below code in Model class.I want to know how can i handle the scenario where user clicks on a button "Latest Entry" and it will connect to a service and executes a method asynchronously.Once the data is returned i have to display the latest record in UI which has 3 text fields EmpName,EmpID,Address.
Code in Model Class:
public class EmpDetailsModel:INotifyPropertyChanged
{
private string _EmpName;
public string EmpName
{
get { return _EmpName; }
set {
if (value != _EmpName)
{
_EmpName = value;
RaisePropertyChanged("EmpName");
}
}
}
private string _EmpId;
public string EmpId
{
get { return _EmpId; }
set {
if (value != _EmpId)
{
_EmpId = value;
RaisePropertyChanged("EmpId");
}
}
}
private string _Address;
public string Address
{
get { return _Address; }
set {
if (value != _EmpId)
{
_EmpId = value;
RaisePropertyChanged("Address");
}
}
}
#region myfirstmodel inotify members
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
The code to connect to service is below:
EmpAzureSer empAzureSer = new EmpAzureSer();
empAzueSer.GetLatestEntry += new GetLatestEntryCompletedEventHandler(LatestEntryCompleted);
private void LatestEntryCompleted(object sender, GetLatestEntryCompletedEventArgs e
{
//get the data from e as e.Name,e.Id and e.Address and bind them to UI.
}
view xaml code:
<Button Name="FetachLAtest" Click="FetachLatest_Click"></Button>
<TextBlock Name="EmployeeName"></TextBlock>
<TextBlock Name="EmployeeID"></TextBlock>
<TextBlock Name="EmployeeAddress"></TextBlock>
I was following the link http://msdn.microsoft.com/en-us/library/windowsphone/develop/gg521153(v=vs.105).aspx.
It was very helpful but I want to know where do i put the code to connect to service (model ? or Viewmodel ? How does the viewmodel should look like ?
There are various ways to implement MVVM into an application, it varies depending on developpers and application requirements.
But for started, let's try to keep things simple and to focus on ViewModels (because this seems to be where is your interest).
MVVM means Model View ViewModel, Model is your business/domain code, View is basically your XAML and its associated code behind, and ViewModel is the link/glue between Views and Models.
An important thing to note is that ViewModels mustn't know Views (meaning don't reference them). This ensures a better separation of concerns, and thus try to build an application easier to test and to maintain.
So to make a long story short, ViewModels don't know Views but they have to communicate with them... And this magic is made possible thanks to Bindings!
XAML/UI components display data, these data comes from the ViewModel which is bound to the View through Bindings mechanisms (provided on WP by the Silverlight framework).
This means the ViewModel contains all the data required by the View, actually a ViewModel represents all the data or behaviors of a View.
Being not the best person to describe the whole MVVM pattern and all its subtilities, i'll leave this sensitive task to most knowledgeable people in the field ;). Here are some really great links that should help you :
From Josh Smith
Wikipedia with code samples for ViewModel
If you already know MVC or MVP patterns, this one will help you to spot differences
All this being told, you must be a little bored with theory, so let's try to write some code. The problem is that there are many ways to organize your code, so all that follow is just a kind of pseudo code, it cannot be used directly into your application!
In your case, you could create just a ViewModel like this one
public class WhateverYouWantViewModel : INotifyPropertyChanged
{
private EmpDetailsModel _model;
public EmpDetailsModel Model
{
get { return _model; }
set
{
if (value != _model)
{
_model = value;
RaisePropertyChanged("Model");
}
}
}
public void GetLastestEntries()
{
// put in here the code calling your service
}
}
About assignements from data service to your this.Model, we are dealing with an asynchronous callback, so maybe it would be wiser to use the Dispatcher in case the callback is not called from the UI Thread :
EmpAzureSer empAzureSer = new EmpAzureSer();
empAzueSer.GetLatestEntry += new GetLatestEntryCompletedEventHandler(LatestEntryCompleted);
private void LatestEntryCompleted(object sender, GetLatestEntryCompletedEventArgs e
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
this.Model = new EmpDetailsModel()
{
//get the data from e as e.Name,e.Id and e.Address and bind them to UI.
};
});
}
Creating a new EmpDetailsModels before assigning it to this.Model will trigger RaisePropertyChanged and notify the View this property has changed. More specifically, the UI component bound to this property will be notified for being updated.
To bind your UI components to the ViewModel, you can do something like that :
<Button Name="FetachLAtest" Click="FetachLatest_Click"></Button>
<TextBlock Name="EmployeeName" Text="{Binding Model.EmpName}"></TextBlock>
<TextBlock Name="EmployeeID" Text="{Binding Model.EmpId}"></TextBlock>
<TextBlock Name="EmployeeAddress" Text="{Binding Model.Address}"></TextBlock>
Do not forget to set the DataContext of your View with your ViewModel instance.
Last but not least, you have to bind your "Latest Entry" Button to the ViewModel.GetLastestEntries method by calling it from your *View.FetachLatest_Click* event handler. All this can be achieved this way :
public partial class YourView : BasePage
{
private WhateverYouWantViewModel _viewModel;
public YourView()
{
InitializeComponent();
_viewModel = new WhateverYouWantViewModel();
this.DataContext = _viewModel;
}
private void FetachLatest_Click(object sender, RoutedEventArgs e)
{
_viewModel.GetLastestEntries();
}
}
And that's (almost) it! Why almost? Because the link between the View and the ViewModel is quite strong and defined into the code behind (which is something we are usually trying to avoid in MVVM).
Fortunately, there are some solutions to solve this issue :
What we call a ViewModelLocator could be used to store and to locate
ViewModels
A Command could be created in WhateverYouWantViewModel and bound to the "Lastest
Entry" Button, instead of calling directly the GetLastestEntries method in code behind
The downside of all this is that you would have to write more code and that's where MVVM framweworks come up! These frameworks will help you to write clean MVVM applications with minimum effort.
As a beginner, i would warmely advice you to visit MVVM Light Toolkit website. It contains lots of useful articles about the MVVM pattern, to learn how to design an MVVM application and to handle common scenarii using this framework. MVVM Light is not the only MVVM framework running on Windows Phone but i'm quoting it because it is widely used, it has a big community and it strives to keep things as simple as possible.
I'm aware this answer is only a starting point to achieve what you want. I only give you some ideas that need further study, but i hope it will help you move in the right direction.

Categories

Resources