WPF动态添加菜单按钮

WPF动态添加菜单按钮

近期研究了SharpDevelop这个项目,发现菜单解耦的方式非常好用,虽然还没有学到精髓,但决定用自己现有的知识实现一样的功能。SharpDevelop使用的是加载dll的方式,以及加载xml文件,这种方式非常好,就是编写xml文件也是件困难的事。我是使用的是MEF的方式,动态加载插件。

先写抽象类,在后续的代码的菜单中继承这个类型

    public abstract class AbsCommand:ICommand
    {
        public virtual bool IsEnabled
        {
            get { return isEnabled; }
            set { isEnabled = value; }
        }
        bool isEnabled = true;

        public object Owner { get; set; }

        public abstract void Run();

        void ICommand.Execute(object parameter)
        {        
            Run();
        }
        //必须注册事件,才能实现菜单的使能在后续的插件中编辑
        event EventHandler ICommand.CanExecuteChanged
        {
            add { CommandManager.RequerySuggested+=value; }
            remove { CommandManager.RequerySuggested -= value; }
        }  

        bool ICommand.CanExecute(object parameter)
        {
            return this.IsEnabled;
        }
    }

使用特性的方式,将命令导出,所以首先定义导出元数据的接口,主要包含菜单的名称,以及菜单栏所在的父节点

    public interface ICommandPath
    {
        string MenuPath { get;  }

        string LabelContent { get;  }
    }


    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false), MetadataAttribute]
    public class ExtensionPathAttribute : Attribute, ICommandPath
    {
        public ExtensionPathAttribute() : base() { }

        public string MenuPath { get; set; }

        public string LabelContent { get; set; }

    }

使用一个容器动态加载全部的指令

    /// <summary>
    /// MEF容器,加载全部的指令
    /// </summary>
    public sealed class MEFContainer : IDisposable
    {
        #region 构造函数
        public MEFContainer()
        {
            InifMef();
        }


        #endregion 构造函数

        #region 公有函数
        /// <summary>
        /// 通过名称获取指令
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public IEnumerable<Tuple<AbsCommand, ICommandPath>> GetAllCommand()
        {
            foreach (Lazy<AbsCommand, ICommandPath> current in Tools)
            {
                yield return Tuple.Create<AbsCommand, ICommandPath>(current.Value, current.Metadata);
            }
        }


        public void Dispose()
        {
            if (null != catalog)
            {
                foreach (var cat in catalog.Catalogs)
                {
                    cat.Dispose();
                }
                catalog.Dispose();
                catalog = null;
            }
            if (null != container)
            {
                container.ReleaseExports(Tools);
                container.Dispose();
                container = null;
            }

            Tools = null;
        }
        #endregion 公有函数

        #region 私有函数
        private void InifMef()
        {
            catalog = new AggregateCatalog();
            //新添加的菜单放在根目录ExpCom文件夹下,且必须UntilComm开头(可根据需要修改)
            catalog.Catalogs.Add(new DirectoryCatalog(basepath + "\\ExpCom", "UntilComm.*"));
            container = new CompositionContainer(catalog);
            container.ComposeParts(this);
        }
        #endregion 私有函数

        #region 私有变量
        private string basepath = AppDomain.CurrentDomain.BaseDirectory;
        [ImportMany(typeof(AbsCommand))]
        private IEnumerable<Lazy<AbsCommand, ICommandPath>> Tools;
        private AggregateCatalog catalog;
        private CompositionContainer container;

        #endregion 私有变量
    }

界面加入菜单,博主使用的是WPF做的,首先在界面添加一个x:name=“MainMenu” 的 Menu,然后在控件的构造函数下插入如下的代码,即可完成加入菜单选项

        public EmptyGrid()
        {
            InitializeComponent();
            DataContext = this;
            //this.CommandBindings.Add(new CommandBinding(ApplicationCommands.New, CreateNewDataFile));
            //this.CommandBindings.Add(new CommandBinding(NewTableCom, New_Table_Comm, NewTable_CanExcute));
            //this.CommandBindings.Add(new CommandBinding(DelTable, DelTabExcute));
            //this.CommandBindings.Add(new CommandBinding(ReNameTable, RenameTabExcute));

            mEFContainer = new MEFContainer();
            var allcomm = mEFContainer.GetAllCommand();
            foreach (var item in allcomm)
            {
                string[] ret = item.Item2.MenuPath.Split(';');
                if (ret == null || ret.Length == 0)
                    continue;
                ItemCollection collection = MainMenu.Items;
                foreach (var label in ret)
                {
                    MenuItem matchitem = collection.OfType<MenuItem>().FirstOrDefault(child => child.Header.ToString() == label);
                    if (null == matchitem)
                    {
                        matchitem = new MenuItem { Header = label };
                        collection.Add(matchitem);
                    }
                    collection = matchitem.Items;
                }
                MenuItem additem = new MenuItem();
                AbsCommand command = (AbsCommand)Activator.CreateInstance(item.Item1.GetType());
                command.Owner = this;
                additem.Header = item.Item2.LabelContent;
                additem.Command = command;
                collection.Add(additem);
            }
        }

新建菜单

    [ExtensionPath(MenuPath = "工具", LabelContent = "数据库查询")]
    [Export(typeof(AbsCommand))]
    public class TestCom : AbsCommand
    {
        /// <summary>
        /// 菜单栏的使能条件
        /// </summary>
        public override bool IsEnabled
        {
            get
            {
                //EmptyGrid form = Owner as EmptyGrid;
                //if (form == null)
                //    return false;
                //if (!File.Exists(DBConstParame.DbPath) || string.IsNullOrEmpty(form.SelectTable))
                //{
                //    return false;
                //}
                return true;
            }
        }

        public override void Run()
        {
            //可通过Owner获取主界面对象
            //EmptyGrid form = Owner as EmptyGrid;
            Trace.WriteLine("My First Menuiten");
        }
    }

这个是第一次写博客,后续有什么好的想法,继续和大家分享,这个是在公司编写的demo,由于文件加密无法上传源码。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值