|
This is great, but what if you can drag the node, but need to cancel the drag according to the node you are trying to drop it on? Like a document, shouldnt become a child of another document. But when I try to do a DragDrop event to cancel it, it never gets called. Is there a fix I need to do to this control, or would you be able to post a fixed update that would allow this?
Thanks!!!!
invid
http://invid.funxion.net
|
|
|
|
|
hi, how can I drag data from other control to this treeview, It will not fire the dragcomplete event.
|
|
|
|
|
I have a number of nodes that I need to update as part of a process i.e. change the icon as each node task is completed. However when I use the node.EnsureVisible() method, if the node text is long and the scroll bars are visible it scrolls way over to the right, hiding the navigation lines/crosses - quite annoying.
I created a method "EnsureVisibleWithoutRightScrolling" that can be called instead of EnsureVisible() and added the following code to fix this:
// Sendmessage constants
private const int WM_HSCROLL = 276;
private const int SB_LEFT = 6;
[DllImport("user32.dll")]
private static extern int SendMessage(IntPtr hWnd, int wMsg, int wParam, int lParam);
public void EnsureVisibleWithoutRightScrolling(TreeNode node)
{
this.BeginUpdate();
// we do the standard call..
node.EnsureVisible();
// ..and afterwards we scroll to the left again!
SendMessage(this.Handle, WM_HSCROLL, SB_LEFT , 0);
this.EndUpdate();
}
Original article for EnsureVisibleWithoutRightScrolling is here:
http://www.codeproject.com/cs/miscctrl/NoScrollingTree.asp[^]
|
|
|
|
|
you can use the API function CreateIconIndirect to create any cursor from a bitmap. There is not size limit so you can draw the item into a bitmap and convert it into a cursor. The following class can be used for that. An instance of this class must exists as long as the cursor is displayed.
<br />
using System;<br />
using System.Windows.Forms;<br />
using System.Drawing;<br />
using System.Runtime.InteropServices;<br />
<br />
namespace aux<br />
{<br />
public class BitmapCursor : IDisposable<br />
{<br />
#region Win-API imports<br />
<br />
[StructLayout(LayoutKind.Sequential)]<br />
public struct ICONINFO<br />
{<br />
public bool fIcon;<br />
public uint xHotspot;<br />
public uint yHotspot;<br />
public IntPtr hbmMask;<br />
public IntPtr hbmColor;<br />
}<br />
<br />
[System.Runtime.InteropServices.DllImport("USER32.DLL")]<br />
public static extern IntPtr CreateIconIndirect( ref ICONINFO iconinfo );<br />
[System.Runtime.InteropServices.DllImport("USER32.DLL")]<br />
public static extern bool DestroyIcon( IntPtr hIcon );<br />
<br />
#endregion<br />
<br />
#region private<br />
<br />
private ICONINFO iconInfo;<br />
private Cursor cursor = null;<br />
private IntPtr handle = IntPtr.Zero;<br />
<br />
private void Create()<br />
{<br />
handle = CreateIconIndirect( ref iconInfo );<br />
cursor = new Cursor( handle );<br />
}<br />
<br />
<br />
#endregion<br />
<br />
#region constructors and destructor<br />
<br />
public BitmapCursor( System.Drawing.Bitmap bmp, int HotSpotX, int HotSpotY )<br />
{<br />
iconInfo = new ICONINFO();<br />
iconInfo.fIcon = false;<br />
iconInfo.xHotspot = 0;<br />
iconInfo.yHotspot = 0;<br />
iconInfo.hbmMask = bmp.GetHbitmap();<br />
iconInfo.hbmColor = bmp.GetHbitmap();<br />
Create();<br />
}<br />
public BitmapCursor( System.Drawing.Bitmap bmp, Cursor Cursor )<br />
{<br />
iconInfo = new ICONINFO();<br />
iconInfo.fIcon = false;<br />
iconInfo.xHotspot = 0;<br />
iconInfo.yHotspot = 0;<br />
using( System.Drawing.Bitmap bmpdup = bmp.Clone() as System.Drawing.Bitmap )<br />
{<br />
using( Graphics g = Graphics.FromImage( bmpdup ) )<br />
{<br />
Cursor.Draw( g, new Rectangle( new Point( 0, 0 ), Cursor.Size ) );<br />
}<br />
iconInfo.hbmMask = bmpdup.GetHbitmap();<br />
iconInfo.hbmColor = bmpdup.GetHbitmap();<br />
Create();<br />
}<br />
}<br />
<br />
<br />
~BitmapCursor()<br />
{<br />
Dispose( false );<br />
}<br />
<br />
#endregion<br />
<br />
#region virtual methods<br />
<br />
protected virtual void Dispose( bool disposing )<br />
{<br />
try<br />
{<br />
if( handle != IntPtr.Zero )<br />
DestroyIcon( handle );<br />
}<br />
catch<br />
{<br />
}<br />
}<br />
<br />
<br />
#endregion<br />
<br />
#region public properties<br />
<br />
public Cursor Cursor<br />
{<br />
get<br />
{<br />
return cursor;<br />
}<br />
}<br />
<br />
<br />
#endregion<br />
<br />
#region IDisposable Member<br />
<br />
public void Dispose()<br />
{<br />
GC.SuppressFinalize( this );<br />
Dispose( true );<br />
}<br />
<br />
#endregion<br />
}<br />
}
Example for creating a drag cursor from a TreeNode that look like Windows-Explorer:
<br />
private BitmapCursor dragBitmapCursor = null;<br />
<br />
protected virtual Cursor CreateDragCursor( TreeNode node )<br />
{<br />
int width = node.Bounds.Width;<br />
int height = node.Bounds.Height;<br />
Rectangle r = new Rectangle( 0, 0, width, height );<br />
using( Graphics g0 = CreateGraphics() )<br />
using( Bitmap bmp = new Bitmap( width, height * 4, g0 ) )<br />
using( Graphics g = Graphics.FromImage( bmp ) )<br />
{<br />
g.Clear( Color.FromArgb( 0, 0, 0, 0 ) );<br />
<br />
Color cb1 = Color.FromArgb( 255, 0, 89, 181 );<br />
Color cb2 = Color.FromArgb( 0, 0, 89, 181 );<br />
using( Brush b = new System.Drawing.Drawing2D.LinearGradientBrush( r, cb1, cb2, 0, false ) )<br />
g.FillRectangle( b, 0, 0, width, height );<br />
<br />
Color ct1 = Color.FromArgb( 255, 255, 255, 255 );<br />
Color ct2 = Color.FromArgb( 64, 255, 255, 255 );<br />
using( Brush b = new System.Drawing.Drawing2D.LinearGradientBrush( r, ct1, ct2, 0, false ) )<br />
g.DrawString( node.Text, Font, b, 0, 0 );<br />
<br />
<br />
DragBitmapCursor = new BitmapCursor( bmp, Cursors.Default );<br />
return DragBitmapCursor.Cursor;<br />
}<br />
}
|
|
|
|
|
CreateDragCursor: creates Cursor with expanded Subnodes...
Public Class BitmapCursor
Implements IDisposable
#Region "variables"
Private icon_Info As ICONINFO
Private cursor_ As System.Windows.Forms.Cursor = Nothing
Private handle As IntPtr = IntPtr.Zero
#End Region
#Region "constants"
Private Shared ReadOnly TreeviewOffset As System.Drawing.Point = New System.Drawing.Point(-1, 2)
Private Const Alpha As Double = 0.33
#End Region
<system.runtime.interopservices.structlayout(system.runtime.interopservices.layoutkind.sequential)> _
Private Structure ICONINFO
Public fIcon As Boolean
Public xHotspot As System.UInt32
Public yHotspot As System.UInt32
Public hbmMask As IntPtr
Public hbmColor As IntPtr
End Structure
<system.runtime.interopservices.dllimport("user32.dll")> _
Private Shared Function CreateIconIndirect(ByRef iconinfo As ICONINFO) As IntPtr
End Function
<system.runtime.interopservices.dllimport("user32.dll")> _
private Shared Function DestroyIcon(ByVal hIcon As IntPtr) As Boolean
End Function
Private Sub Create()
handle = CreateIconIndirect(icon_Info)
cursor_ = New Cursor(handle)
End Sub
Private Sub New(ByVal bmp As System.Drawing.Bitmap, ByVal HotSpotX As Integer, ByVal HotSpotY As Integer)
icon_Info = New ICONINFO()
icon_Info.fIcon = False
icon_Info.xHotspot = 0
icon_Info.yHotspot = 0
icon_Info.hbmMask = bmp.GetHbitmap()
icon_Info.hbmColor = bmp.GetHbitmap()
Create()
End Sub
' ' Creates a cursor from a bitmap and combines it with another cursor.
'
Private Sub New(ByVal bmp As System.Drawing.Bitmap, ByVal CursorToCombine As System.Windows.Forms.Cursor)
icon_Info = New ICONINFO()
icon_Info.fIcon = False
icon_Info.xHotspot = 0
icon_Info.yHotspot = 0
Using bmpdup As System.Drawing.Bitmap = CType(bmp.Clone(), System.Drawing.Bitmap)
Using g As Graphics = System.Drawing.Graphics.FromImage(bmpdup)
CursorToCombine.Draw(g, New System.Drawing.Rectangle(New Point(0, 0), CursorToCombine.Size))
End Using
icon_Info.hbmMask = bmpdup.GetHbitmap()
icon_Info.hbmColor = bmpdup.GetHbitmap()
Create()
End Using
End Sub
Private ReadOnly Property Cursor() As System.Windows.Forms.Cursor
Get
Return Me.cursor_
End Get
End Property
Public Shared Function CreateDragCursor(ByRef NodeToDraw As System.Windows.Forms.TreeNode, ByRef CursorToCombine As System.Windows.Forms.Cursor) As Cursor
If Not NodeToDraw Is Nothing Then
Dim NodesBounds As System.Drawing.Rectangle = GetNodesBounds(NodeToDraw)
Dim ControlScreenshot As System.Drawing.Bitmap = New System.Drawing.Bitmap(NodeToDraw.TreeView.Width, NodeToDraw.TreeView.Height)
Dim CursorBitmap As System.Drawing.Bitmap = New System.Drawing.Bitmap(Math.Max(NodesBounds.Width, CursorToCombine.Size.Width), Math.Max(NodesBounds.Height, CursorToCombine.Size.Height))
Dim CursorBitmapRectangle As System.Drawing.Rectangle = New System.Drawing.Rectangle(0, 0, NodesBounds.Width, NodesBounds.Height)
NodeToDraw.TreeView.DrawToBitmap(ControlScreenshot, New System.Drawing.Rectangle(0, 0, ControlScreenshot.Width, ControlScreenshot.Height))
Using g As Graphics = Graphics.FromImage(CursorBitmap)
g.Clear(NodeToDraw.TreeView.BackColor)
g.DrawImage(ControlScreenshot, CursorBitmapRectangle, NodesBounds, GraphicsUnit.Pixel)
End Using
Call AdjustAlpha(CursorBitmap, Alpha, NodeToDraw.TreeView.BackColor)
Return New BitmapCursor(CursorBitmap, CursorToCombine).Cursor
Else
Return CursorToCombine
End If
End Function
Private Shared Function GetNodeBounds(ByRef NodeToDraw As System.Windows.Forms.TreeNode) As System.Drawing.Rectangle
If Not NodeToDraw Is Nothing Then
Try
Return New Rectangle(NodeToDraw.Bounds.Left - NodeToDraw.TreeView.ImageList.ImageSize.Width + TreeviewOffset.X, NodeToDraw.Bounds.Top + TreeviewOffset.Y, NodeToDraw.Bounds.Width + NodeToDraw.TreeView.ImageList.ImageSize.Width, NodeToDraw.Bounds.Height)
Catch ex As Exception
Return New Rectangle(NodeToDraw.Bounds.Left - 1, NodeToDraw.Bounds.Top + 2, NodeToDraw.Bounds.Width, NodeToDraw.Bounds.Height)
End Try
Else
Return System.Drawing.Rectangle.Empty
End If
End Function
Private Shared Function GetNodesBounds(ByRef NodeToDraw As System.Windows.Forms.TreeNode) As System.Drawing.Rectangle
Dim ChildNode As System.Windows.Forms.TreeNode
Dim Result As System.Drawing.Rectangle
If Not NodeToDraw Is Nothing Then
Result = GetNodeBounds(NodeToDraw)
If NodeToDraw.IsExpanded AndAlso NodeToDraw.Nodes.Count > 0 Then
If NodeToDraw.Nodes.Count > 0 Then
For Each ChildNode In NodeToDraw.Nodes
Result = System.Drawing.Rectangle.Union(Result, GetNodesBounds(ChildNode))
Next ChildNode
End If
End If
Return Result
Else
Return System.Drawing.Rectangle.Empty
End If
End Function
^
private Sub AdjustAlpha(ByRef ImageToAdjust As System.Drawing.Bitmap, ByVal Alpha As Double, ByRef TransparencyColor As System.Drawing.Color)
Dim ActColor As System.Drawing.Color
Dim x, y As Integer
Dim AlphaByte As Integer = CInt(Math.Round(Alpha * 255))
For x = 0 To ImageToAdjust.Width - 1 Step 1
For y = 0 To ImageToAdjust.Height - 1 Step 1
ActColor = ImageToAdjust.GetPixel(x, y)
If ActColor.R = TransparencyColor.R AndAlso _
ActColor.G = TransparencyColor.G AndAlso _
ActColor.B = TransparencyColor.B Then
ImageToAdjust.SetPixel(x, y, Color.FromArgb(0, ActColor))
Else
ImageToAdjust.SetPixel(x, y, Color.FromArgb(AlphaByte, ActColor))
End If
Next y
Next x
End Sub
Protected Overrides Sub Finalize()
Dispose(False)
End Sub
Private Sub Dispose() Implements IDisposable.Dispose
GC.SuppressFinalize(Me)
Dispose(True)
End Sub
Private Sub Dispose(ByVal disposing As Boolean)
Try
If Not handle = IntPtr.Zero Then
DestroyIcon(handle)
End If
Catch ex As Exception
End Try
End Sub
End Class
|
|
|
|
|
In my app I wanted to make the icon jump a certain amount of pixels instead of following the mouse pointer px per px.
So i have implemented an onMouseMove event which has to change the hotspot of the cursor continiously. Therefore everytime a new cursor has to be created.
After a few hundred times you'll get an numericargumentexception from the win32 api.
solution: you have to destroy the current handle with "DestroyIcon" before creating a new one.
also constructor with hotspot was not setting the hotspot
Here the new class with my updates and changes to my needs.
This was a great help for me, as suprisinly, there's not alot to find on the topic.
<br />
using System;<br />
using System.Windows.Forms;<br />
using System.Drawing;<br />
using System.Runtime.InteropServices;<br />
<br />
namespace PerdumImageEditor._Klassen<br />
{<br />
public class clsBitmapCursor : IDisposable<br />
{<br />
#region Win-API imports<br />
<br />
[StructLayout(LayoutKind.Sequential)]<br />
public struct ICONINFO<br />
{<br />
public bool fIcon;<br />
public uint xHotspot;<br />
public uint yHotspot;<br />
public IntPtr hbmMask;<br />
public IntPtr hbmColor;<br />
}<br />
<br />
[System.Runtime.InteropServices.DllImport("USER32.DLL")]<br />
public static extern IntPtr CreateIconIndirect( ref ICONINFO iconinfo );<br />
[System.Runtime.InteropServices.DllImport("USER32.DLL")]<br />
public static extern bool DestroyIcon( IntPtr hIcon );<br />
<br />
#endregion<br />
<br />
<br />
private ICONINFO iconInfo;<br />
private Cursor cursor = null;<br />
private IntPtr handle = IntPtr.Zero;<br />
<br />
<br />
#region constructors and destructor<br />
public clsBitmapCursor(System.Drawing.Bitmap bmp)<br />
{<br />
iconInfo = new ICONINFO();<br />
iconInfo.fIcon = false;<br />
iconInfo.xHotspot = 0;<br />
iconInfo.yHotspot = 0;<br />
iconInfo.hbmMask = bmp.GetHbitmap();<br />
iconInfo.hbmColor = bmp.GetHbitmap();<br />
Create();<br />
}<br />
public clsBitmapCursor( System.Drawing.Bitmap bmp, uint HotSpotX, uint HotSpotY )<br />
{<br />
iconInfo = new ICONINFO();<br />
iconInfo.fIcon = false;<br />
iconInfo.xHotspot = HotSpotX;<br />
iconInfo.yHotspot = HotSpotY;<br />
iconInfo.hbmMask = bmp.GetHbitmap();<br />
iconInfo.hbmColor = bmp.GetHbitmap();<br />
Create();<br />
}<br />
<br />
~clsBitmapCursor()<br />
{<br />
Dispose( false );<br />
}<br />
<br />
#endregion<br />
<br />
<br />
protected virtual void Dispose( bool disposing )<br />
{<br />
try<br />
{<br />
if( handle != IntPtr.Zero )<br />
DestroyIcon( handle );<br />
}<br />
catch<br />
{<br />
}<br />
}<br />
<br />
<br />
public void moveHotspot(int x, int y)<br />
{<br />
iconInfo.xHotspot = (uint)x;<br />
iconInfo.yHotspot = (uint)y;<br />
<br />
Create();<br />
}<br />
<br />
private void Create()<br />
{<br />
if (handle != null) DestroyIcon(handle);<br />
<br />
handle = CreateIconIndirect(ref iconInfo);<br />
cursor = new Cursor(handle);<br />
}<br />
<br />
public Cursor Cursor<br />
{<br />
get<br />
{<br />
return cursor;<br />
}<br />
}<br />
<br />
<br />
public void Dispose()<br />
{<br />
GC.SuppressFinalize( this );<br />
Dispose( true );<br />
}<br />
<br />
}<br />
}<br />
<br />
<br />
<br />
|
|
|
|
|
I have two types of nodes in my treeview, regular nodes and 'myTreeNodes' which just contain some extra information (such as an Image object). I noticed that if i move one regular node into another regular node, all the 'myTreeNodes' in the node i just moved get messed up, losing all the values for their custom fields.
i traced this to the Clone method you suggested using when copying the nodes.
This is the fix i propose, it works well:
In the DragDrop method:
<br />
dragNode.Remove();<br />
targetNode.Nodes.Insert(0, dragNode);<br />
targetNode.Expand();<br />
you don't need to clone it, because removing it only takes it out of its parent nodelist, as long as you maintain a reference to the object, you maintain all the data in the object. I used Insert because i was short on time and i used Insert for node-swapping in my treeview, add should work just as well.
by the way, thanks for your code, it was very helpful
|
|
|
|
|
Hi,
Thanx for this control. It saves me a lot of time. The OnDragLeave event does not set the previous node to the 'not selected' color. This means that whenever you leave the control while dragging something the previous node stays 'selected'. Your whole tree can appear selected after a while.
I made this code change:
if ( this._previousNode != null )
{
this._previousNode.BackColor = SystemColors.HighlightText;
this._previousNode.ForeColor = SystemColors.ControlText;
}
Cheers
|
|
|
|
|
actually the problem is that the node is never ACTUALLY selected.
instead of tracking the previous and next node, and then setting the colors, why don't you just do
this.selectedNode = targetNode (or something to that extent.. i don't have the code up right now)
but anyway, yes... treeviews have a property for the selected node, just set that property to the node that is at the current point.
[note: i'm on .net 2.0 beta 2 right now, so you may not have that]
|
|
|
|
|
Hi smallguy78!
when I wrote my node named MyNode extend TreeNode, ur control didnt work???
Can u show me how to correct it?
Thanks
|
|
|
|
|
|
Hi!!!
Can I do this functionality with Vb.net ?
Michel
|
|
|
|
|
(Not being the author) but my guess would be "absolutely, yes". You would need to convert this all from C# to VB.NET of course - or you could take the simpler option and create this project as C#, and then include a reference to the project in your VB.NET application. Probably save you some conversion time, but up to you of course
|
|
|
|
|
Great job with this control.. Just what I was looking for.
However, I'm having trouble with the treeview's width automatically being reduced when a drag event starts.
The problem manifests itself in your demo application if you change the treeview's dock style to none. In my case, I have a treeview and a listview docked to a splitter control, and when I start a drag operation, the treeview's width is reduced so that none of the node captions are visible.
|
|
|
|
|
I'm not quite sure why, but the following line in OnItemDrag:
int width = Width = this._selectedNode.Text.Length * (int) this._formDrag.labelText.Font.Size;
probably should be:
int width = this._selectedNode.Text.Length * (int) this._formDrag.labelText.Font.Size;
It is setting the treeview controls width at the same time as it is assigning the value to the dragged item. This seems to work for me, hopefully it doesn't break anything else!
|
|
|
|
|
When Dragging an item, the drag image is a arrow with a plus in it, companying with the item image and its text string. sometimes, when dragging the item to some place where the drop is not allowed, the drag image becomes to: a circle with a line in it, companying with the item image and its test string.
Would you please tell how to binding the arrow or the circle(with a line in it) with the item image and the item text string?
Many Thanks to You!
Lucy
|
|
|
|
|
;PThere is a variable declaration:
private FormDrag _formDrag = new FormDrag();
May I know what's the data type of FormDrag? Because I could not find it anywhere.
Many Thanks,
lucy.
|
|
|
|
|
Have a look for "internal class FormDrag : System.Windows.Forms.Form" - it's an internal form that's used to hold the icon you drag with in a picturebox.
|
|
|
|
|
Thanks!
So, Would you tell me where I could find it? in which file, or in which DLL file, or any where else? I tried, but failed to find it in the projects downloaded from this webpage.
Regards,
Lucy
|
|
|
|
|
Hi, SmallGuy78,
I just find it.
Many Thanks to you!
Lucy
|
|
|
|
|
If the problem is still unsolved, you may try this code block (just idea that's works)
<br />
protected override void OnDragOver(System.Windows.Forms.DragEventArgs e)<br />
{ <br />
Point pt = ((TreeView)this).PointToClient(new Point(e.X, e.Y));<br />
TreeNode treeNode = this.GetNodeAt(pt);<br />
this.SelectedNode=treeNode; <br />
}
All other highlighters of treenodes in override methods remains unchanged.
|
|
|
|
|
Hi,
flickering is caused by redrawing the control on every move of the cursor.
This should get rid of it:
private void DragOverEvent(object sender, DragEventArgs drag)
{
// Point mouse = new Point(drag.X, drag.Y);
// return(this.GetNodeAt(this.PointToClient(mouse)));
TreeNode node = NodeAtClient(drag);
if (previousNode != node)
{
if (previousNode !=null)
{
//previousNode.BackColor =this.BackColor;
//previousNode.ForeColor =this.ForeColor;
NodeColorDefault(previousNode);
}
if (node != null)
{
//node.BackColor =dragOverBackColor;
//node.ForeColor =dragOverForeColor;
NodeColorHighlight(node);
}
previousNode =node;
}
}
Instead of overriding the events I did the following:
public TreeViewDD()
{
InitializeComponent();
MouseDown +=new MouseEventHandler(MouseDownEvent);
ItemDrag +=new ItemDragEventHandler(ItemDragEvent);
DragEnter +=new DragEventHandler(DragEnterEvent);
DragDrop +=new DragEventHandler(DragDropEvent);
DragOver +=new DragEventHandler(DragOverEvent);
}
Hope this helps
|
|
|
|
|
actually i found that it is really more efficient to just make it select nodes instead of changing all the colors manually.
|
|
|
|
|
Yes, its more efficient. But don't forget call treeview.Select(), e.g.:
private void treeView1_DragOver(...)
{
...
treeControl.SelectedNode = targetNode;
treeControl.Select();
...
}
When you are dragging data between 2 different trees, the target tree wouldn't show the selection, so this is the reason for calling method Select().
|
|
|
|
|
That's interesting. i'm not near a computer with visual studio right now, so what does select do?
Also, why would it matter? You're calling select() on the first tree, not the target tree..
|
|
|
|
|