Possible to InvalidateVisual() on a given region instead of entire WPF control? - wpf

I have a complex WPF control that draws a lot of primitives in its OnRender (it's sort of like a map). When a small portion of it changes, I'd only like to re-issue render commands for the affected elements, instead of running the entire OnRender over. While I'm fine with my OnRender function's performance on a resize or whatever, it's not fast enough for mouse hover-based highlighting of primitives.
Currently the only way I know how to force a screen update is to call InvalidateVisual(). No way to send in a dirty rect region to invalidate.
Is the lowest granularity of WPF screen composition the UI element? Will I need to do my renders of primitives into an intermediate target and then have that use InvalidateVisual() to update to the screen?

When you want to write WPF custom/composite controls, you should try to avoid overriding OnRender as possible especially if you plan to invalidate portions of it. It's much easier to use AddVisualChild + override VisualChildrenCount + override GetVisualChild + override Measure & Arrange like this (pseudo code with 2 children):
private void BuildMyControls()
{
AddVisualChild(subControl1);
AddVisualChild(subControl2);
}
protected override int VisualChildrenCount
{
get
{
return 2;
}
}
protected override Visual GetVisualChild(int index)
{
if (index == 0) return subControl1;
if (index == 1) return subControl2;
return null; // should never be called in fact...
}
protected override Size MeasureCore(Size availableSize)
{
base.Measure...
BuildMyControls();
.. measure them, probably call subControlX.Measure(...);
}
protected override void ArrangeCore(Rect finalRect)
{
base.ArrangeCore(finalRect);
... arrange them, probably call subControlX.Arrange
}
With this kind of code, you can invalidate just one portion, with something like subControlX.InvalidateXXX();

WPF doesn't work quite like that, so you can't invalidate regions. However, there are some optimizations that can be made. There is a Measure, Arrange, and then Render pass. If a control moves but what actually renders doesn't change then you can tell WPF to do only the arrange pass. You can trigger these invalidations off of dependency property value changes with FrameworkPropertyMetadata and FrameworkPropertyMetadataOptions (http://msdn.microsoft.com/en-us/library/system.windows.frameworkpropertymetadataoptions.aspx).

You shouldn't be using InvalidateVisual() unless the size of your control changes, as it causes a fairly expensive re-layout of your UI.
WPF is a retained drawing system. That means OnRender() might better be called AccumulateDrawingObjects(). It's actually accumulating a tree of live drawing objects, which only needs to happen once per layout. It then uses these objects to draw your UI whenever it needs to. To change how a portion of your UI looks without re-layout, some objects (like DrawingGroup, RenderTargetBitmap, and WriteableBitmap) can be updated after OnRender(), anytime you like.
To update a portion of your UI later, wrap those commands in a DrawingGroup and put that object into the DrawingContext. Then you can Open() and update it whenever you want, and WPF will automatically repaint that portion of the UI.
This is what it looks like:
DrawingGroup backingStore = new DrawingGroup();
protected override void OnRender(DrawingContext drawingContext) {
base.OnRender(drawingContext);
Render(); // put content into our backingStore
drawingContext.DrawDrawing(backingStore);
}
// I can call this anytime, and it'll update my visual drawing
// without ever triggering layout or OnRender()
private void Render() {
var drawingContext = backingStore.Open();
Render(drawingContext);
drawingContext.Close();
}

Related

WPF optimize loading on the UI thread

I am trying to optimize the loading times for my WPF prism application. The loading is basically a loop of using reflection to create instances of UI elements and then adding them to the main window (the shell) in a tab control.
Since we are limited to using a single thread to create all the objects, what would be the optimal way to speed up loading / create a better user experience?
These are the options I have so far:
Use lazy loading. Only load the tab when the user first clicks on it. But that would have a 4-5 second delay opening the first time as it gets initialized on demand.
Cache all the reflection calls. I actually did that, but it did not speed anything up at all. Most of the time occurs during the rendering of the controls...
?
Any suggestions would be greatly appreciated for this tricky problem.
You're pretty much stuck as you can only load objects on the main thread, so I don't think you'll make it load any faster.
What you can do is distract the user: I have an animated splash screen that take about 10 seconds to work its way through the animation sequence. This serves a number of purposes:
It shows the user motion - so they have a visual cue that something is going on
It distracts them and fills the space taken by the initial load
To ensure smooth animation you need to create a second dispatcher. Here's how I do it:
public class AppEntry : Application
{
private static ManualResetEvent _resetSplashCreated;
internal static Thread SplashThread { get; set; }
internal static SplashWindow SplashWindow { get; set; }
private static void ShowSplash()
{
SplashWindow = new SplashWindow();
SplashWindow.Show();
_resetSplashCreated.Set();
Dispatcher.Run();
}
[STAThread]
public static void Main()
{
_resetSplashCreated = new ManualResetEvent(false);
SplashThread = new Thread(ShowSplash);
SplashThread.SetApartmentState(ApartmentState.STA);
SplashThread.IsBackground = true;
SplashThread.Name = "Splash Screen";
SplashThread.Start();
_resetSplashCreated.WaitOne();
var app = new App();
app.DispatcherUnhandledException += new DispatcherUnhandledExceptionEventHandler(app_DispatcherUnhandledException);
app.InitializeComponent();
app.Run();
}
static void app_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
// MessageBox.Show(e.Exception.StackTrace);
}
}
I set the AppEntry class as my Startup Object in the Project Properties/Application tab.
I close my splash screen at the end of my OnStartup method in App:
AppEntry.SplashWindow.Dispatcher.BeginInvoke(DispatcherPriority.Background,
new Action(() => AppEntry.SplashWindow.Close()));
Is this faster? No
Does the user think it's faster? Yes
Sometimes, if you can't give them speed, you can give them activity. It's a nice placebo.
As you mentioned, You cannot Multithread if your objects are DependencyObjects. Kent Boogart discusses this. That's why you must leverage INotifyPropertyChanged and do POCO objects to hold your data. That way you can multithread to obtain the data and then bind these to your UI. Another drawback of using DependencyObjects is that you're tying your application too much to the WPF framework (DependencyObject being a class defined in the System.Windows namespace in a WPF assembly (don't remember if PresentationCore or PresentationFramework)). If refactoring is not an option, you will have to consider a solution like the one LastCoder proposed. Be aware that you will be able to do very little multithreading (if any at all), therefore your application is not going to be very responsive all the time.
I would implement a timer that loads a few controls or tabs every tick (iteration). The timer will run on the same thread as the UI (control messages for it will be queued up on the Windows Message Loop). Once all of the work is done you can kill the timer.
The timer interval and the number of controls to load per tick will boil down to use-testing; try something like 100ms and 2 controls a tick that will give you ~20 controls a second, so if you had 10 tabs with 15 controls each it would take ~8seconds, but the UI shouldn't lock up as bad.
The best answer to speeding up the loading is to simply hide the container while the visual tree is being constructed.
This prevents the screen from constantly needing to update itself.
When all the elements have been added to the visual tree, then setting the container visibility to visible renders the tab container once.
We also implemented some simple lazy-rendering to the tab control items.
Net result: loading times from 2 minutes down to about 20 seconds.

In WPF how can I get the rendered size of a control before it actually renders?

I'm doing custom rendering in a Decorator subclass. Our rendering requires creating complex geometries which only neded to be re-created when the actual rendered size changes. As such, I have moved the geometry creation into its own function called UpdateGeometry which creates, then freezes the geometry for use in OnRender. This new function only needs to be called in response to a change in ActualWidth or ActualHeight.
Even better, it looks like we should be able to simply override OnRenderSizeChanged, which according to the documentation states...
"When overridden in a derived class,
participates in rendering operations
that are directed by the layout
system. This method is invoked after
layout update, and before rendering,
if the element's RenderSize has
changed as a result of layout update."
However, regardless if I'm using the override or listening to the property change notifications of ActualWidth and ActualHeight, my logging consistently shows OnRender as happening first! Um... Wha??
To be sure it wasn't something I was doing in my code, I created a bare-bones test decorator subclass and added logging there, both on entry and exit to the overrides. Here is the entire class...
using System;
using System.Windows.Controls;
public class TestControl : Decorator
{
protected override void OnRender(System.Windows.Media.DrawingContext drawingContext)
{
Console.WriteLine("OnRender Entered");
base.OnRender(drawingContext);
Console.WriteLine("OnRender Exited");
}
protected override void OnRenderSizeChanged(System.Windows.SizeChangedInfo sizeInfo)
{
Console.WriteLine("OnRenderSizeChanged Entered");
base.OnRenderSizeChanged(sizeInfo);
Console.WriteLine("OnRenderSizeChanged Exited");
}
}
And as I feared... here is the output...
OnRender Entered
OnRender Exited
OnRenderSizeChanged Entered
OnRenderSizeChanged Exited
So what am I missing here?
More importantly, how can I get the ActualWidth and ActualHeight values after the layout subsystem has done its job, but before the control is rendered so I can create the geometry before it's needed in the OnRender override?
My latest implementation overrides ArrangeOverride as the value that's passed in there is a size containing what the ActualWidth and ActualHeight values should be after the core layout system takes into consideration HorizontalAlignment and VerticalAlignment with values of 'Stretch', minimums and maximums, etc, but what they actually are depends on the value that's returned from that override so it's a little more complex than that.
Either way, I'm still wondering why the OnRenderSizeChanged call doesn't happen when it's supposed to. Thoughts?
Mark
In general, you should be able to get the correct size from ArrangeOverride. This doesn't include things like Margin, but that probably shouldn't be taken into account. You could either use the size passed as a parameter as your "render" size or use the return value of the base.ArrangeOverride call.
EDIT:
The OnRender method is called from the Arrange method, after OnArrangeOverride is ultimately called. The OnRenderSizeChanged on the other hand is called from UpdateLayout, which is effectively dispatched to be executed all at once for a given section of the visual tree. This is why the OnRenderSizeChanged is called after the OnRender.
The documentation may refer to the "rendering" as in actually rendered to the screen, not when OnRender is called. WPF can cache the rendering instructions for a given element and execute them when needed. So the fact that OnRender is called before OnRenderSizeChanged, doesn't mean it's actual rendering instructions are committed to the screen at that time.
You can modify your OnRenderSizeChanged to force OnRender to be called again using:
protected override void OnRenderSizeChanged(System.Windows.SizeChangedInfo sizeInfo)
{
Console.WriteLine("OnRenderSizeChanged Entered");
base.OnRenderSizeChanged(sizeInfo);
this.InvalidateVisual();
Console.WriteLine("OnRenderSizeChanged Exited");
}
You may also want to skip your OnRender code if RenderSize is "0,0".

General Question About WPF Control Behavior and using Invoke

I have been putting off activity on SO because my current reputation is "1337". :)
This is a question of "why" and not "how". By default, it seems that WPF does not set focus to the first control in a window when it's opening. In addition, when a textbox gets focus, by default it does not have it's existing text selected. So basically when I open a window, I want focus on the first control of the window, and if that control is a textbox, I want it's existing text (if any) to be selected.
I found some tips online to accomplish each of these behaviors, and combined them. The code below, which I placed in the constructor of my window, is what I came up with:
Loaded += (sender, e) =>
{
MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
var textBox = FocusManager.GetFocusedElement(this) as TextBox;
if (textBox != null)
{
Action select = textBox.SelectAll;
//for some reason this doesn't work without using invoke.
Dispatcher.Invoke(DispatcherPriority.Loaded, select);
}
};
So, my question. Why does the above not work without using Dispatcher.Invoke? Is something built into the behavior of the window (or textbox) cause the selected text to be de-selected post-loading?
Maybe related, maybe not--another example of where I had to use Dispatcher.Invoke to control the behavior of a form:
WPF Focus In Tab Control Content When New Tab is Created
All WPF controls have thread affinity. The Dispatcher manages the thread that each control was created on (typically this is a single thread for every control in the application, but not necessarily). Work is queued on this thread and executed in priority order.
Any UI-manipulating code has to be executed on the same thread as the control was created on - the Dispatcher thread - and so any method has to invoke back to that thread before it can do anything that would affect the UI (such as selecting text in the TextBox).
That said, it's my understanding that the Loaded eventhandler would fire on the Dispatcher thread by default, so I'm not entirely sure why you're seeing this behaviour in your specific example!
I should start by mentioning i had no issues making that work w/ out the dispatcher call in .net 4.0 (it may have been fixed in the framework update)- however, what the previous poster mentioned is accurate and has been the pardigm since the dawn of winforms (.DoActions() and .Invoke()). However, in 3.5 the above did work w/ out dispatcher if you use a method defined in the codebehind as the target call in your lambda:
Loaded += (sender, e) =>
{
this.SelectText();
};
void SelectText()
{
MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
var textBox = FocusManager.GetFocusedElement(this) as TextBox;
if (textBox != null)
{
textBox.SelectAll();
}
}
As to why, I cant really give you the specifics but I've run into similar issues w/ using lambdas to route events on presenters. I want to say its something to do w/ reference or context of the compiled expression- in this case it needs a ref to the containing object in order to know how to delegate the operation (selecting the textbox text on the right thread). I also believe that GC can occasionally clean up resources so deferred execution gets botched (seen it in F#...believed that was the cause of my issue in C# as well).

Designing Windows.Form with multiple panels -> How to hide one panel (like a PS layer)

How can I hide one panel in Visual Studio 2008 Form Designer like a layer in PS? Otherwise, can someone recommend another better method to design multiple "screens" that must be clicked through by the user?
What you describe is a wizard, and you might want to investigate the approach from Eric J.
However, when I have cases where I want to have multiple panels in the same space within my UI and I want to switch between them in the designer, I like to use a TabControl and hide the tabs on the TabControl. This makes the UI easier to manage at design time and the code is pretty simple to switch between the tabs at run time.
I made a custom control that derives from TabControl called HiddenTabsControl that is very simple. The class only overrides the WndProc and lets the TabControl base class handle everything else. All you need to do is:
Add a New Item to your project
Choose Custom Control,
Name it something like HiddenTabsControl.
Change the base Class to TabControl, remove the Constructor and the OnPaint override that Visual Studio added.
Copy this override for WndProc into the class:
protected override void WndProc(ref Message m)
{
// Hide tabs by trapping the TCM_ADJUSTRECT message
if (m.Msg == 0x1328 && !DesignMode)
{
m.Result = (IntPtr)1;
}
else
{
base.WndProc(ref m);
}
}
Now you can change tabs in the designer and design the UI easily and in the code you can handle events to change tabs as needed. Changing the Selected tab is easily done with:
this.hiddenTabsControl.SelectedTab = this.tabPageYouWantVisible;
One side effect of removing the tabs is the space that the tabs occupy when the control is constructed. Removing them will make the space the HiddenTabsControl occupies change by shrinking it. I usually set the Anchor of the HiddenTabsControl to bottom to keep it from shrinking.
I used this Wizard code in a recent project and it worked well.
It provides the basic experience you are after.
Another less elegant, but quick hack, approach is to simply not add the panel to the parent form until runtime. In doing that, the designer has no idea where the panel belongs prior to compilation, and it won't be displayed.
For example, find the block of code where you add controls to the parent form:
//this->Controls->Add(this->panel_X);
this->Controls->Add(this->tabControl);
this->Controls->Add(this->menuStrip_topMenu);
Comment or remove the statement, then find the handle to the event that occurs when the form is loaded:
this->Load += gcnew System::EventHandler(this, &MainForm::MainForm_Load);
Then in the definition of the event handler, add the control to the form:
System::Void MainForm_Load(System::Object^ sender, System::EventArgs^ e) {
...
...
this->Controls->Add(this->panel_X);
}
I haven't experienced any unwanted side effects by doing this, but if anyone has a good reason to not I'd be interested in hearing it.

Silverlight Usercontrol Onload Event when generated in another Usercontrol, to apply farseer physics

Didn't know how to phrase the question better but basically I have a usercontrol that I dynamically add into another usercontrol. The child usercontrol has a set of images and basically I need to know if they have fully loaded inside the parent usercontrol since I need their dimentions for a method (not just height and width).
Is there a type of event that can trigger when all the images in the child usercontrol have loaded in the parent?
To further explain my scenario, I need this since I am using farseer physics engine for silverlight and it aquires the shape of the image to use for collision detection, and since when I try to turn the images into a physics object it would not find the shape since the image is still being generated on screen and therefore throws an exception.
Update: This is what I have came up with so far
void currImg_ImageOpened(object sender, RoutedEventArgs e)
{
var ImagesLoaded = true;
for (int i = 0; i < ImageLoaded.Count-1; i++)
{
if (!ImageLoaded[i])
{
ImagesLoaded = false;
ImageLoaded[i] = true;
break;
}
}
if (ImagesLoaded)
{
addPuppetPhysics(pg.currPuppet);
}
}
Where ImageLoaded is a list of booleans, the method addPuppetPhysics applies the farseer physics to the usercontrol pg.currPuppet . Thing is the physics engine finds the first half of the images then doesn't find anymore (crashes on the same Image). If I merely load the usercontrol, then apply the physics with a button click, it works perfectly.
Can you detect when the images are finished loading in the child control? If so you may be able to create you own event -- ex: ImagesLoaded -- and handle that in the parent.

Resources