Accessing Xamarin.Forms inside ViewModel - c#

I am very new to Cross platform development in Xamarin.forms. My code should be highly unit testable and I have created a ViewModel class to communicate between my View and the Model. I want to know, If we are going to access Xamarin.forms (using Xamarin.Forms;), inside the viewmodel, is it a bad practice or violating MVVM concept. If so How we can use Command inside ViewModel to bind with the view.
Thanks

Accessing the view from the view model is indeed "against" the MVVM principle. So, you're right on that! Binding a Command though, isn't that hard and works just as any other binding that you might've used.
In your view model, just define a property for the Command:
public class YourViewModel
{
public Command YourCommand { get; set; }
}
Then in the constructor define the logic that should be executed:
public YourViewModel()
{
YourCommand = new Command(() =>
{
Console.WriteLine("TADA!");
});
}
Of course, you could define it is it's own separate method or declare it another way. For brevity, I have done it inline for now.
To bind to it from XAML, simply do:
<Button Text="Make it so!" Command="{Binding YourCommand}" />
and make sure that you have set the BindingContext property on your page to the view model. If you are using an MVVM framework you might have already done so.
At some point, you will probably want to bind to a cell in a ListView or similar. In that case, keep in mind that the scope changes. You are then binding to the view model behind that cell and not the page. So, you will have to make a reference to the page to reach that BindingContext. But cross that bridge when you get there, just keep in mind that there is something special about it.

Use below code, so you don't need to import Xamarin.Forms in your ViewModel:
ButtonBehavior
public class ButtonBehavior : Behavior<Button>
{
protected override void OnAttachedTo(Button bindable)
{
base.OnAttachedTo(bindable);
bindable.Clicked += Bindable_Clicked;
}
private void Bindable_Clicked(object sender, EventArgs e)
{
//Use you logic here
}
protected override void OnDetachingFrom(Button bindable)
{
base.OnDetachingFrom(bindable);
bindable.Clicked -= Bindable_Clicked;
}
}
View
<Button Text="Click Me" HeightRequest="50" WidthRequest="80">
<Button.Behaviors>
<behavior:ButtonBehavior/>
</Button.Behaviors>
</Button>

Related

MVVM: Is code-behind evil or just pragmatic?

Imagine you want a Save & Close and a Cancel & Close button on your fancy WPF MVVM window?
How would you go about it? MVVM dictates that you bind the button to an ICommand and inversion of control dictates that your View may know your ViewModel but not the other way around.
Poking around the net I found a solution that has a ViewModel closing event to which the View subscribes to like this:
private void OnLoaded(Object sender
, RoutedEventArgs e)
{
IFilterViewModel viewModel = (IFilterViewModel)DataContext;
viewModel.Closing += OnViewModelClosing;
}
private void OnViewModelClosing(Object sender
, EventArgs<Result> e)
{
IFilterViewModel viewModel = (IFilterViewModel)DataContext;
viewModel.Closing -= OnViewModelClosing;
DialogResult = (e.Value == Result.OK) ? true : false;
Close();
}
But that is code-behind mixed in with my so far very well designed MVVM.
Another problem would be showing a licensing problem message box upon showing the main window. Again I could use the Window.Loaded event like I did above, but that's also breaking MVVM, is it not?
Is there a clean way or should one be pragmatical instead of pedantic in these cases?
First, create an interface that contains only the Close method:
interface IClosable
{
void Close();
}
Next, make your window implement IClosable:
class MyWindow : Window, IClosable
{
public MyWindow()
{
InitializeComponent();
}
}
Then let the view pass itself as IClosable as command parameter to the view model:
<Button Command="{Binding CloseCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
And lastly, the command calls Close:
CloseCommand = new DelegateCommand<IClosable>( view => view.Close() );
And what have we now?
we have a button that closes the window
we have no code in code-behind except , IClosable
the view model knows nothing about the view, it just gets an arbitrary object that can be closed
the command can easily be unit tested
There is nothing wrong or right with using code behind, this is mainly opinion based and depends on your preference.
This example shows how to close a window using an MVVM design pattern without code behind.
<Button Name="btnLogin" IsDefault="True" Content="Login" Command="{Binding ShowLoginCommand}" CommandParameter="{Binding ElementName=LoginWindow}"/>
<!-- the CommandParameter should bind to your window, either by name or relative or what way you choose, this will allow you to hold the window object and call window.Close() -->
basically you pass the window as a parameter to the command. IMO your viewmodel shouldn't be aware of the control, so this version is not that good. I would pass a Func<object>/ some interface to the viewmodel for closing the window using dependency injection.
Take a look at some toolkits e.g. MVVMLight has EventToCommand, which allows you to bind command to events. I generally try my best to limit logic in View, as it's harder to test it.
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:command="http://www.galasoft.ch/mvvmlight"
...
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<command:EventToCommand Command="{Binding YourCommandInVM}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
Sometimes I use a work-around.
Assumes u have a view "MainWindow" and a viewmodel "MainWindowVM".
public class MainWindowVM
{
private MainWindow mainWindow;
public delegate void EventWithoudArg();
public event EventWithoudArg Closed;
public MainWindowVM()
{
mainWindow = new MainWindow();
mainWindow.Closed += MainWindow_Closed;
mainWindow.DataContext = this;
mainWindow.Loaded += MainWindow_Loaded;
mainWindow.Closing += MainWindow_Closing;
mainWindow.Show();
}
private void MainWindow_Loaded()
{
//your code
}
private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
//your code
}
private void MainWindow_Closed()
{
Closed?.Invoke();
}
}
Here I store my view in a private variable so you can access it if you need it. It breaks a bit the MVVM.
In my viewmodel, I create a new view and show it.
Here I also capture the closing event of the view an pass it to an own event.
You can also add a method to the .Loaded and .Closing events of your view.
In App.xaml.cs you just have to create a new viewmodel object.
public partial class App : Application
{
public App()
{
MainWindowVM mainWindowVM = new MainWindowVM();
mainWindowVM.Closed += Mwvm_Close;
}
private void Mwvm_Close()
{
this.Shutdown();
}
}
I create a new viewmodel object and capture it own close-event and bind it to the shutdown method of the App.
Your description indicates that the view model is some kind of a document view. If that's correct then I would leave Save, Close, etc. to be handled by the document container e.g. the application or the main window, because these commands are on a level above the document in the same way as copy/paste are on the application level. In fact ApplicationCommands has predefined commands for both Save and Close which indicates a certain approach from the authors of the framework.

UserControl bind to a Model property

I have a Silverlight/WPF application I'm enhancing. I have a UserControl and it needs to disable or enable some controls when a property on the model changes. It has to do some other logic, so I can't just bind them to the property.
In the control's code behind, I have a reference to the model. I know there is a way to bind to certain properties, and I know how to do it in XAML, but not in the code-behind.
I've seen a lot of instances say to use the INotifyPropertyChanged interface, but it doesn't seem to apply in this case.
An example of what I'm trying to do:
public partial class MyControl : UserControl
{
private readonly MyModel _model;
public MyControl(MyModel model)
{
_model = model;
// bind to model's ImportantThing property here
}
...
// Some method gets called when property changes
...
}
public class MyModel
{
...
public bool ImportantThing
{
get { return _importantThing; }
set
{
_importantThing = value;
// This is existing code and notifies some controls, but not the ones
// I'm interested in. It should notify MyControl as well. I know in
// most applications, this is OnPropertyChanged();
RaisePropertyChanged("ImportantThing");
}
}
}
Any pointers?
Some Pointers....
Your issue\solution sounds like a task for a ValueConverter. But first, I can see code in the UserControl code-behind file, you really should adopt and apply the MVVM pattern... OK there is a [steep] learning curve and sometimes you wonder if it's worth the effort (know I did when I started with XAML)... But take my word for it.... MVVM, there simply in no other way to develop using WPF. If you try to apply the WinForms UI Logic to WPF it will become an unmaintainable, unmanageable monolithic pile of spaghetti code....
you might find this link to Rachel Lim's Blog useful....
https://rachel53461.wordpress.com/category/mvvm/
and for ValueConverter take a look at this.....
http://www.wpftutorial.net/ValueConverters.html
I apologize, my original question wasn't all that clear, but I've found a solution. It turns out the UserControl (MyControl in my original example) was already watching the Model for changes:
_myModel.PropertyChanged += Model_PropertyChanged;
In the existing callback (Model_PropertyChanged()), I just looked for the property I was interested in and added everything else I needed:
void Model_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "StatusEnabled")
{
// do stuff
}
else if (e.PropertyName == "ImportantThing")
{
// my stuff
}
}
Thanks for everyone's input!

Calling a method in one viewmodel from a different viewmodel

I'm developing with MVVMLight on a Windows Phone 8.1 app. We have a setting for sorting a list of users by first or last name. After changing this setting (performed by a ListPicker binded to a property in SettingsViewModel), I want to call a method in a different view model (OtherViewModel) to re-sort a list of users on OtherViewModel's corresponding view. A settingsStore is being used to store the sort setting on the user's phone.
I'd prefer not to create view model dependencies by way of var vm = new ViewModel(), since there is not a parent/child relationship between the two view models. I've been told a delegate would be a good choice, but I'm not very familiar with how I'd implement a solution using a delegate.
Any help would be appreciated.
Settings View
<toolkit:ListPicker x:Name="ContactsSortParametersListPicker"
ItemsSource="{Binding ContactsSortParameters, Mode=OneTime}"
SelectedItem="{Binding ContactsSortParametersSelected, Mode=TwoWay}"
SelectionChanged="ContactsSortParametersListPicker_SelectionChanged"/>
Settings View (code-behind)
private void ContactsSortParametersListPicker_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// want to call method from MainViewModel that updates a list in Main View
}
SettingsViewModel
public IEnumerable<SortOptions> ContactsSortParameters
{
get { return (IEnumerable<SortOptions>)Enum.GetValues(typeof(SortOptions)); }
}
private SortOptions _sortContactsParameterSelected;
public SortOptions ContactsSortParametersSelected
{
get { return _sortContactsParameterSelected; }
set
{
SetProperty(ref _sortContactsParameterSelected, value);
_settingsStore.ContactsSortParameter = _sortContactsParameterSelected;
}
}
OtherViewModel
public async Task LoadDirectory()
{
...relevant logic here...
}
If you use MVVM Light, I assume that you have ViewModelLocator instance in your App.xaml resources defined like below.
<vm:ViewModelLocator xmlns:vm="clr-namespace:WPApp.ViewModel" x:Key="Locator" />
In your settings view code behind:
private async void ContactsSortParametersListPicker_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
await ((ViewModelLocator)App.Current.Resources["Locator"]).OtherViewModel.LoadDirectory();
}

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.

Update UI when Entity Model changes in ViewModel

I am trying to come up with a good way of implementing the MVVM pattern using Entity-Framework where my entities are my models. My DataContext is my viewmodel. This is a small reproduction of the problem.
View
<TextBox Text="{Binding MyText}" />
ViewModel:
I have the requirement of needing to navigate record by record from my DB. When a button is clicked in the View a command is sent to the Viewmodel that executes nextRecord(). EF does its magic and _myObject is the next row/record from the database
public class myViewModel: INotifyPropertyChanged
{
private MyEntityObject _myObject;
public string MyText
{
get { return _myObject.MyText; }
set
{
if (_myObject.MyText != value)
{
_myObject.MyText = value;
OnPropertyChanged("MyText");
}
}
}
private void _nextRecord()
{
_myObject = myEntitiesContext.NextRecord() //pseudocode
}
}
Autogenerated Entity Model
public partial class MyEntityObject
{
public string MyText { get; set; }
}
Since the View has no knowledge of _myObject changing, it doesn't update when _myObject changes. A few approaches I have thought of.
I haven't tested wrapping my entities in a INotifyPropertyChanged wrapper class but am wary to do this as I have a lot of entity objects.
I could call OnPropertyChanged("...") for all properties, but some of my entities have a lot of properties to them, which would be ugly. Possible to use reflection to make it cleaner, but I may have properties that aren't databound.
I might be able to defer this to the UI, somehow refreshing the bindings when I click "Next Record", but this breaks MVVM and looks dirty
How can I get the UI to recognize changes from _myObject?
As I've mentioned in the comments, calling OnPropertyChanged("") or OnPropertyChanged(null) invalidates all properties and is equivalent to calling OnPropertyChanged for each and every property. This behavior is also documented here:
The PropertyChanged event can indicate all properties on the object
have changed by using either null or String.Empty as the property name
in the PropertyChangedEventArgs.
This means that you can simply add a call to OnPropertyChanged("") when you update your object to force WPF to reevaluate all bindings to your view model:
private void _nextRecord()
{
_myObject = myEntitiesContext.NextRecord();
OnPropertyChanged("");
}
That being said, I'd still go with #Anand's solution (+1). There's an ongoing debate on whether it's OK or not for the viewmodel to expose the model as a property, and I tend to go with exposing it until you need to introduce some view model specific logic. Most of the time you won't have to and it's not worth the trouble of wrapping model properties.
The problem with your code is that when _myObject changes the MyText property changed event is not fired. A work around would be to create a new property to hold you entity
and make this property as your Grids DataContext in your view as shown below. Now when this line is executed MyObject = myEntitiesObject.NextRecord() your view will be notified about the change.
public class myViewModel : INotifyPropertyChanged
{
private MyEntityObject _myObject;
public MyEntityObject MyObject
{
get { return _myObject; }
set {
if (_myObject != value)
{
_myObject = value;
OnPropertyChanged("MyObject");
}
}
}
private void _nextRecord()
{
MyObject = myEntitiesObject.NextRecord() //pseudocode
}
}
View:
<Grid DataContext="{Binding MyObject}">
<TextBlock Text="{Binding MyText}"/>
</Grid>
An extremely simple but not very elegant solution that I believe would meet needs: upon switching records, set the DataContext to null, then back to the ViewModel.
However, there are arguably more elegant alternatives that require more work to meet all requirements. See Anand's answer for an improvement upon this.
The tag in View should have the mode and UpdateSourceTrigger attribute set with values.

Categories

Resources