Working with a listbox in windows phone 7 I am trying to make an async web service call then update the listbox on success.
The method that calls the webservice looks like this:
public void GetReadingList(Action<ObservableCollection<MiniStoryViewModel>> success, Action<string> failure)
I am calling the method with this code:
api.GetReadingList(
(items) => Dispatcher.BeginInvoke(() =>
{
lsbNewest.ItemsSource = items;
}),
(error) =>
{
MessageBox.Show(error);
});
Using this code nothing happens ui wise until I click or scroll on the listbox - then its contents is updated correctly. I am assuming that the code is not being run on the correct thread, how can i fix this?
No errors in your code, you are right regarding the spec...
Try : flush the ItemsSource, then fill it with the items with addRange, or anything else.
Check if your UI is automatically updated.
If not, create a basic silverlight app on Windows, and compare the two behaviours... maybe a bug ;=)
I solved the issue I was having by implementing inotifypropertychanged on my view model and raising the property changed event on the ui thread.
Related
I have a XAML page whose DataContext is set to my ViewModel. A switch control on the page is bound to the following code in the ViewModel:
public bool TeamLiveTileEnabled
{
get
{
return Data.Subscriptions.Any(s => s.TeamName == this.Team.Name);
}
}
When this page is initialized, Data.Subscriptions is an empty list. I retrieve the list of subscriptions through an async web service call, so it comes back after the getter above is called.
When the web service call comes back, Data.Subscriptions has items added to it, and I'd like the UI to update based on the new result of the LINQ expression. Right now nothing happens, and I confirmed that Data.Subscriptions contains items that satisfy the condition above.
Data.Subscriptions is an ObservableCollection of Subscription items.
Can someone point me to what to do? Thanks!
The problem is that your ViewModel is not aware of any changes to the ObservableCollection. Within the ViewModel, subscribe to the CollectionChanged event of Data.Subscriptions.
Data.Subscriptions.CollectionChanged += SubscriptionsChangedHandler;
Within the event handler notify listeners of TeamLiveTileEnabled by sending a PropertyChanged notification
NotifyPropertyChanged( "TeamLiveTileEnabled" );
I'm developing a Windows Forms GUI in a design pattern which is a combination of MVP and MVVM.*
I'm binding the View to the View Model using code: the view subscribes to the VM's PropertyChanged event. If the presenter sets some property of the VM, the event is raised and the View executes its callback.
So every callback in the View needs to be wrapped in this.Invoke( { ... } ).
I wish there was a way to define a property whose setter will automatically be invoked in the UI thread. Some way to write an auto-delegating property or method.
I tagged this question with WPF as well, because I'm using C# 4.0, so if such a mechanism exists in WPF, I'll be happy to use it for Windows Forms.
*I'm using MVVM because the application will probably be converted to WPF at some stage.
I suddenly realized that the IoC Framework I'm using, Castle Windsor, can give me exactly that. I just attached an interceptor to all the methods of the form, and the interceptor does this:
if (invocation.InvocationTarget.InvokeRequired)
{
invocation.InvocationTarget.Invoke(invocation.Proceed);
}
else
{
invocation.Proceed();
}
Later I modified the above code by putting it in an extension method with the following signature:
public void InvokeIfRequired(this Control uiObject, Action action)
To define a property setter that always gets invoked on the UI thread, you could do something like this:
public int MyPropertyForTheUIThread
{
get
{
return this.myRealValue;
}
set
{
if (this.myRealValue != value)
{
this.myRealValue = value;
this.Invoke((Action)(()=>
{
// The stuff I want to do on the UI thread when this property changes.
});
}
}
}
I'm using Prism v4 , and MVVM.
In my viewmodel i have:
private TB_COMPANY tb;
public TB_COMPANY Tb {
get {
return this.tb;
}
private set {
if ( this.tb != value ) {
this.tb = value;
this.RaisePropertyChanged(() => this.Tb);
}
}
}
In my Page, i have a datagrid (i tried with a listview too, don't work!):
<DataGrid ItemsSource="{Binding Tb.TB_ADDRESS.RL_ADDRESS_PHONE}" .../>
RL_ADDRESS_PHONE is a list of phones of the company...
So, in some moment i add phone to the list:
private void MyCommand()
{
...
Tb.TB_ADDRESS.RL_ADDRESS_PHONE.Add(
new RL_ADDRESS_PHONE
{
TB_PHONE = new TB_PHONE
{
NU_PHONE = _txtTelefone,
ST_TYPE = _txtTipoTelefone
}
});
...
}
But nothing happens to the UI...
But in the Debug, the list is fullfiled....
What should i do to update the UI?
The RL_ADDRESS_PHONE property of TB_ADDRESS must implement INotifyPropertyChanged as well. If it is a List, you should use ObservableCollection which implements INotifyCollectionChanged which is necessary for the behaviour you are trying to achieve.
Also, as a piece of advise - have a look into the design guidelines and naming convention of the C# language, you can find it in the msdn (while it says .net 1.1, it still applies for any following versions of the framework).
Does your binding work? Have you checked the Output window? Are all the properties on your binding path public? Needs more code.
All property notifications aside: If your binding finds the ObservableCollection it will update if you add objects. So i think you might be using a field or a non-public property somewhere on the path.
If this is not the case make sure you do not overwrite the reference to the ObservableCollection unless you have a proper PropertyChanged notification in place. (You should implement that interface (INotifyPropertyChanged) in every class on your binding path since every overwritten reference that the view is not notified of will break your update).
You mentioned the use of a WCF service. Is it possible that your observable collection is being created on a different thread to the UI thread. If this is the case you UI will not be able to bind to it. Bea Stollnitz discusses issues with cross thread binding on her blog here -> http://bea.stollnitz.com/blog/?p=34.
You should be able to get round it by invoking the creation of the observable collection onto the UI thread.
I have a few questions regarding to building WPF MVVM applications.
1) I'm using ICollectionView objects for databound controls such as ListView and ComboBox. I found this was the simplest way of gaining access to/tracking the selected item of these controls. What is the best way to replace the contents of ICollectionView? Currently I'm doing it like so:
private ICollectionView _files;
public ICollectionView Files {
get { return _files; }
}
void _service_GetFilesCompleted(IList<SomeFile> files) {
this.IsProcessing = false;
_files = CollectionViewSource.GetDefaultView(files);
_files.CurrentChanged += new EventHandler(FileSelectionChanged);
OnPropertyChanged("Files");
}
I didn't know whether it was necessary to reattach the handler every time I refresh the list of files?
2) Now that I've got my head round it, I am starting to like the MVVM pattern. However, one concept I'm not completely sure about is how to send notifications back down to the view. Currently I am doing this by binding to properties on my ViewModel. For example, in the above code I have an "IsProcessing" property that I use to determine whether to display a ProgressBar. Is this the recommended approach?
3) Following on from 2) - there doesn't seem to be a standard way to handle exceptions in an MVVM application. One thought I had was to have one method on my ViewModel base class that handled exceptions. I could then inject an IMessagingService that was responsible for relaying any error messages. A concrete implementation of this could use MessageBox.
4) I have a few tasks that I want to perform asynchronously. Rather than adding this logic directly in my service I created a decorator service that runs the underlying service methods on a new thread. It exposes a number of events that my ViewModel can then subscribe to. I have listed the code below. I understand that BackgroundWorker is a safer option but did not know whether it was suitable for running multiple asynchronous tasks at once?:
public void BeginGetFiles()
{
ThreadStart thread = () => {
var result = _serviceClient.GetUserFiles(username, password);
GetFilesCompleted(result.Files);
};
new Thread(thread).Start();
}
Finally, I realize that there are a number of MVVM frameworks projects that handle some of these requirements. However, I want to understand how to achieve the above using built-in functionality.
Thanks
If you have ListViews and ComboBoxes, you should really be considering an ObservableCollection<> to bind to these controls. Adding and removing items from the collection will automatically notify the control the property has changed.
If you're doing background processing, you can look at the BackgroundWorker or DispatcherTimer to handle updates to the UI. These both have the capability of acting on the UI thread, and can be thread safe.
To get the selected item from a combo box, expose an INotifyCollectionChanged object such as ObservableCollection and bind it to the itemsource, then create another property for the current item and binding ComboBox.SelectedItem (or ComboBox.SelectedValue if required) to it. You will need to manage the selection when updating the collection.
On the face of it, ICollectionView seems like the obvious choice but the WPF implementation really forces your hand on some threading code that you really shouldn't be troubled with.
I used ICollectionView and CollectionViewSource recently (for filtering) and have become frustrated with how many dispatcher issues have crept in. Today I am probably going to revert to the method i describe above.
I am new to MVVMLight and have started to use it in my WP7 app. I have a View/page which registers for MessageDialogs and then my VM sends the message to show it. This works great. However, when you go back to the previous screen (with WP7 back button) and then enter the page again (using AppBar menu item) then the message fires twice (and increments every time you view the page). I assume it the View is registering every time and old versions are subscribing to the message, but I am not sure of how it should work.
I tried to call VM.Cleanup in my NavigatedFrom event to ensure the old messages are unregistered when they leave the page, but this did not help. Here is my code:
View:
public AboutPage()
{
InitializeComponent();
Messenger.Default.Register<DialogMessage>(this, msg => { var result = MessageBox.Show(msg.Content, msg.Caption, msg.Button); });
}
protected override void OnNavigatedFrom(NavigationEventArgs args)
{
ViewModelLocator.AboutViewModelStatic.Cleanup();
base.OnNavigatedFrom(args);
}
AboutViewModel: (Code gets fired by a command)
var message = new DialogMessage("Why does this fire multiple times?", DialogMessageCallback) { Button = MessageBoxButton.OK, Caption = "" };
Messenger.Default.Send(message);
That's all there is too it, but each time you come to this page it fires once more... I assume it is something to do with Cleanup but I am not sure how it is supposed to work in WP7... any tips appreciated...
A view is created & destroyed as you navigate through the application. Therefore, in your AboutPage view's constructor, the view is registering for the message every time it is created.
A better approach is to setup the registration in the ViewModel's constructor, use a ViewModelLocator and databind the View to the ViewModel. The ViewModel is created once and used throughout the lifetime of the application. Jonas Follesoe's FlightsNorway is good WP7 application to learn about MVVMLight, you can find the MVVMLight Messenger class being used very nicely.
HTH, indyfromoz