I’ve just finished creating the new samples for the upcoming Version 3 of Krypton and wanted to show a picture. One of the scenarios I imagine being quite common is dragging and dropping from a TreeView into a Navigator or Workspace instance. So I created a sample that implements just that.

The sample contains a TreeView derived class that has been customized to recognize the user starting to drag a node and then hooks into the Krypton page dragging functionality. So you just need to create a DragManager instance and attach it to the PageDragNotify property of the new control and your ready to roll.

Hook up your Navigator/Workspace instances to the same DragManager and they all work nicely together. The sample also allows you to drag from the Navigator and drop it on the TreeView so the page becomes part of the tree again.

You can apply the same technique to any control and so easily integrate your own list control, button or custom control into the page dragging of Krypton. Just look at the code in the sample and apply it to your favorite control and your up and running.

Vista Docking Indicators

Just a quick update to show a couple of pictures with a new indicator style. If you are using Visual Studio 2008 under Vista then you will have noticed that the docking indicators are not square anymore. Under Vista it uses rounded indicators so of course we need to provide that style as well. Here we have the mouse hovering over the cell top edge indicator…

And over the middle indicator…

Intelligent Defaults

All of the built in palettes test for the operating system in order to decide on the appropriate feedback. So on Vista you get rounded and on earlier systems you get the square view as seen in the previous post. It also tests to ensure that your display has more than 8 bits per pixel color.

This is important because when running over remote desktop or terminal services the bandwidth is kept down by using only 256 colors per-pixel. In that case you cannot use the WS_EX_LAYERED setting that is required for the rounded appearance. In order to get a smooth rounded edge you need per-pixel alpha capabilities and so you must use a layered window. With only 256 colors per-pixel your not going to get proper alpha blending and so it is best to fall back to using the docking squares.

Edge Indicators

Instead of using solid blocks to represent drag/drop feedback we now have full docking style indicators. Here you can see the mouse over the right control edge indicator. When the mouse is over the indicator it shows a solid blue rectangle indicating where the drop will occur.

Cell Indicators

As you move the mouse over a workspace cell it displays the possible drop options. Here we have the mouse over the center indicator that shows that dropping will cause the page to be transferred into the cell itself.

Notice that when we hover over the original cell it does not show any center indicator. This is because it makes no sense to drop the page back on the cell it is coming from.

Workspace Hierarchy

One unique feature of the KryptonWorkspace control is the hierarchical structure to the layout definition. It means you can easily and quickly create complex layouts for your document area. But this strength is also a potential weakness because moving pages around could cause the structure to become inefficient and leave redundant entries.

To prevent this happening there are some flags that indicate how to perform a compacting phase just before the layout of the contents occurs. Each of the compact flags is used to resolve a potential problem. The flags are exposed as the CompactFlags property of the KryptonWorkspace.

CompactFlags.RemoveEmptyCells

Using drag and drop you can transfer a page from one to cell to another. If you remove the last page in the cell you are left with a cell that has no pages. Some applications may want this to happen but many others would prefer the cell be automatically removed as an empty cell is redundant. When this flag is set the compacting phase will search for any cells that have no pages defined and remove them from the hierarchy.

CompactFlags.RemoveEmptySequences

Removing empty cells can result in a workspace sequence existing that has no contents. Once all the cells contained in the sequence have been removed we are left with a redundant sequence. The user will not see the sequence as it is likely to be zero sized but it still exists in the hierarchy. During compacting this flag will search for sequences that have no children and automatically remove them.

CompactFlags.PromoteLeafs

A more subtle scenario involves a sequence that contains just a single child. If a sequence contains just a single child item then that sequence itself is redundant as it only encloses one item. This flag will promote the single child into the place of the sequence and delete the no longer needed sequence.

This situation occurs if you used drag and drop to move pages around. You can end up with a sequence that contains a single sequence child that contains yet another single sequence child and so forth. Although you would not see any visual impact it is clearly inefficient to have long chains of single sequences.

CompactFlags.AtLeastOneVisibleCell

Last but by no means least is a flag that ensures we always have at least one visible cell in the workspace. If the user deletes the last showing page in the last showing cell then the RemoveEmptyCells flag above would remove that last cell and leave the workspace area blank. Some applications require that there is always at least one cell showing even if it has no pages in it. This flag will ensure that is the case.

Layout and Compacting

By default all the above compact flags are set and this ensures that the child hierarchy is always small and efficient. Compacting occurs at the start of control layout activity and so you do not need to worry that it wil be applied whilst you are programmatically changing the structure.  So you can make many changes and the compacting will not interfere until the OnLayout is processed. If you are worried then simply wrap your changes inside the SuspendLayout/ResumeLayout pair of commands.

Instance Dragging

The previous post showed that you can drag pages around the KryptonWorkspace control in order to change the layout of the workspace area. We now need to extend this another level because in the real world you may want to drag pages between different control instances. As the KryptonNavigator also deals with KryptonPage instances it makes sense to allow pages to be dragged to and from Navigator as well as Workspace controls.

Drag Navigator to Workspace

The following images show a Form that has two KryptonNavigator instances at the top and a single KryptonWorkspace control at the bottom. We start by selecting page P1 from the top left Navigator…

And then drag it over to right side of the Workspace…

Dropping creates a new cell inside the Workspace with the page transferred.

Drag Workspace to Navigator

Moving in the opposite direction is just as easy. Just start dragging P1 from the Workspace and move it over the client area of the Navigator instance…

Notice that the Navigator gives feedback by showing the light blue area over the entire Navigator instance. There is only one thing the Navigator can do with a page drop and that is adding it to the end of its existing set of pages. So the only feedback, no matter where you move the mouse, is to show the entire control as the feedback area. Once you drop the page you get the following result…

Drag Navigator to Navigator
Drag Workspace to Workspace

You can ignore the Workspace entirely and just transfer pages from one Navigator instance to the other. Just drag P1 and move the mouse over to the other Navigator like so…

So you have the flexibility of dragging pages from/to Navigator /Workspace instances. This is not happening by using the traditional drag and drop mechanism provided in Windows. Starting the drag operation is not using the DoDragDrop call that you might be familiar with. Instead I have created my own class that manages the process.

Targets/Notify/Manager

You need three things to perform drag and drop. First you need a set of potential targerts that can accept the drop and for that we have the IDragTargetProvider interface. Second we need to allow drag operations to be started and we have the IDragPageNotify interface for that. Finally we need an instance that manages the orchestration and so there is a class called DragManager.

IDragTargetProvider

In practice this means that the Navigator and Workspace both implement the IDragTargetProvider interface
because both are capable of being drop targets for page dragging. This public interface could be implemented by any control so you could certainly make your own controls drop targets. The samples for the next release will give a couple of examples showing how to make a simple Button control or TreeView a drop target.

IDragPageNotify

The Navigator and Workspace also expose a property called DragPageNotify that stores an instance of the IDragPageNotify interface. Whenever you start a drag operation it callw back on this provided interface with the relevant mouse events. DragManager implements this interface and so can be passed into the DragPageNotify property of any control that wants to be able to start a drag. Again, the samples will show how to implement this on a Button and TreeView.

So the real story of this post is not that you can easily drag and drop pages between Navigator and Workspace instances, but you can easily integrate your own controls into this dragging mechanism. The above three controls are linked together using the following simple code…

DragManager dm = new DragManager();  

 

// Add drop targets
dm.DragTargetProviders.Add(kryptonNavigator1);
dm.DragTargetProviders.Add(kryptonNavigator2);
dm.DragTargetProviders.Add(kryptonWorkspace1); 

// Add drag sources
kryptonNavigator1.DragPageNotify = dm;
kryptonNavigator2.DragPageNotify = dm;
kryptonWorkspace1.DragPageNotify = dm;