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
Related
I'm currently preparing core framework for our upcoming project based on WinForms and MVP design pattern.
I'm not sure, what would be the best way, how to communicate between two Views/Presenters. To be more specific - I have a ListView and a DetailView. When user clicks on an item in the ListView, I need to display edit form for this item, which in my case is DetailView.
Options:
Should ListPresenter create DetailPresenter on the click event? (A Factory could be of a help.)
Should instance of DetailPresenter be injected in ListPresenter constructor?
I feel 2) might be the "right" solution, but I would prefer creating DetailView/DetailPresenter just in time I really need it - i.e. when user clicks the button.
The next problem, I don't know how to go about it is the objects lifetime. When I inject a View into Presenter, who is responsible for the cleanup? I'm used to behaviour, where cleanup is made by the same party who created it. But in this case I could imagine View could be disposed by the Presenter.
I hope the questions are not too generic, I have read a lot of articles about MVC/MVP, but they mostly don't go further than showing how to implement single View-Presenter communication.
Thank you.
You could wrap the ListPresenter and the DetailPresenter in a ListDetailPresenter.
public class ListDetailPresenter
{
private ListPresenter _listPresenter;
private DetailPresenter _detailPresenter;
public ListDetailPresenter()
{
_listPresenter = new ListPresenter();
_detailPresenter = new DetailPresenter();
_listPresenter.SelectionChanged += OnSelectionChanged;
}
private void OnSelectionChanged(object sender, EventArgs e)
{
_detailPresenter.SetItem(_listPresenter.SelectedItem);
}
}
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.
I want to update my UI. Should I use BackgroundWorker? Do I put the BackgroundWorker in the MainWindowViewModel and instantiate the repositories again, or do I put it in the OrdersQueueViewModel and do something with the properties?
The UI just displays the contents of lists created by LINQ. The lists are ObservableCollection and are properties of the OrdersQueueViewModel. I have a ViewModel MainWindowViewModel that creates a collection ViewModels, so that I can bind to that collection from the MainWindow.xaml (view).
MainWindowViewModel.cs:
public MainWindowViewModel()
{
_printQueueRepos = new OrdersPrintQueueRepository();
_holdQueueRepos = new OrdersHoldQueueRepository();
_linesToPickRepos = new LinesToPickRepository();
_linesPerHourRepos = new LinesPerHourRepository();
//create an instance of viewmodel and add it to the collection
OrdersQueueViewModel viewModel = new OrdersQueueViewModel(_printQueueRepos, _holdQueueRepos, _linesToPickRepos, _linesPerHourRepos);
this.ViewModels.Add(viewModel);
}
MainWindow.xaml:
<Window.Resources>
<DataTemplate DataType="{x:Type vm:OrdersQueueViewModel}">
<vw:OrdersQueueView></vw:OrdersQueueView>
</DataTemplate>
</Window.Resources>
Example of a property in the OrderQueueViewModel that uses a repository:
public ObservableCollection<LinesToPick> LinesToPick
{
get
{
return new ObservableCollection<LinesToPick>(_linesToPickRepos.GetLinesToPick());
}
}
So I haveLinesToPick bound in the OrdersQueueView, and as the database updates the lists should change in the UI. I'v spent some time reading about BackgroundWorker, but I'm not quite sure what to do to update the lists. I'm hoping because they are ObservableCollections I can just "refresh" them and they will use INotifyPropertyChanged and update the UI automatically. Very new to all this, trying to get my head around it, thanks in advance for any help.
EDIT: Using James's suggestion I have ended up with this In my OrdersQueueViewModel. However I am getting the error "This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread", when the code gets to .Clear() on the 2 lists, which is what I thought the dispatcher was used for. Any suggestions?
Action workAction = delegate
{
_worker = new BackgroundWorker();
_worker.DoWork += delegate
{
LinesThroughput.Clear();
LinesToPick.Clear();
//refresh LinesToPick
foreach (var item in _linesToPickRepos.GetLinesToPick())
{
LinesToPick.Add(item);
}
//refresh LinesThroughput
List<LinesThroughput> Lines = new List<LinesThroughput> (_linesPerHourRepos.GetLinesThroughput());
foreach (var item in GetLinesThroughput(Lines))
{
LinesThroughput.Add(item);
}
};
_worker.RunWorkerAsync();
};
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Normal, workAction);
You can do it either way - in the MainWindowViewModel or one of the child view models. I would choose based on which way produces lower coupling and higher cohesion between components. (Lower coupling - fewer dependencies. Higher cohesion - things go together that belong logically together.)
And BackgroundWorker is a reasonable technique. Just remember to dispatch to the UI thread to update the collection. As for your ObservableCollection code... That needs some work. Don't reinstantiate the ObservableCollection. Do something like this:
public ObservableCollection<LinesToPick> LinesToPick { get; private set; } // Don't forget to nstantiate in ctor
public void Refresh()
{
LinesToPick.Clear();
foreach(var item in _linesToPickRepos.GetLinesToPick())
{
LinesToPick.Add(item);
}
}
By keeping the same ObservableCollection that was databound, your UI will automatically pick up changes to the collection. If you replace the collection, you lose the binding to it and your UI won't update until you notify it that the property containing the collection changed. Much easier to just keep the same collection.
Greetings! Am enjoying using MVVM light -great framework - has made my life much easier, and has removed a number of barriers that were proving difficult to overcome....
Question:
I am attempting to setup a custom dialog box for editing messages users send to each other. I am attempting to construct a silverlight custom dialog box using the ChildWindow object using the MVVM framework.
Was wondering if there were any suggestions as to how this might be accomplished
Following the dialog MVVM sample code I found here: http://mvvmlight.codeplex.com/Thread/View.aspx?ThreadId=209338 I got stuck because the ChildWindow dialog object in Silverlight is async, and has a different Result class.
So - the Basic idea I have now is using the view model of the class (in this case the Matrix.MessageViewModel) to create an instance of the custom dialog box, send it through the Messenger.Send<>, process the registered message in the view to display the dialog, then have the ChildWindow dialog box's Save button handler fire a Messenger.Send with the modified contents that is then stored using the Save method on the viewmodel...
Seems a bit round-about - so wanted to make sure there wasn't a cleaner way....
Relevant code bits:
view model:
messageDialogBox = new MessageEditorDialog(
selectedMessage, this.SelectedSiteId, this.LoggedOnEmployee.Id, this.Projects);
DialogMessage editMessage = new DialogMessage(
this, messageDialogBox,"Edit Message", DialogMessageCallback);
Messenger.Default.Send(editMessage);
View:
public ViewHost()
{
InitializeComponent();
Loaded += new RoutedEventHandler(ViewHost_Loaded);
if (!ViewModelBase.IsInDesignModeStatic)
{
// Use MEF To load the View Model
CompositionInitializer.SatisfyImports(this);
}
ApplicationMessages.IsBusyMessage.Register(this, OnIsBusyChange);
Messenger.Default.Register<DialogMessage>(this, msg => ShowDialog(msg));
}
private void ShowDialog(DialogMessage msg)
{
MessageEditorDialog myDialog = (MessageEditorDialog) msg.Target;
myDialog.Show();
}
Dialog Save:
private void ButtonSave_Click(object sender, RoutedEventArgs e)
{
Messenger.Default.Send<Message>(
this.MessageItem, CommandMessages.MessageTypes.MessageSave);
}
This ties back into the ViewModel, that has a Messenger.Default.Register<> watching for the CommandTypes.MessageSave which routes the resulting MessageItem to the model for storage.....
That's pretty darn close to what I'd do, except there are a couple of things I do differently.
I'd have a view model for my dialog view, and move the messaging logic to it rather than the view's code behind.
I'd use a Save command in my view model, and bind the ButtonSave to that command. That moves the save logic to the view model instead of the code behind of your view.
You're using a different message when the save button is clicked. Also, you're not using the DialogMessage's callback. Assuming you change to using a Save command, you could save the message in a private member in the view model, then use message's callback when the user saves.
You may want to think about re-using the dialog view, or ensuring that the view is being cleaned up correctly so you don't end up with a memory leak.
Here's the changes I'd make to your view model following suggestions 2 & 3.
public class MessageEditorDialogViewModel : ViewModelBase
{
private DialogMessage _dialogMessage;
public RelayCommand SaveCommand { get; private set; }
public DialogMessage Message { get; set; }
public MessageEditorDialogViewModel()
{
SaveCommand = new RelayCommand(SaveCommandExecute);
}
private SaveCommandExecute()
{
Message.Execute();
}
}
In a WPF app, is there a object I can assign to FileSystemWatcher.SynchronizingObject?
I can make my own, but if there is one available, I would like to use it.
Reflector shows that the only class that implements ISynchronizeInvoke (i.e., the type of the FileSystemWatcher.SynchronizingObject property) is System.Windows.Form.Control (and its subclasses); there do not appear to be any WPF objects that implement this interface.
You need to create an ISynchronizeInvoke object that wraps the System.Windows.Threading.Dispatcher instance from the Window. That class is the closest thing WPF has to an ISynchronizeInvoke object.
Inside the wrapper class, simply forward the BeginInvoke call to the dispatcher you've wrapped. I did a bit extra work and also wrapped the DispatcherOperation that results from the Dispatcher.BeginInvoke method to call its Wait method inside the ISynchronizeInvoke.EndInvoke method.
Everything seems to be working correctly so far, it's too bad Microsoft didn't see fit to have the Dispatcher class implement the interface for ease of use.
There is one way. FileSystemWatcher when you enabling events (EnableRaisingEvents = true) creates it's own thread to monitor the FS events. Via ISynchronizeInvoke it can async invoke members of your Form for example, (it's thread can async interact with the main thread - UI thread).
In WPF there's no implementation of ISynchronizeInvoke, but there is a possibility to
interact with the UI thread, via Dispatched property of your Window like this:
var fsw = new FileSystemWatcher()
{
//Setting the properties: Path, Filter, NotifyFilter, etc.
};
fsw.Created += (sender, e) =>
{
Dispatcher.Invoke(new Action<params_types>((params_identifiers) =>
{
//here the code wich interacts with your IU elements
}), here_params);
};
//... in this way (via Dispatcher.Invoke) with the rest of events
fsw.EnableRaisingEvents = true;
Use the DispatcherTimer rather than the system timer.
This will work fine for WPF.
DispatcherTimer t1 = new DispatcherTimer();
private void Window_Loaded(object sender, RoutedEventArgs e)
{
t1.Interval = new TimeSpan(0,0,0,0,200);
t1.Tick += new EventHandler(t1_Tick);
t1.Start();
...
void t1_Tick(object sender, EventArgs e)
{
//some work
}