# 21 Nisan 2008 Pazartesi
Some time ago, Stuart Kent published the roadmap for DSL Tools. The idea of Dsl extensibility and WPF-based design surface mentioned there easily make someone drool. In the comments to the same post, he also mentions a way to provide your own editor instead of the built-in one. Here's my take on using that technique to provide a custom editor-wannabe in WPF.
  • Create a new Domain-Specific Language Designer (File \ New \ Project \ Other Project Types \ Extensibility)
    Name the project as WPFDSLDesigner
  • Select Class Diagrams template and click Finish to accept the defaults.
  • On the DSL Explorer tool window, click on the Editor node. Make a note of the FileExtension property in the Properties Window. Right click Editor node and Delete it.
  • Right click on the WPFDSLDesigner root node and select Add New Custom Editor. Set the FileExtension property. Set Root Class property to ModelRoot.

  • Transform All Templates using the rightmost button on top of the Solution Explorer.
  • Try building the project. The compiler error will lead you to the customization point. The cool thing with the DSL Tools is that, it clearly marks customization points expected by the developer with appropriate comments, all the time. We will add a partial class to supply our own getter in this case, as described by the comment in the code.
  • Add a project reference to WindowsFormsIntegration assembly (The last element in the dialog, most probably)
  • Add a class named WPFDesignerDocView to the DSLPackage project. (I created a DocView folder to group added files in a single place.) Change the code as follows:

    namespace Company.WPFDSLDesigner.DslPackage

    {

        using System.Windows.Forms;

        using System.Windows.Forms.Integration;

     

        internal partial class WPFDSLDesignerDocView

        {

            private ElementHost host;

            public override IWin32Window Window

            {

                get

                {

                    if (host == null)

                    {

                        host = new ElementHost { Dock = DockStyle.Fill };

     

                        WPFDesigner designer = new WPFDesigner(this);

                        host.Child = designer;

                    }

     

                    return host;

                }

            }

     

            protected override bool LoadView()

            {

                bool result = base.LoadView();

                if (result)

                {

                    ((WPFDesigner)host.Child).DocumentLoaded();

                }

     

                return result;

            }

        }

    }

  • Add a User Control (WPF) item to the project, name it WPFDesigner. Change the Xaml code of the UserControl as follows:

    <UserControl x:Class="Company.WPFDSLDesigner.DslPackage.WPFDesigner"

        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

        <UserControl.Resources>

            <Style TargetType="{x:Type ListBox}">

                <Setter Property="ItemsPanel">

                    <Setter.Value>

                        <ItemsPanelTemplate>

                            <StackPanel />

                        </ItemsPanelTemplate>

                    </Setter.Value>

                </Setter>

                <Setter Property="ItemTemplate">

                    <Setter.Value>

                        <DataTemplate>

                            <Border BorderBrush="Black" BorderThickness="1" CornerRadius="5" Margin="6">

                                <Label Content="{Binding Path=Name}"></Label>

                            </Border>

                        </DataTemplate>

                    </Setter.Value>

                </Setter>

            </Style>

        </UserControl.Resources>

     

        <Grid>

            <ListBox Name="modelClassViewer" ItemsSource="{Binding Mode=OneWay}" />

        </Grid>

    </UserControl>

  • And change the code of the UserControl to:

    namespace Company.WPFDSLDesigner.DslPackage

    {

        using Microsoft.VisualStudio.Modeling.Shell;

        using System.Collections.Generic;

     

        public partial class WPFDesigner

        {

            public ModelingDocView DocView { get; set; }

            protected ModelRoot root;

     

            public WPFDesigner()

            {

                InitializeComponent();

            }

     

            public WPFDesigner(ModelingDocView docView)

                : this()

            {

                DocView = docView;

            }

     

            public void DocumentLoaded()

            {

                if (DocView != null && DocView.DocData.RootElement != null)

                {

                    // Had some problems binding to ModelRoot.Types directly. Using a custom list instead.

                    var list = new List<ModelType>();

                    list.AddRange(((ModelRoot)DocView.DocData.RootElement).Types);

                    modelClassViewer.DataContext = list;

                }

            }

        }

    }


  • Your final project structure should be looking something like this:

  • Run the solution to open the Debugging project. Open the Sample.mydslx file. That's all.
And the result is:



Nothing fancy at the moment, but it's pure WPF. Add some compartment shapes and connectors, layout the entities rather than using a StackPanel and you're almost done. Nested shapes and such should be a breeze to implement.

Thank god they're working on this some future version of DSL Tools, WPF is so powerful to customize. I'm expecting that in the final version, we'll optionally be able to provide our own templates instead of the generated ones for customize-like-hell experiences.

Here are some future research points for the implementation above:
  • We can write a code generator to create shapes based on the Diagram Element properties in DslDefinition.dsl. Compartments, decorators, appearance properties etc. Why enumerate a bunch of types at runtime when we're already generating the rest design time?
  • Databinding to LinkedElementCollection failed pretty bad, VS kept crashing in every single case. Hey, I want to use built-in collections. Any INotifyCollectionChanged, by the way?
  • We should be mapping WPF menu items to package menus (VSCT). Add new property, validate etc.
  • We should either use the original .designer file structure to persist the layout etc. or find a way to store that data somewhere. (Designer file is the best)
  • Performance is not so great. I tried some 3D and it's even worse, much worse. There's something fishy going-on when you host a WPF control on top of a win32 container, or in VS, or both.