I'd like to load Xaml from code running in a background thread.
I understand I would have to sync with the dispatcher. However, it fails (throws an exception).
Why?
Here is the code
public MainWindow()
{
InitializeComponent();
Thread thread = new Thread(new ThreadStart(delegate
{
Dispatcher.Invoke(new Action(delegate
{
Content = XamlReader.Parse(
"<Button xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
Content='Hello World'/>");
}));
}));
thread.Start();
}
As Pavlo mentioned, you need to also set your content within the Dispatcher.
However, I will say - this is fairly useless.
Remember, when you call Dispatcher.Invoke or BeginInvoke, you're explicitly saying to run that code on the UI thread. By starting a background thread that does nothing but invoke back to the UI thread, you're effectively doing the same work on the UI thread, with the disadvantage of extra overhead being added to the system as well as harder debugging. In this case, you should just load the file directly.
This seems like a bad idea for a couple reasons. Are you expecting to get XAML fragments from a database or some other storage and you can't create the instances of these controls in C#? You could just create a Button directly.
Are you able to allow the data to drive your visualization? A good example of this is having a collection of ICommand objects (CommandViewModel, RelayCommand, etc) and a CommandView that you want to use to represent your command? In this case, it could be a <Button> with a binding to the CommandViewModel Title or Content property.
You're background thread could then be used to drive the population of data (collections, properties) and you're UI would be designed to flexibly accommodate the expected data patterns.
Related
I have a helper class I've written which can be used to run a long running task on my GUI. What it does is use styles to display a "working" animation and fades out the content so while the task is running, the user can see that something is in progress.
My problem is that when the long running task completes, it fades the content back in and hides the working animation - which is what it should do, but because I am using MVVM and primarily data binding for all my content display, the updates to the GUI components happen separately to the long running task. ie the data binding OnPropertyChanged("") events fire and then these are picked up by the GUI thread AFTER the long running task completes. But the problem is the Worker Animation closes when the long running task completes, but BEFORE the data bindings update.
So the end result is you get the worker animation displaying as expected while the task runs, but the data binding update takes a good 4-5 seconds or even longer for large datasets for all the tree data and during this time, the application is not in "working animation mode" and just freezes.
Is there a way I can have my worker animation continue to run not only for the Long running Method, but for the associated data binding updates from OnPropertyChanged as well?
Consider using BusyIndicator from Extended WPF toolkit. It should provide functionality you described. It has IsBusy property which you can bind to property in your ViewModel and set it to False after all work is done.
You can always change the style of BusyIndicator same way as you do with other controls. In my solutions I always use this control along with BackgroundWorker class from System.ComponentModel and I usually set IsBusy=false at the end of RunWorkerCompleted
private void LongRunningMethod()
{
this.IsBusy = true;
var worker = new BackgroundWorker();
worker.DoWork += this.LongMethodDoWork;
worker.RunWorkerCompleted += this.RunWorkerCompleted;
worker.RunWorkerAsync();
}
private void LongMethodDoWork(object sender, DoWorkEventArgs doWorkEventArgs)
{
...
}
private void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs runWorkerCompletedEventArgs)
{
...
this.IsBusy = false;
}
Thanks all for the answers. I've actually come across a solution that may be a bit controversial as some would construe it is a little bit of a hack, but it does exactly what I want it to do and there seems to be no other way to do it, so to me that is a code solution, not a hack.
I'm using the WPFBackgroundProgressIndicator open source project I downloaded from codeproject (I think) which has the option to show the busy indicator in the main content with or without a fade out, or as a popup and it runs as a background thread which is ideal and why I chose it.
The problem was that when you run a long running method, the code execution completes synchronously but all the binding OnPropertyChanged("") updates run asychronously and queue on the Dispatcher thread, so your work method completes before the WPF controls have a chance to call the Getters of the dependency properties, to retrieve the new value. What you need to do is effectively "block" until all the Dispatcher events have completed and that is why not everyone will like this solution as it "blocks", but then that is exactly what I am trying to do. I WANT to block the application until the full update has completed as I dont want the user to be able to do anything visually while data is still rendering, so that is my requirement. Clean blocking is preferable to messy interaction.
So the solution, believe it or not, is a single line of code just after the work method call. It is as follows.
Application.Current.Dispatcher.Invoke(new Action(() => { }), DispatcherPriority.ContextIdle, null);
Which as you can see effectively queues a new task on the Dispatcher thread and blocks current code execution until it finishes, but as you give it the lowest priority, this call will wait until all OTHER dispatcher execution finishes, ie all rendering completes. Once render is complete, this line will be executed and you will exit with all rendering complete. The full method I have used it in context is below. I welcome your thoughts and discussion on this approach.
public void LongRunningTaskWithFade(BusyDecorator busy, Action longTask)
{
if (loading) return;
loading = true;
busy.FadeTime = TimeSpan.Zero;
busy.IsBusyIndicatorShowing = true;
// in order for setting the opacity to take effect, you have to delay the task slightly to ensure WPF has time to process the updated visual
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
try
{
longTask();
Application.Current.Dispatcher.Invoke(new Action(() => { }), DispatcherPriority.ContextIdle, null);
}
finally
{
HideBusyDisplay(busy);
}
}), DispatcherPriority.Background);
}
I am relatively new to MVVM, and I am trying to code up a basic Status Bar for an MVVM WPF application. I think I have the gist of things, but for some reason, the status bar does not always update, and I am not sure why.
In my ViewModel, I have a basic property that I update when I change a status message:
public string StatusMessage
{
get { return _statusMessage; }
set
{
if (value == _statusMessage) return;
_statusMessage = value;
base.OnPropertyChanged(() => this.StatusMessage);
}
}
My OnPropertyChanged method (which I have in a base ViewModel class that implements INotifyPropertyChanged) looks like so (got this idea from Gunther Foidl; wish I could claim credit for it because I think it's slick but I'm not quite that smart):
protected virtual void OnPropertyChanged<T>(Expression<Func<T>> exp)
{
MemberExpression me = exp.Body as MemberExpression;
string propName = me.Member.Name;
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propName));
}
}
At any rate, this all works great for all of my controls except one. On my MainWindow.xaml file, I have a StatusBarItem control bound to the above property, like so (the rest of the XAML has been trimmed for space reasons):
<StatusBarItem Grid.Column="0">
<TextBlock TextTrimming="CharacterEllipsis" Text="{Binding Path=StatusMessage}" />
</StatusBarItem>
When I run my application (which hits a couple of DBs in addition to generating a document from template and a bunch of other fairly resource-intensive stuff), some, but not all, messages show up on the status bar. I have debugged and verified that the messages all make it into the StatusMessage property, above (and the ensuing private variable), they just don't seem to be refreshing in the UI.
I have looked at several examples that use BackgroundWorker instances for ProgressBar controls, but haven't seen any for StatusBarItem controls, and am not really sure how to translate one to the other.
I have also used Tasks before in previous C# 4.0 and WPF apps, and figure it's probably a good way to go, but I haven't really been able to figure out how/where to designate the UI task (I've always done it in the code-behind for the MainWindow before, but I'm striving for a zero-code-behind to stay in keeping with MVVM here).
I'm pretty sure that a multi-threaded approach is the way to go; I just don't know enough about one approach (I know a little bit of this and a little bit of that) to make it work. I did see a couple of posts that used the older threading approach directly, but I pretty much stayed away from multithreading programming until I started using Tasks with .NET 4.0 (finding them a little easier to comprehend and keep track of), so I had a bit of trouble making sense of them.
Can anyone take pity on me and point me in the right direction, or suggest further debugging I can do? Thanks!
1)Reflection based binding can be source of error sometimes because of inlining. Try to see what happens if you notifypropertychanged with simple string instead of reflection.
2) if you are using multi threads there maybe a chance that you setup StatusMessage not from UIThread in that case it won't be able to update UI, you could invoke setter code on UI Dispatcher to see if that helps
3) check whether binding works , in constructor of xaml form modify StatusMessage directly on VM and see whether the change is shown on UI without invoking multithreaded service calls which introduce additional variables to simple textblock - string binding
4) if that doesn't help you could create a simple xaml form with single textblock bind it to your big viewmodel and see what happens, if nothing works you can begin cutting VM class to make it simpler so binding eventually starts to work and you find an error
5) if you think that statusbar is the problem see if single textblock without statusbar (extract xaml part from your example) works
Somewhere the notification does not get through.
I would try :
Add a dummy valueconverter on the textbinding so you can set a breakpoint and see if you are called
Dispatching the property set to set the value at a "better" time - that is sometimes nessesary.
Dispatching the set might do the trick.
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'm writing tests which will check correctness of Binding elements specified in XAML. They work so far, the only issue is that I do not know how to correctly force databinding to happen. Surprisingly it is not enough to simply set something in DataContext, binding won't happen until you show your control/window. Please not that I'm writing 'unit'-tests and I'd like to avoid showing any windows.
Take a look at following code:
// This is main class in console application where I have all WPF references added
public class Program
{
[STAThread]
public static void Main()
{
var view = new Window();
BindingOperations.SetBinding(view, Window.TitleProperty, new Binding("Length"));
view.DataContext = new int[5];
//view.Show(); view.Close(); // <-- this is the code I'm trying not to write
Console.WriteLine(view.Title);
}
}
Here I'm creating a Window and putting an array as DataContext to that window. I'm binding Window.Title to Array.Length so I expect to see number 5 printed in console. But until I Show window (commented line) I will get empty string. If I uncomment that line then I will receive desired 5 in console output.
Is there any way I can make binding happen without showing a window? It is pretty annoying to look at ~20 windows while launching tests.
P.S.: I know I can make windows more transparent and etc, but I'm looking for more elegant solution.
UPDATE Code above is simplified version of what I really have. In real code I receive a View (some UIElement with bindings) and object ViewModel. I do not know which exactly binding there were set on View, but I still want all of them to be initialized.
UPDATE 2: Answering to the questions regarding what I test and I why. I do not intend to test that classes like Binding, BindingBase, etc are working as expected, I assume they are working. I'm trying to test that in all my XAML files I have written bindings correctly. Because bindings are stringly typed things, they are not verified during compilation and by default they cause only errors in output window, which I'm missing occasionally. So if we take my example from above and if we will made a typo there in binding: {Binding Lengthhh} then my tests will notify you that there is no property with name Lengthhh available for binding. So I have around 100 XAML files and for each XAML I have a test (3-5 lines of code) and after launching my tests I know for sure that there are no binding errors in my solution.
The bindings are updated by the dispatcher with the DispatcherPriority.DataBind - so if you wait for a dummy task with SystemIdle priority you are sure that any pending databinding is done.
try
{
this.Dispatcher.Invoke(DispatcherPriority.SystemIdle, new Action(() => { }));
}
catch
{
// Cannot perfom this while Dispatcher in suspended mode
}
If you are trying to test correctness of your view, I suggest you test your view :-)
Why not run the UI from a unit test and write code that checks content of UI after changing data.
VS2010 does have GUI testing, or you could take a look at the code of tools such as Snoop.
Edit following comment:
If ALL you want to do is test a few simple bindings, try writing a static code test that runs as a post build event using reflection on view models and regular expressions on XAMLs. Add attributes on VM or use a config file so your test will know which view receives which View Model as DataContext. Compare property names and types in View Models with binding strings in View (automatically search XAML for these) and throw exception (thus failing build) if strings do not match.
If your bindings are more complex (converters, multibindings, ...) this may be a bit more complicated to implement.
I think you should first set the DataContext and then do the Binding, e.g.:
view.DataContext = new int[5];
BindingOperations.SetBinding(view, Window.TitleProperty, new Binding("Length"));
I'm not sure if this is real solution for your general problem, but it works in this case.
I don't believe the Window's bindings will run without calling Show or ShowDialog, because that is the only way it gets associated with the UI message loop/dispatcher.
Your best bet would be to set it to be as least visible as possible, potentially using an extension method to clean things up:
public static void PokeWindowDispatcher(this Window window)
{
window.WindowState = WindowState.Minimized;
window.ShowInTaskbar = false;
window.Visibility = Visibility.None;
using (var wait = new ManualResetEvent())
{
Action<object, RoutedEventArgs> loaded = (sender, e) => wait.Set();
window.Loaded += loaded;
try
{
window.Show();
wait.WaitOne();
}
finally
{
window.Loaded -= loaded;
window.Close();
}
}
}
I had the same problem, and from sixlettervariables gave me an idea. It's very simple.
I am using WPF in WinForms application, so I use ElementHost control to host Wpf controls on WinForms control. To enforce WinForms control initialization you can just read value of Handle (which is actually Windows HWND) and this will force control to fully initialize itself including child ElementHost and all Wpf binding work.
I didn`t try to perform the same thing for pure Wpf control. But you can easily use ElementHost to initialize your Wpf controls like this:
var el = new ElementHost();
var p = new TextBlock();
p.DataContext = new { Data = "1234" };
p.SetBinding(TextBlock.TextProperty, "Data");
el.Child = p;
var t = el.Handle;
Debug.Assert(p.Text == "1234");
PS: Found, that everything work better, if you first set DataContext and only then force a Handle to be created (just like my example). But, I think, this is already the case for you, so should not be a problem.
Have you tryed to use the IsDataBound
http://msdn.microsoft.com/en-us/library/system.windows.data.bindingoperations.isdatabound.aspx
Also check this out:
System.Windows.Interop.WindowInteropHelper helper = new System.Windows.Interop.WindowInteropHelper(view).EnsureHandle();
http://msdn.microsoft.com/en-us/library/system.windows.interop.windowinterophelper.ensurehandle.aspx
My other question is why you trying to do a UNIT test on something that has been technically tested already? By the way I am not critising, just want to understand a little better.
Not sure, but maybe something like this will work?
view.GetBindingExpression(Window.TitleProperty).UpdateTarget();
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.