I Have following Viewmodels in my WPF application.
CustomerViewModel
OrderViewModel
The application is implemented as a multi tabbed interface using Conductor.Collection.OneActive method.
The customer record is consist of an order record.
I need to open a new order screen from customer screen by clicking a context menu.
Simply I need to open a new order window from customer window.
I have initialized an instance of OrderViewModel inside CustomerViewModel and activated it.
But the new screen is opened but I cannot see any data on it.
How can I transfer data from CustomerViewModel to CustomerOrderViewModel while creating a new window in a better way.
you can use INavigationService for this
public class SecondPageViewModel: ViewModelBase
{
public string Title { get; set; }
public SecondPageViewModel(INavigationService navigationService) : base(navigationService)
{
Title = "Second Page";
}
}
public void GoToSecondPage()
{
navigationService.UriFor<SecondPageViewModel>().WithParam(l=>l.Title, "Navigated from MainViewModel").Navigate();
}
you can find sample code links hear :
Navigation with parameters
page navigation and passing complex state
Related
I'm fairly new to Xamarin and stumbled across MVVM and really like it as an architectural pattern. However, I found that most HowTo's and tutorials out there only address the VVM (i.e. View-ViewModel) side of things, probably for simplicity sake!?
I would like to know how the communication between a ModelView and its associated models takes place using the INotifyPropertyChanged paradigm and other things.
If I understand correctly, I personally would put stuff like data handling, data storage (collections), db connections and stuff like that into a model. At least this is how I would've been doing it in the good old MVC days. Following questions arouse in my mind:
Where do I create the model(s) and how do I assign them to ViewModels?
How do I properly connect Model and ViewModel such that property updates are propagated and can be handled correctly?
Would you set the model as a member of the ViewModel?
In my current example, I would like to implement a SensorModel which provides several sensory data which layers above can subscribe to. I would like to send updates whenever new sensor data is available to the layers above; i.e. a ViewModel, for instance.
I'd basically had something like this in mind:
class Sensor
{
int _id { get; set; }
string _name { get; set; }
}
class SensorModel
{
private List<Sensor> _sensors { get; set; }
public void addSensor(Sensor s) ...
public void removeSensor(Sensor s) ...
}
Does anybody have links to actual/complete MVVM examples, including the connection between Model and ViewModel?
Any help appreciated.
Use Lastest stable Xamarin Forms
MODELS
In the Project, create a Models folder
To store data, i usually use SQLite or a temp store:
class DataStore
{
public static List<SensorModel> SensorStore { get; set; }
}
Create the SensorModel model
class SensorModel
{
internal int Id { get; set; }
internal string Sensor { get; set; }
}
VIEWMODELS
In the Project, create a ViewModels folder
Create a SensorVM viewmodel
class SensorVM : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public System.Windows.Input.ICommand StartCommand { get; set; }
public string SensorName { get; set; }
public SensorVM()
{
DataStore.SensorStore = new List<SensorModel>();
StartCommand = new Xamarin.Forms.Command(StartSubmit);
}
private void StartSubmit(object paramter)
{
var sensor = new SensorModel()
{
Id = 1,
Sensor = SensorName
};
AddSensor(sensor);
}
public void AddSensor(SensorModel sensor)
{
//do something
DataStore.SensorStore.Add(sensor);
}
}
VIEWS
In the Project, create a Views folder
Create a Sensor.xaml view
<ContentPage.Content>
<StackLayout Spacing="10" Orientation="Vertical">
<Entry Text="{Binding SensorName}" />
<Button Command="{Binding StartCommand}" Text="Start" />
</StackLayout>
</ContentPage.Content>
In the code behind:
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Sensor : ContentPage
{
SensorVM vm;
public Sensor()
{
InitializeComponent();
BindingContext = vm = new SensorVM();
}
}
Hope that helps.
I would like to know how the communication between a ModelView and its
associated models takes place using the INotifyPropertyChanged
paradigm and other things.
I think the best way to create a communication in MVVM is Messaging Center.
https://learn.microsoft.com/pt-br/xamarin/xamarin-forms/app-fundamentals/messaging-center
It's not coupled from device (sensor) code to view models ...
Your messages, in this model, active events that could acess your viewmodels as well as other structures.
A sample of this
In your view use :
public void MessegingCenterInit()
{
#region Bluetooth
MessagingCenter.Subscribe<string, string>("App", "Status_name", (sender, arg) =>
{
App.PVM.Name = $"{arg}";//using INotifyPropertyChanged and view model
viewmodelMethod();//using only a viewmodel
});
#endregion
}
in your model use:
public string Name
{
get { return name; }
set
{
name = value;
App.PVM.Add_patient.AddCanExecuteChanged();//PVM is a viewmodel
//The view model need to have INotifyPropertyChanged as a interface
}
}
In specific code you have (into a generic method or event):
string new_name = John;
MessagingCenter.Send<string,string>("App","Status_name",new_name);
There are several ways to do it, its a simple one, you can try use objects as sender with less information.
Regards
Xamarin itself gives a really good example with their default Master-Detail Solution.
Just create a new Xamarin.Forms App and select the Master-Detail Layout.
It includes several Views, ViewModels (with the BaseVIewModel) and some MockUp Data Classes.
For a start just have a look around there :)
In almost all cases there is no communication between the Model and ViewModel, and very rarely there is communication between the Model and View. If you need to communicate between Model and ViewModel it is extremely likely that you are doing something wrong.
To explain, your model usually describes some entity, like that you have the class Cat:
public class Cat
{
public string Color {get; set;}
}
It is generally used in ViewModel either as the field or as a Collection like:
public class CatsViewModel
{
public List<Cat> Cats {get; set;}
}
The cat shouldn't be able to update by itself, if it is updated it is done either by bindings with the view or somewhere from ViewModel.
So you have some architectural problems in your app, I think.
I am using Xamarin to develop ios and android apps on one platefrom, c# is the language used.
I have a design that mimics the one below. A manager class that handles operations done to a collection, a Model class that groups data, and a View that displays the models it gets from the manager. I am displaying the models in a table, the view below is how I am doing so on iOS.
The view subscribes to any changes done to the collection in the manager and triggers a view update whenever one occurs. The model is responsible for removing itself from the collection if it is no longer used after a certain amount of time, thus I pass a reference to the list when the model is created.
This pattern is working on android devices but doesn't seem to work when ran on iOS. I've traced the problem to the removeDevice in the model class but can't seem to figure out why it isn't working. My guess if a pass by reference / value problem but I am not sure. Is there something obvious I am missing or do I need to use a different pattern for iOS?
New Information
After experimenting more this seems relevant. The iOS table is in a tab view and not the initial tab which means ViewController is not initialized until the tab is clicked. I found that if I didn't click the tab it would remove the Models from the list. Something else interesting is if I removed the CollectionChanged subscription it would as well.
public partial class ViewController: UIViewController
{
private List<Model> itemList;
private BlueTooth ble;
private UITableView deviceTable;
public ViewController (IntPtr handle) : base (handle)
{
ble = BlueTooth.Instance;
}
public override void ViewDidLoad ()
{
manager = ble.getManager();
itemList = manager.modelList.ToList();
manager.modelList.CollectionChanged += UpdateView;
deviceTable = new UITableView
{
Source = new DeviceTableSource(itemList)
};
View.AddSubview(deviceTable);
}
void UpdateView(object sender, EventArgs e)
{
itemList = ble.getManager().ModelList.ToList();
deviceTable.Source = new DeviceTableSource(itemList);
deviceTable.ReloadData();
}
}
Manager Class
class Manager{
public ObservableCollection<Model> modelList {get; set;}
public Manager(){
modelList= new ObervableCollection<Model>();
}
public addModel(){
Create a Model object with reference to ModelList
Add Model to ModelList
}
public updateModel(){
update Model in List
}
}
Model Class
class Model{
private ObservableCollection<Model> list;
private Timer removeTimer;
Model(modelList){
removeTimer += onTimedEvent
}
OnTimedEvent(){
removeDevice();
}
removeDevice(){
modelList.remove(this);
}
}
I'm currently trying to create a "log" text box that gets messages between multiple view models (tied to multiple views) that I have. I've tried the approach described by user Blachshma here (Multiple Data Contexts in View) but it does not seem to be working.
I have three classes. Class AViewModel, Class BViewModel and Class ABViewModel.
The view for A binds to AViewModel using the following code in its constructor:
this.InitializeComponent();
this.model = new AViewModel();
this.DataContext = this.model;
The view for B and AB follows the same pattern.
The class structures are as follows:
public class A : INotifyPropertyChanged
{
private string log = string.empty;
public class A()
{
}
public string ALog
{
get
{
return this.log;
}
set
{
this.log = value;
this.NotifyPropertyChanged("ALog");
}
}
private void NotifyPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
/* Function that executes when relay command is clicked */
private void ExecuteCommand()
{
this.ALog += "here";
}
}
Class B is defined the same way with property BLog
Class ABViewModel has properties for each other view model
public class ABViewModel
{
public AViewModel AVM
{
get;
set;
}
public BViewModel BVM
{
get;
set;
}
}
In the xaml I simply have
<TextBox Text="{Binding ABViewModel.AVM}" />
My plan is to eventually using Multibinding to concatenate both logs together, but at the moment I can't even get the one View Model to update my string. It looks like my container view model ABViewModel isn't getting updated, but I don't really understand why, but I'm not entirely sure how to fix this.
Any suggestions are extremely appreciated!
Thanks!
Edit:
I debug my code and see that my string ALog is getting updated, but I don't see the change on the UI. For more information, I click a button that's connected to a RelayCommand in class A. This button invokes a method to connect to a COM port. I'm able to use the COM port from other view models successfully after opening it. The log is supposed to update saying that the com port was opened but I never see any text added to the log in the GUI even though the instance of ALog that I can debug through has the added text.
I can't use Prism or MVVM-light for this particular project.
I am new to Windows Phone development.
I am writing an application for Windows Phone 8 using MVVM Light Toolkit.
I have a MainPage with longlistselector navigating to the details page with the relaycommand and everything is good.
Now in the detail page I have to fill out the UI controls with the binding context received from the MailPage (selecteditem of the longlistselector). My problem is that I have in the detail page and which selecteditem should be bound to the data context received from the mainpage.
Just to give an example in the mainpage I have the lostlingselector bound to a list of task objects of the mainviewmodel; every task have its own category which could be selected from the availabe task categories. How could I approach this? Is it possible to bound the ItemSource of the ListPicker control in the detail page to a different viewmodel and the SelectedItem of the same control to the proprties Category of the default viewmodel (selected task object)?
Thank you.
You can create a new view with it's own viewmodel and pass the data between viewmodels using the MVVM Light Messenger class.
Something like:
public class DetailsViewModel
{
public DetailsViewModel()
{
Messenger.Default.Register<Item>(this, "ItemDetails", i=> ViewItemDetails(i));
}
public void ViewItemDetails(Item i)
{
//Now you can bind it to your UI
}
}
And pass the object from your main viewmodel just like this (ItemDetails it's just a token to identify the listeners)
Messenger.Default.Send<Item>(SelectedItem, "ItemDetails");
You shouldn't really mess up with bindings in between different ViewModel. To setup interaction between viewmodel's you can use Messnger from MvvmLight toolkit, inject it in both objects and define proper pub-sub relations.
public class FirstViewModel : INotifyPropertyChanged
{
private IMesseger messenger;
...
public FirstViewModel(IMessenger messenger)
{
this.messenger = messenger;
}
public Item SelectedItem
{
get
{
return this.selectedItem;
}
set
{
this.selectedItem = value;
this.messenger.Send(new GenericMessage<Item>(this.selectedItem));
this.OnPropertyChanged("SelectedItem");
}
}
}
public class SecondViewModel : INotifyPropertyChanged
{
private IMesseger messenger;
...
public SecondViewModel(IMessenger messenger)
{
this.messenger = messeger;
this.messenger.Register<GenericMessage<Item>>(this, this.HandleItemSelected);
}
...
}
So after sharing same instance of messenger between two VM's you'll have desired functionality with loosely coupled relations which is good from testing perspectives.
I am looking for some advice regarding the design of the below scenario:
High level info:
I have a WPF GUI that contains a grid listing some cars info.
It contains a button, "Add new Car..." that pops-up a form with some base fields so that the user can add a new ICar object in the list.
Both main window and form are designed following up the MVVM pattern (and some decoupling of the commands as well).
Flow and question:
The button "Add new Car..." is bound to a main window command that loads up the form.
The form is bound to a background object, so that when the user presses "Ok", I would want the object to be returned to the original window.
However I don't know how to design that last step, i.e. should I:
Have some public methods in my form to be called so that it is by itself:
Loading the form (ShowDialog..).
Does the properties bindings (already done)
Returns the new Car object to the caller (here the main window)?
Or:
Call the form.ShowDialog() from the main window.
Do something else (which I can't find how to) to get back the new Car object defined by the user?
Thank you!
Something like this:
interface IPresentationService
{
bool ShowInDialog(ViewModel viewModel);
}
class CarViewModel : ViewModel {}
class MainViewModel : ViewModel
{
[Import]
private IPresentationService presentationService;
private void AddNewCar()
{
var car = new CarViewModel();
if (presentationService.ShowInDialog(car))
{
Cars.Add(car);
}
}
public MainViewModel()
{
Cars = new ObservableCollection<CarViewModel>();
AddNewCarCommand = new RelayCommand(AddNewCar);
}
public ObservableCollection<CarViewModel> Cars { get; private set; }
public ICommand AddNewCarCommand { get; private set; }
}
Where IPresentationService is a service, which is intended to create and show a popup window. Instance of IPresentationService could be obtained via service location or dependency injection ([Import] means dependency injection using MEF).
As the MainView has a button New Car, the Main ViewModel might have a property NewCar.
You could create a new instance of a car when the button is clicked and pass the instance to the dialog that allows the user to enter values for the new car.
When the dialog is closed, the main view could do any additional validation (unique licence plate) against the collection of cars and when found correct, add the new car to the cars collection and possibly propagate this addition/change to the model.
This way the dialog is responsible for the details of the car and the main view is responsible for adding the car to the collection.