Is there a way to delay the drawing of a WindowsFormsHost? It's hosted in WPF.
It hosts a COM object which is an ESRI ArcEngine AxTocControl. Visually it looks like a table of contents tab.
I'm removing a number of objects from the table of contents and adding a different objects in a single function.
When I remove an object, the TOC control blinks and removes and object, then I add an object, and it blinks and adds an object. Since I am adding and removing 20 objects, its blinking 20 times. I would like to delay the drawing -- do all my operations -- and then resume drawing -- hopefully i will only have 1 or two blinks / flashes.
So to summarize, is there a function or technique (I don't care how hacky it is) to delay the drawing of a WindowsFormHost - let some things execute - and then resume drawing?
Well, you can create your own WindowsFormsHost subclass for this specific scenario and override WndProc. From there, you can decide to silently ignore (by not calling base.WndProc) all drawing-related messages as long as a "suppress updates" condition holds.
The condition you can implement any way you like, e.g. set up a public PausePainting() / ResumePainting() method in your class that sets a boolean field.
You will also have to remember to repaint when e.g. ResumePainting() is called, because you don't want your control to never update.
In the end it's not going to be as simple as A-B-C, but the concept is sound so it should work out.
You can use Thread.Sleep(0) to introduce a delay.
You could try processing everything, adding them one by one to a list, then add the whole list at one time.
myWpfControl.Clear();
List<EsriModel> items = new List<EsriModel>();
// Code to populate your items
myWpfControl.AddRange(items.ToArray());
Could it be that simple? That should prevent your WPF Control from flickering as you add them one by one.
Related
I have a WPF project where I have many small control "widgets" I display (these can add up to over a few thousand in some instances)
I now wish to add a small ongoing animation to scroll through one of the text fields to swap between various different values. So I need to add some sort of timer to trigger the animation (or change the value, which would then trigger an animation)
Since there are so many instances, I am not sure whether to do the neater and simpler way I adding a DispatchTimer to the control class (so there is an instance of DispatchTimer for each instance of the control), or, if there is some performance overhead of doing this, have some static, or injected single instance of the DispatchTimer that would then just call a method on each control.
I am always careful here, as in the Winform days, there were always limits on Handles, User objects etc.
Looking at the Source, I do see a HandleRef being created at line 2982..
SafeNativeMethods.SetTimer(
new HandleRef(this, _window.Value.Handle),
TIMERID_TIMERS,
delta);
however, HandlRef seems to be nothing more than a simple wrapper.
I haven;t examined all the source code, was wondering if anyone had any prior knowledge about this?
Thanks in advance for any help
I have inherited a C# GUI that I need to make some changes to. The main thing I need to do is to action the view controllers to fill in forms and things automatically and without a user.
Being very new to C#, is it possible to initialise GUI elements and event handlers so that they are valid objects without actually displaying them on the screen? The end goal is to create a new API that effectively forms a command line style variant of what already exists.
I appreciate this question might come across a little confused, as I'm still dipping my toe in and trying to feel my way around the approach.
Being very new to C#, is it possible to initialise GUI elements and event handlers so that they are valid objects without actually displaying them on the screen?
Yes, you can set their visibility to false. And they won't be visible. Or alternatively you don't add them to any displayed control. If you go to designer.cs file of your form you will see this line.
this.Controls.Add
this is basically adding your control to the form. If you skip this line it won't be displayed.
If you are going to write a Command Line Interface for a GUI Application (not the usual approach), then how about instantiating and just not calling .Show() on it? You can control the individual elements from the .Controls -Property of the Form.
TLDR version...
We're trying to create as basic Panel subclass with an observable Items property. The control then uses those data items to create one or more related child UI objects per data item.
Our first thought naturally was to simply subclass ItemsControl, but that doesn't seem to fit because it uses an ItemContainerGenerator which only generates one 'container' per item whereas again, we need to potentially create several (which aren't containers anyway.) Plus, all the created items have to be direct children on the panel, not held in a container which is why we can't go the route of data templates.
As such, I'm just using a standard Control and I'm trying to find the proper place/event where I should be instantiating/destroying the resulting child UI elements in response to changes in the Items collection.
Now the details...
First things first. If there was something like a ItemMultiContainerGenerator, that would be perfect, but I know of no such thing.
Ok, so simply monitor the collection for changes and put the UI generation in the CollectionChanged event! Right? That was our first guess too. The problem there is for every new 'Add' or 'Remove', we have to spin through all existing controls to 'defrag' certain indexing properties on them (think along the lines of a Grid.Row or ZIndex property) meaning if you add ten items, you run the defrag ten times, not once at the end.
Plus, that change event may come in on a different thread. If you attempted to dispatch to the main thread, your performance takes a nose-dive.
Our other attempt was to use MeasureOverride, since that was called only once in response to an InvalidateMeasure call, regardless of how many children we added or removed. The issues (there are many) with this approach is we lose context of whether something was added or removed, meaning we had to throw away all children and re-add back all new ones making this extremely inefficient. Plus, mucking around with the visual tree or setting bindings could cause the layout pass to execute multiple times since we were changing something that affects the layout, namingly the panels children.
What I'm trying to find is something that happens as part of the overall rendering process (i.e. from the time the control is told its invalid until it renders), but before the Measure/Layout passes are called. That way I can cache the adds/removes in the CollectionChanged event and simply mark the control as invalid, wait for this mystery event, then process the changes en mass, then send off the results to the layout engine and be done with it.
Using Reflector, I've tried to find out where the ItemsControl adds its children to the panel, but I didn't get too far considering the complexity of the control/ItemContainerGenerator pairing.
So where is the best place to create/add UI elements to a control based on data item changes?
I think you're going to have to listen to collection changes manually. There are a few reasons:
Changing your visual children will invalidate your layout. If you change your children in measure or arrange, you will have an infinite loop.
Rendering happens after layout. By changing children after layout you are asking for an infinite loop.
Hopefully you won't have to support any collections other than ObservableCollection, because it is obviously easier to deal with the one type of collection. But, I think you can definitely make a responsive control that will do these things. Here are some tips
Implement a custom Collection that has support for AddRange and RemoveRange (the INotifyCollectionChanged event supports multiple items being added or removed) so you don't have to do the same work 10 times for 10 new items.
In your collection change handler, use the e.AddedItems and e.RemovedItems instead of accessing the underlying collection. This will prevent you from receiving exceptions due to the collection changing while you're enumerating it.
Use BeginInvoke to prevent blocking the producer thread when dispatching back to the UI thread
To address your initialization concerns, implement ISupportInitialize, and use it to suspend your "defrag" process if you have to add or remove multiple items one at a time. WPF will automatically add the Begin/End calls for you when your control is created in XAML.
Derive from FrameworkElement if you don't want a ControlTemplate. Lower overhead.
If this still isn't working because the speed at which your underlying collection changes is too fast, and children are fairly simple, perhaps you should draw them in OnRender
One other option that just occurred to me is that you can schedule all these operations on the Dispatcher, so that if multiple changes happen, you would only need to do it once. Basically, you would store a reference to the operation like so:
private DispatcherOperation pendingDefragOperation;
protected void ScheduleDefrag()
{
if (pendingDefragOperation == null)
{
pendingDefragOperation = Dispatcher.BeginInvoke(
DispatcherPriority.Render, // You may want to play around with this
new Action(Defrag));
}
}
And you would call this on a CollectionChanged. And in your Defrag call, you'd set pendingDefragOperation to null.
I have a setup where potentially thousands of items (think 3000-5000) will be added to an ObservableCollection that is binded to some visual interface. Currently, the process of adding them is quite slow (approx. 4 seconds/1000 items), and of course the GUI is unresponsive during that time. What is a good method to handle moving that many items at once into a collection without worrying about the system locking up? I've looked at DispatcherTimer but I'm not sure if it will provide everything I need it to.
Another question - Is there something I can do to speed up the creation of these objects so that it doesn't take so long to add them to the collection? Currently I use them like so: Collection.Add(new Item(<params>)) Would generating the items beforehand, in a background thread probably, decrease the time it takes to add them by a noticeable amount?
Edit: Virtualization is not possible. The requirements specify a WrapPanel look, so the display is actually a ListBox which has a templated ItemsPanel
Edit2: According to the stopwatch, the bottleneck is actually putting items into my ObservableCollection. I will try changing that collection type and doing my own notification to see if that speeds it up substantially.
Edit3: So the answer is in one place - I solved this issue (with help from below) by creating a class which inherits from ObservableCollection. This class did two things - expose a method to add collections at one time, and added the ability to suppress the CollectionChanged Event. With these changes the time it takes to add 3000 items is roughly .4 seconds (97% improvement). This link details the implementation of these changes.
You've said 1000, so I'll stick to that number just for instance.
IIRC, the observable collection has a small drawback - if you add the items one by one, it raises notifies once per each item. That means that you have 1000 notifications for 1000 of items and the UI thread will run at deadly speed just to keep up with redrawing the screen.
Do you need to redraw ASAP? Maybe you can batch the additions? Split the 1000 of items into a few packed of 100 items, or a little more packets of 50 or 20 items. Then, instead of putting all items one by one, put them in packets. But beware: you have to use some methods like AddRange implemented by the collection it self, not by LINQ, or else you will again have one-by-one insertion. If you find such method, it should cut the number of events significantly, because the collection should raise the Changed event only once per AddRange call.
If observable collection does not have AddRange, either use different collection, or write your own, just a wrapper will probably be sufficient. The goal is to NOT raise Changed event at every single Add(), but after a reasonable count of them, or - maybe just skip raising Changed when items are added and raise Changed at some regular time intervals? This would be beneficial especially, if your data "flows in" indefinitely at a constant rate.
Of course, at that number of items coming onto the screen, you may just as well be held at the rendering it self. If your ItemTemplates are complicated, a 1000 of objects times 1000 of instances of visual layers/properties may simply kill the user experience. Have you simplified the ItemTemplates to the bare minimum?
Last thing: consider using virtualizing StackPanels as the ItemPanels in your ItemsControl/ListBoxes. It can greatly reduce the memory footprint and the number of items drawn at a single point of time. This will not necessarily help in the number or events raised, but it may help greatly when you have complex item templates!
Edit: you are using ObservableCollection, so I've assumed WPF/Silverlight.. update the question if this is not correct
WPF Binding supports concurrency for this reason. Try setting Binding.IsAsync to true. In addition.
Don't use ObservableCollection<T>, it is slow for this because each time an item is added it raises events. Use something faster like List<T> and raise your property change notification after all your items are added.
Pre-create your items in a background thread, then push them into your collection.
Check other parts of code involved to see if there is bloat, and trim.
By request, here is how I solved this issue. I started by creating a class which inherits from ObservableCollection. This class did two things - expose a method to add entire collections at once, and added the ability to suppress the CollectionChanged Event. With these changes the time it takes to add 3000 items is roughly .4 seconds (97% improvement). This link details the implementation of these changes.
Another thing you can try: subclass ObservableCollection and make it support bulk loading (AddRange). Here is an article:
AddRange and ObservableCollection
for second question
if in your GUI you are using WPF technologie, you can increase performance using VirualizingStackPanel allowing you to create only visible items
I am making a game based on Game State Managment pattern. I made 20 button on a 2nd page that let the user pick the level.
LevelButton : TexturedDrawableGameComponent
Inside LoadContent() buttons are created and added to GameComponent collection. If I get this right the GameCompoenent is a sort of global collection, so when the user goes back from 4th page to 2nd app will add another 20 buttons.
I added a loop that clears all LevelButtons from the collection, but seems this is wrong.
Is it DrawableGameComponent the wrong tool for creating buttons?
Yes.
DrawableGameComponent and its friends are optional bits of the XNA framework. Consider them helpers. Simple example implementations.
If you need more or different functionality to what they provide, you must replace them with your own implementation!
In this case it seems that what you want is a "Button" object (like your LevelButton), probably with methods like Update and Draw.
And then you want some kind of "Button Container" object that will hold all these buttons, and keep them drawn and updated when they are on-screen. But not do anything with them while they are off-screen.
(You could create your own instance of GameComponentCollection to manage this, and keep using game components. But you still have to provide your own draw/update/load/unload/initialise logic. The logic for Game.Components is internal to Game and not reusable. I recommend just using a List - keep it simple!)
The alternative to creating multiple collections - which is to add and remove (or disable/enable) components from the global collection - is horrifically ugly and error-prone. Don't do it.
I think that it is not wrong if it runs.
Maybe you can approach this in several more elegant ways or improving the way you do it... but the matter is that it have to run.
Maybe you are trying to remove the button when you click it, this will give you problems because that button is being updated.
When the level selection screen becomes inactive or destroyed is the time to remove the buttons.
Of course, you can create a button collection class, that manages the list of buttons, and then you only have enable/disable or remove one DrawableGameComponent, and can be useful in other screens.