Receive Notifications from Child ViewModels - windows-phone-7

I might be missing something simple, so bear with me.
I have a ViewModel that contains the following:
public ObservableCollection<Person> PersonCollection
{
get { return personCollection; }
set
{
if (personCollection != value)
{
personCollection = value;
RaisePropertyChanged("PersonCollection");
}
}
}
Then in another ViewModel I have:
public ObservableCollection<Person> PersonCollection
{
get
{
PersonViewModel vm = (App.Current.Resources["Locator"] as ViewModelLocator).PersonViewModel;
return vm.PersonCollection;
}
}
public PersonViewModel PersonViewModel
{
get
{
return ((App.Current.Resources["Locator"] as ViewModelLocator).PersonViewModel)
}
}
In my XAML if I bind to PersonCollection then updates don't happen on my view, but if I bind to PersonViewModel.PersonCollection it does. so is this the "proper" way to do it or is there a way for the view to detect the notifications using the first approach?

Change your binding to {Binding PersonViewModel.PersonCollection}
Your wrapped PersonCollection property has no change notifications, so the view doesn't know that the property has changed (it certainly has no way of knowing it originally came from PersonViewModel in order to get change notifications from it)

Related

List split into two - not updating Xamarin Forms

I have the below (simplified) model to display and return Chats. On the UI I want to show New Chats and Existing chats separately. When I first load the chats, it works, but if I update a Chat object, the UI doesn't update.
In the Xamarin Forms UI I have a 1) CollectionView binding to NewChats and 2) CollectionView binding to Existing Chats.
I update the IsNew flag programmatically, but that is not reflecting in the UI.
Any thoughts on how to approach this?
public class Chat
{
public string UserId {get;set;}
public bool IsNew {get;set;}
}
private ObservableCollection<Chat> _chats;
public ObservableCollection<Chat> Chats
{
get
{
return _chats;
}
set
{
this._chats= value;
OnPropertyChanged(nameof(Chats));
OnPropertyChanged(nameof(NewChats));
OnPropertyChanged(nameof(ExistingChats));
}
}
public ObservableCollection<Chat> NewChats
{
get
{
if (_chats!= null)
{
return new ObservableCollection<Chat>(_chats.Where(x => x.isNew);
}
return new ObservableCollection<Chat>();
}
}
public ObservableCollection<Chat> ExistingChats
{
get
{
if (_chat!= null)
{
return new ObservableCollection<Chat>(_chats.Where(x => !x.isNew);
}
return new ObservableCollection<Chat>();
}
}
As the comment above says For change notification to occur in a binding between a bound client and a data source, your bound type should either:
Implement the INotifyPropertyChanged interface (preferred).
Provide a change event for each property of the bound type.
you can check here to get offical sample:https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.inotifypropertychanged?view=net-6.0#definition

Dynamic ViewModel navigation

I am trying to find a way to be able to set from the View to what ViewModel I have to navigate. This is to be able to change the navigation flow without changing the core project.
I thought the easier way would be creating an interface, setting the target ViewModel there and injecting the interface into the ViewModel to then perform the navigation.
public interface IModelMapping
{
MvxViewModel ViewModelToNavigate();
}
public class MyViewModel : MvxViewModel
{
readonly IMvxNavigationService navigationService;
readonly IModelMapping modelMapping;
public MyViewModel(IMvxNavigationService navigationService, IModelMapping modelMapping)
{
this.navigationService = navigationService;
this.modelMapping = modelMapping;
}
public IMvxAsyncCommand GoContent
{
get
{
IMvxViewModel vm = modelMapping.ViewModelToNavigate();
IMvxAsyncCommand navigateCommand = new MvxAsyncCommand(() => navigationService.Navigate<vm>());
return navigteCommand;
}
}
}
The problem with this code is I am getting an error setting the navigationService.Navigate(). The error is 'vm is a variable but it is used like a type'
What about using the URI navigation together with the facade? See also https://www.mvvmcross.com/documentation/fundamentals/navigation#uri-navigation
Say you are building a task app and depending on the type of task you want to show a different view. This is where NavigationFacades come in handy (there is only so much regular expressions can do for you).
mvx://task/?id=00 <– this task is done, show read-only view (ViewModelA)
mvx://task/?id=01 <– this task isn’t, go straight to edit view (ViewModelB)
[assembly: MvxRouting(typeof(SimpleNavigationFacade), #"mvx://task/\?id=(?<id>[A-Z0-9]{32})$")]
namespace *.NavigationFacades
{
public class SimpleNavigationFacade
: IMvxNavigationFacade
{
public Task<MvxViewModelRequest> BuildViewModelRequest(string url,
IDictionary<string, string> currentParameters, MvxRequestedBy requestedBy)
{
// you can load data from a database etc.
// try not to do a lot of work here, as the user is waiting for the UI to do something ;)
var viewModelType = currentParameters["id"] == Guid.Empty.ToString("N") ? typeof(ViewModelA) : typeof(ViewModelB);
return Task.FromResult(new MvxViewModelRequest(viewModelType, new MvxBundle(), null, requestedBy));
}
}
}

Prism - moving data between viewmodels

I'm struggling to find the best implementation.
I'm using Prism and I have a View (ParentView), which has a small region within it. Depending on the item in a ddl, another smaller view (ChildView) gets injected into the region of the ParentView.
The ChildView will just have some properties which I would like to access from the ParentView.
So I realize I can use a Publish/Subscribe method to move data between viewmodels, but the issue is I have nothing to hang the Publish on. The view is made up of TextBoxes and no event triggers. The ChildView can be vastly different based on the selection of the ddl. I like the clean separation of each ChildView being it's own view injected inside the ParentView.
What is the best way to achieve this?
One solution can be to implement the interface INavigationAware in your viewmodels. After that you can use the methods onNavigatedFrom(), onNavigatedTo() and onNavigatingTo() to register your event.
EDIT:
If you want launch the event when a field in the child is changed you can do something like this:
private string _yourField;
public string YourField
{
get { return _yourField; }
set { SetProperty(ref _yourField, value);
//Here you can launch the event
}
}
In this case when YourField change the event is launched.
I tried a few implementations, but the one that worked was creating a singleton instance of the ChildView (childviewmodel) and then gaining access to the properties through the instance. It may not be pretty, but it works.
private static ChildViewModel _instance = new ChildViewModel ();
public static ChildViewModel Instance { get { return _instance; } }
#region Properties
private ChildModel _childModel= new ChildModel ();
public ChildModel _childModel
{
get { return _instance._childModel; }
set
{
SetProperty(ref _instance._childModel, value);
}
}
private string _childProperty1;
public string ChildProperty1
{
get { return _childProperty1; }
set
{
SetProperty(ref _childProperty1, value);
ChildModel.ChildProperty1= _childProperty1;
}
}
In reality - there were many childproperties. I only listed one for demo. And then I call it in ParentView
var _instance = ChildViewModel.Instance;
var _cm = _instance.ChildModel;
_parentModel = new ParentModel
{
Property1= ParentViewProperty1,
Property2= _cm.ChildProperty1,
};
Hope that helps someone else.

mvvmlight - what's the "proper way" of picking up url parameters for a view model

I'm just switching a project across to mvvmlight and trying to do things "the right way"
I've got a simple app with a listbox
When an item is selected in the listbox, then I've hooked up a RelayCommand
This RelayCommand causes a call on an INavigationService (http://geekswithblogs.net/lbugnion/archive/2011/01/06/navigation-in-a-wp7-application-with-mvvm-light.aspx) which navigates to a url like "/DetailPage.xaml?DetailId=12"
The DetailPage.xaml is then loaded and ... this is where I'm a bit unsure...
how should the DetailPage get hooked up to a DetailView with DetailId of 12?
should I do this in Xaml somehow using a property on the ViewLocator?
should I do this in the NavigatedTo method?
Please feel free to point me to a full sample - sure this has been done a (hundred) thousand times before, but all the blogs and tutorials seem to be skipping this last trivial detail (focussing instead on the messaging and on the ioc on on the navigationservice)
Thanks!
The only place you can retrieve the URL parameter is in the view. So since your view is likely depending on it, you should fetch it in the OnNavigatedTo method.
Then, you should pass it along to your viewmodel, either using messaging (to expensive if you ask me), or by referring to your datacontext (which is the viewmodel I presume), and execeuting a method on that.
private AddTilePageViewModel ViewModel
{
get
{
return DataContext as AddTilePageViewModel;
}
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
var postalCode = NavigationContext.TryGetKey("PostalCode");
var country = NavigationContext.TryGetStringKey("Country");
if (postalCode.HasValue && string.IsNullOrEmpty(country) == false)
{
ViewModel.LoadCity(postalCode.Value, country);
}
base.OnNavigatedTo(e);
}
I'm using some special extensions for the NavigationContext to make it easier.
namespace System.Windows.Navigation
{
public static class NavigationExtensions
{
public static int? TryGetKey(this NavigationContext source, string key)
{
if (source.QueryString.ContainsKey(key))
{
string value = source.QueryString[key];
int result = 0;
if (int.TryParse(value, out result))
{
return result;
}
}
return null;
}
public static string TryGetStringKey(this NavigationContext source, string key)
{
if (source.QueryString.ContainsKey(key))
{
return source.QueryString[key];
}
return null;
}
}
}
Create a new WindowsPhoneDataBound application, it has an example of how to handle navigation between views. Basically you handle the navigation part in your view, then set the view's DataContext accord to the query string. I think it plays nicely with the MVVM pattern since your ViewModels don't have to know anything about navigation (which IMO should be handled at the UI level).

MVC3 shared-search model confusion

(couldn't think of a better title, sorry)
So I've got my layout page, on this page there is a searchbar + options. Choosing whatever, should take you through to the search page, with the results etc. Fairly standard. What I've done to get this working is to create a MasterModel class, with a SearchDataModel class member on it. This SearchDataModel contains the various parameters for the search (search term, what fields to search on etc).
I've then strongly typed my layout page to the MasterModel class, and using a Html.BeginForm... I've constructed the search form for it. However all the checkboxes relating to the fields aren't checked by default, even though the default value for all the fields is true (via a private getter/setter setup).
Yet when I submit the form to the SearchController, all the checkboxes are set to true. So I'm a bit confused as to why it knows they should be true, yet not set the checkboxes to be checked?
Putting breakpoints in key places seems to show that the model isn't insantiated on the get requests, only the post to the Search controller?
I may be going about this all wrong, so if so, pointers as to the right way always appreciated.
public class MasterModel {
public SearchDataModel SearchModel { get; set; }
}
public class SearchDataModel{
private bool _OnTags = true;
private bool _OnManufacturers = true;
private bool _OnCountries = true;
[Display(Name= "Tags")]
public bool OnTags {
get { return _OnTags; }
set { _OnTags = value; }
}
[Display(Name= "Manufacturers")]
public bool OnManufacturers {
get { return _OnManufacturers; }
set { _OnManufacturers = value; }
}
[Display(Name= "Countries")]
public bool OnCountries {
get { return _OnCountries; }
set { _OnCountries = value; }
}
[Required]
[Display(Name="Search Term:")]
public string SearchTerm { get; set; }
}
Then in the _layout page:
#Html.CheckBoxFor(m => m.SearchModel.OnTags, new { #class="ddlCheckbox", #id="inpCheckboxTag" })
#Html.LabelFor(m =>m.SearchModel.OnTags)
Make sure you return a MasterModel with initialized SearchModel from your views:
public ActionResult Index()
{
var model = new MasterModel
{
SearchModel = new SearchDataModel()
};
return View(model);
}
Another possibility to implement this functionality than strongly typing your master layout to a view model is yo use Html.Action as shown by Phil Haack in his blog post.

Resources