|
Hi - thanks for the great control - it seems to be doing exactly what I need for my project, although I'm getting a bit of weird behaviour.
Each of my treenodes can potentially have a different imagekey set. When the tree is drawn it all displays correctly, but any nodes that are deselected seem to revert to the tree's default imagekey instead of using the node's imagekey.
[edit] it also happens with multiselect, if I click one node, then shift-select another, the first node gets the default image key[/edit]
[edit2] on closer inspection, it appears that the imagekey property of my nodes is getting cleared at some point after selection [/edit2]
Is there a property I need to set to fix this (which works fine in the regular MS tree) or do I need to add some extra code when adding my nodes to instruct the tree what to do?
Thanks,
Richard
-- modified at 4:29 Thursday 20th September, 2007
|
|
|
|
|
When you call Nodes.Clear, all nodes are correctly removed from the tree, but the selection (SelNodes) remains, which means you get an InvalidOperationException if you call FullPath on one of the Nodes in SelNodes, as it no longer has a parent.
Is this correct behaviour that SelNodes should not be affected by removal of nodes even when removing all nodes?
|
|
|
|
|
Call myMWTreeView.RemoveNode(s) if you want them removed from SelNodes as well - which I'm sure is what everyone wants.
If you have a solution as to how to find out when the Nodes property is cleared, I'm all ears
|
|
|
|
|
Hi!
Usually on a tree view when you press a key it moves the selection to the first matching node.
This default behavior is suppresed when SameLevelMultiBranch mode is selected.
How can I keep it and still use SameLevelMultiBranch mode too?
Thanks,
Laurentiu
|
|
|
|
|
This is actually true for all TreeViewMultiSelect values except Classic, not just SameLevelMultiBranch.
I have not had the intention of having this functionality in the MWTreeView.
|
|
|
|
|
Did you notice if the checkboxes are visible (with default state images) when you change nodes (eg. navigate by key down/up) the checkboxes flicker a lot?
Why do they flicker? Is there any quick fix?
|
|
|
|
|
I have researched a little bit and get it very smoothly and cool!
Add this code to it and allow the DoulbeBufferStyle to rock!
(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint) //for .net 2.0 (for 1.1 remove the Optimized word)
Here are the changes:
#1 So, first of all: uncomment the UserPaint from the ctor:
this.SetStyle(System.Windows.Forms.ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(System.Windows.Forms.ControlStyles.DoubleBuffer, true);
this.SetStyle(System.Windows.Forms.ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(System.Windows.Forms.ControlStyles.ResizeRedraw, true);
this.SetStyle(System.Windows.Forms.ControlStyles.Selectable, true);
this.SetStyle(System.Windows.Forms.ControlStyles.SupportsTransparentBackColor, true);
this.SetStyle(System.Windows.Forms.ControlStyles.UserPaint, true);
#2 Then declare two private variables
private Bitmap internalBitmap = null;
private Graphics internalGraphics = null;
#3 The most important thing: Change the behavior of WndProc
protected override void WndProc(ref Message message)
{
const int WM_ERASEBKGND = 0x0014;
const int WM_PAINT = 0x000F;
const int WM_PRINTCLIENT = 0x0318;
switch (message.Msg)
{
case WM_ERASEBKGND:
message.Msg = (int)0x0000;
return;
case WM_PAINT:
if (internalGraphics == null)
OnResize(EventArgs.Empty);
Win32.RECT updateRect = new Win32.RECT();
if (Win32.GetUpdateRect(message.HWnd, ref updateRect, false) == 0)
break;
Win32.PAINTSTRUCT paintStruct = new Win32.PAINTSTRUCT();
IntPtr screenHdc = Win32.BeginPaint(message.HWnd, ref paintStruct);
using (Graphics screenGraphics = Graphics.FromHdc(screenHdc))
{
PaintEventArgs e = new PaintEventArgs(internalGraphics, Rectangle.FromLTRB(
updateRect.left,
updateRect.top,
updateRect.right,
updateRect.bottom));
OnPaintBackground(e);
IntPtr hdc = internalGraphics.GetHdc();
Message printClientMessage = Message.Create(Handle, WM_PRINTCLIENT, hdc, IntPtr.Zero);
DefWndProc(ref printClientMessage);
OnPaint(e);
Win32.BitBlt(screenHdc, 0, 0, updateRect.right , updateRect.bottom , hdc, 0, 0, Win32.SRCCOPY);
internalGraphics.ReleaseHdc(hdc);
}
Win32.EndPaint(message.HWnd, ref paintStruct);
return;
}
base.WndProc(ref message);
}
#4 Add cleaning up methods and change resizing behavior:
private void DisposeInternal()
{
if (internalGraphics != null)
internalGraphics.Dispose();
if (internalBitmap != null)
internalBitmap.Dispose();
}
protected override void OnResize(EventArgs e)
{
if (internalBitmap == null
|| internalBitmap.Width != Width
|| internalBitmap.Height != Height)
{
if (Width != 0 && Height != 0)
{
DisposeInternal();
internalBitmap = new Bitmap(Width, Height);
internalGraphics = Graphics.FromImage(internalBitmap);
}
}
}
#5 Call the cleaning on dispose: Change the Dispose method as below:
protected override void Dispose(bool disposing)
{
if (disposing)
{
DisposeInternal();
if (components != null)
{
components.Dispose();
}
}
base.Dispose(disposing);
}
#6 The final step: Add the Win32 P/Invoke helper class
internal class Win32
{
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern int BitBlt(IntPtr hDC, int x, int y, int nWidth, int nHeight, IntPtr hSrcDC, int xSrc, int ySrc, int dwRop);
public const int SRCCOPY = 0xcc0020;
[DllImport("User32.dll")]
public static extern int GetUpdateRect(IntPtr hwnd, ref RECT rect, bool erase);
[DllImport("User32.dll", SetLastError = true)]
public static extern bool GetWindowRect(IntPtr handle, ref RECT rect);
[DllImport("User32.dll")]
public static extern IntPtr BeginPaint(IntPtr hWnd, ref PAINTSTRUCT paintStruct);
[DllImport("User32.dll")]
public static extern bool EndPaint(IntPtr hWnd, ref PAINTSTRUCT paintStruct);
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[StructLayout(LayoutKind.Sequential)]
public struct PAINTSTRUCT
{
public IntPtr hdc;
public int fErase;
public RECT rcPaint;
public int fRestore;
public int fIncUpdate;
public int Reserved1;
public int Reserved2;
public int Reserved3;
public int Reserved4;
public int Reserved5;
public int Reserved6;
public int Reserved7;
public int Reserved8;
}
}
#7 That's all!
Now it works just great! Thanks to GOD that M$ invented BitBlt, as the coolest function of the whole Windows API.
Hope this helps!
P.S. To the author: Please include the changes in the downloadable sources. Thanks!
Laurentiu Macovei (softer)
email: alonecomp -at- gmail -dot- com
|
|
|
|
|
I just noticed that if the BackColor is Window the TreeView's background will be a mixture of backColor and black.
To fix this inconvenience I have updated the WndProc as following:
protected override void WndProc(ref Message message)
{
const int WM_ERASEBKGND = 0x0014;
const int WM_PAINT = 0x000F;
const int WM_PRINTCLIENT = 0x0318;
switch (message.Msg)
{
case WM_ERASEBKGND:
message.Msg = (int)0x0000;
return;
case WM_PAINT:
if (internalGraphics == null)
OnResize(EventArgs.Empty);
Win32.RECT updateRect = new Win32.RECT();
if (Win32.GetUpdateRect(message.HWnd, ref updateRect, false) == 0)
break;
Win32.PAINTSTRUCT paintStruct = new Win32.PAINTSTRUCT();
IntPtr screenHdc = Win32.BeginPaint(message.HWnd, ref paintStruct);
using (Graphics screenGraphics = Graphics.FromHdc(screenHdc))
{
IntPtr hdc = internalGraphics.GetHdc();
using (Graphics g = Graphics.FromHdc(hdc))
{
PaintEventArgs e = new PaintEventArgs(g, Rectangle.FromLTRB(
updateRect.left,
updateRect.top,
updateRect.right,
updateRect.bottom));
OnPaintBackground(e);
Message printClientMessage = Message.Create(Handle, WM_PRINTCLIENT, hdc, IntPtr.Zero);
DefWndProc(ref printClientMessage);
OnPaint(e);
}
Win32.BitBlt(screenHdc, 0, 0, updateRect.right, updateRect.bottom, hdc, 0, 0, Win32.SRCCOPY);
internalGraphics.ReleaseHdc(hdc);
}
Win32.EndPaint(message.HWnd, ref paintStruct);
return;
}
base.WndProc(ref message);
}
Cheers!
Laurentiu Macovei (softer)
email: alonecomp -at- gmail -dot- com
|
|
|
|
|
Thank you for all these additions.
I have had as a goal not to do anything relating in particular to the Windows operating system when developing the MWTreeView. Therefore, however nice it works, I cannot add the code you have just proposed.
If anyone wants to use these additions it is easy to add them of course.
|
|
|
|
|
Where can I purchase a comercial version of the Treeview?
|
|
|
|
|
A commercial version can be bought directly from me by contacting me at my current email address: mikATnetatonce.net where you replace 'AT' with '@'.
Contact me at this address for more information.
|
|
|
|
|
So when a node is selected/deselected the ForeColor property is changed. In my little partial implementation of your control, I'm setting the highlights to SystemColors.HighlightText and the BackColor is set to SystemColors.Highlight so that it retains the users system settings for highlights and what not. The problem occurrs when the control is disabled. Because the ForeColor/BackColor colors have been set, it no longer draws the nodes in the disabled state, but retains the ForeColor/BackColor that was set when the node was highlighted/unhighlighted. This can cause some very strage color combinations as nodes are programatically selected/deselected while the control is both enabled and disabled. If you've got an easy fix for this, please let me know.
Jebrew
|
|
|
|
|
I'm interested in your latest version. You mentioned that you were going to work on DataBinding next. Please give me information on the control and let me know what the price is.
Thanks,
Jacob
|
|
|
|
|
Hi,
before everything I would like to say, great job and thank you givin' us this great TreeView "update".
So well... ok. How to explain it.
As an example, when you have to remove node(s) and you forget to call the ClearSelNodes() method you've got really bad news. In fact, 'cause in .Net you cannot have dangling pointers, after calling the Clear() method of the TreeNodeCollection, the ArrayList (or hashing table in our case) that contain the selected nodes will contain nodes that now... got no treeview associated. This will givin' you "null reference" exception at Deselect() method in MWTreeNodeWrapper class, the next time you try to select a node.
<br />
public static void Deselect(MWTreeNodeWrapper mwtnw)<br />
{<br />
...<br />
<br />
if(mwtnw.Node.TreeView.Enabled)<br />
<br />
...<br />
The way I think we can fixe this "logical bug", is by overriding the Clear() method of the TreeNodeCollection and adding the ClearSelNodes() method call. The problem is, we cannot implemente a "new" TreeNodeCollection, I tried before and failed for some out of reach reasons caused by the Microsoft product itself.
The other way we can fixe it, is to manually check for "orphan" nodes (nodes without a TreeView) at strategicals locations in the MWTreeView control itself. "Patching" the TreeView is not a great way solving the problem and I don't wanna do this for the simple reason that if you release a new version of the TreeView, I'll be stuck to done the job each time.
Sorry for my bad english, I hope my explanations was pretty easy to understand.
So... what's your opinion about this "bug" ?
|
|
|
|
|
Thanks for the nice words.
I think I have written more code to 'help' the MS TreeView than the whole MS TreeView required...
Note that there is no bug when selecting TreeNodes when programming for it properly in current versions. Read on and I will explain why.
The code has changed quite a bit for the latest versions. This is what the static Deselect method looks like now:
public static void Deselect(MWTreeNodeWrapper mwtnw)<br />
{<br />
mwtnw.Node.ImageIndex = mwtnw.ImageIndex;<br />
mwtnw.Node.SelectedImageIndex = mwtnw.SelectedImageIndex;<br />
<br />
if( mwtnw != null &&<br />
mwtnw.Node != null &&<br />
(mwtnw.Node.TreeView == null ||<br />
mwtnw.Node.TreeView.Enabled))<br />
{<br />
mwtnw.Node.BackColor = mwtnw.BackColor;<br />
mwtnw.Node.ForeColor = mwtnw.ForeColor;<br />
}<br />
}
So no exceptions should be thrown.
It is really up to the programmer using the MWTreeView to remove TreeNodes from the SelNodes collection if they are removed from the MWTreeView.
I.e. if a ContextMenu has a 'Delete Node' item then the code behind this item should both remove the TreeNode from the SelNodes collection and from its parent (whether that is a TreeNode or the TreeView itself).
It is really a shame that you cannot instantiate TreeNodeCollections etc. Hope this changes sometime in the future.
What you write about checking for orphan TreeNodes (orphan, parent, child - what is this, some family reunion or something? ) would not be necessary if the code where TreeNodes are removed, cleared or whatever also deals with the SelNodes collection.
If we could override the TreeNodeCollections class etc the whole job of the MWTreeView would have been so much easier.
But part of the challenge is to be able to do it to the current MS TreeView
Considering all your TreeView-related questions, are you creating your own TreeView, inheriting from the MS TreeView or using the MWTreeView (curious)?
|
|
|
|
|
With all respect I've got for you and your job, I inheriting from your treeview of course !
Blindly modify your control directly in your code isn't a great programming practice and I consider peoples doin' this as robbers stealing you.
Here I'm working on an kind of personnal "Help Documentation Editor" 100% object oriented with section, sub section, each of these are makin' of "Elements" (text element, tips element, link element, table element, picture element, list element). Using WinForm with MDI Forms, I think you can see how important the treeview is. It must handle Drag&Drop, easy multiple selection and some "internals law" that telling orders like "you cannot drop a section (node) before the root section of the document (~the ultimate parent of all nodes in the treeview)".
So yeah, I'm workin' hard on the TreeView Control... and it's really pain fully !!!
I agree with you about that there's no bug, at it's real meaning, between the SelNodes and the Nodes
collections. But I think we also agree that we must "synchronize" these 2 collections and the best way doing it, is by implementing our version of the TreeNodeCollection. Seems that's impossible, I agree with you about the shame limitations of the -MS- TreeNodeCollections...
Thanks for the "uptodate version" of the Deselect() method. Are you planning to release a third version of you TreeView (soon or not) ? (I'm also curious )
|
|
|
|
|
Yep these two collections have to be synched - a shame this has to be done.
The further versions of the MWTreeView have gone commercial.
I am up to v2.1.1.4 as of yesterday actually. This version has full support for awesome very visual drag & drop (supporting multiple TreeNodes of course). There are also so many changes I just cannot list them all here.
I am also working on v3.x of the MWTreeView which has support for proper DataBinding, XML document reading etc. This is done to the extent that if a DataSource that is bound to the MWTreeView is resorted the MWTreeView immediately reflects this. DataBinding is also very customizable.
The version posted here on CodeProject is the last non-commercial version - it is still my work and nobody else should claim it as theirs yada yada yada...
|
|
|
|
|
where and how much will cost to buy it ?
|
|
|
|
|
Contact me on my mik adress. It can be found on netatonce.net.
Cannot write it in plain-text since I will get too much spam - and I don't eat spam
|
|
|
|
|
I'm using VS .NET 7.0
How to create a project...?
_____________________________
...and justice for all
APe
|
|
|
|
|
I will assume that you are using .NET Framework v1.1 (that is possible with VS 2002 isn't it?).
Unfortunately this requires some manual file copying etc:
You just create a new solution in VS called MWTreeViewTestApp and add a new Windows Control Library project which you call MWTreeView.
When this has been created you can remove the .cs file that is created by default (not AssemblyInfo.cs). Then copy all the .cs and .txt files from my solution directory into your new one using Windows Explorer. Do not copy any .resx, .csproj, .suo or other files.
Click on the project in the Solution Explorer of VS and a little icon will appear just below the name 'Solution Explorer - MWTreeView'. This icon is called 'Show All Files'. Now you will see all the files you manually copied from my solution directory tou yours. Right click on these files and select 'Include In Project'. When this has been done for all the files, you should be able to compile the project/solution.
The same procedure can be performed for the MWTreeViewTestApp project which is a Windows Application project added to the same solution. This will also have to have the Images directory copied intact.
In the MWTreeViewTestApp there is also one ImageList (ilMWTreeView) that you need to add Images to. The images should be 001.jpg and 002.jpg from the Images directory.
If you right-click on the projects and click Properties you can also enter 'MWControlSuite' as the Default Namespace.
Now you should have an MWTreeView and MWTreeViewTestApp that are the same as the ones for VS 2003.
Maybe there is even some converter application that has been written. I don't really know.
If you are using .NET Framework v1.0 I really suggest you upgrade since there is a bug in the MS TreeView that, if I remember correctly, raises the OnMouseUp event straight after the OnMouseDown event. I had a workaround for this in an older version of the MWTreeView, but I don't think I could achieve all the functionality in the MWTreeView v2 using .NET Framework v1.0.
|
|
|
|
|
It seems that the node ForeColor is lost with MWTreeView. Just set the ForeColor property to some node:
node.ForeColor = Color.LightGray
As soon as you click the node to select it and then you unselect it, the original ForeColor isn't restored.
I don't know if I can still use the ForeColor property on a TreeNode or if I need to use another property of the node wrapper implemented in MWCommon.
Marco Tenuti - www.tencas.com
|
|
|
|
|
It should work.
Have a look at the screenshot TreeNodeColors (the bottom one). Here I am using many different ForeColors (and BackColors).
The only problem you might be encountering is that you can't set ForeColor (or BackColor, ImageIndex and SelectedImageIndex) on a TreeNode when that TreeNode is selected. ForeColor and the other Properties that exist in the MWTreeNodeWrapper class must be set on the MWTreeNodeWrapper object for that particular TreeNode in the SelNodes Hashtable.
|
|
|
|
|
Your treeview control is very powerful and wonderful!Thank you!
Would you please give me an example code about a project codes that one treeview can drop into another treeview(those can be multi-selected).Thank you very much !
My email is :ycjin@citiz.net
David King
|
|
|
|
|
Do you mean one application with two MWTreeViews in it? If so, then I am working on code that does this. It is essentially finished, and has been for months now, but I haven't had much spare time lately to finish it up. It is code that is very visual; you can see exactly where you are moving or copying TreeNode(s) whether it be into other TreeNode(s) or in-between other TreeNodes.
You will just have to wait for this code to be finalized
At the moment I don't have any drag & drop code that works between two MWTreeViews in different applications since the TreeNodes are not serializable. I believe that the best way to do this is to write some form of serializer yourself, XML e.g.
|
|
|
|
|