I personally think that the IE8 developer tools are better than Firebug, but let's face it: Firebug was first. Besides, Firebug has also a completely javascript version called Firebug lite that you can inject into any page. Well, you might say, as a developer it is a nice tool, but how can I, the Internet surfer, use this in any page? And the answer is: the favorites links!
It all started from this brilliant post: Bookmark to inject FireBug Light into Internet Explorer. that showed how you can create a specific URL file, add it to the Favorites folder of your Windows user and then use it on any page. Elegant it may be, but it's lazy. It only works on pages that have loaded jQuery. So I did the version that works with simple javascript.
Then I realized that, since I like the Internet Explorer tools better, this is useless for me. How cool would it be to have Firebug lite on Chrome, which has yet to provide a decent developer tool. And it's even easier! Here is how you do it.
For Internet Explorer, follow the steps described in the ElegantCode post but use generic javascript: create a file called Firebug.url containing
And that is it. Click on the link in any of the said browsers and a small icon will appear in the bottom-right corner. Click on it and voila: Firebug! Of course, one can install the Firebug extension, which is actually also a javascript link, albeit really weird, but I think this is more clear and user friendly.
When I first tried to do drag and drop in WPF I thought it would be something easy like DragAndDrop.IsDraggable="True". Boy,was I wrong! The mechanism for this has remained almost unchanged from the Windows Forms version. It is completely event driven and has nothing in the way of actual graphical feedback of what is going on except making the mouse cursor look different. If you want to do it using MVVM, you have another thing coming.
Disclaimer:Now, I have found a good solution for all the problems above, but the drag and drop behaviour is part of a larger framework that I have been working on and creating a separate project just for it might prove difficult. I mean, you want to show MVVM drag and drop, you should also have Views and ViewModels and base classes and helpers and everything. The solution, I guess, is to make separate articles for the main features in the framework, then present the project as a whole in the end. The framework itself is work in progress and untested in a real life project, so this might have to wait, as well. I will make sure, though, to put much code directly in this post.
Ok then, let's define the requirements of this system. We need:
A drag item
A drop target
Showing the target is being dragged
Showing the target can or cannot be dropped
Showing the item being dragged
Changing the appearance of the original dragged element while dragging
Changing the appearance of the drop target while dragging something over
Allowing for drag and drop between Windows
Allowing for drag and drop between applications
Changing an application that works but has no drag and drop in an easy and maintainable way
Using as simple a system as possible
Doing everything using the Model-View-ViewModel pattern
From these requirements we can form a basic idea of the way we would like this to work. First of all, we need the ability to mark any element as a drag item. Also, we need a container that can be marked as a drop target. We can do this using boolean IsDragSource and IsDropTarget Attached Properties; once set they will force a bind of the drag and drop events to some special handlers that would then direct decisions to ICommands.
As we are doing it in MVVM, we don't use the elements directly, but the data they represent, so we work with dragging and dropping commands using data objects. The classes responsible with the decisions for the drop permissions and actions should be in the ViewModel. We could, of course, link all events to commands, but that would be very cumbersome to use. Besides, we want it simple, we don't really want the user of the system to care about the drag and drop inner workings. Therefore, the solution is to change the IsDragSource and IsDropTarget to DragSource and DropTarget properties that accept objects of type IDragSource and IDropTarget containing all the methods needed for the events in question:
/// <summary> /// Holds the data of a dragged object in a drag-and-drop operation. /// </summary> publicinterface IDraggedData { /// <summary> /// A dictionary with the format as the key and the data in that format in the value /// </summary> IDictionary<string, object> Values { get; }
/// <summary> /// Optional object for additional information /// </summary> object Tag { get; } }
/// <summary> /// Business end of the drag source /// </summary> publicinterface IDragSource { /// <summary> /// Gets the supported drop effects. /// </summary> /// <param name="dataContext">The data context.</param> /// <returns></returns> DragEffects GetDragEffects(object dataContext);
/// <summary> /// Gets the data. /// </summary> /// <param name="dataContext">The data context.</param> /// <returns></returns> object GetData(object dataContext); }
/// <summary> /// Defines the handler object of a drop operation /// </summary> publicinterface IDropTarget { /// <summary> /// Gets the effects. /// </summary> /// <param name="dataObject">The data object.</param> /// <returns></returns> DragEffects GetDropEffects(IDraggedData dataObject);
/// <summary> /// Drops the specified data object /// </summary> /// <param name="dataObject">The data object.</param> void Drop(IDraggedData dataObject); }
We need the methods for the effects to instruct the system about the types of operations that are allowed during drag and drop: None, Copy, Move, Scroll, All.
If you are going for the purist approach, you should use your own DragEffects enumeration, as above, since the DragDropEffects enumeration is in the System.Windows assembly, which in theory should have nothing to do with the ViewModel part of the application (one could want to use the ViewModel in a web environment, for example, or in a console application).
You will notice that the GetDropEffects method receives an IDraggedData object. This is also because the IDataObject interface and the DataObject class used in Windows drag and drop operations are also in the System.Windows assembly.
The IDraggedData interface is basically a dictionary that uses the data format as the key and the dragged data object stored in that format as the value. An important fact is that, when trying to drag and drop between applications, you need that the dragged data object be binary serializable. If not, you will only get the expected result when dragging to the same application. Here is an implementation of the interface, complete with a totally lazy way of getting the data based on which type is more "important":
/// <summary> /// Holds data for a drag-and-drop operation /// </summary> publicclass DraggedData : IDraggedData { #region Instance fields
/// <summary> /// A dictionary with the format as the key and the data in that format in the value /// </summary> /// <value></value> public Dictionary<string, object> Values { get { if (mValues == null) { mValues = new Dictionary<string, object>(); } return mValues; } }
/// <summary> /// A dictionary with the format as the key and the data in that format in the value /// </summary> /// <value></value> IDictionary<string, object> IDraggedData.Values { get { return Values; } }
/// <summary> /// Optional object for additional information /// </summary> /// <value></value> publicobject Tag { get; set; }
/// <summary> /// A dictionary for exceptions when retrieving the data in a specified format /// </summary> /// <value>The exceptions.</value> public Dictionary<string, Exception> Exceptions { get { if (mExceptions == null) { mExceptions = new Dictionary<string, Exception>(); } return mExceptions; } }
#endregion }
In the IDragSource interface we need the GetData method to extract the data object associated with a dragged object, since the Windows drag and drop mechanism encapsulates the objects in an application agnostic way, so one can perform drag and drop between applications or to/from the operating system. Finally, we need the Drop method in the IDropTarget interface to handle in the ViewModel what happends when an item is dropped.
Let's get to the juicy part: a DragService static class that will register the attached properties that we need. Besides the DragSource and DropTarget properties we need status properties like DragOverStatus (for the target), DraggedStatus and IsDragged (for the original dragged item) as well as two properties called BringIntoViewOnDrag and ActivateOnDrag which would bring an item completely into view or activate it (if a Window) when a valid drop target. This one is long. It also contains some extension methods that would be explained later.
Most of the code here is self explanatory: you have attached property that bind to the element drag and drop events and handle them either through direct code or by delegating to the values set. There are some extension methods like ExecuteWhenLoaded and ExecuteWhenUnloaded which execute an action when the element has finished loading or when it is unloading. An important aspect here is that a window closing does not trigger the Unloaded event for its child elements, so you need to bind to the Dispatcher.StartedShutdown event as well:
That is pretty much it for the drag and drop itself. You can implement IDropTarget on the ViewModel directly, but IDragSource needs to be binary serializable if you intend to drag and drop across application domains, so you usually implement it into a separate class that is a property of the dragged item view model or DataContext.
Because the DragService sets the status properties for each element, you can manipulate the appearance and behaviour of both drag source and drop target based on them. However, at this point the mouse will be the only indication that you are dragging something. You might want to actually drag something, especially since in a move operation the original element would be hidden during the drag. The problem here is that the drag source element cannot control the display of the dragged item in other applications, nor should it in its application, since it is not its responsibility. The solution: decorate an element over which you would drag something (like the root element of the entire window) with something that knows how to display dragged items:This code uses an adorner to display the dragged item over it. Nothing fancy, since the actual template for the dragged data is defined in the decorator as the content. This is not the place to discuss the adorner, though, so here is just the code:You would only need to decorate a view and then define the AdornerContent property for the decorator and, optionally, the grab point offset for the dragged element.
All that is left here is to show some usage examples. Let's assume we need a View over which we can drag and drop items:
Here you have a user control view which has the drop target set to its own view model and it is set to update the DragOverStatus property of the ViewModel when its attached DragOverStatus property is changed. The status properties are inheritable, so all the children of the view have them set. It is easy to define a Button style that has its text bolded when a copy operation is allowed for a drop item:
The container for the items is a simple WrapPanel and it is placed in a dock panel together with add and remove item buttons. This dock panel is decorated as a drag visual container, and the content that is dragged is set to a custom control called ContentItem, with a drag point set to 40,40. The DataContext property of the item is set to the DraggedData property so that it expresses the actual dragged object.
Now we have set up a container to be a drop target for items. It displays the items as they are dragged over it. All we have left is to set up the items, the ContentItem control, to be a DragSource:
The control style defines as a drag source the DragSource property of the data context of the item and synchronizes with the data context the properties of IsDragged and DragStatus. Triggers then make is pinkish when dragged and greenish when it can be dropped. Notice that this applies to the original item, while its representation is dragged, so you have a feedback of what is going on with the item right at the source.
I won't put here the ViewModels or the data items, since they are pretty much part of the business context, not the drag and drop. Just return DragEffects. All on the effects methods and you can drag anything anywhere, for example.
That's it, folks: drag and drop completely MVVM, without as much as writing an event handler or caring about the actual elements in the viewmodel. It would be even easier if you would allow references to WPF assemblies in the ViewModels, since you could also get the source elements and do stuff with them, but that wouldn't be much of an MVVM pattern, would it?
And here is the AdornerBase class, just a simple helper class:
/// <summary> /// Basic adorner class that exposes simple Attach and Dettach methods /// </summary> publicabstractclass AdornerBase : Adorner { #region Instance fields
privatebool mIsDettached;
#endregion
#region Properties
publicbool IsDettached { get { return mIsDettached; } }
public AdornerLayer AdornerLayer { get; private set; }
Pete Postlethwaite is dead. This has hit me hard and it is difficult to describe why. He has played in so many movies that I loved, but I also loved his acting and manner. Now, at only 64, he died peacefully after a lengthy illness. How can anyone die peacefully of cancer is beyond me. This disease is just ridiculous, somebody get rid of it already!
Anyway, Mr. Postlethwaite was a great actor and I will personally miss him.
I've remembered this song when reading a review of the third movie in the Lost Boys franchise and watching its trailer. Really, you should watch the first one. The others two are a probably a completely different thing. I haven't watched them, yet. The remade version of the song in the trailer immediately rekindled some of the feelings I had when watching as a kid the atmospheric original film; this is proof of its value, I believe. Also, many artists have covered the song in different and interesting ways, listed below.
I will not get into the whole "Poor Corey Haim" thing, I didn't really have much love for the guy, but in Lost Boys he was cool. Here is the song, with a fan made video:
I've just seen the last episode of the first season of Caprica, the series offshooting from Battlestar Galactica. There will be no second season, as the show has been cancelled. Why is that?
Let's start with something other than pure financial interests (oh, you TV network Guatrau!) and discuss the effort that was put in the series. You have a very successful and innovative series like Battlestar Galactica, creating an entire universe with its technology and religions and cultures, and then you go further and create a new series that actually reproduces the lives of people on those worlds. You get cultural criminal organisations, religious fanatics, technologists, robots, AI, virtual worlds, space travel, fashion, music, everything! This is the hardest part of any story: the setup. So you get the setup done and then you... stop writing the story because of low viewer turnout? Are you stupid?! Just tell it in a way that will please your stupid audience and also continue the effort, or can't your little brain get around that?
Then it is about the actor effort. God(s), I hated Clarice Willow and you know why? Because Polly Walker did a great job playing that character, down to the microexpressions and little scheming eye movements. Eric Stoltz, Esai Morales, even the teenagers and kids played well, creating really complex likeable or hateable characters. The creator, Ronald D. Moore, which I totally hate for ruining the end seasons of Battlestar Galactica, also did his best and I can see that it is a great show. And you are going to piss on that? The success of any project lies in the motivation to do it. All these people were motivated and you, money grabbing assholes, pissed in their faces. Shame doesn't begin to cover it. I don't want to shame you, I want you to die a horrible death, you soulless vampires!
And then there is the audience, your precious little numbers. Do you guys know why your network is called SyFy for? I will tell you, so you can say "oh!" with your carefully arranged heads; I guess this is coming as a surprise: it comes from the old name of SciFi, which in turn comes from science fiction. Now you fuckers get what your audience is? Or did you rename the channel only because you have no idea what it was about anyway? Kudos for HBO for raising the standards cause you are just letting them drop in the gutter.
So, in conclusion: Fuck you, SyFy Channel and all your executives, and as you are doing everything possible to stiffle creativity, I wish you a very creative and painful death!
I am a .NET programmer living and working in Bucharest, Romania.
Posts are divided into programming and misc.
Check out the icons above on how to contact or chat with me.
Unless otherwise specified, all code or any type of work you find on this blog is under MIT license. While I welcome attribution, I don't require it. Just use anything in any way you see fit. Consider it completely and utterly shared for the lulz.