Blogs

Paul Kimmel's Blog

March 2010 - Posts

  • Validating Using Regular Expressions or an IExtenderProvider

         

    I don’t do the full-on tourism thing that much. If I am out and about and end up at a popular tourist attraction then it’s okay, but rushing place to place to snap photos of things just never appealed to me that much. I have been coming to LA since 1999 and had never been on Hollywood Blvd. I was enjoying a sunset and a beer years ago and saw Venice transmogrify from street carnies to husky-voiced (ahem!) women in a couple of minutes, but it was an accident.

    Last Friday I decided to play tourist and went to the Hollywood Bowl and Hollywood Blvd snapping pictures of everything. I was taking pictures of “Stars” until I saw that Max Factor had a star—I thought this was makeup. Disenchanted I put the iPhone away and ended up at Loteria—the restaurant—for a bite to eat. Loteria—the game—as it goes is a game played with cards that look like Tarot cards and is played like bingo. I dubbed it Tarot-bingo (after a couple of El Milagros I pronounced it “terror bingo”.) The food at Loteria was pretty good, it seemed less fancy than authentic. I watched women shuck Pablano peppers, roll out dough for tortillas, and even swapped a quick holy mole recipe with the open-kitchen help. What struck me about  the restaurant was the place was cool, it was packed full of patrons, but the food presentation wasn’t fancy; in fact as presentation goes it was pretty simple. I had a Mole Con Pollo burrito and that’s what I got—a chicken burrito with Mole sauce and a few chips. No one took any time make yin and yang decorations with the Mole sauce, there wasn’t really any garnish either. The food just tasted fresh, good, and I got to watch them make it. To boot the prices of the entrees were reasonable too.

    Solutions to programming problems can be fancy, full of frills, unique or a solutions can be straight forward, simple, and satisfying. When writing for an audience I might jazz up the code with some garnish, try something clever, or twist and torque the code like test driving a Porsche on the autobahn. In practice straight and simple code-fare may be the way to go more often than not.

    A customer asked something along the lines of “how do I create a custom editor—like one of DevExpress’ XtraEditors—to use with a repository”. So the response in the form of a blog was a custom editor (see http://community.devexpress.com/blogs/paulk/archive/2010/03/19/creating-a-custom-repository-editor-with-validation.aspx). Here is the rub: is it necessary to write a custom editor to provide custom editing? The answer is no. You could write custom validation in an event handler and be done with it. You could use the regular expression language and alternation constructs—or conditions—and use more than one expression. The general rule I use is keep it simple if I need something once—which translates into using an event handler or a composite regular expression—and create a new something if I need to reuse a solution in a lot of places—which translates into a custom editor or some other kind of reusable chunk of code.

    Knowing how to implement an event handler is a general skill that everyone should know. Implementing a custom XtraEditor is a more specific skill that applies to working with XtraEditors and DevExpress controls. Using Regular Expressions is another good, general skill that is useful for all programmers to know. Implementing an IExtenderProvider is a skill more specific to .NET Framework programming that is useful to know especially if you are interested in extending existing controls, whether DevExpress controls or standard WinForms controls. The first example in this blog demonstrates how to use more than one Regular Expression with our XtraEditors controls to screen for data input in a specific format, and the second half demonstrates how to implement an IExtenderProvider to extend controls in general—but in this instance XtraEditor controls derived from BaseEdit.

    Validating a TextEdit Control Using Compound Regular Expressions

    XtraEditor controls like CalcEdit, DateEdit, and TextEdit are controls that DevExpress literally provides to customers at no charge (see http://www.devexpress.com/Products/Free/WebRegistration60/). Edit controls can be used to design a form and they appear in container controls like the XtraGrid and XtraTreeList. Derived from the BaseEdit control is the TextEdit control and its descendants. Introduced at the TextEdit level is the Mask property and sub-properties. The Mask property supports entering a variety of edit mask types and masks for preliminary validation. Using Figure 1 as a visual guide you can select the mask type, enter an edit mask or select from a predefined list of masks, chose an auto-complete mode, and experiment with input test data—all at design time.

    In the scenario mentioned at the beginning of this blog, the customer asked about entering time values in hh:mm, ##.##, or ## formats to represent elapsed time. The first format was a short format. The second format was hours and fractional hours, and the final format was total elapsed minutes. The solution referenced in the blog above demonstrated a custom editor. By using an alternation construct—an or condition—in the regular expression edit mask you can roughly achieve the same result provided by the custom editor. The following composite regular expression matches all three input types:

    (\d*\.\d+|\d+:[0-5][0-9]|\d+)

    The first sub-expression matches 0 or more digits, a decimal, and one or more digits. The pipe (|) separates expressions and is treated as an either/or condition. The next sub-expression matches one or more digits, a colon (:), and two digits with the first of the paired digits being between 0 and 5 and the second between 0 and 9. The third expression matches one or more digits. Consequently, unless there is a bug in my expressions this single compound regular expression satisfies the requirements of validating a variety of time inputs.

    image
    Figure 1: Choose mask type like RegEx, define or select from existing masks, and provide test input all at design time. 

    The solution in this part is a little like food at the Loteria—satisfactory, wholesome and a fairly simple presentation. If you use the regular expression mask then you will get the pre-defined behavior provided by the editor when the mask condition isn’t met. If you want custom behavior—for example additional masks—or code that you can package up and share with co-workers then you can create an IExtenderProvider. (Of course, you could email a regular expression too.)

    Because regular expressions are represented by a grammar and things like alternation constructs are supported (either/or conditions) you can effectively have multiple expressions for the TextEdit Mask property. However, if you use one of the other mask types like Simple then you will be limited to a single mask. By implementing an IExtenderProvider you can provide new properties for an existing control without inheritance.

    IExtenderProvider is an implementation of the Decorator design pattern. IExtenderProvider (Decorator) permits you to add behaviors to an object dynamically. By implementing System.ComponentMode.IExtenderProvider and adorning your provider with a few choice attributes you can effectively extend a category of controls without inheriting from each (or any of those controls). This is how the WinForms ToolTip works—it is an extender.

    The way IExtenderProvider works is that you define a class, inherit from Component and IExtenderProvider. This control will be placed in the components tray of your WinForms application and everything the extender extends will dynamically be giving the additional properties as defined by the provider. Because one extender/provider will be providing the additional features for potentially any number of controls the extender/provider will need a way to track the value of the extended properties and the control those values are provided to. The ideal way to do this is a Hashtable. The next challenge is that a specific control’s extended properties will be set one at a time; this means that the control and property values will need to be sent back and forth to the extender/provider. Clearly, if there are multiple arguments then the actual property construct won’t work; methods are needed. The general solution then becomes implement IExtenderProvider, add a Hashtable to store per-control property values, and define methods for passing in the control and its associated extended properties. The final piece is to add metadata that describes what the provided property is and the type of the control being extended.

    For the sample solution I elected to define an Expressions property to be associated with BaseEdit controls. Listing 1 contains the solution for the extender/provider followed by an analysis of the code and some notes that may be generally useful if you plan on implementing extender/providers in the future.

    Listing 1: My IExtenderProvider provides a string collection as an Expressions property; these strings are treated as regular expressions used for testing the Text property of the editor.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ComponentModel;
    using DevExpress.XtraEditors;
    using System.Collections;
    using System.Collections.Specialized;
    using System.Drawing.Design;
    using System.Windows.Forms;
    using System.Text.RegularExpressions;

    //PROPS to : http://mastersact.blogspot.com/2007/06/string-collection-editor-for-property.html
    namespace EditorExtenderProvider
    {
      [ProvideProperty("Expressions", typeof(BaseEdit))]
      [Category("Design")]
      public class EditExtender : Component, IExtenderProvider
      {
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        private Hashtable expressions = null;
        public EditExtender()
        {
          expressions = new Hashtable();
        }

        public EditExtender(IContainer parent) : this()
        {
          parent.Add(this);
        }

        [Editor(@"System.Windows.Forms.Design.StringCollectionEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a",
          typeof(System.Drawing.Design.UITypeEditor))]
        public StringCollection GetExpressions(BaseEdit edit)
        {
          StringCollection list = (StringCollection)expressions[edit];
          if(list == null)
          {
            list = new StringCollection();
          }

          return list;
        }

        [Editor(@"System.Windows.Forms.Design.StringCollectionEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a",
          typeof(System.Drawing.Design.UITypeEditor))]
        public void SetExpressions(BaseEdit edit, StringCollection list)
        {
          if (list == null)
            list = new StringCollection();

          expressions[edit] = list;

          edit.Validating += new CancelEventHandler(edit_Validating);
        }

        void edit_Validating(object sender, CancelEventArgs e)
        {
          e.Cancel = false;

          if (sender is BaseEdit == false)
            return;

          // no text nothing to do
          BaseEdit edit = (BaseEdit)sender;
          if (edit.Text == "") return;
          // get the expressions
          StringCollection list = (StringCollection)expressions[sender];
          // no regular expressions for this control
          if (list.Count == 0) return;
          // as soon as it passes one expression bail
          foreach (string pattern in list)
          {
            if (Regex.IsMatch(edit.Text, pattern))
              return;
          }

          e.Cancel = true;
        }

        #region IExtenderProvider Members

        public bool CanExtend(object extendee)
        {
          return extendee is BaseEdit;
        }

        #endregion

      }
    }

    The ProvidePropertyAttribute is used to name the provided properties and the type of object the properties are provided to. The convention is to use a property name, like Expressions, and then define two methods with a Get and Set prefix to support the provided property. Thus, if the provided property is Expressions then you need the methods GetExpressions and SetExpressions. The Get method accepts an argument of the provided type and returns the property values for that object. The Set method accepts an argument of the provided type and sets the values for that type. The CategoryAttribute can be used to define the category in the Properties window used to organize the provided properties. The DesignerSerializationVisibilityAttribute specifies the type of persistence to use when serializing. DesignerSerializationVisibility.Content instructs the designer to serialize the contents of the adorned item. (In the example, the DesignerSerializationVisibilityAttribute isn’t doing anything other than demonstrating how to apply it. By default properties are Visible unless the DesignerSerializationVisibilityAttribute specifies otherwise.)

    The Hashtable is used to pair specific expressions with controls. The control object plays the role of indexer and the string expressions play the role of value. The default constructor creates an instance of the Hashtable. The constructor that takes an IContainer adds the extender to the IContainer and calls the default constructor—: this().

    Here is a sticky part. If you define a StringCollection (or something like List<string>) for one of the provided properties the default editor will be the String Collection Editor shown in Figure 2. This editor will not permit you to add strings to it. In fact, if you try the IDE will raise a “Constructor on type ‘System.String’ not found” exception—see Figure 3. The String Collection Editor you want is the one shown in Figure 4. Unfortunately, this editor is tucked away inside the .NET Framework and is only accessible by using the fully qualified assembly name. The application of the EditorAttribute on the GetExpressions and SetExpressions methods means that the String Collection Editor in Figure 4 will be used to edit expression values.

    image
    Figure 2: The default String Collection Editor will not let you edit extended string collection properties correctly.

    image
    Figure 3: If you try to use the String Collection Editor in Figure 2 for StringCollection or List<string> objects you will get an IDE exception.

    image
    Figure 4: This is the String Collection Editor you want, but it is protected in the .NET Framework so you will have to use a fully qualified assembly name to load it with your extender.

    GetExpressions will be called when you invoke the Expressions property editor in the IDE. The Hashtable is indexed using the argument control. The Hashtable value is that control’s StringCollection. If the StringCollection is null a new empty one is returned. The SetExpressions method indexes the Hashtable and assigns the argument StringCollection to that hash position and binds an event handler to the edit control. The Validating event handler provides the validation behavior. The sender argument is the editor. The editor is used to get the StringCollection from the Hashtable and each item in the collection is treated as a regular expression, comparing the BaseEdit control’s Text property against the StringCollection expression. As long as one of the expressions passes—see the foreach loop—the input value is deemed to pass validation and the method exits. If no expressions exist then validation passes. If there are some expressions but none pass then validation fails. Finally, the IExtenderProvider.CanExtend method indicates the class of the type being extended. Figure 5 shows how the extender provider will appear in an edit control’s Properties window. The extender provider itself will be a component in the Toolbox you add to the component tray.

    image
    Figure 5: Extender/providers show up as properties on the extended controls; one extender provider control can provide its extended properties to every control instance of the type it extends.

    Over the last few blog posts we took a pretty good look at DevExpress’ XtraEditors. XtraEditors can be used by themselves and are used as editors in container controls like the XtraGrid and XtraTreeList. You can define XtraEditors in internal repositories or external repositories, defining an editor in one place, setting its properties one time, and then using that editor in many places. You can define custom extra editors, use events for validation, use masks for validation, or implement an IExtenderProvider for full, custom control. This is a whole lot of flexibility in what on the surface looks like a tiny little control—a TextEdit control for example—and the XtraEditors Suite contains 35 of the 60 free controls that DevExpress gives away.

  • Fries with That Shake

         

    “I don’t make jokes. I just watch government and report the facts.”
    --Will Rogers

    Each of us is probably more a product of our environment than we want to admit. I was born in Flint, Michigan—Little Motown—famous for gangsta rappers like Scurvy, Buicks, and Michael Moore. (My daughter Alex was actually in a Scurvy video “Flintstone Kids” filmed in Flint, Michigan about 7 years ago.) Family legend has it that when I came home from the St. Joseph’s Hospital my dad took me in his arms on his Indian motorcycle and raced up and down the street yelling “it’s a boy”. Three years later—1969—it was a Gladys Knight and the Pips performance at a local Flint bar. Just those two events may explain a lot about my personality. I like rap but hardly anyone would guess that upon meeting me. I can go from napping or reading to riding my dirt bike or aerobatic flying in an instant. My kids are well mannered, but my youngest son has fired an M-16 machine gun on full automatic—gotta love Tennesseans. (Thrill seeking behavior that belies my outward Mr. Rogers exterior.) I like theatre-Rent, Cats, Phantom, Chorus Line—and opera but like playing Blackjack in Vegas, live Boxing and take Tae Kwon Do and Yoga, go figure.

    It is with these written contradictory revelations in mind that I can tell you that Do Fries Go with That Shake? is a song by George Clinton and Parliament (and a popular catcall in Flint’s 1970s Grand Traverse circus) and was even more effective as a marketing strategy by McDonald’s as related in John Love’s book McDonald’s: Behind the Arches. In the simplest re-telling of the McDonald’s version of the concept, with every order the counterperson says do you want fries with that (shake)? The margin on French Fries may only be 5%, but if you have sales in the tens of billions of dollars then 5% is a whole lot of coin. The modern implementation of “fries with that shake” is do you want to “supersize” your order? Again the idea is that for .59 cents you get a bucket sized fountain drink which costs almost nothing extra as the true costs are probably in the labor and the cup where fountain drinks are concerned, but for the average order it represents about a 10% increase in sales. Again, 10% across billions of dollars is serious dough. These are really simple, genius, and effective sales strategies.

    Now whether it is fries with shakes, supersize me, or biggy-sized orders, the result is an effective sales synergy. The customer gets substantially more product at a fraction of the price, suppliers sell more supplies, and revenues are significantly increased. Synergy is where different entities cooperate advantageously for a final outcome. The final outcome in the fast food example is that everyone is pretty happy. Synergy is a term attributed to Buckminster Fuller—the Bucky (geodesic) dome guy (see Figure 1). Synergy is a pretty cool concept, and in my round about way synergy, solution synergy rather than sales synergy, is what I want to talk about.


    Figure 1: The Montreal Biosphere by Buckminster Fuller, 1967.

    Supplier, Developer, and Customer Synergy, or, Avoiding U-Tweaks and Ah-Reports

    There are two recurring problems that software developers see over and over. The first is customers want to tweak GUIs, and the second is that customers often want quick reports. Call these problems UI Tweaks and Ad Hoc Reports. For short let’s use “U-Tweaks” and “Ah-Reports”.

    The challenge with U-Tweaks is that developers and customers can negotiate, often a lot, during development to get the UI right, or contrive some sort of user-customization feature. The first U-Tweak—haggling—can kill a schedule. The second U-Tweak—user customization widget—can be very expensive to contrive, is seldom along the main deliverable path, and ends up being a distraction. Tragically, having experienced this first hand, a lot of good will can be chewed up during negotiation and on-the-fly customization tools can be kludgy (because they often aren’t part of the original plan).

    The challenge with Ah-Reports is that developers and customers can negotiate, often a lot, during development over which reports to include in the budget and what to put in them, or contrive or incorporate some kind of end-user reporting system. The first Ah-Report solution, negotiation, means some part of your constituency doesn’t get their report, resulting in a semi-satisfied customer collective. The second ah-report solution, end-user reporting, means you have to build or buy a tool and provide training. Building, buying, and training on an ah-reporting system can be a budget killer too.

    The solution: buy tools at a fixed cost that incorporate U-Tweaking and Ah-Reporting built in. Think Developer Express. DevExpress’ suite of controls have built in features that address scenarios that make it very easy to allow end-user customization and ad hoc reporting. Three such controls or capabilities are readily available. One of the first is the LayoutControl, a second is the PropertyGridControl, and a third is the built-in ability to print the contents of control based on the PrintingSystem and PrintControl. The LayoutControl is based on the concept of grouped regions. Place container controls like, the XtraGrid or XtraEditor controls in a LayoutControl and the alignment and organization are managed for you. The added benefit is that the developer can easily arrange the controls at design time, and the end-user can re-arrange the layout or visible controls post-delivery. The result is that the end-user, on a user-by-user basis can rearrange the layout, hide or show fields, and create a visual layout that makes the data more meaningful to them without developer intervention or additional cost. (All of this happens with deployed, running code.) Additionally, the user can change their individual layout (or layouts) and restore that layout with a simple file save and open operation. The next control, the PropertyGridControl, will let the user change properties at run-time, effectively changing the appearance of a form. (For more on the PropertyGridControl see my blog Imagineering with the PropertyGridControl at http://community.devexpress.com/blogs/paulk/archive/2010/03/23/imagineering-with-the-propertygridcontrol.aspx.)

    Changing structural layout or property settings through a visual interface (without being able to break the application or affecting other users) is the equivalent of designing and ad hoc report, and ah-report. There is one piece missing: turning the digital UI representation of the data into a printed or PDF report. Fortunately, the core printing system available through the PrintControl, PrintingSystem, or these capabilities built into the various controls, like the LayoutControl, printing a screen that represents an ad hoc report requires a line of code.

    For the modest cost of DXperience you get dozens of great controls and ad hoc end-user customization and reporting (U-Tweaks and Ah-Reports). This is one of those times where a video really illustrates the simplicity and power of these features, so a video on tv.devexpress.com will follow. The example that follows walks you through the basic steps, demonstrating the LayoutControl and some screen (snap) shots that show you some basic layout configuration changes and an idea of what the printed representation of the control will look like.

    To create the basic demo follow these steps:

    1. Create a Windows Forms project
    2. From the Navigation & Layout tab of the Toolbox drag and drop a LayoutControl on the Form
    3. Dock the LayoutControl to the Form
    4. From Visual Studio’s Data menu select Add New Data Source
    5. Step through the database wizard and configure a data source for the Northwind Employees table
    6. Open the Data Sources viewer in Visual Studio and from the drop down for the Employees table click the Details option—see Figure 1
    7. Drag and drop the Employees data source onto the LayoutControl; in Details mode this will have the effect of painting individual labels and edit controls in the LayoutControl—see Figure 2
    8. Using the Details view option will cause a binding source, data source, navigator, and controls to be added to the components tray for the form
    9. Select the employeesBindingNavigator and change Dock to Bottom—it is shown at the top in Figure 2
    10. Open the Data Sources view, expand the Employees dataset, and change the control type for Photo to PictureBox
    11. Drag and drop the Photo column onto the LayoutControl; the LayoutControl will automatically add a label and position the photo
    12. Again select the employeesBindingNavigator. Click the Add ToolStripButton and add a Button to the navigator
    13. Change the new ToolStripButton’s Text property to Print and DisplayStyle to Print
    14. Double-click the Print ToolStripButton and add a line of code that calls the LayoutControl’s Print method
    15. Run the application—see Figure 3—and click Print-see Figure 4; you effectively have a workable application with basic reporting capabilities

    Listing 1 contains the (almost no) code for the solution. Keep reading because the important part—end user customization is where things start to get more interesting.

    image 
    Figure 1: The Data Sources view lets you choose between grid or details mode; selecting Details mode will have the effect of rendering a GUI as individual edit controls.

    image
    Figure 2: With a couple of mouse-clicks you have a basic editing form.

    image
    Figure 3: A basic, (almost) no code solution.

    image
    Figure 4: The basic output is not quite WYSIWYG, but for no touch reporting it is functional.

    Listing 1: The sample requires almost no code.

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;

    namespace LayoutControlWithPrinting
    {
      public partial class Form2 : Form
      {
        public Form2()
        {
          InitializeComponent();
        }

        private void employeesBindingNavigatorSaveItem_Click(object sender, EventArgs e)
        {
          this.Validate();
          this.employeesBindingSource.EndEdit();
          this.tableAdapterManager.UpdateAll(this.northwindDataSet1);

        }

        private void Form2_Load(object sender, EventArgs e)
        {
          // TODO: This line of code loads data into the 'northwindDataSet1.Employees' table. You can move, or remove it, as needed.
          this.employeesTableAdapter.Fill(this.northwindDataSet1.Employees);

        }

        private void toolStripButton1_Click(object sender, EventArgs e)
        {
          layoutControl1.Print();
        }
      }
    }

    The statement layoutControl1.Print is the only line of code you have had to write at this point. Now, without argument from me there are some users that would be satisfied with an application like this—basic edit screens and simplistic reporting-and some others that wouldn’t. If you want total control then you can always use XtraReports and design reports. You can also use design time features to tweak and enhance the basic default layout. But let’s stick to the theme of what your end-users can do by themselves with this basic out of the box solution.

    If your right click over the region where the LayoutControl is you can select Customize Layout. This small dialog has tool buttons for opening saved layouts (.xml files), saving a layout as XML, and Undo and Redo buttons. The Hidden Items tab supports adding spaces, Labels, Separators, and Splitters. The Layout Tree View tab shows you everything on the layout control as a tree of items, making it pretty easy to find any item. With the Customization dialog open the form is in customization mode and additional context menus are available. Hide Customization Form, Rename, Hide Text, Text Position, Hide Item, Group, Create EmptySpace Item, and Size Constraints. Here is a brief description of what these options do/what behaviors they support:

    • Hide Customization Form hides the Customization form and takes the form out of customization mode
    • Rename supports renaming text elements {i.e. changing label text}
    • Hide Text can be used to hide labels
    • Text Position repositions the edit control’s label relative to its associated editor
    • Hide Item hides the item from the user and places it on the Hidden Items tab
    • Group creates a new group item which is an additional area for managing layout
    • Create EmptySpace Item creates an empty region which can also be used for layout
    • And, Size Constraints supports locking and resizing items

    image 
    Figure 5: Built-in is end user customization and the ability to save and restore layouts.

    image
    Figure 6: Additional menu items for customization.

    Keeping in mind that an end-user can customize as much as they want without hurting anything because the user can stop and restart the application or use the Undo button to put things back where they were. Once a layout is the way a user wants it they can save the layout on their workstation (and layout XML files can be shared if desired). The idea here you can encourage experimentation.

    To see what your users will see try a few simple customizations:

    1. Right click to show the Customization dialog
    2. Right click over the photo region and click Group
    3. Click on the group item header and drag and drop the group to the top left corner—see Figure 7
    4. Add an Empty Space under the photo to compact the photo region
    5. Right click where the Text Photo is and select Hide Text
    6. Right click over the group item label, select Rename and change Item0 to Photo
    7. Right click over Employee ID and select Hide Item (relational database keys aren’t that important for users or reports)
    8. Use the same step to hide the Photo Path and Report To items
    9. Create a Group around the Last Name item, drag all of the personal information into that group and rename the group Personal
    10. Create a Group around the Address item and drag other address information into that group, and rename that group Address
    11. Close the customization window and click the Print button—see Figure 8

    image
    Figure 7: No-code customizations require a little knowledge of just a few menu items and a little dragging and dropping.

    image
    Figure 8: With a few minor changes the user can create a customized layout and with the built-in printing capabilities effectively have a personalized report.

    Spending a few minutes on a layout can dramatically change its appearance. If you use the save feature then each user can restore their personalized layouts or share them by sharing the XML file.

    A good strategy for selling your customers on this feature is to walk one or two key super users through a demo. Try adding splitters, show them how easy it is, and then let them try it. End users U-Tweaks and Ah-Reports sort of sell themselves because it offers localized control, and localized control can resolve a lot of potential difficulties of the sort—what if I want changes? what if accounting views the data one way and personnel another? can I create an ad hoc report? Sometimes we just need to print the screen can we do that? can we quickly hide sensitive data? The answer is yes. This is solution synergy. Customers get some of what they routinely ask for, DevExpress has incorporated the capability into our products, and you adroitly manage costs, passing features onto your customers. Solution synergy with DevExpress. (You don’t have to tell your customers that these features were part of a toolset. Let them think you are brilliant; we don’t mind.)

  • Instrumenting Your Application with the PropertyGridControl

         

    The word “instrumenting” sounds like a three dollar word. One definition for instrumentation is adding a line of code that keeps track of what your code is doing (or reports on it) for every line of code that solves a problem. Another definition of instrumentation is code that performs tracing, debugging, performance measuring, and logging so an application can be managed by an external management tool. A three dollar word, but a pretty important concept. Knowing an application’s state at given points can be a useful element of debugging, so a mechanism for displaying state can be a an equally useful mechanism.

    In the early 1990s, I was writing an application based on EGA graphics rendered using ASCII characters or a third party library to render the GUI. This was a time before multi-monitors were common place. Having read Peter Norton’s book Inside the IBM PC & PS/2 when Norton was writing books, it became clear to me that by writing directly to the CGA video buffer memory I could hook up a second monitor—EGA and a monochrome CGA monitor—and add debugging code and sent it as text directly to the second monitor. The EGA monitor had the ASCII rendered GUI and the CGA monitor had the text, tracking what was going on. All of this was accomplished by re-writing or modifying C++ functions like sprintf and printf and sending additional information to the various video memory addresses. (You can of course still experiment with video buffers directly by opening a cmd window, type debug, and entering hexadecimal values at B800:0 using the e command—see Figure 1. However, a too-long discourse on EGA buffer memory and obscure video layouts is probably like being 40 something and talking about high school baseball glory.)

    image 
    Figure 1: You can still tweak the video buffer directly showing how far we have come and perhaps how much history we are dragging around with us.

    What is relevant here is defining a mechanism for tracking what your application is doing is not a new concept, its nom de guerre, “instrumentation”, is relatively new though. In Visual Studio one of the demo solutions in the last few years was an ObjectDumper. ObjectDumper used Reflection to dump an object’s state. I have used a LINQ query to dump object state in more recent publications. Either of these approaches is valid for dumping and debugging an object’s state, but the visual result is usually formatted text sent to the console or debug output window. Perhaps a better refinement is to use DevExpress’ PropertyGridControl in conjunction with some of these other techniques. Simply assign an object to the PropertyGridControl.SelectedObject (or SelectedObjects) property and you have a built in GUI that is as easy to use and as intuitive as Visual Studio’s Property window and a whole lot less code than even using the LINQ dumper—which can be done with a select against an array of PropertyInfo objects.

    How you incorporate the PropertyGridControl in your application is up to you, but here is a scenario. Suppose you have a a simple database browser/editor. You elect not to show key fields but for debugging purposes you may want to see them. You could combine an XtraGrid control for the user and a PropertyGridControl for deploy-time debugging scenarios.

    This solution is so easy to implement, but I can tell you unabashedly that easy is what you want and what you get. When you work with a service provider—in this case Developer Express—to make your objectives and solutions easier to achieve you want the solution to be easy to assemble. Having spent at least 100 hours re-writing I/O functions for C++ to write to both EGA and CGA memory, it is a comparative breeze to achieve the same sort of “what is the state of my objects” behavior with the PropertyGridControl. To implement the sample solution where the XtraGrid shows some of the data to the user for editing, but not information like keys that can be changed, try these steps:

    1. Place a SplitContainerControl on a Windows Form
    2. Set SplitContainerControl.Horizontal to False
    3. Drop a GridControl on the top half of the splitter
    4. and a PropertyGridControl on the bottom half of the container
    5. Using the XtraGrid (GridControl’s) smart tags menu add the Northwind database’s Orders table
    6. On the XtraGrid click Run Designer and in the Main tab click the Columns button and set the OrderID and EmployeeID column’s Visible property to False—see Figure 2
    7. Close the Property Editor for the XtraGrid
    8. Click on the OrdersBindingSource component added by the database wizard
    9. Switch the Properties window in Visual Studio to the Events view
    10. Create a PositionChanged event for the OrdersBindingSource
    11. In the PositionChanged event set the PropertyGridControl.SelectedObject to the current object in the BindingSource (see Listing 1)

    image
    Figure 2: Hide the OrderID and EmployeeID columns.

    Listing 1: The code-behind for this solution is about easy as it gets, but the utility is pretty cool.

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;

    namespace InstrumentingWithPropertyGrid
    {
      public partial class Form1 : Form
      {
        public Form1()
        {
          InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
          // TODO: This line of code loads data into the 'northwindDataSet.Orders' table. You can move, or remove it, as needed.
          this.ordersTableAdapter.Fill(this.northwindDataSet.Orders);

        }

        private void ordersBindingSource_PositionChanged(object sender, EventArgs e)
        {
          propertyGridControl1.SelectedObject =
            ((BindingSource)sender).CurrencyManager.Current;
        }
      }
    }

    As the user scrolls the PositionChanged event is fired and the (orders) BindingSource.CurrencyManager.Current object—the current row—is assigned to the PropertyGridControl.SelectedObject property and the rest is magic. The current row’s data is displayed in the PropertyGridControl and an administrative user or debug user or independent tester could verify additional information like the hidden key fields. (Look at Figure 3 for the running solution.)

    image 
    Figure 3: The basic running solution showing “administrative” fields as determined by our scenario.

    As is if the PropertyGridControl were always visible then the hidden key fields could be updated by updating the values through the PropertyGridControl. In some cases this may be what you want; perhaps an administrative user is permitted to update the EmployeeID of record. If the scenario is that the “administrative” PropertyGridControl view is read-only then set the PropertyGridControl.OptionsBehavior.Editable property to false.

    At this point it becomes a fairly trivial or routine problem to hide or show the PropertyGridControl, for example based on login credentials. Also, it really isn’t important what you point the SelectedObject at. If you want a BindingSource or custom objects in an IList you can use the same basic approach: figure out what event means the focused object has changed and update the PropertyGridControl.SelectedObject property, pointing it at that object.

  • Imagineering with the PropertyGridControl

         

    The PropertyGridControl is an implementation of the XtraVerticalGrid. At its core is roughly a grid with row headers instead of column headers, property names, and property values. Associate an object with a grid and use reflection to read all of the property names and values into a collection and bind to the grid. If one were to oversimplify what the property grid control is then that about captures it. Of course, if you want robust functionality then there is the difficulty of nested row headers instead of column headers, properties, property editors, updating bound objects and dozens of details that go into it. (El Diablo is always in the details.)

    Since DXperience from DevExpress already provides us with a PropertyGridControl derived from the XtraVerticalGrid our less challenging difficulty is what do we do with it. Well, Visual Studio uses the a Properties window to display properties of all kinds and let’s developers manipulate these properties without writing code. Clearly, if you are writing some kind of editor then you might want a property grid. That is an obvious hindsight. Here are a couple of ideas for other uses for the PropertyGridControl. Suppose you wanted to write a user customizable application.The PropertyGridControl binds to objects and provides a UI for changing an object’s state. Let the user change state at runtime and you have some user configurability. A second use might be to create a demo or training application. Such an application would let a user point at a component or control and tweak the properties to experience the results. That’s the demo I created for this blog.

    Here is a scenario I used to encounter all of the time: an objective was to provide an experiential environment where trainees or potential customers were interested in learning about a product or technology. The difficulty is some invitees weren’t tech-savvy and many times the computers didn’t have the right tools or versions of those tools, like Visual Studio. Dragging pre-configured PCs around or installing Visual Studio for a couple of days training was a huge expense. A possible solution—which could easily apply to demoing our controls and components as demoing your custom applications would be to add a demo mode that shows a PropertyGridControl and let’s a non-technical user experience customizations. All that would be needed is the .NET Framework and a brief install for the demo application.

    The sample in this section uses a modeless window (WinForm) with a PropertyGridControl on it. The other form has some DevExpress controls on it, including a RichEditControl, an XtraGrid (GridControl), and two basic buttons. The code let’s you hover the mouse cursor over any of the controls and the PropertyGridControl is bound to that control. Change a setting in the PropertyGridControl and you automatically see the change reflected in the running control. For my purposes this might make moderately useful demo to let people see what our LookAndFeel technology—which supports skinning—looks like in action without compiling, debugging, and restarting over and over. (I think in the context of a conference or technology fair this may be a snappier use of available time.) This particular demo—Listings 1 and 2—also binds all of the controls on the demo Form to a ComboBoxEdit control on the property browser form letting the user pick a control from the combo as well as hover.

    Listing 1 just has a RichEditControl, two WinForms buttons, and an XtraGrid (GridControl) bound to the Northwind database’s Orders table. The only real code is the Form_Load event that loads the XtraGrid’s data and calls an Update method on Form1—more on this later—and a MouseHover event bound to every control’s MouseHover event. MouseHover calls an overloaded instance of Form1.Update. Here is Listing 1. Listing 2 does all of the real work.

    Listing 1: richEditControl1_MouseHover is reused for the two buttons, the Form, the XtraGrid, and the RichEditControl’s MouseHover event.

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;

    namespace PropertyGridDemo
    {
      public partial class Form2 : Form
      {
        public Form2()
        {
          InitializeComponent();
        }

        private void Form2_Load(object sender, EventArgs e)
        {
          // TODO: This line of code loads data into the 'northwindDataSet.Orders' table. You can move, or remove it, as needed.
          this.ordersTableAdapter.Fill(this.northwindDataSet.Orders);
          Form1.Update(this);
        }

        private void richEditControl1_MouseHover(object sender, EventArgs e)
        {
          Form1.Update(sender);
        }
      }
    }

    in Form2_Load.Update the form instance is sent and this is used to fill Form1’s ComboBoxEdit control with controls on Form2. In MouseHover sender is the control being hovered over and this version of Update updates the PropertyGridControl. Listing 2 contains the code for the form containing the PropertyGridControl.

    Listing 2: This form contains a ComboBoxEdit control and PropertyGridControl.

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using DevExpress.XtraVerticalGrid.Rows;
    using DevExpress.Utils.Win;
    using DevExpress.Utils;
    using System.Reflection;
    using DevExpress.XtraEditors;

    namespace PropertyGridDemo
    {
      public partial class Form1 : Form
      {
        public Form1()
        {
          InitializeComponent();
        }

        private static Form1 form = null;
        public static void Update(object selected)
        {
          CreateForm();
          if (selected == null) return;
          form.propertyGridControl1.SelectedObject = selected;
          form.Text = selected.GetType().Name;
        }

        private static void CreateForm()
        {
          if (form == null)
          {
            form = new Form1();
            form.TopMost = true;
            form.StartPosition = FormStartPosition.CenterScreen;
          }

          form.Show();
        }

        public static void Update(Form referenceForm)
        {
          CreateForm();
          if (referenceForm == null) return;

          form.comboBoxEdit1.Properties.Items.Add(referenceForm);
          AddChildren(referenceForm);
        }

        private static void AddChildren(Control control)
        {
          foreach (Control child in control.Controls)
          {
            form.comboBoxEdit1.Properties.Items.Add(child);
            if (child.HasChildren)
              AddChildren(child);
          }

        }

        public static void CloseUp()
        {
          if (form != null)
          {
            form.Close();
            form.Dispose();
            form = null;
          }
        }

        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
          form = null;
        }

        private void comboBoxEdit1_SelectedValueChanged(object sender, EventArgs e)
        {
          propertyGridControl1.SelectedObject =
            (sender as ComboBoxEdit).SelectedItem;
        }

      }
    }

    Listing 2 uses a static helper method technique that I employ to treat a dialog much the same way MessageBox.ShowMessageDialog works. Update(object selected) creates the static Form field and binds the object parameter to the propertyGridControls1.SelectedObject property. That’s at the heart of what it takes to populate the PropertyGridControl—set SelectedObject. The Form’s Text property is set to the object parameters type name. CreateForm creates an instance of the containing Form class and shows it as a modeless dialog. The second version of Update accepts a form object and recursively adds child controls to the ComboBoxEdit control. CloseUp cleans up the static form field. Finally, the ComboBoxEdit.SelectedValueChanged event updates the PropertyGridControl.SelectedObject right from the ComboBoxEdit.SelectedItem property. Since the DevExpress ComboBoxEdit control can hold objects you can store references to controls right in the ComboBoxEdit control. Figures 3 and 4 show the reference form and the form containing the PropertyGridControl, respectively.

    image
    Figure 1: A reference form—which intentionally plain—so when properties are changed the changes are noticeable.

    image
    Figure 2: The form containing the PropertyGridControl; change a property and see the change reflected on the reference form (Figure 1).

    In this demo if I wanted to demo the LookAndFeel property for the RichEditControl I could select the RichEditControl from the ComboBoxEdit control, expand LookAndFeel, change the SkinName to Lilian, and UseDefaultLookAndFeel to False (see Figures 3 and 4).

    image
    Figure 3: Note the subtle change to the RichEditControl caused by the runtime property settings changes (see Figure 4).

    image 
    Figure 4: A runtime demo tool  that shows changes without change, compile, and debug steps.

    A similar approach could be used to demo almost any applications customization settings. Just brain storming here, but something like this might work for non-technical sales  people, to speed up demos in general, or for a training environment. For hardcore developers a better use for a property grid might be for a custom designer (or development environment) or for instrumentation of an application.

  • Creating a Custom Repository Editor with Validation

         

    A customer asked about a Custom Repository Editor for “pilots”. Being a pilot it seems reasonable that one might enter flight times in a variety of formats—90, 1:30, or 1.5 each representing 90 minutes}, perhaps for a digital log book. One might want to enter a variety of formatted data for any individual field. A good general solution might be support for multiple formatting masks. Of course, this solution (in this blog) could also be implemented by just defining a Validating event for the control (XtraGrid) in this example, but creating a custom editor is a more interesting challenge.

    A repository editor exists as an internal editor for container controls like the XtraGrid or XtraTreeList, and there is an external repository (PersistentRepository) component. You can define multiple editors in either place. A repository let’s you predefine edit controls for column data. When the container is placed in edit mode the editor associated through the column’s ColumnEdit property—if defined—is displayed and the settings defined are used. You have the option of using the default editor determined by the data type, one or more editor’s in an internal repository, an external repository, or defining a custom editor and registering that with the repository.

    The example in this blog borrows from the help document at--Custom Editors—and extends that solution, internalizing the validation as a series of regular expressions. The input can be entered for the custom editor as a decimal value, a (very) short time value, or an integer. All non-integer values are converted into an integer representing the time as an integer. Thus, 1:30 is treated as an hour and thirty minutes, converted to 90 minutes, 1.5 is an hour and a half and again converted to 90 (minutes), and integer entries are left as is. (The basic code for the OnValidating method could be used in the Validating event handler for the container control if you don’t want to create a custom editor.)

    The solution is in two parts: a class that inherits from an existing repository editor with the UserRepositoryItem attribute used for editing in the repository and a class that represents the editor when it is invoked and provides the additional validation/conversion behavior. Listing 1 shows the new repository editor, Listing 2 shows the runtime editor with the additional behaviors, in this instance validation. Listing 3 is a Form with an XtraGrid and a single List with a collection of “FlightTime” objects.

    Listing 1: The RepositoryItemFlightTimeEdit class.

    using System;
    using DevExpress.XtraEditors.Repository;
    using DevExpress.XtraEditors.Registrator;
    using DevExpress.XtraEditors.ViewInfo;
    using System.Drawing;
    using DevExpress.XtraEditors.Drawing;
    using System.ComponentModel;
    using DevExpress.XtraEditors;

    namespace AnyTimeEditor
    {
      // see Custom Editors
      [UserRepositoryItem("RegisterCustomEdit")]
      public class RepositoryItemFlightTimeEdit : RepositoryItemTextEdit
      {
        static RepositoryItemFlightTimeEdit() { RegisterCustomEdit(); }
        public RepositoryItemFlightTimeEdit()
        {
          useDefaultMode = true;
        }

        public const string CustomEditName = "CustomEdit";
        public override string EditorTypeName
        {
          get
          {
             return CustomEditName;
          }
        }

        public static void RegisterCustomEdit()
        {
          Image img = null;
          // load image
          EditorRegistrationInfo.Default.Editors.Add(
            new EditorClassInfo(CustomEditName,
            typeof(CustomEdit),
            typeof(RepositoryItemFlightTimeEdit),
            typeof(TextEditViewInfo),
            new TextEditPainter(),
            true, img));
        }

        private bool useDefaultMode;
        public bool UseDefaultMode
        {
          get { return useDefaultMode; }
          set
          {
            if (useDefaultMode != value)
            {
              useDefaultMode = value;
              OnPropertiesChanged();
            }
          }
        }

        public override void  Assign(RepositoryItem item)
        {
          BeginUpdate();
          try
          {
            base.Assign(item);
            RepositoryItemFlightTimeEdit source = item as RepositoryItemFlightTimeEdit;
            if (source == null) return;
            useDefaultMode = source.UseDefaultMode;
          }
          finally
          {
            EndUpdate();
          }
        }
      }
    }

    In the repository the type of the repository editor is RepositoryItemFlightTimeEdit. UserRepositoryItem is used to define the registrator method name. The static constructor invokes the registrator method. The constructor sets the default values for properties (in this example.) The EditorTypeName provides a name for the editor. The Registrator is a helper that calls EditorRegistrationInfo.Default.Editors.Add to provide type information for the repository editor and the editor to create, providing information like a bitmap (if you have one handy). The code in Listing 1 also shows how you can define custom properties repository editors and store new property values (see the Assign method).

    Listing 2: The CustomEdit Helper class.

    using System;
    using DevExpress.XtraEditors.Repository;
    using DevExpress.XtraEditors.Registrator;
    using DevExpress.XtraEditors.ViewInfo;
    using System.Drawing;
    using DevExpress.XtraEditors.Drawing;
    using System.ComponentModel;
    using DevExpress.XtraEditors;
    using System.Text.RegularExpressions;

    namespace AnyTimeEditor
    {
      public class CustomEdit : TextEdit
      {

        //Initialize the new instance
        public CustomEdit()
        {
          //...
        }

        //Return the unique name
        public override string EditorTypeName
        {
          get
          {
            return
              RepositoryItemFlightTimeEdit.CustomEditName;
          }
        }

        //Override the Properties property
        //Simply type-cast the object to the custom repository item type
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public new RepositoryItemFlightTimeEdit Properties
        {
          get { return base.Properties as RepositoryItemFlightTimeEdit; }
        }
        protected override void OnValidating(CancelEventArgs e)
        {
          TextEdit edit = ((TextEdit)this);
          string text = edit.Text;

          int toMinutes = 0;

          // hh:mm
          bool hourMinute = Regex.IsMatch(text, @"^\d+:[0-5][0-9]$");

          e.Cancel = false;
          if (hourMinute)
          {
            toMinutes = ChangeTimeToMinutes(text);
            edit.Text = toMinutes.ToString();
            return;
          }
          // minutes
          bool decimalFormat = Regex.IsMatch(text, @"^\d*\.\d+$");
          if (decimalFormat)
          {
            toMinutes = ConvertDecimalToMinutes(text);
            edit.Text = toMinutes.ToString();
            return;
          }

          //decimal
          bool minutes = Regex.IsMatch(text, @"^\d+$");
          if (minutes)
            return;
          e.Cancel = true;
        }

        private int ConvertDecimalToMinutes(string text)
        {
          return ConvertDecimalToMinutes(Convert.ToDecimal(text));
        }

        private int ConvertDecimalToMinutes(decimal dec)
        {
          return Convert.ToInt32(dec * 60);
        }

        private int ChangeTimeToMinutes(string text)
        {
          return ChangeTimeToMinutes(DateTime.Parse(text));
        }

        private int ChangeTimeToMinutes(DateTime dateTime)
        {
          return dateTime.Hour * 60 + dateTime.Minute;
        }
      }
    }

    The RepositoryItemFlightTextEdit stores properties for repository editors and the registered class is the type of editor created. At runtime when the XtraGrid column associated with the listed editor is created a CustomEdit control will be created. EditorTypeName is used to generate a unique name for the editor in the code-behind. The public property Properties surfaces the constituent properties of RepositoryItemFlightTimeEdit, and the remaining code provides the validation/conversion behavior.

    When the user changes the value of the editor OnValidating is called. Regular expressions are used to look for ##:## formats, #.## formats, or ## formats. The first two represent a short time and a decimal number. These are converted using basic arithmetic and stored back in the editor’s text field and ultimately the underlying data store.

    To create the basic form to test the editor, compile the library containing the classes in Listings 1 and 2. Define a WinForms project. Place an XtraGrid on the form. Add one column and bind it to a field Named FlightTime. Using the XtraGrid Designer switch to the Repository tab and using the Add button add an instance of CustomEdit to the Repository—see Figure 1. Click the Main tab, Columns button, and associate the CustomEdit object with the (only) column’s ColumnEdit property—see Figure 2. Finally, define a class with a FlightTime field—to match the column’s FieldName property (see Listing 3)—and run and test the sample.

    image
    Figure 1: Add an instance of CustomEdit to the In-place Editor Repository.

    image
    Figure 2: Associate the editor with the column or columns that will use the new custom editor.

    Listing 3: Test form with XtraGridControl.

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.Text.RegularExpressions;
    using DevExpress.XtraEditors;

    namespace CustomRepositoryEditor
    {
      public partial class Form1 : Form
      {
        public Form1()
        {
          InitializeComponent();

          List<FlightTime> times = new List<FlightTime>(){
            new FlightTime(10),
            new FlightTime(10),
            new FlightTime(10) };

          gridControl1.DataSource = times;

        }
      }

      public class FlightTime
      {
        /// <summary>
        /// Initializes a new instance of the FlightTime class.
        /// </summary>
        /// <param name="time"></param>
        public FlightTime(int time)
        {
          this.time = time;
        }

        private int time;
        public int Time
        {
          get { return time; }
          set { time = value; }
        }
      }

    }

  • More Editor Repository Goodness

         

    A little boy stops a policeman on the 48th street and asks, ‘how do I get to Carnegie Hall?” The police man responds, “practice! practice! practice!”

    That’s how I learned C++ twenty years ago. I read everything I could get my hands on, re-wrote and tweaked all the code I could find and practiced. (Let me tell you that overloaded operators like >> for output gave me fits. I just happened to be taken a C++ class at Moo U—Go State!—and the teaching assistant was trying to explain templates (generics). She said literally, “don’t worry about templates; they aren’t that important.”. Of course, never knowing when to leave well enough alone I tried to correct her. Uh, yeah, they are pretty important. I didn’t say it like that, but I am sure though I was trying to be helpful that’s probably how she took it. (Some times the TA’s try to stay one step ahead in the book. Some times it shows a little.) Anyway I still learn that way. I bisect, dissect, vivisect and digest something until I have got it plugged in my head. Fortunately, we work in an industry where there is always something to learn.

    If you haven’t seen Amanda’s great presentation of my video Benefits of the Editor Repository at http://tv.devexpress.com/#XtraGridRepository.movie then it is worth checking out (and you can see that I have been chewing on editor repositories lately). The example in this blog demonstrates one way to use the in-place editor repository for the XtraTreeList.

    If you are one of those people saying what is an in-place editor repository, here are the Cliff notes.  One of the cool features about DevExpress container controls like the XtraTreeList and XtraGrid is that these controls automatically look at the data and try to figure out the best editor control. So instead of editing all items with a simple text control you get a calendar for DateTime values and there about 30 other kinds of editors available depending on the data type. The in-place editor repository is accessible in the designer. The in-place editor repository let’s you expressly pick an editor, assign values to its properties and then associate that editor with columns the the ColumnEdit property. So, suppose you have two combo boxes representing states (or regions). You can define one editor and its properties and associate it with the two date columns; instead of meticulously setting properties and items twice (or many times) just define one editor and reuse it. Making changes also then only has to happen in one place, reducing the amount of time you spend tweaking properties.

    The example uses the Northwind.Orders table. An RepositoryItemComboBox is defined and associated with the Region column. The code-behind demonstrates how to use LINQ to DataTables to populate the combobox. Here are the steps:

    1. Create a WinForms application
    2. Place an XtraTreeList (TreeList from the DevExpress Data tab) from the Toolbox onto the form and dock it
    3. Click on the TreeList smart tag (upper right corner) and expand Choose Data Source
    4. Click Add Project Data Source and walk through the wizard picking the Orders column. (You have a basic functioning at application at this point.)
    5. Right-click on the TreeList and click Run Designer
    6. On the Main tab click In-place Editor Repository, next to the Add button click the drop down ComboBoxEdit. (This will add repositoryItemCombobox1 to the repository-see Figure 1.)
    7. In the Main tab click Columns
    8. Click on the ShipRegion Column and pick repositoryItemComboBox1 for the ShipRegion’s ColumnEdit property—see Figure 2.
    9. Close the designer
    10. Switch to the form’s code-behind and implement the code to populate the repository combobox—see Listing 1.

    image
    Figure 1: Add and configure the editors you want in the Property editor for the TreeList.

    image 
    Figure 2: Associate the configured editor with the column or columns that will use that editor.

    Listing 1: The code-behind populates the repository editor item using ADO.NET and LINQ to DataTables.

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.Data.SqlClient;

    namespace RepositoryTreeList
    {
      public partial class Form1 : Form
      {
        public Form1()
        {
          InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
          // TODO: This line of code loads data into the 'northwindDataSet.Orders' table. You can move, or remove it, as needed.
          this.ordersTableAdapter.Fill(this.northwindDataSet.Orders);
          LoadRepositoryEditor();
        }

        private void LoadRepositoryEditor()
        {
          //treeList1.RepositoryItems[0] is repositoryItemComboBox too

          string connectionString =
            @"Data Source=FRANKLIN\SQLEXPRESS;Initial Catalog=Northwind;"+
            "Integrated Security=True";

          string sql = "SELECT DISTINCT ShipRegion FROM Orders WHERE ShipRegion IS NOT NULL";
          DataTable table = new DataTable();

          using (SqlConnection connection = new SqlConnection(connectionString ))
          {
            connection.Open();
            SqlCommand command = new SqlCommand(sql, connection);
            SqlDataAdapter adapter = new SqlDataAdapter(command);
            adapter.Fill(table);
          }

          var regions = from region in table.AsEnumerable()
                        select region.Field<string>("ShipRegion");
          repositoryItemComboBox1.Items.AddRange(regions.ToArray<string>());   
        }
      }
    }

    The first two thirds of LoadRepositoryEditor uses vanilla ADO.NET to populate a DataTable. The LINQ to DataTable query is a variation on using a SqlDataReader and while loop. I like this approach a little better. The repositoryItemComboBox is an actual control you can access directly or through the treeList.RepositoryItems property. The LINQ query returns an IEnumerable<string> object and Items.AddRange adds all the items at once.

    The video and other blog talk about saving time with repository items by setting properties just once—in the repository editor. That will save you a lot of repetitive effort. This approach let’s you write (or use code) just once to populate a ComoBox with selectable items. The alternative would be using an event handler for—ShowingEditor—figuring out what editor is being shown for every editor and then populating the ComboBox. Again this will save you time and speed up your application because users won’t be waiting.

    A spin on this approach would be using a worker thread to populate the editor at startup. Figure 3 shows the TreeList with the ComboBox dropped and the items present. A real world solution would be to look up and limit Cities based on Region or smart-pick a Region based on the City picked and extend that intelligence to the postal code too.

    image
    Figure 3: The repositoryItemComboBox with the populate region values.

  • Dynamic Objects in C# (against an XML Document)

         

    The great thing about programming is there is always something new to learn. There is always something new to learn about DevExpress controls and components and always something new to learn about languages. I try to mix it up, so when you come to this blog you can learn something about DevExpress or something about .NET programming. I am a VB MVP—they asked first—so I try to throw in C# and VB examples.

    New in Visual Studio 2010 for VB and C# is DynamicObject(s). A DynamicObject is a type you inherit from that uses late binding against something like a text file, string, array, or an XML document. You define the DynamicObject child class and use member-of syntax (dynamicobject.member) and the member is essentially looked up and bound as if you had defined a custom object. This is a pretty neat feature and it will be interesting to see if it creeps its way into our DevExpress code base and how. (Maybe the first place it will end up in is as CodeRush templates.)

    To define a DynamicObject you inherit from System.Dynamic.DynamicObject and override one or more of that class’ methods. For instance, if you override DynamicObject.TryGetMember then the first GetMemberBinder argument will contain the name of the member you want to dynamically bind to. Use TryGetMember out parameter to set the value associated with that member and Boolean return value to indicate that the member value was set. Listing 1 contains a DynamicObject that is initialized with an XML document (containing the cells that describe a Scrabble board but could be anything). When TryGetMember is called and the GetMemberBinder.Name is “Cells” an IList containing the cell XElements is returned.

    Listing 1: A DynamicObject that let’s you invoke ScrabbleObjects.Cells to return an IList of XElement objects.

    public class ScrabbleObjects : DynamicObject
    {
       private string path = "";
       private XDocument document = null;
       private IList list = null;

       public ScrabbleObjects(string path)
       {

         if(!File.Exists(path)) throw new FileNotFoundException();
         this.path = path;
         document = XDocument.Load(path);
         list = (from elem in document.Element("Cells").Elements("Cell")
                     select elem).ToList();
       }

       public override bool TryGetMember(GetMemberBinder binder, out object result)
       {
         result = null;
         if(binder.Name == "Cells")
         {
           result = list;
           return true;
         }
         return false;
       }

    }

    With just Listing 1 you will still be working with XML—via XElements. Where want to get to is to be able to treat an XElement like a Cell object. Listing 2 defines a second DynamicObject Cell that let’s you treat the XElement like a Cell object, accessing the Row, Column, Score, Text, and BackColor by writing Cell.Row for instance. Listing 2 contains the DynamicObject Cell. The elided XML document is shown in Listing 3.

    Listing 2: A DynamicObject Cell that let’s you access XElements by writing code like Cell.Column.

    public class Cell : DynamicObject
    {
       private XElement element = null;

       public Cell(XElement element)
       {
         this.element = element;
       }

       public override bool  TryGetMember(GetMemberBinder binder, out object result)
       {
         result = null;
         if(binder.Name == "Row")
         {
           result = Convert.ToInt32(element.Attribute("Row").Value);
           return true;
         }
         else if(binder.Name == "Column")
         {
           result = Convert.ToInt32(element.Attribute("Column").Value);
           return true;
         }
         else if(binder.Name == "Score")
         {
           result = Convert.ToInt32(element.Attribute("Score").Value);
           return true;
         }
         else if(binder.Name == "Text")
         {
           result = element.Attribute("Text").Value;
           return true;
         }
         else if(binder.Name == "BackColor")
         {
           result = Color.FromName(element.Attribute("BackColor").Value);
           return true;
         }

         return false;
       }

       public override string ToString()
       {
         const string mask = "Row: {0}, Column: {1}, Score: {2}, Text: {3}, BackColor: {4}";
         dynamic cell = this;
         return string.Format(mask, cell.Row, cell.Column, cell.Score, cell.Text, cell.BackColor);
       }
    }

    Listing 3: The ellided XML document containing cell definitions. (It doesn’t really matter what the XML contain just map the DynamicObject to the XElements or Attributes.

    <?xml version="1.0" encoding="utf-8" ?>
    <Cells>
      <Cell Row="1" Column="1" Score="3" Text="TW" BackColor="Orange"></Cell>
      <Cell Row="1" Column="2" Score="1" Text="" BackColor="White"></Cell>
      <Cell Row="1" Column="3" Score="1" Text="" BackColor="White"></Cell>
      <Cell Row="1" Column="4" Score="2" Text="DL" BackColor="Blue"></Cell>
      <Cell Row="1" Column="5" Score="1" Text="" BackColor="White"></Cell>
      <Cell Row="1" Column="6" Score="1" Text="" BackColor="White"></Cell>
      <Cell Row="1" Column="7" Score="1" Text="" BackColor="White"></Cell>
      <Cell Row="1" Column="8" Score="3" Text="TW" BackColor="Orange"></Cell>
      <Cell Row="1" Column="9" Score="1" Text="" BackColor="White"></Cell>
      <Cell Row="1" Column="10" Score="1" Text="" BackColor="White"></Cell>
      <Cell Row="1" Column="11" Score="1" Text="" BackColor="White"></Cell>
      <Cell Row="1" Column="12" Score="2" Text="DL" BackColor="Blue"></Cell>

      <!—remaining cell definitions here -->

    </Cells>

    ScrabbleObject is initialized with an XML document. Cell is initialized with an XElement. Listing 4 demonstrates how you can initialize the ScrabbleObject class and use the XElements to initialize Cell classes treating Cell like any old instance of a custom class. (All of the code is supplied together in Listing 4.)

    Listing 4: Initialize ScrabbleObject with the XML document. (The trick to defining a late bound dynamic object is to declare it with the dynamic keyword.)

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Dynamic;
    using System.Xml.Linq;
    using System.IO;
    using System.Collections;
    using System.Drawing;

    namespace XmlAsDynamicObjects
    {
      class Program
      {
        static void Main(string[] args)
        {
          dynamic layout = new ScrabbleObjects("..\\..\\BasicLayout.xml");

          foreach (dynamic cell in layout.Cells)
          {
            Console.WriteLine(cell);
          }
          Console.ReadLine();
        }
      }

      public class ScrabbleObjects : DynamicObject
      {
        private string path = "";
        private XDocument document = null;
        private IList list = null;

        public ScrabbleObjects(string path)
        {

          if(!File.Exists(path)) throw new FileNotFoundException();
          this.path = path;
          document = XDocument.Load(path);
          list = (from elem in document.Element("Cells").Elements("Cell")
                      select elem).ToList();
        }

        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
          result = null;
          if(binder.Name == "Cells")
          {
            result = list;
            return true;
          }
          return false;
        }

      }

      public class Cell : DynamicObject
      {
        private XElement element = null;

        public Cell(XElement element)
        {
          this.element = element;
        }

        public override bool  TryGetMember(GetMemberBinder binder, out object result)
        {
          result = null;
          if(binder.Name == "Row")
          {
            result = Convert.ToInt32(element.Attribute("Row").Value);
            return true;
          }
          else if(binder.Name == "Column")
          {
            result = Convert.ToInt32(element.Attribute("Column").Value);
            return true;
          }
          else if(binder.Name == "Score")
          {
            result = Convert.ToInt32(element.Attribute("Score").Value);
            return true;
          }
          else if(binder.Name == "Text")
          {
            result = element.Attribute("Text").Value;
            return true;
          }
          else if(binder.Name == "BackColor")
          {
            result = Color.FromName(element.Attribute("BackColor").Value);
            return true;
          }

          return false;
        }

        public override string ToString()
        {
          const string mask = "Row: {0}, Column: {1}, Score: {2}, Text: {3}, BackColor: {4}";
          dynamic cell = this;
          return string.Format(mask, cell.Row, cell.Column, cell.Score, cell.Text, cell.BackColor);
        }
      }
    }

    The new keyword dynamic tells the compiler that layout is a late bound object. The layout object is assigned to an instance of ScrabbleObjects. The foreach statement uses dynamic again to implicitly create new Cell objects from the IList Cells—equivalent to writing dynamic cell = new Cell(XElement). Inherit from DynamicObject, override the base classes key member(s), and declare instances using the new dynamic keyword. That’s pretty much all there is to it.

    It will be interesting to see how developers use (or abuse DynamicObject). It will be interesting to see how it crops up with DevExpress code, and it will be interesting what caveats crop up as people figure out how to best use these VS2010 feature.

  • Adding Methods to Enumerations with Extension Methods

         

    I am not a big fan of long methods. They take to long to write and potentially much longer to debug. Avoiding things like switch/Select Case statements in a method along with other code is just something I try to do because case statements are generally long and funky. So, in addition with spending hours looking at our DevExpress features and products I stay abreast of what the platform is doing, and then I try to fit that into what I like about code, or rather how I like to write code.

    Recently, I was looking at Dynamic Objects—a new feature—which let’s you map an object onto an undefined class. A pretty neat concept. While looking at that code an Enum was useful. An enumeration is a special construct that contains names of things—like Color{ Green, Yellow, Red  }—and produces more strongly typed code—Color.Red is pretty straight forward and self-explanatory. The next step was to take an action based on the enumeration value, hence the Select Case statement. What I really wanted was to treat the enumerated value as an actable value, as in do the Color.Red behavior. The problem is that Enums aren’t designed to have behaviors {i.e. methods}. Unless of course you are willing to contort the language a little bit. What I decided to try was an extension method on the enum. That works.

    The actions are simple. When the Light.Red is the value the action will be to report Stop, Light.Yellow will signal speed-up (or slow down if you are safety conscious), and Light.Green’s action is to go-go-go. Here is the simple code that shows you how to add behaviors to enumerations using an extension method.

    Listing 1: Adding behaviors to Enum with extension methods.

    Imports System.Runtime.CompilerServices

    Public Enum Light
      Green
      Yellow
      Red
    End Enum

    Public Module ExtendsLight

      <Extension()>
      Public Sub Action(ByVal state As Light)
        Select Case state
          Case Light.Red
            Console.WriteLine("Stop!")
          Case Light.Yellow
            Console.WriteLine("Speed up")
          Case Light.Green
            Console.WriteLine("Go")
        End Select

      End Sub

    End Module

    Module Module1

        Sub Main()
          Dim state As Light = Light.Yellow
          state.Action()
          Console.ReadLine()
        End Sub

    End Module

    The code quite simply illustrates the technique. Light is an Enum. No great skates there. The Enum construct makes code more meaningful than random numbers for instances. The next Module ExtendsLight defines an extension method Action. A module is equivalent to a static class in C#, and the Extends attribute means basically that the first parameter of this method is treated as the containing construct—think class, struct, or enum—of this method. The result is that you use the member-of operator object.method syntax to invoke the member. (This is compiler magic but useful, especially where it is used to support LINQ.) The Action method hides the subjectively ugly case statement. Now, in the Main module I can call light.Action and based on the Enum value the right action is taken.

    Obviously it might be nice if Enums could have methods. You could also write a global method that performs the same behavior as the Action method, but the member calling convention is nice and tidy. (“Then you'll see, that it is not the spoon that bends, it is only yourself.”)

  • Creating and Using a Persistent Repository with Code

         

    DevExpress controls are feature rich. Hundreds of properties can present a challenge for new programmers, but we are proud of the rich and diverse features that let you customize almost every aspect of our controls. Here is a couple of things newer users may not know. DevExpress controls support skins for WinForms and WebForms (ASP.NET). This means by setting a skin through the LookAndFeel property you can broadly set the look in one shot. Container controls like the XtraGrid use editors for editing data, like Date Fields. Instead of a plain text entry field, if you bind a DateTime value to a column then a DateEdit control (with a calendar) will be displayed and available when you want to edit the data. The second piece of information some of you may not know about is that DevExpress created the Editor Repository. The Editor Repository let’s you define what an editor will look like and how it will behave and then assign that repository editor wherever you want that particular look and feel. So, instead of changing three date column’s properties for example, simply define a repository DateEdit value and associate it with the DateTime column’s ColumnEdit property. All of the changes are automagically populated when the editor is in play. If you need to make changes just change the repository editor’s properties and all associated editors in the container will get the changes. (This beats the heck out of meticulously and tediously changing multiple properties for multiple editors by hand.)

    If you want repository editors for a single control, like a single XtraGrid, then you can use the internal repository capabilities available in the XtraGrid designer. If you want external repository editors for spanning multiple container controls like two XtraGrid or an XtraGrid and an XtraTreeList then you can use an external repository editor via the PersistentRepository component. Repository settings whether internal or external are persistent—stored—so you make the changes in one place and the changes are stored and used each time your application is run.

    You also have the option of creating repository items with code. The code in Listing 1 demonstrates how to create an instance of the PersistentRepository, a PersistentItem—a DateEdit repository item—set that object’s properties and then associate the repository editor with all of the DateTime columns (from the Orders table in the Northwind database.)

    Listing 1: Sample code that demonstrates how to create and use a DateEdit item in the PersistentRepository.

    Imports DevExpress.XtraEditors.Repository
    Imports DevExpress.XtraGrid.Columns

    Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
      'TODO: This line of code loads data into the 'NorthwindDataSet.Orders' table. You can move, or remove it, as needed.
      Me.OrdersTableAdapter.Fill(Me.NorthwindDataSet.Orders)

      CreateDateEditRepositoryItem()

    End Sub

    Private repository As PersistentRepository

    Private Sub CreateDateEditRepositoryItem()

      repository = New PersistentRepository(Me)
      Dim repositoryItem As RepositoryItem = repository.Items.Add("DateEdit")
      repositoryItem.Name = "repositoryDateEdit"
      repositoryItem.Appearance.BackColor = Color.GreenYellow

      Dim DateEditor As DevExpress.XtraEditors.DateEdit = _
        DirectCast(repositoryItem.CreateEditor(),  _
        DevExpress.XtraEditors.DateEdit)

      DateEditor.Properties.LookAndFeel.SetSkinStyle("Money Twins")
      DateEditor.Properties.LookAndFeel.UseDefaultLookAndFeel = False

      Dim column As GridColumn

      For Each column In GridView1.Columns
        If (column.FieldName.Contains("Date")) Then
          column.ColumnEdit = repositoryItem
        End If
      Next

    End Sub

    End Class

    CreateEditRepositoryItem creates a new instance of PersistentRepository and adds a DateEdit item to the repository. The Name, Appearance.BackColor are set. The middle block of code creates the DateEdit instance and applies a known skin. Finally, the for loop walks through each column in the GridView—the default view in the XtraGrid—and every Date column’s ColumnEdit property is assigned the to the repositoryItem, consequently adopting the properties defined in the repositoryItem (see Figure 1 for the results).

    image
    Figure 1: Shows the editor background and the applied skin for the DateEdit control.

    To create the Demo solution drag and drop an XtraGrid onto a WinForm and use the smart tags menu to bind to the Northwind database’s Orders table.

    Sure, there are a lot of possibilities when it comes to customizing DevExpress controls. But, if you use skins or the repository editor then you can set them in one place. Doing one of anything isn’t hard at all.

More from DevExpress
Live Chat
Have a pre-sales question?
Need assistance with your evaluation?
We are here to help.
Chat is one of the many ways you can contact members of the DevExpress Team. We are available Monday-Friday between 8:30am and 5:00pm Pacific Time.
If you need additional product information, require pre-sales assistance, or want help with your order, write to us at info@devexpress.com or call us at
+1 (818) 844-3383.