views:

1320

answers:

3

I want to verify a drag & drop operation is allowed. A valid item can come from another one of our "controls", or internally from within the custom treeview. Currently I have this:

bool CanDrop(DragEventArgs e)
{
    bool allow = false;
    Point point = tree.PointToClient(new Point(e.X, e.Y));
    TreeNode target = tree.GetNodeAt(point);
    if (target != null)
    {
        if (CanWrite(target)) //user permissions
        {
            if (e.Data.GetData(typeof(DataInfoObject)) != null) //from internal application
            {
                DataInfoObject info = (DataInfoObject)e.Data.GetData(typeof(DataInfoObject));
                DragDataCollection data = info.GetData(typeof(DragDataCollection)) as DragDataCollection;
                if (data != null)
                {
                    allow = true;
                }
            }
            else if (tree.SelectedNode.Tag.GetType() != typeof(TreeRow)) //node belongs to this & not a root node
            {
                if (TargetExistsInNode(tree.SelectedNode, target) == false)
                {
                    if (e.Effect == DragDropEffects.Copy)
                    {
                        allow = true;
                    }
                    else if (e.Effect == DragDropEffects.Move)
                    {
                        allow = true;
                    }
                }
            }
        }
    }
    return allow;
}

I've moved all the checking code to this method to try to improve things, but to me this is still horrible!

So much logic, and so much of it to do things that I'd expect the treeview would do itself (eg. "TargetExistsInNode" checks whether the dragged node is being dragged to one of its children).

What is the best way to validate input to a control?

+3  A: 

I use the TreeNode.Tag property to store small "controller" objects that makes up the logic. E.g.:

class TreeNodeController {
  Entity data; 

  virtual bool IsReadOnly { get; }
  virtual bool CanDrop(TreeNodeController source, DragDropEffects effect);
  virtual bool CanDrop(DataInfoObject info, DragDropEffects effect);
  virtual bool CanRename();
}

class ParentNodeController : TreeNodeController {
  override bool IsReadOnly { get { return data.IsReadOnly; } } 
  override bool CanDrop(TreeNodeController source, DragDropEffect effect) {
    return !IsReadOnly && !data.IsChildOf(source.data) && effect == DragDropEffect.Move;
  }
  virtual bool CanDrop(DataInfoObject info, DragDropEffects effect) {
    return info.DragDataCollection != null;
  }
  override bool CanRename() { 
    return !data.IsReadOnly && data.HasName;
  }
}

class LeafNodeController : TreeNodeController {
  override bool CanDrop(TreeNodeController source, DragDropEffect effect) {
    return false;
  }
}

Then my CanDrop would be something like:

bool CanDrop(DragDropEventArgs args) {
  Point point = tree.PointToClient(new Point(e.X, e.Y));
  TreeNode target = tree.GetNodeAt(point);
  TreeNodeController targetController = target.Tag as TreeNodeController;

  DataInfoObject info = args.GetData(typeof(DataInfoObject)) as DataInfoObject;
  TreeNodeController sourceController = args.GetData(typeof(TreeNodeController)) as TreeNodeController;

  if (info != null) return targetController.CanDrop(info, e.Effect);
  if (sourceController != null) return targetController.CanDrop(sourceController, e.Effect);
  return false;
}

Now for each class of objects that I add to the tree I can specialize the behaviour by choosing which TreeNodeController to put in the Tag object.

Hallgrim
A: 

That's certainly better.

I'm already using Tag for another custom object (related to what type of record in the db the node represents). These objects can not be extended, and I'm a little aderse to having something like

class TagObject
{
    public DbObject db
    {
        get;
        set;
    }

    public ValidationController val
    {
        get;
        set;
    }
}

However, your method is certainly a little more OO than my initial ifelse orgy.

Any experience with Validation Events in WinForms? I haven't and I've been programming C# client side for a good couple of years now.

Matt Jacobsen
Then create a new tag object: class NewTagObject { public TagObject DbTag; public TreeNodeController Controller; }
Hallgrim
That's what my TagObject above is for.
Matt Jacobsen
Please update your original question with your further information or additional clarifications.
Robert P
+1  A: 

Not strictly answering your question, but I've spotted a bug in your code. DragDropEffects has the flags attribute set so you could get e.Effect to be a bitwise combination of copy and move. In which case your code would incorrectly return false.

Nigel Hawkins
You're right. Thanks!
Matt Jacobsen