现在的位置: 首页 > 综合 > 正文

VsxHowTo — 把Windows Forms Designer作为自己的编辑器(2)

2011年07月31日 ⁄ 综合 ⁄ 共 3506字 ⁄ 字号 评论关闭

     我们在上一篇文章里利用Windows Forms Designer做了一个简单的表单设计器,但这个设计器还存在一些问题,比如控件不能自动命名;文档窗口不会自动加入dirty标记;不能undo/redo和copy/paste;不能保存和读取数据等等。这一篇我们来逐一解决这些问题。

控件自动命名

     从toolbox里拖入一个控件时,如果想让控件自动命名,我们需要往DesignerHost里加一个INameCreationService的服务,我没有研究过为什么BasicDesignerLoader不默认帮我们加上,不过好在msdn上有一个例子,我稍微简化了一下它,如下:

using System;

using System.ComponentModel;

using System.ComponentModel.Design.Serialization;

using System.Collections.Generic;

using System.Collections;

 

namespace Company.WinFormDesigner

{

    class NameCreationService : INameCreationService

    {

        #region INameCreationService 成员

 

        public string CreateName(IContainer container, Type dataType)

        {

            IList<string> list = new List<string>();

            for (int i = 0; i < container.Components.Count; i++)

            {

                list.Add(container.Components[i].Site.Name);

            }            

            return CreateNameByList(list, dataType.Name);

 

        }

 

        public bool IsValidName(string name)

        {

            //name is always valid

            return true;

        }

        public void ValidateName(string name)

        {

            //do nothing

        }

 

        #endregion

 

        /// <summary>

        /// 创建一个基于baseName并且在array中不存在的名称

        /// </summary>

        public static string CreateNameByList(IList<string> list, string baseName)

        {

            int uniqueID = 1;

            bool unique = false;

            while (!unique)

            {

                unique = true;

                foreach (string s in list)

                {

                    if (s.StartsWith(baseName + uniqueID.ToString()))

                    {

                        unique = false;

                        uniqueID++;

                        break;

                    }

                }

            }

            return baseName + uniqueID.ToString();

        }

    }

}

     我们需要把它的实例加到DesignerHost中。在DesignerLoader中重写Initialize方法:

 

protected override void Initialize()

{

    base.Initialize();          

    LoaderHost.AddService(typeof(INameCreationService), new NameCreationService());        

}

     按ctrl + F5运行,启动VS实验室之后,打开一个.form文件,在工具箱中拖入一个Button,看,是不是已经自动有了Name了呢?

支持Undo/Redo

     这个问题纠结了我好久,因为实在有太多的方法实现Undo和Redo了,最后通过reflector才找到最佳的解决方案。和控件的自动命名一样,不能Undo/Redo也是由于少了一个Service:ComponentSerializationService。.net framework提供了一个默认的实现:System.ComponentModel.Design.Serialization.CodeDomComponentSerializationService。我一度以为不能直接使用这个类,因为我们并没有CodeDom,不过在尝试了很多方法后,最后在无奈的情况下才去使用这个类,居然成功了,看来绝不能以貌取人。由于已经有实现了,我们只需要把它加到DesignerHost里就行了,依然要修改DesignerLoader的Initialize方法:

protected override void Initialize()

{

    base.Initialize();    

    LoaderHost.AddService(typeof(INameCreationService), new NameCreationService());

    LoaderHost.AddService(typeof(ComponentSerializationService), new CodeDomComponentSerializationService(LoaderHost));

}

 

     大家可以测试一下效果,真的可以undo/redo了,呵呵。

支持Copy/Paste

     到目前为止,我们的设计器似乎不支持复制和粘贴。选中一个控件后,复制的按钮是可用的,但粘贴却一直是灰色的,这是怎么回事呢?没错,你猜对了,DesignerHost里少了相应的Service。这个Service叫做System.ComponentModel.Design.Serialization.IDesignerSerializationService。这个接口有两个方法,分别处理序列化和反序列化的工作,我没有找到.net framework里公开的实现,所以我们不得不自己实现这个接口了。不过好在我们可以充分利用undo/redo里提到的ComponentSerializationService来帮助我们去序列化和反序列化控件。我的实现类如下:

using System;

using System.ComponentModel.Design.Serialization;

using System.ComponentModel.Design;

using System.Collections;

 

namespace Company.WinFormDesigner

{

    class DesignerSerializationService : IDesignerSerializationService

    {

        private IServiceProvider serviceProvider;

        private ComponentSerializationService serializer;

 

        public DesignerSerializationService(IServiceProvider sp)

        {

            serviceProvider = sp;

            serializer = serviceProvider.GetService(typeof(ComponentSerializationService)) as ComponentSerializationService;

        }

 

        #region IDesignerSerializationService 成员

 

        public ICollection Deserialize(object serializationData)

        {

            if (serializer != null && serializationData is SerializationStore)

            {

                return serializer.Deserialize((SerializationStore)serializationData);

            }

            return null;

 

        }

 

        public object Serialize(ICollection objects)

        {

            if (objects == null)

            {

                objects = new object[0];

            }

            if (serializer != null)

            {

                SerializationStore store = serializer.CreateStore();

                using (store)

                {

                    foreach (object obj in objects)

                    {

                        serializer.Serialize(store, obj);

                    }

                }

                return store;

            }

            return null;

        }

 

        #endregion

    }

}

     接下来,我们需要把它加到DesignerHost里:

抱歉!评论已关闭.