This project has moved and is read-only. For the latest updates, please go here.

Question for DevLynx regarding DynamicCommandEventLink

Sep 10, 2008 at 5:30 PM
I just read your post  (on http://devlynx.livejournal.com/ ) regarding the usefulness of CAB's command structure.  I recently came to similar conclusion as you and am currently in the midst of developing my own UIExtenstion site infrastructure for our CAB applicartion.  The code snippet you posted looks similar to where I was headed.  Do you have a sample that demonstrates how it all hangs together with the CAB infrastructure?  I am attempting to avoid re-inventing the wheel!

Any suggestions would be appreciated!  BTW, I am using DevExpresses Bar objects as Toolbar/Menubars, not Ribbons, but I don't think that matters...

Marcus


Your Code snippet:

private void ribbonControl_ItemClick(object sender, ItemClickEventArgs e)
{
   if (e.Item.Tag != null && e.Item.Tag is DynamicCommandEventLink)
   {
       DynamicCommandEventLink eventLink = e.Item.Tag as DynamicCommandEventLink;
       EventTopic eventTopic = rootWorkItem.EventTopics[eventLink.EventTopicName];
       if (eventTopic != null)
       {
           EventArgs<DynamicCommandEventLink> ex = 
new EventArgs<DynamicCommandEventLink>(eventLink);
           eventTopic.Fire(sender, ex, null, PublicationScope.Global);
       }
    }
}
Sep 11, 2008 at 4:09 AM
Edited Sep 11, 2008 at 4:11 AM
Hi Marcus,

I'm assuming that you are using the CAB DevExpress Extension Kit. There is an example of using the menuing portion of in the Bank Teller example. However, there is nothing for tool bars. I've added a tool bar example with the following code - the rest is directly from the Bank Teller example.

First, you will need to add a main menu and a tool bar to the main form. Then, in the XtraFormApplication descendent you would add the following two lines to register the ui tool bar extension and add the skins:

    RootWorkItem.UIExtensionSites.RegisterSite(ExtensionSiteNames.SkinsToolBar, Shell.skinsBar);

    BankTellerDynamicSkins.Skins.AddSkins(RootWorkItem);


The BankTellerDynamicSkins class is as follows:

using System;

using System.Collections.Generic;

using System.Drawing;

using BankTellerModule.Constants;

using CABDevExpress.UIElements;

using DevExpress.LookAndFeel;

using DevExpress.Skins;

using DevExpress.Utils.Drawing;

using DevExpress.XtraBars;

using DevExpress.XtraEditors;

using Microsoft.Practices.CompositeUI;

using Microsoft.Practices.CompositeUI.EventBroker;

 

namespace BankShell

{

    public class BankTellerDynamicSkins

    {

        private static readonly BankTellerDynamicSkins instance = new BankTellerDynamicSkins();

        private DevExpress.XtraBars.BarSubItem skinMenu;

 

        private BankTellerDynamicSkins() { }

 

        public static BankTellerDynamicSkins Skins

        {

            get { return instance; }

        }

 

        public void AddSkins(WorkItem workItem)

        {

            skinMenu = new DevExpress.XtraBars.BarSubItem();

            // named this menu Look & Feel so that it was differentiated between

            // the SkinMenu option.

            skinMenu.Caption = "Look && Feel";

            workItem.UIExtensionSites[ExtensionSiteNames.MainMenu].Add(skinMenu);

            workItem.UIExtensionSites.RegisterSite(ExtensionSiteNames.SkinsDropDown,

                new BarLinksCollectionDynamicUIAdapter(skinMenu.ItemLinks, skinMenu.Manager.Items, workItem));

            AddSkinItems(workItem);

 

            EventTopic eventTopic = workItem.EventTopics[EventNames.SkinPopup];

            eventTopic.AddPublication(skinMenu, "Popup", workItem, PublicationScope.Global);

            eventTopic.AddSubscription(this, "SkinMenuPopup", workItem, ThreadOption.UserInterface);

 

            workItem.EventTopics[EventNames.SwitchSkin].AddSubscription(this, "ModifyLookAndFeel", workItem, ThreadOption.UserInterface);

        }

 

        private void AddSkinItems(WorkItem workItem)

        {

            foreach (string skinName in GetSortedSkinNames())

            {

                BarItem menuItem = new BarCheckItem(skinMenu.Manager, SkinManager.DefaultSkinName == skinName ? true : false);

                menuItem.Caption = skinName;

                menuItem.Hint = skinName;

                BuildSkinImages(skinName, menuItem);

                menuItem.Tag = new DynamicCommandEventLink(EventNames.SwitchSkin, skinName);

                workItem.UIExtensionSites[ExtensionSiteNames.SkinsDropDown].Add<BarItem>(menuItem);

                workItem.UIExtensionSites[ExtensionSiteNames.SkinsToolBar].Add<BarItem>(menuItem);

            }

        }

 

        private static void BuildSkinImages(string skinName, BarItem item)

        {

            SimpleButton imageButton = new SimpleButton();

            imageButton.LookAndFeel.SetSkinStyle(skinName);

 

            item.Glyph = GetSkinImage(imageButton, 20, 20, 2);

        }

 

        /// <summary>

        /// Gets the skin image. This method closely follows the one from the DevExpress Simple

        /// Ribbon Pad demo. Thanks to DevExpress for letting us use this routine!

        /// </summary>

        private static Bitmap GetSkinImage(SimpleButton button, int width, int height, int indent)

        {

            Bitmap image = new Bitmap(width, height);

            using (Graphics g = Graphics.FromImage(image))

            {

                StyleObjectInfoArgs info = new StyleObjectInfoArgs(new GraphicsCache(g));

                info.Bounds = new Rectangle(0, 0, width, height);

                button.LookAndFeel.Painter.GroupPanel.DrawObject(info);

                button.LookAndFeel.Painter.Border.DrawObject(info);

                info.Bounds = new Rectangle(indent, indent, width - indent * 2, height - indent * 2);

                button.LookAndFeel.Painter.Button.DrawObject(info);

            }

            return image;

        }

 

        private static List<string> GetSortedSkinNames()

        {

            List<string> skinNames = new List<string>(SkinManager.Default.Skins.Count);

 

            foreach (SkinContainer skinContainer in SkinManager.Default.Skins)

                skinNames.Add(skinContainer.SkinName);

 

            skinNames.Sort();

            return skinNames;

        }

 

        // The subscriber to the EventNames.SwitchSkin event

        public void ModifyLookAndFeel(Object sender, DynamicEventArgs<DynamicCommandEventLink> e)

        {

            UserLookAndFeel.Default.SetSkinStyle((e.Data as DynamicCommandEventLink).Data.ToString());

            SetSkinsChecked();

        }

 

        private void SetSkinsChecked()

        {

            foreach (BarItemLink item in skinMenu.ItemLinks)

            {

                if (item.Item is BarCheckItem)

                    ((BarCheckItem)item.Item).Checked = UserLookAndFeel.Default.ActiveSkinName == item.Caption;

            }

        }

 

        // The subscriber to the EventNames.SkinPopup event

        public void SkinMenuPopup(object sender, System.EventArgs e)

        {

            SetSkinsChecked();

        }

    }

}


I hope that the code is fairly self explanatory. Obviously, the foreach loop in the AddSkinItems method is the meat of the class where the BarItem is created, initialized and then added to the two UiExtensionSites.

If this doesn't help or you have additional questions, please let me know and I'll do my best to help.

dlx
Sep 14, 2008 at 12:11 AM
Thank you for your response.  The code sample is very clear.  I implemented a service that wraps very similar functionality as what you described in your code sample.  It allows for dynamic toolbar/menubar creation and registration as a UIExtension site.  So far so good!!


Marcus
Feb 8, 2009 at 11:10 PM
Hello,
I too have found these posts for the same reasons. But I cannot find any references to DynamicCommandEventLink  in any of the BankTeller Quick Starts in the various CAB DevExpress drops.
Can you advise whether it is posted somewhere else.

Thanks
John
Feb 9, 2009 at 12:38 AM
Hello again,
I did a Google search and found that it's in the SVN version.

Thanks for all your hard work on this. It's been very interesting and the Dynamic Event approach is very impressive.##

John

Feb 9, 2009 at 12:56 AM
John,

Thanks for your nice comment. I really appreciate it. I'm glad that you find it useful.

DevLynx
Feb 20, 2009 at 11:08 PM

I’ve been very interested in this post. DevLynx’s Tag implementation is very straightforward and simple, but I was intrigued to see if there was a CAB way of doing it. Having read discussions the last thing I want to do is cause more dispute, but in looking up the Command pattern, the well-known example is the Short Order Cook. In the description, the Waitress does take the Order and give it to the Cook, so I feel quite happy with a version of the CAB command that has some real EventArgs !!

What are we trying to achieve.

public partial class MainController : Controller

    {       

        [ExtendedCommandHandler("FileExitExtended")]

        public void OnFileExitExtendedCommand(object sender, ItemClickEventArgs e)

        {

            ExtendedCommandWorkItem wi = WorkItem;

            MessageBox.Show("File Exit Extended");

        }

              

        [CommandHandler("FileExitNormal")]

        public void OnFileExitNormalCommand(object sender, EventArgs e)

        {

            MessageBox.Show("File Exit Normal");

        }

             

        [EventSubscription("topic://DevExpressShell/ItemClick", Thread = ThreadOption.UserInterface)]

        public void OnItemClick(object sender, DynamicEventArgs<DynamicCommandEventLink> e)

        {

            DynamicCommandEventLink e2 = e.Data as DynamicCommandEventLink;

            MessageBox.Show("File Exit Dynamically");

        }

 

Here are the three examples of EventHandler, my ExtendedCommand, the original CAB and the DevLynx method using Events.  ItemClickEventArgs is DevExpress’s EventArgs for their XtraBars, and in my example, sender is the BarButtonItem that is clicked.

To make ExtendedCommandHandler attribute work we need an ExtendedCommandHandlerAttribute class and that means we need a ExtendedCommandStrategy subclassed from BuilderStrategy.

In this new class we need to change RegisterCommandHandlers() and UnregisterCommandHandlers(), where I introduce an ExtendedCommand and an ExtendedCommands collection in a subclassed WorkItem, so I can write

ExtendedCommand cmd =  workItem.ExtendedCommands[attr.CommandName];

That completes the subscription side.

 

What do we need to do on the publish side. I’ve implemented my example in a Module, so I have a ModuleInit, MainWorkItem and MainController. 

 

My MainWorkItem derives from ExtendedCommandWorkItem, which itself is derived from WorkItem, and the ExtendedCommandWorkItem class contains 

 

public ManagedObjectCollection<ExtendedCommand> ExtendedCommands {}

 

And finally I create ExtendedCommand subclassed from Command, and an ExtendedCommandAdapter

 

The reason for all this work is to be able to change the Delegate to

 

public delegate void DevEventHandler(object sender, ItemClickEventArgs e);

 

And we add the Invoker in the MainWorkItem, thus

if (this.ExtendedCommands.Contains("FileExitExtended"))

{

cmd = this.ExtendedCommands["FileExitExtended"];

cmd.Item = barButtonItem1;

cmd.AddInvoker(barButtonItem1, "ItemClick");

}

I’ve also implemented my version of the SkinsMenu using a BarCheckItem with the click event handled by the new ExtendedCommandHandler.

In principle the ItemClickEventArgs could be changed to anything and could be a generic container like DynamicEventArgs.

I’m sure other can improve on this and I don’t think it’s particularly useful, but I’ve certainly learnt a lot about CAB in doing it.