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.
Related
I setup a very simple app to get me started and trying things out. It has a label and two buttons. The buttons are linked to relaycommands in the main view model are used to send a message to a server using Mqtt which work as intended. The label is used to show part of the data received from the server. Everything seems to work fine except the labels won't update as soon as the message is received event though I can see in debugging that the property is set. The label will update as soon as I press one of the two buttons...
I'm new to the whole Xamarin android thing and have used mvvm light once in a WPF application.
Main Activity :
public partial class MainActivity
{
// UI Elements
public TextView ScanInfoLabel { get; private set; }
public Button UnlockButton { get; private set; }
public Button RegisterButton { get; private set; }
// Keep track of bindings to avoid premature garbage collection
private readonly List<Binding> _bindings = new List<Binding>();
// Get view model
private MainViewModel mainViewModel { get { return App.Locator.Main;}}
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// Set view from the "main" layout resource
SetContentView(Resource.Layout.Main);
// Get the UI elements by ID
ScanInfoLabel = FindViewById<TextView>(Resource.Id.ScanInfoLabel);
UnlockButton = FindViewById<Button>(Resource.Id.UnlockButton);
RegisterButton = FindViewById<Button>(Resource.Id.RegisterButton);
// Set Bindings for textviews
_bindings.Add(
this.SetBinding(
() => mainViewModel.ScanInfoLabel,
() => ScanInfoLabel.Text));
// Set the bindings for commands
UnlockButton.SetCommand("Click", mainViewModel.UnlockCommand);
RegisterButton.SetCommand("Click", mainViewModel.RegisterTagCommand);
}
In Main view model :
// RelayCommands
public RelayCommand UnlockCommand;
public RelayCommand RegisterTagCommand;
public RelayCommand MqttConnectCommand;
// Bindable properties
private string _scanInfoLabel = "Test";
public string ScanInfoLabel
{
get { return _scanInfoLabel; }
set { Set(ref _scanInfoLabel, value); }
}
// New scan message received
private void RFIDScanReceived(RFID.Scan scan)
{
ScanInfoLabel = BitConverter.ToString(scan.UID);
}
I would expect the label to show the data as soon as the mqtt message is received (which is then sent to the mainviewmodel using Messenger.Default.send<>() from mvvm light). But nothing is changed in the UI until I click on one of the buttons and then the correct information in displayed.
I don't really know where to start being new to xamarin android and none of my searches seemed to be of any help.
Any help will be appreciated, thanks!
ViewModels generally implement the INotifyPropertyChanged interface,
which means that the class fires a PropertyChanged event whenever one
of its properties changes. The data binding mechanism in Xamarin.Forms
attaches a handler to this PropertyChanged event so it can be notified
when a property changes and keep the target updated with the new
value.
Solution:
Make you model inherit from INotifyPropertyChanged and add PropertyChanged inside the set part. Then the labels will update as soon as the value of ScanInfoLabel changed.
public class BaseViewModel : INotifyPropertyChanged
{
// Bindable properties
private string _scanInfoLabel = "Test";
public string ScanInfoLabel
{
get { return _scanInfoLabel; }
set
{
_scanInfoLabel = ScanInfoLabel;
PropertyChanged(this, new PropertyChangedEventArgs("ScanInfoLabel"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
You can refer:data-bindings-to-mvvm
Well I found out it was a threading issue since I set the property from a Messenger call. This apparently is not an issue in WPF which is why I was a bit stuck but using the DispatcherHelper did the trick.
// New scan message received
private void RFIDScanReceived(RFID.Scan scan)
{
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
ScanInfoLabel = BitConverter.ToString(scan.UID);
});
}
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.
As a learning exercise I'm trying to build a basic WPF app that makes use of the MVVM pattern that shows a list of objects (lets just use customers for simplicity) in a main tab and then allowing the user to edit customers by opening another tab with some sort of edit view. What I'm stuck on is how to go about communicating changes between the views if I want to make use of deferred saving. I've seen some stuff about using a MessageBus but I've also seen a lot about how that's an anti-pattern in MVVM. This is the code I've come up with so far
Model
public class Customer
{
public string Name {get; set;}
}
Customer View Model
public class CustomerViewModel
{
public string Name
{
get { return _model.Name; }
set {_model.Name = value; RaisePropertyChanged(); }
}
public void Save()
{
//Save the model to a DB/whatever
}
private Customer _model;
}
Edit View Model
public class EditCustomerViewModel
{
public string Name
{
get { return _name; }
set {_name = value; RaisePropertyChanged(); }
}
public void Save()
{
_model.Name = _name;
_model.Save();
}
private string _name;
private CustomerViewModel _model;
}
List View Model
public class CustomerListViewModel
{
public ObservableCollection<CustomerViewModel> Customers
{
get { return _customers; }
}
private ObservableCollection<CustomerViewModel> _customers;
}
The benefit of this code is that to edit a customer I create an edit view model that has a reference to the same view model this list is using so when changes are saved they show up in the list. The downside is I have to repeat a lot of code in the edit view. Not a problem when there's a few properties but definitely an issue with a lot. As well I can't imagine this scales when connected to a DB with many customers as I'd have to load all the customers into the list just to edit one (though maybe there's other solutions to this).
Is this the best way to approach a problem like this or is there a practice in MVVM that handles this kind of issue?
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 am trying to implement MVVM (Model View ViewModel) pattern for my WinForms application. I am using C# 2005.
My application has a MainForm (View) with 2 multi line textboxes and 3 buttons. The purpose of the 1st textbox is to show a running commentary of what the application is doing, when the button is clicked. I keep on appending lines to the TextBox to update the user what is happening. The purpose of the 2nd textbox is to update the user about any error condition, conflicts, duplicate values; in short, anything which is required by the user to review. It classifies each message as either an INFO or a WARNING or an ERROR. Each of the 3 buttons perform an action, and keeps updating the 2 textboxes.
I have created a MainFormViewModel class.
1st question:
When the user clicks on the button in MainForm, I have to clear the contents of the 2 textboxes, and disable the button so that it cant be clicked again until 1st operation is completed. Should I do this textbox and button updation directly in the MainForm or I should use MainFormViewModel in some way?
2nd question:
The button click calls a method on the MainFormViewModel class. Before calling the method and after calling the method, I want to show a message in the 1st textbox something like "Operation A started / ended". I do this by calling a Common class which has a Log method to log messages to a TextBox or a file or both. Again whether it is ok to do this directly from the MainForm? I call this logging method at the start and end of the event handler.
3rd question:
How do I propagate error messages from ViewModel back to View? I have created a custom Exception class "TbtException". So do I have to write 2 catch blocks in each and every button, one for TbtException and other for genetic Exception class?
Thanks.
You should perform operations in the view only with regard to the state of the ViewModel object. E.g. you shouldn't assume the view model is calculating when you click a button, but you should add a state to the view model that says it's doing something longer and then recognize that state in the view. You shouldn't disable or enable buttons in the view as you please, but only if there's a state that demands these buttons to be changed. This can go as far as to have a property that indicates which item in a list is currently selected, so the UI doesn't call the list control's SelectedItem member, but the viewmodel's. And when the user clicks remove, then the view model will remove the selected member from its list and the view is automatically updated through state changes in the form of events.
Here's what I would call a view model for your view. It exposes messages through an observable collection to which the view can bind (ie. register event handlers, since binding is not well supported in WinForms). The textbox at any time renders only the contents of the collection. It has actions for clearing those collections which your view can call. The view can also call actions of the underlying model, but it should be updated through the viewmodel only! The view should never register any event handlers for events exposed by the underlying model. If you ever want to do that you should hook up that event in the view model and expose it to the view there. Sometimes that may feel like "just another level of indirection" which is why it may be overkill for very simple applications such as yours.
public class MainFormViewModel : INotifyPropertyChanged {
private object syncObject = new object();
private MainFormModel model;
public virtual MainFormModel Model {
get { return model; }
set {
bool changed = (model != value);
if (changed && model != null) DeregisterModelEvents();
model = value;
if (changed) {
OnPropertyChanged("Model");
if (model != null) RegisterModelEvents();
}
}
}
private bool isCalculating;
public bool IsCalculating {
get { return isCalculating; }
protected set {
bool changed = (isCalculating != value);
isCalculating = value;
if (changed) OnPropertyChanged("IsCalculating");
}
}
public ObservableCollection<string> Messages { get; private set; }
public ObservableCollection<Exception> Exceptions { get; private set; }
protected MainFormViewModel() {
this.Messages = new ObservableCollection<string>();
this.Exceptions = new ObservableCollection<string>();
}
public MainFormViewModel(MainFormModel model)
: this() {
Model = model;
}
protected virtual void RegisterModelEvents() {
Model.NewMessage += new EventHandler<SomeEventArg>(Model_NewMessage);
Model.ExceptionThrown += new EventHandler<OtherEventArg>(Model_ExceptionThrown);
}
protected virtual void DeregisterModelEvents() {
Model.NewMessage -= new EventHandler<SomeEventArg>(Model_NewMessage);
Model.ExceptionThrown -= new EventHandler<OtherEventArg>(Model_ExceptionThrown);
}
protected virtual void Model_NewMessage(object sender, SomeEventArg e) {
Messages.Add(e.Message);
}
protected virtual void Model_ExceptionThrown(object sender, OtherEventArg e) {
Exceptions.Add(e.Exception);
}
public virtual void ClearMessages() {
lock (syncObject) {
IsCalculating = true;
try {
Messages.Clear();
} finally { IsCalculating = false; }
}
}
public virtual void ClearExceptions() {
lock (syncObject) {
IsCalculating = true;
try {
Exceptions.Clear();
} finally { IsCalculating = false; }
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropetyChanged(string property) {
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(property));
}
}
EDIT: Regarding exception handling
I would rather catch exceptions in the ViewModel than in the view. The view model is better suited to prepare them for display. I don't know how that works in WPF though. I've yet to program an application in WPF, we're doing a lot of WinForms still.
Opinions may vary, but I think generic try/catch clauses aren't really exception handling. I think you should rather test your UI very well and include exception handling only when necessary. Which is why you unit test your view model and user test the view. However if you really stick to the principle and avoid logic in the view, you can do a lot with unit tests.