Preventing model elements from being deleted
Tags: dotnet, dsl, JaDAL, visual studio, vsxAdd comments
One would think, it is a simple feature in the DSL Tools framework to allow and forbid the deletion of model elements, but it isn’t.
There are a few posts in the DSL Forum and it seems there are even a few methods to forbid the deletion. But every single solution has its pros and cons and you have to write a little bit of code yourself. Just take a look at the following postings:
- http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=73606&SiteID=1
- http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=780425&SiteID=1
One solution is to throw an exception from within a DeletingRule, but I personally don’t like this. It means, the user is able to click on a delete menu item and then an error message box pops up. This is not a good user experience!
I think the best would be to hide the "delete" commands from the menus. If you disable the commands, the delete key isn’t working, too. But there are two places where the user can find such a "delete" command for the model elements: on the design surface of the DSL Editor and on the items of the DSL Explorer (and further more: the DSL Explorer has a "delete" and a "delete all" command on different tree nodes). You have to handle both cases.
I would just like to have somewhere a bool CanDelete()
method that is called every time the menu is shown and asks my component whether to allow the deletion or not.
Now the good news: I added this Method and wrote a little piece of code for it!
First I created a very simple Interface
public interface IDynamicCanDelete { bool CanDelete(); }
With this interface you can add the missing method to your shapes or model elements. I recommend to implement this method in the model elements since the model explorer knows nothing about your shapes and cannot call this method if implemented in the shape classes. You don’t need to implement this method for every model element, but only for those you want to forbid deletion. Just return false
or implement some logic based on the model element state.
If this feature would be part of DSL Tools that is all you need to do. Though since it is only an addition to it, you have to connect some methods with my library:
- In the
DslPackage
project add a partial class for yourMyLanguageCommandSet
and override the following method:
protected override void ProcessOnStatusDeleteCommand (MenuCommand command) { OnStatusDeleteCommandLogic.ForEditor(command, this.CurrentDocumentSelection, base.ProcessOnStatusDeleteCommand); }
In the same project add a partial class for your MyLanguageExplorer
and override two methods:
protected override void ProcessOnStatusDeleteCommand (MenuCommand cmd) { OnStatusDeleteCommandLogic.ForExplorerDelete(cmd, this.SelectedElement, base.ProcessOnStatusDeleteCommand); } protected override void ProcessOnStatusDeleteAllCommand (MenuCommand cmd) { OnStatusDeleteCommandLogic.ForExplorerDeleteAll(cmd, this.ObjectModelBrowser.SelectedNode, base.ProcessOnStatusDeleteAllCommand); }
That’s all, but what’s happening inside the OnStatusDeleteCommandLogic
class? I’m looking for the selected elements (that are shapes in the editor or TreeNodes in the DSL Explorer) and check whether they implement the IDynamicCanDelete
interface. If there is this interface I will call the CanDelete()
method and if it returns false
the delete menu command will be disabled and set to invisible.
For the Editor I will check the shape and the corresponding model element. If there are multiple elements selected only one must report false
to disable the command.
The DSL Explorer provides a Delete All command on some nodes. For this command I will check all children but not the grandchildren.
For more details please take a look at the source code.
Download the files
Update
This code is now part of the JaDAL – Just another DSL-Tools Addon Library project. Please download the current version from that page. The download over at CodePlex contains the source code described here, an example DSL language and the library as a binary. Future enhancements of the code will be published there, too.
Additional bug fix for the explorer
DuncanP mentioned at the end of his article a small bug in the behavior of the model explorer. This bug can make the “Delete All” command visible when you don’t want it to be. He described an idea for a bug fix. I can find the same wrong behavior in the current release for Visual Studio 2008 and implemented a bug fix the way DuncanP pointed out:
Add the following code in the MyLanguageExplorer
class
public override void AddCommandHandlers(IMenuCommandService menuCommandService) { base.AddCommandHandlers(menuCommandService); MenuCommand deleteAllCommand = menuCommandService.FindCommand (CommonModelingCommands.ModelExplorerDeleteAll); this.ObjectModelBrowser.AfterSelect += delegate { ProcessOnStatusDeleteAllCommand(deleteAllCommand); }; }
May 16th, 2008 at 9:18 am
Thanks,
Exactly what I needed. Simple solutions like this are always the best.
Peter
May 28th, 2008 at 12:11 am
[…] 9. Preventing model elements from being deleted […]
July 15th, 2008 at 7:26 pm
[…] Article #1 […]
February 16th, 2010 at 4:05 pm
Hi Benjamin,
I’ve added your code to my DSL and it works great at suppressing the delete command. But I can still delete elements by simply cutting them instead. How did you prevent this in your examples?
Kevin