Thursday, April 23, 2009

Dragging In Silverlight

I started writing this blog to help improve my communication skills. I thought it might be interesting to document a startup from the beginning. Much to my chagrin, though totally understandably, this is not why most people end up at my blog. According to Google Analytics, most people only want me for my code. Well, in the words of Seth Godin or the Kinks or Red Skelton, "Give them what they want."

The simplest piece of code I could dig out on short notice was my drag provider. Sure, this one has been done to death, but with the refactoring into a provider and another little convenience or two I thought it might still be nice to share. Before I get to the code I'm going to describe those useful bits as well as some assumptions the code makes. Though you can always scroll down and get what you really came here for.

First, the provider assumes that the drag element is a child of a canvas. This might be obvious since that is the easiest way, but I thought I'd lay it out there. The top most root layout of our application is a 1x1 Grid with child Grids and DockPanels for the real layout. A simple rule that we follow is that the RootLayout of any of our draggable controls must be a canvas. This works out well for us for too many reasons to list here, but in retrospect, you could probably dynamically add the canvas when creating the control. Either way, the end result is a 1x1 Grid that takes up the entire browser with its top most visible layer being the canvas that in turn contains the draggable element.

Second, the PositionDragElement routine in the sample goes to a bit of extra trouble to make sure that the element being dragged around the screen always stays in full view within the Silverlight plugin. If the mouse leaves the plugin, the drag element will follow the mouse around the edges of the plugin until the mouse returns or the button is released. This may not be desired in some cases, but for us, it prevents users from dragging dialogs out of the Silverlight application and not being able to close them.

Third; also about the PositionDragElement routine; it was pulled out of a common class used both in our draggable provider and our drag and drop framework. If something looks out of place or doesn't work quite right, forgive me. I did some on the fly munging to simplify this example a bit. Also, you obviously don't need to use this provider as is. It makes a decent example of how to perform dragging for many situations.

And so, here's the code.

public class DraggableProvider
{
#region Constructors
 public DraggableProvider(FrameworkElement dragElement)
{
if (null == dragElement)
throw new ArgumentNullException("dragElement");
_dragElement = dragElement;

_dragElement.MouseLeftButtonDown += new MouseButtonEventHandler(DragElement_MouseLeftButtonDown);
_dragElement.MouseLeftButtonUp += new MouseButtonEventHandler(DragElement_MouseLeftButtonUp);
_dragElement.MouseMove += new MouseEventHandler(DragElement_MouseMove);
}

#endregion

#region Event Handlers

void DragElement_MouseMove(object sender, MouseEventArgs e)
{
if (_isMouseDown)
{
var currentMousePosition = e.GetPosition(null);
DragHelper.PositionDragElement(currentMousePosition);
_lastMousePosition = currentMousePosition;
}
}

void DragElement_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
_isMouseDown = false;
_dragElement.ReleaseMouseCapture();
}

void DragElement_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_isMouseDown = true;
_lastMousePosition = e.GetPosition(null);
_dragElement.CaptureMouse();
}

#endregion

#region Private Fields

private FrameworkElement _dragElement;
private bool _isMouseDown;
private Point _lastMousePosition;

#endregion

#region Private Methods
 private void PositionDragElement(Point currentMousePosition)
{
var xPosition = (double)_dragElement.GetValue(Canvas.LeftProperty);
var yPosition = (double)_dragElement.GetValue(Canvas.TopProperty);
var elementOriginPosition = new Point(xPosition, yPosition);
  var xDelta = currentMousePosition.X - _lastMousePosition.X;
var yDelta = currentMousePosition.Y - _lastMousePosition.Y;
var dragElementWidth = _dragElement.ActualWidth;
var dragElementHeight = _dragElement.ActualHeight;
  // Verify that the drag element contains the mouse.
// This is important when first picking up the element.

var newX = elementOriginPosition.X + xDelta;
if ( currentMousePosition.X < newX )
newX = currentMousePosition.X - .05 * dragElementWidth;
else if (currentMousePosition.X > newX + dragElementWidth)
newX = currentMousePosition.X -
.95 * dragElementWidth;
  var newY = elementOriginPosition.Y + yDelta;
if ( currentMousePosition.Y < newY )
newY
= currentMousePosition.Y - .05 * dragElementHeight;
else if (currentMousePosition.Y > newY + dragElementHeight)
newY = currentMousePosition.Y -
.95 * dragElementHeight;
    // Validate that draggable item is still within the browser.
// This takes precedence over the mouse staying inside the element.
var rootPanel = Application.Current.RootVisual as Panel;
newX = Math.Min(newX, rootPanel.ActualWidth - dragElementWidth);
newX = Math.Max(newX, 0);
newY = Math.Min(newY, rootPanel.ActualHeight - dragElementHeight);
newY = Math.Max(newY, 0);

_dragElement.SetValue(Canvas.LeftProperty, newX);
_dragElement.SetValue(Canvas.TopProperty, newY);
}
 #endregion
}
And now, this is how we make our dialogs draggable. We also, add a background that stretches vertical and horizontal to make the dialog modal. Have fun.
 Application.Current.RootVisual as Panel.Children.Add(Background);
Application.Current.RootVisual as Panel.Children.Add(Dialog);
_draggableProvider = new DraggableProvider(Dialog);

Tuesday, April 7, 2009

New Partner at Milyli

OK, this is not really news in that it is not new. Not by six months or so anyway. But we gained a third partner back in October-ish and I never said anything about it.

I guess I'll also take this time to say that I'm not going to write much about my partners in general. They can expose their own lives and/or views if they so desire. I only mention it for a little bit of context to those two regular readers out there so they are not confused.

But I am retroactively and even still excited about it because we gained a person that compliments our existing skill set wonderfully.

Roles Over Process

So I started this little rant a while back about processes. I realized that it wasn't process itself that was the culprit, but managers that micromanage by creating a process to define how their employees do their jobs. And now, after some illness and long running issues at work, I can try to tackle the topic of a better way, in my humble opinion, to manage people.

The short version: create high level roles for each employee. Is this more difficult? Maybe, but I think the benefits are substantial.

One responsibility of a good manager is to define roles that describe what each position is accountable for. Do not think about how people will get their work done. At least, not any more than is needed to define the roles. How people work is process, and in the end, it is best for each employee to figure out the best process to accomplish their goals. They will end up knowing their jobs better than their manager ever could. Designers need to design an application that helps emu farmers keep track of their flocks. Developers need to deliver that application. Those tasks are what the respective roles are responsible for. If you can't figure out what everyone should be doing, they certainly won't be able to and no one will know how to work together.

That's another of the manager's responsibilities, figure out how to get the groups to work together. Help work through communications issues. That might be facilitating a meeting that determines a specification format; that is what the specification contains, not how it gets written. Or it may require making a command decision on who ultimately has jurisdiction over apparently overlapping responsibilities. Do the designers need to convince the developers a feature should be implemented to spec, or are the final designs the last word; I've seen both approaches work.

Hold employees responsible for their work. If results are not up to standards, find out why and how you can help. Get people the tools and training they need. Create a good environment. Adjust roles and responsibilities as needed. Offer advice from your own experience while being careful not to lay down any laws. And be ready to make the tough decision to let someone go if they just don't fit in at your organization for some reason.

After having said all that, I realize these responsibilities are definitely are not easy. That is probably the reason many people end up managing by process. But the benefits are profound. The best part is that skilled, creative workers will be happier when they are allowed to get a job done the way they want. This builds trust and a sense of ownership; intrinsic motivations to do a job well. The other side of that coin is that the manager does not need to keep tabs on the details of every iota of work. Instead, the manager is doing what they should be doing, taking care of employees, communicating, removing roadblocks where needed and holding people accountable.

I think the hardest part about this approach for most is people is when it means giving up something that they enjoy doing. As a programmer, I am not whole-heartedly looking forward to the day where I turn development over to other people. That means I won't have the final say on how things get coded anymore. But how the code is written won't be my responsibility at that point either. I just need to make sure that I trust the people put in place to accomplish what their goals are. I hope that I will do this as well as the managers I have worked with that I admire. I hope that people will enjoy working for me just as much.