Windows Phone 8 Binding - c#

I have a Windows Phone 8 Pivot application which contains the auto generated MainViewModel Binding mechanism.
I have changed the MainViewModel class to suit my needs and changed the MainPage.xaml markup so that it binds to the correct properties.
All good.
Now - I have a class which calls a Web Service and a call-back method which fires when data is received:
private void GetSigns_Completed(object sender, OpenReadCompletedEventArgs e)
{
using (var sr = new StreamReader(e.Result))
{
var data = sr.ReadToEnd();
var result = JsonConvert.DeserializeObject<SignViewModel>(data);
}
}
As you can see, I am not returning or populating anything with the result object as I do not know how to do so. The MainViewModel's properties have private setters:
public ObservableCollection<SignViewModel> Signs { get; private set; }
public ObservableCollection<TweetsViewModel> Tweets { get; private set; }
Each of these objects have a NotifyPropertyChange event.
So how do I populate the Signs and Tweets objects and make the whole binding work automatically when retrieving data from a Web Service? How do I make the UI/Mainpage.xaml communicate with the Web Service Class and the MainViewModel class?

To solve the issue, I simply changed the setters to public for Signs and Tweets. Once this was in place, the NotifyPropertyChange worked it's magic and automatically bound to the View. Magic! I wonder why Microsoft Developers made the setters private .....

Related

How to handle events that happened on different page

I have a UWP Project with 2 pages so far. The MainPage.xaml is the basic layout of the app ( hamburger menu, search bar, etc.). The other part of this MainPage contains a frame into which the other page LandingPage.xaml is loaded. I want to capture the user input from an AutosuggestBox in the MainPage.xaml and show the results on LandingPage.xaml ( which is in a frame present inside MainPage.xaml).
I tried inheriting the MainPage, but that's not allowed.
While Marian's answer would certainly work, I think it's far from being 'clean' or 'good' code.
First and foremost, you should implement the MVVM pattern in your UWP apps (if you don't do it already) and use a dependency injection framework for that. A very basic, easy to understand one is MVVMLight, while a more sophisticated framework of choice could be Autofac. I advise you to start with the former, it's much quicker to wrap your head around it first.
In MVVM there's a concept that solves just your problem: messengers. I wouldn't like to get into the details here, since there already a lot of very good resources about this written by much smarter people than me. For example this article from the author of MVVMLight himself: https://msdn.microsoft.com/en-us/magazine/jj694937.aspx (I know it's from 2013 and speaks about Windows 8, but fear not, the concepts are just the same.)
The idea is that distinct ViewModels shouldn't have strict dependencies on each other - it makes unit testing (which is one of the main points of doing MVVM in the first place) hard. So in your case, you should have two ViewModels: MainViewModel and LandingViewModel. One for MainPage, and one for LandingPage, respectively. Now you should implement a handler in MainPage's code-behind for AutoSuggestBox's QuerySubmitted event and call a function in MainViewModel. In that function, you would instantiate a new message with the string coming from your AutoSuggestBox (which you can acquire either from doing data binding to it or through the event handler of QuerySubmitted, it's up to you) and send it via the Messenger. In LandingViewModel, you would subscribe to this exact message and then it's again just a matter of few lines to display the received message through data binding on LandingPage.
I know it looks like a lot of hassle for just something very basic like this, especially if you compare it to Marian's straight to the point solution. But trust me, in the long run writing clean code, nicely separated, easily unit testable ViewModels will make up for the additional effort that you have to put into them initially to make them work. After such a system is set up between two ViewModels, adding a third (which I assume you'll need to do soon) is absolutely trivial and can be done very quickly.
If you're not using MVVM I'd suggest adding x:FieldModifier="public" on the AutoSuggestBox and add a public static property to MainPage to store its instance.
MainPage.xaml.cs
public static MainPage Current { get; private set; }
public MainPage()
{
Current = this;
// Rest of your code in ctor
}
Then you can access it using
string text = MainPage.Current.NameOfYourAutoSuggestBox.Text;
Just use a simple message passing mechanism of your own, like this:
public class Messages {
public static Messages Instance { get; } = new Messages();
private readonly List<Subscription> subscriptions;
private Messages() {
subscriptions = new List<Subscription>();
}
public void Send<T>(T message) {
var msgType = message.GetType();
foreach (var sub in subscriptions)
if (sub.Type.IsAssignableFrom(msgType))
sub.Handle(message);
}
public Guid Subscribe<T>(Action<T> action) {
var key = Guid.NewGuid();
lock (subscriptions) {
subscriptions.Add(new Subscription(typeof(T), key, action));
}
return key;
}
public void Unsubscribe(Guid key) {
lock (subscriptions) {
subscriptions.RemoveAll(sub => sub.Key == key);
}
}
public bool IsSubscribed(Guid key) {
lock (subscriptions) {
return subscriptions.Any(sub => sub.Key == key);
}
}
public void Dispose() {
subscriptions.Clear();
}
}
internal sealed class Subscription {
internal Guid Key { get; }
internal Type Type { get; }
private object Handler { get; }
internal Subscription(Type type, Guid key, object handler) {
Type = type;
Key = key;
Handler = handler;
}
internal void Handle<T>(T message) {
((Action<T>)Handler).Invoke(message);
}
}
It's small and simple but it allows the subscription of different messages in parallel, separated by message type. You can subscribe, in a case similar to yours, with:
Messages.Instance.Subscribe<TextChangeArgs>(OnTextChanged);
and your other pages can send their messages using:
Messages.Instance.Send(new TextChangeArgs(...));
From all subscribers, only those interested in this specific message type will receive the message. You can (and, of course, should) also unsubscribe. Some more error handling could also be necessary in a real world scenario.
If necessary, you can add extra functionality like throttling easily (to avoid too many consecutive messages in a given time period).

Create a PING Monitor in WPF Application - With DataGrid

I'm new in the forum. Sorry for my English... It's not very well.
Please, I read the article:
How to Perform Multiple "Pings" in Parallel using C#
Please, someone give me a help and to explain how can I populate a WPF Datagrid to monitoring a list of IP's ?
I'm build a class:
...
public class ServerMonitor
{
public string ID { set; get; }
public string Timeout { set; get; }
public string IP { set; get; }
public string TTL { set; get; }
}
...
In main method I got it show data in datagridview.
MonitorPing.PingAddressesAsync(new List<IPAddress>() {
IPAddress.Parse ("XXX.XXX.XXX.XXX"),
IPAddress.Parse ("XXX.XXX.XXX.XXX"),
IPAddress.Parse ("XXX.XXX.XXX.XXX"),
IPAddress.Parse ("XXX.XXX.XXX.XXX")
}, delegate (Task<List<PingReply>> tpr)
{
var lr = tpr.Result;
foreach (var pr in lr)
{
//This correct
Dispatcher.BeginInvoke(DispatcherPriority.Normal,(ThreadStart)(() => dataGrid.Items.Add(new ServerMonitor { ID = pr.Buffer.ToString(), Timeout = pr.Status.ToString(), IP = pr.Address.ToString(), TTL = pr.RoundtripTime.ToString() })));
}
});
}
At this point, it's all right.
But how do I get the data to be updated in real time? I use a while, use a System.Timer...
Tks and sorry for anything!
To display a list of items in the DataGrid with values that are automatically updated in the UI you will need to bind your DataGrid to an ObservableCollection of objects which implement INotifyPropertyChanged interface.
The code which performs the actual pinging would reside in your ViewModel which will also contain the ObservableCollection. That code would periodically iterate through the objects in that ObservableCollection and update the desired public Properties of each object with ping results and these values will be automatically updated in the DataGrid because they implement the INotifyPropertyChanged interface.

UITableView to ObservableCollection binding breaks when the containing UIViewController is initialised for the second time

I'm using mvvmcross and xamarin to bind an ObservableCollection to a UITableView. The collection is updated in place using the Add, Remove and Move methods. These calls correctly trigger INotifyCollectionChanged events and the TableView is updated as expected the first time the view containing the table is shown. If the user navigates away from the original view as part of the normal application flow but later returns the correct data is loaded into the table but calls to add, move and remove no longer update the table.
The INotifyCollectionChanged events are still being fired when the collection is updated
If I manually subscribe to these events in my subclass of MvxStandardTableViewSource and try and call ReloadData on the UITableView still does not update
My presenter is creating a new instance of the viewmodel and view each time the page is visited.
I'm also using Xamarin-Sidebar (https://components.xamarin.com/view/sidebarnavigation) for navigation in my application with a custom presenter to load the views but as far as I can tell the view is initialised via exactly the same code path whether it's the first or subsequent visit.
My presenters Show() method looks like this:
public override void Show(MvxViewModelRequest request)
{
if (request.PresentationValues != null)
{
if(NavigationFactory.CheckNavigationMode(request.PresentationValues, NavigationFactory.ClearStack))
{
MasterNavigationController.ViewControllers = new UIViewController[0];
base.Show(request);
}
else if(NavigationFactory.CheckNavigationMode(request.PresentationValues, NavigationFactory.LoadView))
{
var root = MasterNavigationController.TopViewController as RootViewController;
var view = this.CreateViewControllerFor(request) as UIViewController;
root.SidebarController.ChangeContentView(view);
}
}
else
{
base.Show(request);
}
}
The binding in my ViewController looks like this:
public override void ViewDidLoad()
{
base.ViewDidLoad();
View.AutoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight;
var source = new TracksTableSource(TableView, "TitleText Title; ImageUrl ImageUrl", ViewModel);
TableView.Source = source;
var set = this.CreateBindingSet<TracksViewController, TracksViewModel>();
set.Bind(source).To(vm => vm.PlaylistTable);
set.Apply();
}
And my viewmodel is as below where PlaylistTable is a subclass of ObservableCollection with the Update method using add, move and remove to keep the collection up to date.
public class TracksViewModel : MvxViewModel
{
private readonly IPlaylistService _playlistService;
private readonly IMessengerService _messengerService;
private readonly MvxSubscriptionToken _playlistToken;
public PlaylistTable PlaylistTable { get; set; }
public TracksViewModel(IPlaylistService playlistService, IMessengerService messengerService)
{
_playlistService = playlistService;
_messengerService = messengerService;
if (!messengerService.IsSubscribed<PlaylistUpdateMessage>(GetType().Name))
_playlistToken = _messengerService.Subscribe<PlaylistUpdateMessage>(OnDirtyPlaylist, GetType().Name);
}
public void Init(NavigationParameters parameters)
{
PlaylistTable = new PlaylistTable(parameters.PlaylistId);
UpdatePlaylist(parameters.PlaylistId);
}
public async void UpdatePlaylist(Guid playlistId)
{
var response = await _playlistService.Get(playlistId);
PlaylistTable.Update(new Playlist(response));
}
private void OnDirtyPlaylist(PlaylistUpdateMessage message)
{
UpdatePlaylist(message.PlaylistId);
}
}
This setup works perfectly the first time the view is initialised and updates the table correctly, it's only the second and subsequent times the view is initialised that the table fails to update. Can anyone explain why the binding fails when it appears the view is created using the same techniques in both instances?
I can post additional code if required but I believe the issue will be how I'm using the presenter since the code I've not posted from PlaylistTable functions correctly in unit tests and on first viewing.

Datagrid not refreshing

Hi I have a datagrid that is populated via an ICollectionView
public ICollectionView MemberView { get; private set; }
Members = new ObservableCollection<Member>(_memberService.LoadCustomers());
MemberView = CollectionViewSource.GetDefaultView(Members);
All works fine until I update a status on a member to 'Live' from another viewmodel.
I then try and search on this recordset but the record never turns up until I restart the application.
I am using MVVMLight and the messenger to trigger the reload (Not really important here as the messagebox confirms its running correctly)
private void NotificationMessageReceived(NotificationMessage msg)
{
if (msg.Notification == "ReloadClients")
{
Members = _memberService.LoadCustomers();
MemberView.Refresh();
RunFilter();
MessageBox.Show("Clients Reloaded");
}
}
Am I going about this the correct way? Any help will be very much appreciated.
Thanks Scott

WPF MVVM PropertyChanged notifications in the ViewModel triggered by Model Events

I am having a problem understanding how to propagate a property changed event in a Model class up through the ViewModel and into the view. I am trying to conform to the MVVM pattern so please keep that in mind.
I have a Model that I am trying to expose by the ViewModel. My Model class queries an Api call to get the server status and exposes that status in public properties. Ex:
public class ServerStatusRequest : ApiRequest
{
//Exposable properties by request
public ServerStatusHelperClass Status { get; set; }
Where ServerStatusHelperClass is just a wrapper for the combined results in the query:
public class ServerStatusHelperClass
{
public bool ServerStatus { get; set; }
public int OnlinePlayers { get; set; }
The cool thing about my ApiRequest base class is that it checks the cache time of a particular Api call and updates the Results by using a System.Timers.Timer. So, for example, the ServerStatus Api call is cached for 3 minutes on the Api, so every 3 minutes my ServerStatusApiRequest object will have fresh data for it. I expose a UpdatedResults event in all ApiRequest classes to notify when new data comes in.
Now I want my ViewModel to have an instance of ServerStatusApiRequest and bind to its ServerStatusHelperClass Status property and stay up to date with the changes every time the information is updated, but my view (for binding) can't know about my model, and thus, doesn't know about my UpdatedResults event in my ApiRequest class. How can I reflect that out to the View through my ViewModel? Am I doing something completely weird here?
Here is what I have that is semi-working but I feel is a very hacky solution:
In my ViewModel:
public const string EveServerStatusPropertyName = "EveServerStatus";
private ServerStatusRequest _eveServerStatus = new ServerStatusRequest();
public ServerStatusRequest EveServerStatus
{
get
{
return _eveServerStatus;
}
set
{
//if (_eveServerStatus == value)
//{
// return;
//}
//RaisePropertyChanging(EveServerStatusPropertyName);
_eveServerStatus = value;
RaisePropertyChanged(EveServerStatusPropertyName);
}
}
public void UpdateEveServerStatus(object sender, EventArgs e)
{
EveServerStatus = (ServerStatusRequest)sender;
}
And in the ViewModels constructor I subscribe to the Model's event:
EveServerStatus.UpdatedResults += new UpdatedResultsEventHandler(UpdateEveServerStatus);
As you can see, this seems extremely redundant. And I also ran into a problem where I had to comment out the check in the setter for EveServerStatus because at that point the _eveServerStatus was already updated to value just without it knowing and I wanted to fire the event anyway.
I fell like I'm missing a key concept here to link this all together much more easily.
Thanks for any input.
I have come across a much better way to implement the behavior I was looking for. Here is the code in my ViewModel:
private ServerStatusRequest _eveServerStatus = new ServerStatusRequest();
public ServerStatusRequest EveServerStatus
{
get
{
return _eveServerStatus;
}
}
No setter as my ViewModel nor my View should be changing this data. And Inside my ServerStatusRequest class I have a property exposing the ServerStatusHelperClass object as shown in the Question. I have changed the ServerStatusHelperClass and made it implement INotifyPropertyChanged as so:
public class ServerStatusHelperClass : ObservableObject
{
private bool _serverStatus;
public bool ServerStatus
{
get
{
return _serverStatus;
}
set
{
_serverStatus = value;
RaisePropertyChanged("ServerStatus");
}
}
...
ObservableObject is just a simple class that implements INotifyPropertyChanged for me from mvvmlight.
By doing this my View is automatically updated when my ApiRequest class modifies it's ServerStatusHelperClass object.
Input on this solution is welcome.

Categories

Resources