"flickering" pictureboxes due to being drawn while being moved - c#

I am creating a program where users are able to use a GUI to create configuration files that bind commands to certain keys for a different unrelated program. In this program, I have a checkbox that allows the user to chose to use the DVORAK layout or the QWERTY layout. I have PictureBox-es being used for the image of each key. When the user checks the checkbox for the DVORAK layout, the program rearranges these pictureboxes so that they are now in the DVORAK layout instead of the QWERTY format. When this happens, about 90% of the time a few of the keys are drawn before they have moved, leaving 1 frame where there are keys overlapping or missing keys, causing a sort of "flicker".
I was wondering if there is any way to wait until right after the form's draw call has finished and then rearrange the keys to give the maximum possible amount of time for them to rearrange. I would need to be able to know the time until the next draw call (or if there is a constant amount of time inbetween every draw call, the time since the last draw call would also work) wait that amount of time, and then rearrange the pictureboxes.
Thank you for any and all help!

I had a similar problem with TreeViews. And, like TreeViews, PictureBox doesn't have a DoubleBuffered property. So I made a new control, using TreeView as the base.
Here's a PictureBox version of that class:
public class PictureBoxNoFlicker : PictureBox {
public PictureBoxNoFlicker() {
this.SetStyle(ControlStyles.DoubleBuffer, true);
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
}
}
This code replicates double-buffering. Simply use this control instead of the standard PictureBox.
Unfortunately, I can't reproduce your problem, so I am taking a bit of a punt and hoping this resolves your issue.

Related

Controlling how, when, and if child controls are drawn (.NET)

I am writing an application in .NET that has a plugin interface. That plugin interface provides several ways to draw information (controls) onto the surface of the application window. While there are several reasons why I am doing this, the main reason is to provide custom colorization to text, either through the use of a graphic or directly manipulating the color of the text based on the background color. I do this through the use of a "text mask" which is a black and white bitmap that works as an "alpha" map to let the Paint method know where to apply the texture/color changes.
The plugin developer has the option of using regular text (such as with a label), mask text (which is drawn to the mask rather than as a regular control), OR letting the user decide. To go along with this, I have provided a modified label class that can either be drawn "normally' (when the text mask is not set for the control), or to the text mask when the User OR Developer decides (depending on what the plugin developer wishes to offer to the user). Here is the class's code so that you understand how this is being done:
public class MaskingLabel : Label
{
private static readonly SolidBrush maskBrush = new SolidBrush(Color.White);
public Bitmap Mask { get; set; }
public MaskingLabel() : base() { }
protected override void OnPaint(PaintEventArgs e)
{
if (Mask == null)
base.OnPaint(e);
else
{
Graphics g = Graphics.FromImage(Mask);
g.DrawString(Text, Font, maskBrush, Location);
}
}
}
The problem I am running into is that this approach requires that I handle controls in a very specific order so that the form is drawn correctly. I need to find the most efficient approach to get the tasks listed below done in the order given. I have thought of three possibilities discussed further down. For reference, this is the order in which tasks must be done:
All "MaskingLabel" controls that have the bitmap object set to the mask must be drawn first so that the mask is created before the next step.
The mask is applied to the background picture.
The resulting Bitmap is drawn in a way similar to the way a background would be drawn (except that it is modified first).
The rest of the controls are drawn as normal.
Is there a way for me to insure this happens without separating the controls manually? My first guess is no. As such, I have a few guesses below about how I should go about this. I was hoping someone with more in depth knowledge of GDI+ could offer some insight.
One idea that has occurred to me is to draw the masked controls during the OnPaintBackground method. However, I don't want to waste time by painting the controls twice. This means I would need to filter out which controls are drawn during the main Paint method which effectively leads us to option 2 (FAIK):
I can manually filter out the controls which draw to the mask so that they don't get added to the control. My question here though is would they get drawn at all? Can I manually force them to invoke the OnPaint method?
If doing that wouldn't work, then perhaps I can create a separate derived panel control to serve as a "backdrop" child control that acts as the background picture which can be forced to be drawn first?
EDIT (With Part of the answer):
I realized after posting this that I already have part of the solution built into my project. Still, I think it is a legitimate question to ask, so if anyone can add insight beyond what I have done in my description below, it is welcome.
Specifically, my project has only two controls that are added to the "root" form: a bar that goes to the top (docked at the top when it is shown), and a transparent panel that occupies the rest of the space (with a dock style set to fill). So my solution would be to add the mask controls to the main form and add all the rest to the panel. This only leaves one remaining issue to be resolved: How do I make sure that the panel and the bar are drawn last? (As part of step 4 in the first list?)

My application executes slower when I move its window

My question might seem silly to you, but I realized that moving my applications form makes the code inside it run slower. E.g. when I load a bitmap image and apply some image editing algorithms on it, it takes about 22 secs for the whole process to finish. But if I move the form during execution, it adds some 3-4 extra seconds to the elapsed time. I was able to spot the delay using a Stopwatch. So how can I get around this behaviour, if possible at all?
This is just an hypothesis that requires your investigation, as you didn't post any code and thus it is impossible to really know what is going on.
Most probably you move the boundaries of the image outside the screen. When you move in again, the windowing engine will do some draw calls on those rectangles to be redrawn. The same happens on resize when you enlarge but not when you shrink the window.
If this is the case, then you will not experience any extra draw calls as long as you don't cover/uncover areas of the image.
So this is not an answer but in your place I would override the Paint() method and log how many excess calls are made. Based on this, I'd search for a solution, such as suppress those calls like this:
public override void Paint()
{
if (algorithmRunning)
{
return; // suppress any further computations
}
base.Paint(); // do actual redraws
}
This code is just an example, you'll have to fix it according to the MSDN documentation.
What you should NOT do is just hook into the OnPaint() event, because then you'll still have the actual Paint() method called.

More than 2 millions rectangles in a WPF canvas

I am creating a custom control for semiconductor wafermap
Each of those small rectangle need to satisfy following requirements;
1) Tooltip to show the index
2) clickable to include or exclude from the wafermap definition.
no of dies in the wafermap may cross 2 millions in the case of 1400 x 1450 dies.
at certain point i need to show all the dies in a window (most of the clicking will happen in zoomed view).
Currently I am adding each die separately using Rectangle shape and store the coordinate information (index like (10,10)) for the tooltip as an attached property.
I use different style to each die; depending on certain calculation and position of the die.
DieStyle1 = new Style { TargetType = typeof(Rectangle) };
DieStyle1.Setters.Add(new Setter(Shape.FillProperty, Brushes.MediumSlateBlue));
DieStyle1.Setters.Add(new Setter(Shape.StrokeProperty, Brushes.White));
DieStyle1.Setters.Add(new EventSetter(MouseDownEvent, new MouseButtonEventHandler(DieStyle1_MouseDown)));
this approach is slow and use high memory too. so suggest a better way to achieve this in WPF?
In creating a designer for christmas tree lights, I ran into the same problem. Using UIElement or Shapes is way too slow when you get to 100+ items. The best approach to handle a very large number of items entails using double-buffering with your own managed buffer of the image and a structure to handle the clicks. I have posted my project which should give you a good start. It can be obtained at:
http://sourceforge.net/projects/xlightsdesigner/
You are interested in the Controls\ChannelitemsCanvas.cs. It can be modified to suit your needs and uses a quad-tree to store the rectangles so that click events can be quickly determined.

WP7 Pivot control and a WebBrowser control

I have a Pivot which contains a WebBrowser control that practically takes up the whole page (appart from the Pivot header of course).
I would like to figure out how to make the WebBrowser control allow for the user to swipe left/right to activate the Pivot control. Currently it just pans the WebBrowser control left/right
Can this be done??
Thank
While I cannot tell you exactly how to pass the swipes to the pivot, I can tell you how to do a part of the job: how to catch/analyze/disable custom gestures over the WebBrowser.
If I remember correctly, in the 7.0:
the WebBrowser component consisted almost only of an internal TileHost wrapped in some grids/borders
the TileHost did all the work related to processing touch events
the TileHost did it completely internally (in the native layer), without the .Net seeing any manipulation-events (I think), or at least it ignored all the attempts to handle/override the manipulation-event on the upper layer. The WebBrowserInterop class was mostly empty in these matters.
Now, in the 7.5 that I have (maybe on 7.1 too, I dont know), it seems that the MS is working really hard on some WebBrowser manipulation problems --- I think they are working towards having the scrolling/swiping fully processed by the .Net layer. They have written a special class PanZoomContainer and injected them into the VisualTree of WebBrowser's internal template. The WebBrowserInterop was greatly enriched with many tunnels for event notifications. The WebBrowserInterop hooks into PanZoomContainer's ManipulationEvents, then passes them to the native layer. Also, it listens to events/commands from the native layer, called for example "ZoomAndScroll" or "ShowSIP" - and mostly passes them back to the PanZoomContainer. The idea is crystal clear right? They have rewired the event handling from completely-internal to a bit of spaghetti, but have achieved passing them through the PanZoomC.
Now, whats in that for me/us?
It is the PanZoomContainer, whose Mani-Events are inspected. The TileHost does not capture them now. In this version of the WebBrowser control, it's VisualTree consists of some borders, grids, a PanZoomContainer and a TileHost (the renderer). It is similar to that:
WebBrowser
PanZoom
ContentPresenter
Border/Name="border" <- you want this one
TileHost
I've skipped a few Borders and Grids, they are mostly irrelevant to the problem. Now, if the PanZoomContainer's Mani-Events are listened to, let's block them!
Using VisualTreeHelper, just dig deeper and deeper until you find a FrameworkElement.Name=="border". This is the border that wraps the TileHost that is the "renderer" that takes 99% space of the control. Be warned that there's a ContentPresenter, so you may have to wait until the controltemplate gets instantiated (ie. Loaded/LayoutUpdated).
Once you have your hands on that Border, attach all Mani-Event handlers to it: started, delta and completed. PanZoom is a normal xaml/silverlight/.net/etc control, so it actually obeys e.Handled = true :) Now, if you want to disable ie. vertical scrolling, filter the Delta and Completed events for Translation.Y<>0. If you want to disable tapping but leave srolling/panning - filter X==0&Y==0.
And that was the easy part.
The hard part is to experiment with filtering on different Start/Delta/Stop and adjusting the behaviour to your likes.
Although it might look very nice and tempting, this will NOT get you any real/nice results easily. For example, I wrote "if you want to disable vertical scrolling, then set a filter 'if y==0 then e.handled=true' ". Great? easy? Not!
Assume we want to "disable bouncy horizontal panning" while leaving "vertical scrolling". or vice versa, whatever, it is only an example:
Here's a small rectangular device with a sensitive touchscreen. Please make such a vertical swipe/pan/drag on the screen, that the resulting X-compound will be ZERO. If you set such filter, it will be almost impossible to it properly. Your users will want to kill you for forcing them to retry-that-vertical-scrolling for five or more times, until they make a perfect vertical swipe.
Of course you can make it not ==0, but leave some small margin. ok. But if you make the margin too big, the control will catch the intermediate offaxis movement and make a tiny horizontal pan also.. After a few unlucky vertical swipes, the total horizontal pan may accumulate from those small leftovers will accumulate and the diplacement maybe will be noticeable.
But there's some another vile side effects:
Saying shortly, you have commited e.Handled=true. The event is GONE. Dead. Deased. if you just wanted the WebBrowser to SKIP for example horizontal swipes, so that the outer (Pivot) control notices them and processes..... whoops. The event is GONE. Earlier, the TileHost/PanZoomC have extinguished the events, now you have it done yourself. Sounds like a bad joke, eh?
Fortunatelly:
since you have attached your handlers to the bottommost "border", they may not only block the events, but may also actually listen&publish them elsewhere. That is, if those handlers detect an interesting movement, they may e.Handled=true on it, but at the same time they can notify your custom objects about that discovery, and ie. start your storyboards.
mani-events are at hand, but there is also a second layer that listens to the manipulations: the GestureListener/GestureService from the Silverlight Toolkit. It reports events after they are handled by mani-events, but it reports them with no regard to any e.Handled=true that were set on them. It is completely separate gesture-listening mechanism, and you can also use it to detect manipulations that were 'cancelled'
.. and so the fun goes like that and maybe even a little further.
This is similar to putting a Map inside a Pivot - which is discussed here - http://mine.tuxfamily.org/?p=111 - already mentioned in quite a few questions - https://stackoverflow.com/search?q=mine.tuxfamily.org
In general, the advice seems to be usability based:
try to avoid putting controls which use Touch inside the PivotItem's
As an aside, if you are just using the web browser control for a very small amount of static html (so you don't need any scrolling at all) then you could just remove HitTesting from the web browser.
I do not know WP7 Pivot, but are there any Preview* events on the Pivot control that allow you to handle the touches and mark them as processed?
Call the below method and pass your parameter as PivotControl x:name and WebBrowserControl x:name to this method.
Here the WebBrowserControl is placed in second pivot item i.e. Pivot Index is 1 and I am trying to swipe left or right and reach to pivot index 2 or 1 respectively.
public static void SwipteLeftRight(Microsoft.Phone.Controls.Pivot pivotControl, Microsoft.Phone.Controls.WebBrowser webBrowserControl)
{
var gesListener = GestureService.GetGestureListener(webBrowserControl);
gesListener.Flick += ((sen, args) =>
{
if (args.Direction == System.Windows.Controls.Orientation.Horizontal)
{
if (args.HorizontalVelocity < 0)
{
if (((Microsoft.Phone.Controls.PivotItem)(pivotControl.SelectedItem)).Header.ToString().Trim() == "Pivot Item name")
{
pivotControl.SelectedIndex = 2; //Next Pivot item
}
}
else if (args.HorizontalVelocity > 0)
{
if ((Microsoft.Phone.Controls.PivotItem)(pivotControl.SelectedItem)).Header.ToString().Trim() == "Pivot Item name")
{
pivotControl.SelectedIndex = 0; // Previous Pivot Item
}
}
}
});
}
It worked for me. Cheers

Simultaneous updates across two display contexts in openGL

I have a C# .NET application with which I've created a custom image display control. Each image display represents its own display context and draws the image using glDrawPixels (Yes I know it would be better to use textures, I plan to in the futures but this app is already too far along and my time is limited).
I am now trying to have both images pan simultaneously. That is, when one image is moved down ten pixels, the second image moves down ten pixels. Like so:
imageOne.YPan -= 10;
imageTwo.YPan -= 10;
imageOne.Invalidate(); //This forces a redraw.
imageTwo.Invalidate(); //This forces a redraw.
Alright so here is the problem I am having. Only one of the images displays is redrawing. If I place a pause in between the two Invalidate calls and make the pause duration at least 110 milliseconds both will redraw, but not simultaneously. So it looks as if the second image is always trying to catch up to the first. Plus, a 110 millisecond pause slows down the motion too much.
I have tried placing the updating and invalidating of each image in its own thread but this did not help.
At the beginning of drawing I make the appropriate context is current, and at the end I am calling swapbuffers(). I tried adding a glFinish to the end of the draw function, but there was no change.
Could it be that its the graphics card that is the problem? I am stuck using an integrated gpu that only has openGL 1.4.
Hopefully, I have provided enough detail that the answer to my problem can be found.
Its difficult telling what's wrong with what you do since you give so little detail. Here are some pointers which may help.
- before doing something in a context, make sure you make it the current one. If you want to pan two contexts, make the first one current, pan it and then make the second one current and pan it. These is no real reason why this should not work.
- If it looks like there is a timing problem, adding glFinish() at strategic places may help weed the problem out
- As should always be done, on occasions call glError() and see that everything went well.
- I'm not sure how this is done in the framework you're talking about but you should make sure that both contexts get a swapBuffers() call for every frame.
Invalidate doesn't force an immediate redraw. It marks the window invalid, and when the message queue runs out of other messages, a paint message will be created and processed. But that won't happen until you finish processing the current message and return to the main message loop, and it may be delayed even more than that.
Generally OpenGL animation is an exception to the rule of doing all drawing inside Control.OnPaint (or in a handler for the Control.Paint event).

Categories