I have a silverlight application that uses WCF, and I would like to make a WCF call to load some data before the usercontrol is loaded so that I could use the data with an autocompletebox. Any suggestions as to how to accomplish this?
Not sure if your user interface will be suitable to use a loading indicator or a progress bar. If you can use loading indicator, then it might be a good option to display the busy / loading indicator while the async call is in progress. That would disable the user from clicking on the dropdown or any other control while the data is being retrieved from the WCF service.
You can do the async call in the Application_Startup() method of your App.xaml.cs file, and set RootVisual in your async callback instead of in Application_Startup().
All WCF service calls in Silverlight are asynchronous. I've learned to use Lambdas and a very useful class called Action (which is a wrapper for an event and a delegate).
Using the application startup as RobSiklos suggested would work great for getting it before showing the control. This shows the code that could also work inside of the userControl loaded event, incorporating a loading overlay (you could use a border with centered text that goes over the whole app or a Silverlight toolkit control). This approach will give more immediate feedback to the user, especially if your data service call will take a longer time.
public MyUserControl : UserControl
{
public MyUserControl()
{
this.Loaded += new RoutedEventHandler(View_Loaded);
}
void View_Loaded(object sender, RoutedEventArgs e)
{
// start showing loading overlay
MyService service = new Service(...);
service.GetDataCompleted += (o, args) =>
{
var data = args.Results;
// hide loading overlay
}
}
}
Related
I am trying to learn MVVM with MVVM Light Toolkit in WPF. But I am stuck on one simple problem.
I have an AddEditProfileWindow which basically has a textbox for profile name and a confirm button. It adds new profile to database table or updates name of existing profile.
In MainWindow/MainViewModel I have a list of profiles and two buttons: "Add Profile" and "Edit Selected Profile". They both open this window via commands + messages. For example here is command for the "Add Profile" button
public RelayCommand OpenAddProfileWindowCommand
{
get
{
return _openAddProfileWindowCommand ?? (_openAddProfileWindowCommand = new RelayCommand(
() => { Messenger.Default.Send(new NotificationMessage("OpenAddProfile")); }));
}
}
and it's receiver in MainWindow code behind
private void MessageReceived(NotificationMessage msg)
{
if (msg.Notification == "OpenAddProfile")
{
var window = new AddEditProfileWindow();
window.Owner = this;
window.ShowDialog();
}
}
So the problem is that I need to somehow pass a parameter to the AddEdit... Window/ViewModel (set IsEditing bool property in ViewModel for example) to change window behavior and customize it a bit (change title and the confirm button text to "Add" or "Update"). Also for updating I need Profile object (or at least Id) of selected record.
For creating ViewModels I use ViewModelLocator and Unity
public ViewModelLocator()
{
var container = new UnityContainer();
ServiceLocator.SetLocatorProvider(() => new UnityServiceLocator(container));
container.RegisterType<MainViewModel>(new ContainerControlledLifetimeManager()); // singleton
container.RegisterType<AddEditProfileViewModel>();
}
public AddEditProfileViewModel AddEditProfile
{
get
{ return ServiceLocator.Current.GetInstance<AddEditProfileViewModel>(); }
}
I have read a lot of similar threads and examples but still don't understand how should I pass parameters to view models. Some answers suggest creating view models on app startup (and make them singletons) in the ViewModelLocator and then I can send message before opening. But looks like not very clean and also I will need to reset view models before opening (via Cleanup() probably).
Is there any better/easier/cleaner approach?
In my opinion, Messenger and getting AddEditProfileViewModel from IoC are not suitable in this scenario. First you send message from a UI's DataContext to UI. Messenger works between loosely coupled components and usually on the same level, view model and view model for example. If you want view model to notify view, you can use InteractionRequest from Prism. Second, AddEditProfileViewModel can be considered as a temporary, based on its view is a modal dialog, so its creation might depend on the environment that creates it.
One approach, using shared service, maybe called IDialogService, which has a method might called ShowAddEditDialog. Your main view model gets this service from IoC and calls it when executing command, add/edit. When calling the method, main view model also creates AddEditProfileViewModel and passing states, such as add/edit, existing profile, etc.
Another approach, using application controller, if you still want to keep Messenger and IoC. You still can use Messenger here but it is not the view who listens to messages, instead it is an application controller. Now, application controller, main view model, AddEditProfileViewModel and AddEdit window are all resolved from IoC container. The application controller holds both view models and listen to the message. When it got message from main view model, it updates states on AddEditProfileViewModel, resolve dialog, set DataContext and show the dialog. You can put the application controller instance in MainWindow code behind or anywhere since once it gets resolved from IoC, it is autonomous.
This is a side-related question to this other question:
BackgroundWorker in Silverlight ViewModel
I have a TabControl where I load many TabItems when the user selects menu options. I load this Tabs by binding the TabControl ItemsSource to an ObservableCollection. When I add a new TabItem to this Collection, it is shown perfectly.
The problem is I've realized that since user press a button until the tab is created (ViewModel and View creation takes a couple of seconds), the screen is freezed.
I've tried to set "IsBusy" before calling the "loadTab" but it doesn't shows up... I've tried almost everything with async calls but the UI thread is in use and it throws an exception when I create the new tab control.
Is there any trick I'm loosing??? Any ideas??? Thanks in advance.
have you seen this post?
http://www.dotnetspark.com/kb/3524-doesnt-your-girlfriend-deserves-more-time.aspx
It helps when you avoid heavy stuff in the load event and make Visible=true after you finish to load all your resources, so in that sense you avoid the user feeling tempted to click something that is not ready yet.
Not sure if it helps, but how about this idea?
public void DoStuff(Object values)
{
//your values object could be anything,
//they might even be some objects from your form
//as long as you dont modify them in the other thread
imgLoading.Visible=true;
var client = new Proxy();
client.OnWorkCompletedAsync +=client_OnCompleted() ;
client.Work(values);
}
void client_OnCompletedAsync(object sender, EventArgs e)
{
imgLoading.Visible=false;
//now you can update the UI with other stuff
}
I use a browse for files dialog to allow a user to select multiple images. If a lot of images are selected, as expected it takes a bit. Below is an example of what I do with the selected images. I loop through the filepaths to images and create an instance of a user control, the user control has an Image control and a few other controls. I create the instance of this control then add it to a existing stackPanel created in the associating window xaml file. The example just below works fine, but I'm trying to understand BackGroundWorker better, I get the basics of how to set it up, with it's events, and pass back a value that could update a progress bar, but because my loop that takes up time below adds the usercontrol instance to an existing stackPanel, It won't work, being in a different thread. Is BackGroundWorker something that would work for an example like this? If so, what's the best way to update the ui (my stackpanel) that is outside the thread. I'm fairly new to wpf and have never used the BackGroundWorker besides testing having it just update progress with a int value, so I hope this question makes sense, if I'm way off target just let me know. Thanks for any thoughts.
Example of how I'm doing it now, which does work fine.
protected void myMethod(string[] fileNames) {
MyUserControl uc;
foreach (String imagePath in fileNames) {
uc = new MyUserControl();
uc.setImage(imagePath);
stackPanel.Children.Add(uc);
progressBar.Value = ++counter;
progressBar.Refresh();
}
}
below this class i have this so I can have the progressBar refresh:
public static class extensionRefresh {
private static Action EmptyDelegate = delegate() { };
public static void Refresh(this UIElement uiElement) {
uiElement.Dispatcher.Invoke(DispatcherPriority.Background, EmptyDelegate);
}
}
Check out this article on
Building more responsive apps with the Dispatcher
Now that you have a sense of how the Dispatcher works, you might be surprised to know that you will not find use for it in most cases. In Windows Forms 2.0, Microsoft introduced a class for non-UI thread handling to simplify the development model for user interface developers. This class is called the BackgroundWorker
In WPF, this model is extended with a DispatcherSynchronizationContext class. By using BackgroundWorker, the Dispatcher is being employed automatically to invoke cross-thread method calls. The good news is that since you are probably already familiar with this common pattern, you can continue using BackgroundWorker in your new WPF projects
Basically the approach is
BackgroundWorker _backgroundWorker = new BackgroundWorker();
// Set up the Background Worker Events
_backgroundWorker.DoWork += _backgroundWorker_DoWork;
_backgroundWorker.RunWorkerCompleted += _backgroundWorker_RunWorkerCompleted;
// Run the Background Worker
_backgroundWorker.RunWorkerAsync(5000);
// Worker Method
void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// Do something
}
// Completed Method
void _backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Doing UI stuff
if (e.Cancelled)
{
statusText.Text = "Cancelled";
}
else if (e.Error != null)
{
statusText.Text = "Exception Thrown";
}
else
{
statusText.Text = "Completed";
}
}
Using a BackgroundWorker alone won't solve your issue since elements created during the DoWork portion will still have originated from a non-UI thread. You must call Freeze on any objects you intend to use on another thread. However only certain UI objects will be freezable. You may have to load in the images as BitmapImages on the background thread, then create the rest of your user control on the UI thread. This may still accomplish your goals, since loading in the image is probably the most heavyweight operation.
Just remember to set BitmapImage.CacheOption to OnLoad, so it actually loads up the image when you create the object rather than waiting until it needs to be displayed.
Hi I try solve this situation. I have WPF app with MVVM design. I use Caliburn Micro framework and on injection MEF.
In WPF app I use service from external assembly. It works good.
Problem is. I bind observable dictionary to listbox. Listbox can consist from 0 to 400 items.
I have data template on listbox item it consist with image and som texbox. Listbox is like
contact list in skype or google talk.
I call every 3-4 sec method from service, wich returns new data as dictionary. An with this data aj refresh Listbox.
My code look in view model like this:
private DispatcherTimer _dispatcherTimer;
private MyObservableDictionary<string, UserInfo> _friends;
//temp
private MyObservableDictionary<string, UserInfo> _freshFriends;
//bind on listbox
public MyObservableDictionary<string, UserInfo> Friends
{
get { return _friends; }
set
{
_friends = value;
NotifyOfPropertyChange(() => Friends);
}
}
//in constructor of view model I have this:
_dispatcherTimer = new DispatcherTimer();
_dispatcherTimer.Tick += DispatcherTimer_Tick;
_dispatcherTimer.Interval = TimeSpan.FromSeconds(3);
_dispatcherTimer.Start();
// on timer tick I call method from service
private void DispatcherTimer_Tick(object sender, EventArgs eventArgs)
{
//get new data from server
//method GetFriends take much of time
_freshFriends = _service.GetFriends(Account);
//delete old data
_friends.Clear();
//refresh
foreach (var freshFriend in _freshFriends)
{
Friends.Add(freshFriend);
}
}
As I said, problem is that method GetFriends from service take much of time and my app freezes.
How can solve this problem? In winforms app I use background worker, but this is my first WPF app with MVVM. It exist any "patern" or "design" how call method which consume much of time in view model class? Call this method in another thread?
As others have suggested, you can use a BackgroundWorker in a WPF app, or if you are using .NET 4, then use the Task Parallel Library. Stephen Cleary has a nice post on the TPL compared to BackgroundWorker here - http://nitoprograms.blogspot.com/2010/06/reporting-progress-from-tasks.html
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