I am having problems with visibility of the spinner control.
The control itself works... If I set it to visible right after Initialize it shows and animates as expected.
But if I try to show it from the code it never gets drawn...
the .cs file (presenter)
private void SaveDocument(Document aDocument)
{
if (AllowFlag != null)
{
this.View.ShowDocumentProgressSpinner(true);
this.Save(aDocument);
this.View.ShowDocumentProgressSpinner(false);
}
}
the xaml.cs file
void IDocumentView.ShowDocumentProgressSpinner(bool show)
{
if (show)
{
this.DocumentProgressSpinner.Visibility = Visibility.Visible;
}
else
{
this.DocumentProgressSpinner.Visibility = Visibility.Hidden;
}
}
If i set the visibility to visible right after initialize the spinner works!
part of the xaml of the main control (the spinner is custom control)
...
<Viewbox Grid.Row="3" Width="30" Height="30"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<my:DocumentProgressSpinnerView x:Name="DocumentProgressSpinner" />
...
Probably another threading problem, but I have also tried:
Thread.CurrentThread == Dispatcher.CurrentDispatcher.Thread
TRUE
Dispatcher.FromThread(Thread.CurrentThread).CheckAccess()
TRUE
The control gets invoked, because the "windows spinner" gets activated, just the control never gets shown...
The problem is that you are running your save operation on the dispatcher thread and during the save operation the dispatch thread is blocked the whole time. It's only after your save operation has finished that the UI is updated and thus you will never see the "waiting" state. Instead you should spin off a new thread and from within the event dispatch and set the wait indicator to visible. In the separate thread perform the save operation and once the saving is done, use the dispatcher to hide the wait indicator again on the Dispatcher thread.
See this answer for more details on how to implement this.
Related
For a project I use a very simple progress overlay.
It just displays a small marquee progressbar and covers the screen.
So in my ShellView I have
<Border Grid.Row="0"
Grid.RowSpan="11"
Grid.Column="0"
Grid.ColumnSpan="11"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Panel.ZIndex="3"
Background="#9E000000"
BorderBrush="Black"
BorderThickness="3"
Visibility="{Binding IsProgressing,
Converter={StaticResource BoolToVisibility}}">
<!-- omitted progressbar, text etc -->
</Border>
And I have a very simple event, which just sets the Visibility (IsProgressing) binding and some text to show.
Whenever I want to have a progressbar, I just publish that event, like
_eventAggregator.Publish(new ProgressingChange(true, "Loading ..."));
This works very well so far, besides one case:
For the application I use events for navigation of my screens.
So there is another event which I publish, like:
_eventAggregator.Publish(new NavigationEvent(typeof(TargetViewModel)));
which just sets the target screen:
public void Handle(NavigationEvent navigate)
{
var target = _screenFactory.FromType(navigate.TargetScreen);
this.ActivateItem(target);
}
One of my Screens has lots of items and takes about 3 seconds to load.
So I wanted to show my Progress overlay while the screen is loading.
This is what does not work. Both the new Screen and the Overlay are
showing simultaneously when those events are combined.
This is:
_eventAggregator.Publish(new ProgressingChange(true, "Loading ..."));
_eventAggregator.Publish(new NavigationEvent(typeof(LongLoadingViewModel)));
For debugging reasons I did not deactivate the Progressing overlay to the
what is happening.
So the screen is loaded, nothing is shown on the screen for about 3 seconds, and then
both the Progress overlay and the new screen are shown.
I have tried
sleeping after publishing the ProgressingChange event
sleeping in both event handlers
running the publish of both events on separate threads
running only one of those publishes on a separate threads
tried to force UI update like this here force UI update
What am I missing?
What is happening here?
How can I get that to work?
-edit-
Here is the code of my Handler method:
public void Handle(ProgressingChange progressing)
{
this.IsProgressing= progressing.IsProgressing;
this.ProgressingText= progressing.ProgressingText;
NotifyOfPropertyChange(() => IsProgressing);
NotifyOfPropertyChange(() => ProgressingText);
// of course, there is Notify in the setters themselves, too
}
And I used this code from the source linked above to force UI updates,
but that did not work
void AllowUIToUpdate() {
DispatcherFrame frame = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Render, new DispatcherOperationCallback(delegate(object parameter)
{
frame.Continue = false;
return null;
}), null);
Dispatcher.PushFrame(frame);
}
Also, I tried publishing in a critical section to force the first
publish to be executed before the second one, but that did not work either.
-edit2-
Code that at least shows the Progress overlay
_eventAggregator.Publish(new ProgressingChange(true, "Activating ..."));
Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() => _eventAggregator.Publish(new NavigationEvent(typeof(LongLoadingViewModel)))), DispatcherPriority.ContextIdle, null);
It might be a hack, but you could try to first wait for the Border (porgress bar) to be rendered before the navigation-event is published. To do this, you might be able to adapt the solution that is given here to execute some code when the UI-thread is no longer busy (see link for full explanation):
Dispatcher.BeginInvoke(new Action(() => Trace.WriteLine("DONE!", "Rendering")), DispatcherPriority.ContextIdle, null);
If this works, then at least you have something to start off making the code a bit cleaner.
I have a WPF project and from the main window i am creating and loading some bunch of user controls, there is some large data i am loading in background and then updating a built-in control throw the dispatcher, that works fine, the problem is that some of the user controls loads a lot of data, for example the very first thing i load in the main area of my main window, what i want is to put a loading label instead, load the main window as fast as possible so the user see this label and run in background the creation of that user control and when is done add it as a child of my main container area on my main window while i remove the loading label, if i follow the same philosophy i run into the same error like when i run a task and then try to update the window without using the dispatcher. i want to be able of create the user control asynchronous then update the main window.
Code:
User Control:
public partial class CustomUserControlGallery : UserControl
{
public CustomUserControlGallery()
{
InitializeComponent();
}
...
}
On the backend class of the main window:
public partial class MainWindow : Window
{
CustomUserControlGallery _customUserControlGallery;
public MainWindow()
{
InitializeComponent();
Task t = new Task({
//Can't use the _customUserControlGallery's Dispatcher because object is uninitialized and this.Dispatcher not working either.
_customUserControlGallery = new CustomUserControlGallery(); //Error Here.
_gridContainer.Dispatcher.Invoke(new Action(() => _gridContainer.Children.Add(_customUserControlGallery)));
_loadingLabel.Visbility = Visibility.Collapse;
});
t.Start();
}
...
}
I don't know how to handle this situation with the thread associated to the user control and the main thread.
Error:
{"The calling thread must be STA, because many UI components require this."}
You're doing this wrong. All controls must be created & operate on the UI Thread. That said, you can use the BackgroundWorker class to load the data.
You typically do this by disabling the control whose data is being loaded in the background or hiding it & displaying a progress indicator in its place. Then, you start your BackgroundWorker. That can communicate how far along it is using the ReportProgress method. Finally, when it's finished running, the RunWorkerCompleted event is fired, and you use that to either enable the control, or to hide the progress indicator & show the control.
Some quick & dirty (untested) code:
Place this in your Initialize() or control constructor:
private BackgroundWorker loadData = new BackgroundWorker();
loadData.DoWork += loadData_DoWork;
loadData.ProgressChanged += loadData_ProgressChanged; // Only do this if you are going to report progress
loadData.WorkerReportsProgress = true;
loadData.WorkerSupportsCancellation = false; // You can set this to true if you provide a Cancel button
loadData.RunWorkerCompleted += loadData_RunWorkerCompleted;
private void DoWork( object sender, DoWorkEventArgs e ) {
BackgroundWorker worker = sender as BackgroundWorker;
bool done = false;
while ( !done ) {
// If you want to check for cancellation, include this if statement
if ( worker.CancellationPending ) {
e.Cancel = true;
return;
}
// Your code to load the data goes here.
// If you wish to display progress updates, compute how far along you are and call ReportProgress here.
}
}
private void loadData_ProgressChanged( object sender, ProgressChangedEventArgs e ) {
// You code to report the progress goes here.
}
private void loadData_RunWorkerCompleted( object sender, RunWorkerCompletedEventArgs e ) {
// Your code to do whatever is necessary to put the UI into the completed state goes here.
}
What you are essentially saying (I think) is that Your app becomes sluggish while your control renders a large amount of data.
This is a problem that needs to be solved via virtualisation. You cannot create a control on a background thread, have it render its data behind the scenes and then pop it into existence. You can create controls on separate dispatchers, but they cannot share the same visual and logical tree, so you will not be able to have one as a child of the other.
Virtualisation is what you need to focus on. Depending on the control you can use a variety of virtualisation settings. Try googleing the subject as there is a lot of information on how to achieve this effectively. Most likely you will want to use things like virtualizing stackpanels and container recycling.
You cannot create UI controls with different Dispatchers and use them with each other. It's just not allowed. What you want to do is on your Task you do the heavy lifting work without UI updates and when it is done you push it back to the Dispatcher to update the UI.
In your case, I wouldn't even use Dispatcher.Invoke. Since you are using Task, it has a TaskScheduler.FromCurrentSynchronizationContext() that you can pass in the constructor.
What is the purpose of instantiating controls in a different thread if you're just going to put it back to the Main dispatcher? It's not expensive to do that.
In my application I use two sliders to control the brightness and contrast of certain images and the image has to be completely recalculated pixel by pixel every single time when either one of the two sliders changes its value-property.
The recalculation of smaller images goes completely fine and doesn't cause any problems, however, larger images need longer to be recalculated and the slider thumb moves with a slight delay compared to the mouse pointer. I do need the image to be recalculated more or less in real time so simply having an event on DragCompleted or similarly is not acceptable.
The recalculation is initialized using the ValueChanged-event of the slider.
I think a good solution to this problem would be if the event is not fired as quickly as possible but will at least wait say 50ms before it is fired again, but is there a property of a slider that can control that?
Another solution I was thinking of, is removing the event from the slider right at the start when the event gets handled and adding it again some short time later, but that might cause some delay itself which is also not preferred here.
I couldn't really find anything on this topic anywhere, so if somebody has any good suggestions or directions I could use, I would be very greatful.
You can also make use of BindingBase.Delay property introduced in WPF 4.5.
Simply bind Slider's value to a dependency property setting Delay on the binding. This will cause value updates only after certain time (e.g. 500 ms) and this can make your app smoother.
If you think your application don't need to do the calculations every time the ValueChanged event is triggered,You can use the DragCompleted Event in Thumb control to determine the position after the user finished dragging the control.
<Slider Thumb.DragCompleted="Slider_DragCompleted_1"
Height="27" Margin="132,162,0,0" VerticalAlignment="Top" Width="303"/>
When the user stopped dragging,
private void Slider_DragCompleted_1(object sender, DragCompletedEventArgs e)
{
Slider s = sender as Slider;
// Your code
MessageBox.Show(s.Value.ToString());
}
But beware that this works only when user drags the slider.This doesn't get triggered when user clicks on the slider.
Refer this for handling other events like mouse click etc..
If you want to calculate with some time delay then you can use a timer .
EDIT:
Based on your request you can do like this.
In the 'ValueChanged' event.
// Start a new thread only if the thread is stopped
// or the thread has not been created yet.
if (threadPopular == null || threadPopular.ThreadState == ThreadState.Stopped)
{
threadPopular = new Thread(new ThreadStart(Your function));
threadPopular.Start();
}
I might implement this using the Backgroundworker where image processing will be done on Backgroundworker asynchronously.
Also what I will suggest is you can use Timer here and set its tick time to the comfortable value. On every sliderchanged event, you start the timer if it is not enabled. In timer tick event handler you can check if the background worker is working then you can cancel the previous operation and put the new operation on it. In bacgroundworkerdone event handler, just stop the timer.
Thanks
While you could use BindingBase.Delay, this causes a delay even when a single change is required. another approach might be to use a OneWay binding in the Slider Value and use an asynchronous command like so:
XAML code:
<Slider Value="{Binding MyValue, Mode=OneWay}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="ValueChanged">
<mvvmlight:EventToCommand
Command="{Binding SetValueCommand, Mode=OneWay}"
EventArgsConverter="{StaticResource
RoutedPropertyChangedEventArgsToDoubleConverter}"
PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
Value Converter:
using GalaSoft.MvvmLight.Command;
public class RoutedPropertyChangedEventArgsToDoubleConverter : IEventArgsConverter
{
public object Convert(object value, object parameter)
{
var args = (RoutedPropertyChangedEventArgs<double>)value;
var element = (FrameworkElement)parameter;
return args.NewValue;
}
}
And the callback for the command:
double _updateVal;
Task _delay;
private async void SetValue(double val)
{
if (_delay != null)
{
// in case of high frequency updates, most updates will return here
_updateVal = val;
return;
}
// only the first update reaches here
// caluclate the image here
MyValue = val; // update slider
_delay = Task.Delay(500);
await _delay;
// in case there are pending updates:
while (_updateVal.HasValue)
{
// caluclate the image here
MyValue = _updateVal.Value; // update slider
_updateVal = null;
_delay = Task.Delay(500);
await _delay;
}
_delay = null;
}
This way you only get to reduce the frequency of the image calculations without a significant delay on the first value change.
I have a function that takes around 5 seconds to complete if a button is clicked. If the button is clicked, I want to display some kind of notification to indicate that the buttonclick is being processed, something like
<Button Click="OnButtonClick" Content="Process Input" />
<Border x:Name="NotificationBorder" Opacity="0" IsHitTestVisible="False"
Width="500" Height="100" Background="White">
<TextBlock Text="Your input is being processed" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
And in code-behind on the button click:
private void OnButtonClick(Object sender, RoutedEventArgs e)
{
DoubleAnimation da = new DoubleAnimation
{
From = 5,
To = 0,
Duration = TimeSpan.FromSeconds(2.5),
};
// Making border visible in hopes that it's drawn before animation kicks in
NotificationBorder.Opacity = 1;
da.Completed += (o, args) => NotificationBorder.Opacity = 0;
NotificationBorder.UpdateLayout(); //Doesn't do anything
UpdateLayout(); // Doesn't do anything
NotificationBorder.BeginAnimation(OpacityProperty, da);
// Simulate calculationheavy functioncall
Thread.Sleep(5000);
}
Somehow UpdateLayout() isn't rendering fast enough, the notification is only displayed after the 5 seconds of Thread.Sleep are over.
Dispatcher.Invoke((Action)(() => NotificationBorder.Opacity = 1), DispatcherPriority.Render); won't work either.
Additionally, I can't let Thread.Sleep run in a separate worker thread - In the real application, it needs to read data from Dispatcher-owned objects and (re)build parts of the UI.
Is there a way to make it visible before Thread.Sleep() is called?
The problem is any long activity on main thread (UI thread) will lead freeze in UI, so you will not see any animation.
You need to do your calculations in a separate thread. I you need to access to Dispatcher-owned objects you can use uiObject.Dispatcher.BeginInvoke or Invoke. BackgroundWorker can help you the way to execute some long calculations and subscribe to the event Completed which will be fired by BackgroundWorker on the UI thread automatically.
If you are ok with UI freeze, change some property and other stuff re-schedule with Dispatcher.BeginInvoke(DispatcherPriority.Background,...), like
NotificationBorder.Opacity = 1;
Dispatcher.BeginInvoke(DispatcherPriority.Background, (Action)(()=>Thread.Sleep(5000)));
If you want the UI to update, you need to change your long-running operation to be asynchronous, which means properly decoupling the UI dependencies from whatever logic or perhaps IO-bound operations are taking all the time. If you don't do this, none of the UI updates occurring in your long-running operation will actually be reflected in the UI until it's done, anyway.
If you're running with .NET 4.5, here is my preferred pattern for how to do this:
public async Task DoComplexStuff()
{
//Grab values from UI objects that must be accessed on UI thread
//Run some calculations in the background that don't depend on the UI
var result = await Task.Run((Func<string>)DoComplexStuffInBackground);
//Update UI based on results
//Possibly do more stuff in the background, etc.
}
private string DoComplexStuffInBackground()
{
return "Stuff";
}
This is somewhat 'backwards' from the older style BackgroundWorker approach, but I find it tends to result in cleaner logic. Instead of writing your background code, with explicit Invokes for UI updates, you write your UI code around explicit background work calls via Task.Run(). This also tends to lead to a natural separation between the UI-centric code and the underlying logic code, as you need to refactor your logic into methods that can be called in this way. That's a good idea for long-term maintainability anyway.
So you can dispatch both action (UI and your calculations) into the dispatcher and they will be executed in a line
Dispatcher.BeginInvoke(do ui things);
Dispatcher.BeginInvoke(do your logic things);
But you'd better move your calculation into the background thread.
I have a class that has purely static Methods and properties. I am calling an async method on the class "Load" that asks a web service for a chunk of data, which then fires an event that executes the return method, "LoadCompleted". I have no idea how long the call is going to take (the difference between calling the "Load" method, then the "LoadCompleted" getting called).
I would like to block the application from proceeding any further until the callback method has been raised (as the app will try and get stuff from this class, which isn't populated until the "LoadComplete" method sets the data).
How would i go about doing this?
Blocking the main UI thread should be avoided with extreme prejudice.
I would use the BusyIndicator control from the Silverlight Toolkit:-
<UserControl x:Class="StackoverflowSpikes.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit">
<toolkit:BusyIndicator x:Name="busyInd" >
<Grid x:Name="LayoutRoot">
<!-- The rest of your content -->
</Grid>
</toolkit:BusyIndicator>
</UserControl>
Before calling Load use:-
busyInd.IsBusy = true;
then on LoadComplete use:-
busyInd.IsBusy = false;
This will lock out user input on the UI without blocking the main thread and give the use some feedback as to why they can't click anything right now. You can supply your own content for the busy message using the BustContent property. Of course if you don't like the way it looks you can style it to your preference.
If you want to get all MVVM you can bind the IsBusy property to a VM property that indicates that the VM doesn't want anything changing right now.
You can use the ManualResetEvent class to block the main thread if you want. Just call the WaitOne method to block and call the Set method to unblock when the asyc web request completes. Just be aware that if you block your main UI thread, your entire application will become completely unresponsive.
You could consider setting the UI controls to disabled at the start. On load complete you could display your data and then enable the UI controls. No thread blocking is necessary with this approach.