I have two views : MainView and DetailView. I have a list of items to display and when user select an item and I am passing item properties to DetailViewModel and user could able to update these values.
Everything works so far, but I wonder how am I passing back to updated values to the MainViewModel ?
MainViewModel.cs
public MainViewModel SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
ShowViewModel<DetailViewModel>(
new DetailViewModel.Parameter
{
Date = Date,
Age = _selectedItem.Age,
Category = _selectedItem.Category,
Discount = _selectedItem.Discount,
}
);
RaisePropertyChanged(() => SelectedItem);
}
}
DetailViewModel.cs
public class DetailViewModel: MvxViewModel
{
public double Age { get; set; }
public double Category { get; set; }
public double Discount { get; set; }
public class Parameter
{
public DateTime Date { get; set; }
public double Age { get; set; }
public int Category{ get; set; }
public double Discount { get; set; }
}
public void Init(Parameter param)
{
Age = param.Age;
Category = param.Category;
Discount = param.Discount ;
}
}
One way to pass variables between ViewModels is a Messenger based solution.
MvvmCross Messenger can be found in NuGet.
MainViewModel
private readonly IMvxMessenger _messenger;
private readonly MvxSubscriptionToken _token;
public MainViewModel(IMvxMessenger messenger) {
_messenger = messenger;
_token = messenger.Subscribe<SelectedItemMessage>(OnMessageReceived);;
}
private void OnMessageReceived(SelectedItemMessage obj)
{
SelectedItem = obj.SelectedItem;
}
DetailViewModel
private readonly IMvxMessenger _messenger;
public DetailViewModel(IMvxMessenger messenger) {
_messenger = messenger;
}
public void YourUpdateMethod() {
var message = new SelectedItemMessage(this, SelectedItem); //SelectedItem assumed it is a ViewModel property.
_messenger.Publish(message, typeof(SelectedItemMessage));
}
SelectedItemMessage
public class SelectedItemMessage : MvxMessage
{
public SelectedItemMessage(object sender, SelectedItem selectedItem) : base(sender)
{
SelectedItem = selectedItem;
}
public SelectedItem SelectedItem { get; set; }
}
Take a look at http://slodge.blogspot.nl/2013/05/n9-getting-message-n1-days-of-mvvmcross.html for a full guide to MvvmCross Messenges.
Edit using age and category in Message
public SelectedItemMessage(object sender, double age, int category) : base(sender)
{
Age = age;
Category = category;
}
public double Age { get; set; }
public int Category{ get; set; }
}
Changing the MainViewModel OnMessageReceived method
private void OnMessageReceived(SelectedItemMessage obj)
{
Age = obj.Age;
Category= obj.Category;
}
Why not just keep a reference to DetailViewModel when you create it in MainViewModel? Then any values changed in the DetailViewModel instance will be available via that reference in MainViewModel.
private DetailViewModel _detailVM;
public MainViewModel SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
_detailVM = new DetailViewModel.Parameter {
Date = Date,
Age = _selectedItem.Age,
Category = _selectedItem.Category,
Discount = _selectedItem.Discount
};
ShowViewModel<DetailViewModel>(_detailVM);
RaisePropertyChanged(() => SelectedItem);
}
}
Related
I am working on mobile app using xamarin forms, I have a list of object. I have added the rows in list and raise property using this OnPropertyChanged and after save the items i want to update the status of list of object property. How we can update Status Property, Here is my code example , please check the code and update me, Thanks:-
class Test
{
public int ID{ get; set; }
public string Name { get; set; }
public bool Status { get; set; }
}
class Consume : BaseViewModel
{
void main()
{
ObservableCollection<Test> coll = new ObservableCollection<Test>();
coll = await db.GetData();
foreach (var item in coll)
{
item.Status = true;
//How we can update Status property of class
OnPropertyChanged("Status");
}
}
}
Implement INotifyPropertyChanged in your Test class:
class Test : INotifyPropertyChanged
{
public int ID { get; set; }
public string Name { get; set; }
private bool _status;
public bool Status
{
get { return _status; }
set
{
_status = value;
RaisePropertyChanged();
}
}
#region INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged([CallerMemberName]string propertyName = "")
{
Volatile.Read(ref PropertyChanged)?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
And if you have correct binding, after item.Status = true; UI will get change of this property.
I have a list item which has more or less around 10 objects. I could able to detect which item is selected and also I sending this item properties into the DetailViewModel,I am using messageprotocol in mvvmcross.
I could able to observe changes in the MainViewModel when user enters new value in the edittext in DetailViewModel.
I wonder how I am going to put these values back into the selected item and update list.
MainViewModel
private readonly IMvxMessenger _messenger;
private readonly MvxSubscriptionToken _token;
private MainViewModel _selectedItem;
public MainViewModel SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
ShowViewModel<DetailViewModel>(
new DetailViewModel.Parameter
{
Age= _selectedItem.Age,
Category = _selectedItem.Category,
});
RaisePropertyChanged(() => SelectedItem);
}
}
public MainViewModel(IMvxMessenger messenger) {
_messenger = messenger;
_token = messenger.Subscribe<SelectedItemMessage>(OnMessageReceived);;
}
private void OnMessageReceived(SelectedItemMessage obj)
{
// I could observe the DetailView Changes in the MainViewModel
// I wonder how to put these value back to selectedItem
double? Age = obj.Age;
int? Category= obj.Category;
}
public virtual ICommand ItemSelected
{
get{ return new MvxCommand<TestViewModel>(item =>{ SelectedItem = item;});
}
}
private ObservableCollection<TestViewModel> _testViews;
private ObservableCollection<WellTestViewModel> _allTestItemViews;
public void Init(string Id)
{
List<Test> allTests = new List<Test>();
allTests = _TestService.GetAllTestById(Id);
foreach (var test in allTests)
{
_testViews.Add(TestViewModel.CreateViewModel(test, this));
}
_allTestItemViews = _testViews;
}
TestViewModel
public static TestViewModel CreateViewModel(Test entity, MainViewModel parent = null)
{
if (entity == null)
{
return null;
}
return new TestViewModel(parent)
{
Age = entity.Age,
Category= entity.Category,
};
}
public TestViewModel()
{
// parameterless constructor
}
readonly MainViewViewModel _mainViewModel ;
public TestViewModel(MainViewViewModel mainViewViewModel)
{
_mainViewModel = mainViewViewModel;
}
DetailViewModel
private readonly IMvxMessenger _messenger;
public class Parameter
{
public double? Age{ get; set; }
public int? Category { get; set; }
}
public void Init(Parameter param)
{
Age= param.Age;
Category= param.Category;
}
public DetailViewModel(IMvxMessenger messenger) {
_messenger = messenger;
}
public void UpdateMethod() {
var message = new SelectedItemMessage(this, age, category);
_messenger.Publish(message, typeof(SelectedItemMessage));
}
SelectedItemMessage
public SelectedItemMessage(object sender, double? age, int? category) : base(sender)
{
Age = age;
Category = category;
}
public double? Age { get; set; }
public int? Category{ get; set; }
}
Just use your _selectedItem and set the properties on it.
private void OnMessageReceived(SelectedItemMessage obj)
{
_selectedItem.Age = obj.Age;
_selectedItem.Category= obj.Category;
}
You need to update the collection inside the OnMessageReceived method:
var item = _allTestItemViews.FirstOrDefault(i => i.Id == id);
if (item != null)
{
item.Age = age;
item.Category = category;
}
You need to add Id to your model class so that you can uniquely identify the item you need to update.
this should be a stupid problem but i dont know where to start,so i'll ask here.i've got a class named Routing which is binded to a datagrid. Inside this class theres an object from another class:
public class Routing : INotifyPropertyChanged
{
public int Sequenza { get; set; }
private ObservableCollection<Prodotti> availableProducts;
public ObservableCollection<Prodotti> AvailableProducts
{
get { return availableProducts; }
set
{
if (availableProducts != value)
{
availableProducts = value;
OnPropertyChanged("AvailableProducts");
}
}
}
private Prodotti product;
public Prodotti Product
{
get { return product; }
set
{
if (product != value)
{
product = value;
UpdateAvailableCosts();
OnPropertyChanged("Product");
}
}
}
}
and then the Product class:
public class Prodotti
{
public int Product_id { get; set; }
public string Product_description { get; set; }
public int Product_treshold { get; set; }
}
Everything works as intended,and if i just declare the "Sequenza = 1" in the routing the datagrid adds the 1 in the first row/column. But i would like to add more initial values,maybe based on data present in the database. but i cant come up with the constructor for it
Routes.Add(new Routing { Sequenza = 1,Prodotti=... });
Routes.Add(new Routing { Sequenza = 1,Prodotti= new Prodotti{Product_id =1, Product_description = "str", Product_treshold =1} });
I'm developing a simple Crud Application (a windows 8.1 store application) using Caliburn Micro 2.0.0-alpha2
I'm in trouble with navigation between viewmodels, passing object.
I read many times the solution proposed by
Anders Gustafsson (How to pass parameter to navigated view model with WinRT Caliburn.Micro?)
and i tried to adapt it to my scope.
But the object is alwais null.
I need to pass a single object selected from a listView to my crudPage.
The crudPage is composed by an userControl that shown the FormView.
So i want to initialize this Form, with the values of the passed object.
I think that the problem is that the "Parameter" is initialized only after the ViewModel is created, but i don't know how to fix that problem.
There is my code, according with the idea of Anders Gustafsson
TransporterListViewModel (a list of Transporters from Database)
public class TransporterListViewModel : ViewModelBase
{
public string Title { get; set; }
public TransporterListViewModel(INavigationService navigationService)
: base(navigationService)
{
LoadData();
}
public async void LoadData() {
_transporters = await TransporterService.GetAll();
}
private BindableCollection<Transporter> _transporters;
public BindableCollection<Transporter> Transporters
{
get
{
return this._transporters;
}
set
{
this._transporters = value;
NotifyOfPropertyChange(() => this.Transporters);
}
}
private Transporter _selectedItem;
public Transporter SelectedItem
{
get
{
return _selectedItem;
}
set
{
_selectedItem = value;
NotifyOfPropertyChange(() => this.SelectedItem);
navigationService.Navigated += NavigationServiceOnNavigated;
navigationService.NavigateToViewModel<TransporterCrudPageViewModel>(_selectedItem;);
navigationService.Navigated -= NavigationServiceOnNavigated;
}
}
private static void NavigationServiceOnNavigated(object sender, NavigationEventArgs args)
{
FrameworkElement view;
TransporterCrudPageViewModel transporterCrudPageViewModel;
if ((view = args.Content as FrameworkElement) == null ||
(transporterCrudPageViewModel = view.DataContext as TransporterCrudPageViewModel) == null) return;
transporterCrudPageViewModel.InitializeTransporterForm(args.Parameter as Transporter);
}
TransporterCrudViewModel (the page that cointains the UserControl to initialize)
public class TransporterCrudPageViewModel : ViewModelBase
{
public string Title { get; set; }
public Transporter Parameter { get; set; }
public TransporterFormViewModel TransporterFormVM { get; set; }
public async void InitializeTransporterForm(Transporter enumerable)
{
TransporterFormVM = new TransporterFormViewModel(navigationService, enumerable);
await SetUpForm(enumerable);
}
public async Task SetUpForm(Transporter t){
TransporterFormVM.trName = t.trName;
TransporterFormVM.trUrl = t.trUrl;
}
public TransporterCrudPageViewModel(INavigationService navigationService)
: base(navigationService)
{
Title = "TransporterCrud Page";
//this.navigationService = navigationService;
this.InitializeTransporterForm(Parameter);
}
TransporterFormViewModel (the userContol to initialize)
public class TransporterFormViewModel :ViewModelBase
{
public string Title { get; set; }
public Transporter Transporter { get; set; }
public TransporterFormViewModel(INavigationService navigationService,Transporter trans)
: base(navigationService)
{
Transporter = trans;
}
private string _trName;
public string trName
{
get
{
return _trName;
}
set
{
_trName = value;
NotifyOfPropertyChange(() => trName);
}
}
public string trCode { get; set; }
public string trUrl { get; set; }
public int trId { get; set; }
In the constructor TransporterCrudViewModel class you have:
this.InitializeTransporterForm(Parameter);
where Parameter is a property of type Transporter not initialized and you will call the method InitializeTransporterForm with a null parameter. Then you'll call SetUpForm method with a null value of the parameter Transporter t. I think you should initialize in some way this property.
Then, supposing you're continuing in your TransporterListViewModel class with this:
transporterCrudPageViewModel.InitializeTransporterForm(args.Parameter as Transporter);
in the method InitializeTransporterForm, you don't set the passed parameter as value of the property Parameter with something like this:
public async void InitializeTransporterForm(Transporter enumerable)
{
TransporterFormVM = new TransporterFormViewModel(navigationService, enumerable);
this.Parameter = enumerable; //setting the Parameter property..
await SetUpForm(enumerable);
}
Beside these notes, you should put a breakpoint with your IDE in the line
transporterCrudPageViewModel.InitializeTransporterForm(args.Parameter as Transporter);
Make sure that the property Parameter of the NavigationEventArgs object is not null.
I have a datagridview that I am binding to a class. I add to the class but the datagridview is not updating.
My bind:
ScannedChecks = new ScannedChecks();
ScannedChecks.AddCheck(DateTime.Now, "22222", "checknumdd", "routingdd", _checkData, 4);
dataGridView1.DataSource = ScannedChecks;
I went ahead and did the AddCheck to see if it was reaching the datagridview and it isn't... The class is being updated though.
My class:
namespace SSS.Ckentry
{
public class ScannedChecks : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ScannedChecks()
{
ScannedChecksCollection = new ObservableCollection<ScannedCheck>();
}
public void AddCheck(DateTime checkDate, string accountNumber, string checkNumber, string bankRoutingNumber, string bankAccountNumber, decimal checkAmount)
{
var scc = new ScannedCheck
{
CheckDate = checkDate,
AccountNumber = accountNumber,
CheckNumber = checkNumber,
BankRoutingNumber = bankRoutingNumber,
BankAccountNumber = bankAccountNumber,
CheckAmount = checkAmount,
};
ScannedChecksCollection.Add(scc);
}
public ObservableCollection<ScannedCheck> ScannedChecksCollection { get; set; }
public class ScannedCheck
{
public DateTime CheckDate { get; set; }
public string AccountNumber { get; set; }
public string CheckNumber { get; set; }
public string BankRoutingNumber { get; set; }
public string BankAccountNumber { get; set; }
public decimal CheckAmount { get; set; }
}
}
}
Can anyone tell me what I am doing wrong?
Thanks much!
If you ever replace the ScannedChecksCollection with a new ScannedChecksCollection, the property setter should fire the PropertyChanged exent.
private ObservableCollection<ScannedCheck> scannedChecksCollection;
public ObservableCollection<ScannedCheck> ScannedChecksCollection {
get
{
return scannedChecksCollection;
}
set
{
if (value != scannedChecksCollection)
{
value = scannedChecksCollection;
NotifyPropertyChanged("ScannedChecksCollection");
}
}
}
private void NotifyPropertyChanged(string propName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
If checks are modifiable, ScannedCheck should implement INotifyPropertyChanged
Shouldn't you be doing
dataGridView1.DataSource = ScannedChecks.ScannedChecksCollection;