How can I DoubleBuffer a winforms Treeview? - vb.net

I have a standard winforms treeview control that keeps flickering whenever I hover my mouse over any other control on the form. I would like to doubleBuffer the treeview to reduce the flickering but I have no idea how to do so.
Could someone please show me how to achieve my goal?
Many thanks

I had to implement a double buffered TreeView a way back when developing some parts of a financial software, because of the same scenario. The TreeView implementation in .NET is a pretty sketchy one, but here is how I resolved it:
Public Class DoubleBufferedTreeView
Inherits System.Windows.Forms.TreeView
Public Sub New()
' This call is required by the Windows Form Designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Me.SetStyle(ControlStyles.OptimizedDoubleBuffer, True)
Me.SetStyle(ControlStyles.AllPaintingInWmPaint, True)
Me.UpdateStyles()
End Sub
End Class
The other reason I implemented this in this manner was because I had to do some custom drawing to show where the user was dragging-and-dropping the TreeNodes, so I did some custom drawing to display a bar in between nodes.
DoubleBuffering was not a fullproof solution as the TreeView flickered slightly, but that was the best I was able to get it at the time. I also did not want to suspend the TreeView as others have stated, because I still wanted the TreeView to perform its layout and normal operations, even when the user was possibly using different parts of the UI.
PS. the code is almost identical for C#.

All answers here are wrong.
The Treeview ignores
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
completely.
And using BeginUpdate() or SuspendLayout() does not change anything.
The correct answer was given by Hans Passant here: Treeview flickering?

In Addition to activating the double buffering like Int3 already said, I suggest you to suspend the layout logic temporarily while you do the processing that causes the flickering.
You need to first call SuspendLayout to stop building the full tree contents in th UI.
After you have finished your processing, you start the layout logic again eith ResumeLayout().
MSDN documentatiion for SuspendLayout with code sample is here.
private void buildTreeContent()
{
// Suspend the form layout and add two buttons.
this.SuspendLayout();
// Do your work here
// ...
// Make the Form do paint the layout again.
this.ResumeLayout();
}
This should help a great deal of flicker as building the tree element is resource consuming, we've done that a lot of times in our projects.
An alternative approach is to work with the Windows messages.
This is explained in more depth in another SO thread.

DoubleBuffer does not affect Treeview in .NET. If your purpose is to reduce flicker when the TreeView is drawn, then I suggest you have a look at BeginUpdate and EndUpdate.
I had a similar issue, and attempted to double buffer the form in hopes of it fixing the issue with my treeview. As it turns out, setting the DoubleBuffered property does not affect the TreeView control.
Hope this helps. Useful link

Hope you are ok with the C# code as it is trivial.
DoubleBuffer is actually a protected property of the Control. So you can only access it from the declaring class or that are derived.
Following snippet will help you understand how to set this property.
public sealed class MyNonFlickringTreeView:Treeview
{
public MyNonFlickringTreeView()
{
this.DoubleBuffered=true;
}
}

Related

VB.NET - Hiding Form on Load

So theres a few questions on this but they all give the same answer Me.Hide() which "works" if you count that when it loads, It will SHOW but then will hide seconds after which makes a weird Shadow-y-Laggy Effect.
Example:
(The small Window that shows and almost instantly hides is the window im trying to NEVER show)
Is there a way to actually hide the WHOLE form upon load? I know of the whole VisibilityCore method but with that I cant find a way to show it again at a later point.
So is there a way to hide it so it NEVER shows unless I tell it to Show?
Currently im using "Hide() combined with Form.Show Method".
If you set the form's Opacity property to 0 (through the Property Window) you shouldn't have this problem.
When you want to show the form (if it's going to be shown at all) just set the opacity back to 1.0.
Opacity is still a hack. The correct way is to not show it at all, versus making it invisible. To do this, don't make it the "Startup Object" at all.
On the Application tab of the Project Properties screen, there is a Startup Object setting. Create a Module with a Sub Main() and make that the entry point of your app by selecting it as the Startup Object instead of that little form that apparently doesn't do anything visual.
Maybe you have some initialization code in that starting form...move that to Sub Main.
you can minimize the form upon application launch.
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Form fm = new Form1();
fm.WindowState = FormWindowState.Minimized;
Application.Run(fm);
}

How to Customize ListView Column Headers

I am fairly new to programming and I have never attempted writing my own class before. I would like to try. What I am trying to achieve is to write my own (basic) listview control to start with. I am struggling with the first bit...
As a start I want to try and centre all of the Column Headers but keep the row text using their own formatting. I would like to implement a custom property if possible, but to start with I just want to override the DrawColumnHeader event.
Can someone point me in the right direction (maybe an example). I have setup a new class, inherited the listview control and added the following event:
Protected Overrides Sub OnDrawColumnHeader(ByVal e As System.Windows.Forms.DrawListViewColumnHeaderEventArgs)
However, I am finding that any code I place in this event isn't getting fired when the listview gets drawn.
Reason was that I didn't have the OwnerDraw property set to True.

accessing dynamically created form elements from a thread in vb.net

I am new to VB and .Net and I have a task that I am unable to proceed.
I start a thread when screen1 Loads. User then goes to screen2.
On screen2 the thread is still running behind. when the user performs an action(say click) on screen2, it will trigger the thread to access elements on screen2
The elements in screen 2 are dynamically created and not designed in IDE.
So in essence a thread that is created on one form needs to access dynamically created form elements on another form.
If my question is too simple, please forgive me. If it is not clear please let me know and I will rephrase it.
Note: The element that I am talking about is basically a picture box inside a flow layout panel.
Thanks in advance
All help is eagerly(biting nails now) awaited and greatly appreciated.
EDIT
Lets say a thread called ctThread was started in dashboard screen
ctThread.start()
This thread is running endlessly, waiting for a trigger event.
Meanwhile User has gone to a screen called QuizScreen and on this screen(form) I have to update some dynamically created elements whose names I know.
So when the time is right for ctThread which is waiting (Listener Thread) it will call the sub below.
Sub
public sub changeComputerStatus(ByVal node)
Dim flowpanel As FlowLayoutPanel = CType(QuizScreen.FlowLayoutPanel1.Controls("flow_" + node), FlowLayoutPanel)
Dim pictControl As PictureBox = CType(flowpanel.Controls("pict_" + node), PictureBox)
pictControl.Image = System.Drawing.Image.FromFile(Application.StartupPath & "\images\application-on.png")
end sub
here node keeps changing. This is how I differentiate each control I create.
Note : This thread was started in a screen called dashboardscreen and the user is now on a different screen QuizScreen.
The first line of the sub I gave above runs and returns nothing to the flowpanel. Hence when It goes to the next line, it is not able to use the nothing as reference. And hence the above mentioned error.
Two things.
The most important thing you must know is that you cannot directly access a control's properties from a background thread. Controls may only be manipulated from the foreground UI thread. In order to access controls from a background thread, you need to use some form of asynchronous programming, e.g. creating delegates a la .NET 1.x - 3.5 or using the new Task<T> and async and await keywords.
Did you actually add the dynamically created controls to the form's control collection? Mind you, you'll still need to access the controls via delegates or some other asynchronous method as explained in 1 above.
UPDATE:
To answer the question in the OP's comment below: you can also Invoke a method on a UI object. Basically, you're telling .NET to run the invoked method and it runs on the UI object's creating thread (in this case, the UI thread), which is what you want. This will allow you (depending on the method or property invoked) to "update" the control "from the background"—again, this is all sleight of hand; when invoking a method on a UI object, the invkoked method runs on the UI thread possibly using data passed into said method from the background task.
Also, check out the MSDN documentation on the BacgkroundWorker (this was introduced in .NET 2.0 and is superseded by the async and await keywords along with Task<T> in .NET 4.5). There is lots of documentation available that explains exactly how to do what you're asking. It's not hard to find by performing a quick search on MSDN or Bing (or your preferred search engine).

Creating a Partial Class for a Form

I would like to create a partial class for my form. I have a lot of events, and it gets messy, so I would like to break up sections into their own files.
The problem: When I create a partial class of my form, say:
Partial Public Class Form1
End Class
Visual Studio decides I need another form for this partial class.
Questions:
1. How do I create a partial class for a form?
2. If I cant do that, how can I break up all the events in my form into different files?
Yeah, it does. As soon as you drop a control on this phantom form, you'll get the design-time code (InitializeComponent) generated into that source code file. This is compatibility behavior for .NET 1.x, it didn't support the Partial keyword. Which will break the build, there are now two of them. It is somewhat avoidable with careful clicking, but you know it's going to happen sooner or later.
Other things go wrong too btw, the designer can no longer track an event handler when you move it from one file to another. And will readily let you add another, a much trickier source of bugs.
This just doesn't work very well, abandon hope of relying on it to solve your problem.
The generic diagnostic is that a convoluted user interface begets convoluted code. But that ship has sailed, no doubt. A more structural solution is pursuing the MVC model, separating the data from the view of the data. You'll still have a lot of event handlers but they won't do anything more than calling a method of a class that does the real work. Whose source code can of course live in another source code file. The typical hangup is that Windows Forms has no support whatsoever built in for this, you have to craft it by hand. Nothing similar to the MVVM model in WPF.
Something that can work well is isolating control + code into a separate UserControl. You have to do so carefully though, you don't want to have to add a bunch of properties and events that expose internal controls.
Sometimes I create partial classes for better readibility, especially when I have very large classes.
But when I click on the partial class, then the VS IDE will open the form editor showing me an empty form. If I do not care, than I could damage the main form (it seems to be a bug of VS 2008/2010)
A possibility could be using DesignerCategoryAttribute Class
Mark the partial class with the attribute "code".
<System.ComponentModel.DesignerCategory("code")>
Partial Class Form1
End Class
In this way when you click on the file, you open the class in the code editor.
Of course this will apply to all files, also to the main form file.
If you want to edit again your form in the form editor, you have to quote the attribute:
'<System.ComponentModel.DesignerCategory("code")>
Some more details here.
While it does not answer the original question, I found using regions made my code a little more manageable/readable.
#Region "RegionA"
#End Region
I orginally called this method a "hack", thus the comment below.
Not sure what you mean be "Visual Studio decides you need another form", however, are you sure the new Form1 partial class is declared in the corresponding original namespace?
All partial classes for a given .NET type must be declared in the same namespace of course (whatever files they're stored on).
I appreciate the answers given by Hans and I'm not disputing these at all. It is curious though that in Visual Studio 2010, when you create a form called say Main you get a Main.designer.vb which is a partial class. It says 'Partial Class Main' at the top. This class doesn't open a form when clicked. It also includes reference to Event Handlers. So I was wondering how do they get around this? Is there a way to create one of these 'special' partial classes that work as we would expect.
I noticed that when I created a Form Partial class, that the icon went from a class icon to a form icon. The icon associated with the Main.designer.vb file looks like a class icon with a arrow.
what worked for me (VS 2010) was naming Form1 class, already saved in Form1.vb with its own designer (Form1.Designer.vb) as:
Public Class Main 'saved in Form1.vb
VS updated the name in the designer as:
Partial Class Main 'saved in Form1.Designer.vb
then I created another "partial class" with the same name:
Partial Class Main 'saved in Main.vb
Whether I am editing Form1.vb or Main.vb VS shows me on the top navigation pan all the routines, functions, subs, even background workers and timers. For event handlers, to avoid the loophole mentioned earlier (you click on a control in the layout designer and a brand new event handler will be created in the original Form1.vb) I go:
Partial Public Class Main 'in Form1.vb file
Private Sub SomeControl_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles SomeControl.Click
Call SomeControlClick(sender, e)
End Sub
End Class
Partial Public Class Main 'then in Main.vb file
Private Sub SomeControlClick(ByVal sender As Object, ByVal e As System.EventArgs)
'blah blah
End Sub
End Class

Force multi-threaded VB.NET class to display results on a single form

I have a windows form application that uses a Shared class to house all of the common objects for the application. The settings class has a collection of objects that do things periodically, and then there's something of interest, they need to alert the main form and have it update.
I'm currently doing this through Events on the objects, and when each object is created, I add an EventHandler to maps the event back to the form. However, I'm running into some trouble that suggests that these requests aren't always ending up on the main copy of my form. For example, my form has a notification tray icon, but when the form captures and event and attempts to display a bubble, no bubble appears. However, if I modify that code to make the icon visible (though it already is), and then display the bubble, a second icon appears and displays the bubble properly.
Has anybody run into this before? Is there a way that I can force all of my events to be captured by the single instance of the form, or is there a completely different way to handle this? I can post code samples if necessary, but I'm thinking it's a common threading problem.
MORE INFORMATION: I'm currently using Me.InvokeRequired in the event handler on my form, and it always returns FALSE in this case. Also, the second tray icon created when I make it visible from this form doesn't have a context menu on it, whereas the "real" icon does - does that clue anybody in?
I'm going to pull my hair out! This can't be that hard!
SOLUTION: Thanks to nobugz for the clue, and it lead me to the code I'm now using (which works beautifully, though I can't help thinking there's a better way to do this). I added a private boolean variable to the form called "IsPrimary", and added the following code to the form constructor:
Public Sub New()
If My.Application.OpenForms(0).Equals(Me) Then
Me.IsFirstForm = True
End If
End Sub
Once this variable is set and the constructor finishes, it heads right to the event handler, and I deal with it this way (CAVEAT: Since the form I'm looking for is the primary form for the application, My.Application.OpenForms(0) gets what I need. If I was looking for the first instance of a non-startup form, I'd have to iterate through until I found it):
Public Sub EventHandler()
If Not IsFirstForm Then
Dim f As Form1 = My.Application.OpenForms(0)
f.EventHandler()
Me.Close()
ElseIf InvokeRequired Then
Me.Invoke(New HandlerDelegate(AddressOf EventHandler))
Else
' Do your event handling code '
End If
End Sub
First, it checks to see if it's running on the correct form - if it's not, then call the right form. Then it checks to see if the thread is correct, and calls the UI thread if it's not. Then it runs the event code. I don't like that it's potentially three calls, but I can't think of another way to do it. It seems to work well, though it's a little cumbersome. If anybody has a better way to do it, I'd love to hear it!
Again, thanks for all the help - this was going to drive me nuts!
I think it is a threading problem too. Are you using Control.Invoke() in your event handler? .NET usually catches violations when you debug the app but there are cases it can't. NotifyIcon is one of them, there is no window handle to check thread affinity.
Edit after OP changed question:
A classic VB.NET trap is to reference a Form instance by its type name. Like Form1.NotifyIcon1.Something. That doesn't work as expected when you use threading. It will create a new instance of the Form1 class, not use the existing instance. That instance isn't visible (Show() was never called) and is otherwise dead as a doornail since it is running on thread that doesn't pump a message loop. Seeing a second icon appear is a dead give-away. So is getting InvokeRequired = False when you know you are using it from a thread.
You must use a reference to the existing form instance. If that is hard to come by (you usually pass "Me" as an argument to the class constructor), you can use Application.OpenForms:
Dim main As Form1 = CType(Application.OpenForms(0), Form1)
if (main.InvokeRequired)
' etc...
Use Control.InvokeRequired to determine if you're on the proper thread, then use Control.Invoke if you're not.
You should look at the documentation for the Invoke method on the Form. It will allow you to make the code that updates the form run on the thread that owns the form, (which it must do, Windows forms are not thread safe).
Something like
Private Delegate Sub UpdateStatusDelegate(ByVal newStatus as String)
Public sub UpdateStatus(ByVal newStatus as String)
If Me.InvokeRequired Then
Dim d As New UpdateStatusDelegate(AddressOf UpdateStatus)
Me.Invoke(d,new Object() {newStatus})
Else
'Update the form status
End If
If you provide some sample code I would be happy to provide a more tailored example.
Edit after OP said they are using InvokeRequired.
Before calling InvokeRequired, check that the form handle has been created, there is a HandleCreated property I belive. InvokeRequired always returns false if the control doesn't currently have a handle, this would then mean the code is not thread safe even though you have done the right thing to make it so. Update your question when you find out. Some sample code would be helpful too.
in c# it looks like this:
private EventHandler StatusHandler = new EventHandler(eventHandlerCode)
void eventHandlerCode(object sender, EventArgs e)
{
if (this.InvokeRequired)
{
this.Invoke(StatusHandler, sender, e);
}
else
{
//do work
}
}