Mark Miller
  • Great User Interfaces, Clarity, and Information in Parallel

    In the last post, we talked about the elements of design we can adjust to match information relevance. In this post, we'll talk about another, equally important guideline to presenting with clarity, called information in parallel.

    This concept is introduced in an amazing book by Edward Tufte:

    TufteBook Title: Visual Explanations: Images and Quantities, Evidence and Narrative

    ISBN: 0961392126

    The book and title both look dreadfully dry. However, proof of Tufte's genius lies therein.

    The idea behind information in parallel is simple. It's much easier for humans to compare and understand when they see information side-by-side, rather than when the same data is presented in serial. This is crucial for user interfaces, because the very nature of software -- representing a large amount of information in a constrained space -- lends itself to solutions that tend toward the presentation of information in serial.

    Take for example, the trusty standby, the modal dialog box. Since the age of windows, modal dialogs have been with us. Two questions:

    1. Have you ever clicked the mouse down on the title bar of a modal dialog, the dragged it off to the side just so you could see something underneath it?
    2. Have you ever clicked the Cancel button on a modal dialog, just so you could access information behind it (perhaps copy some text to the clipboard),  information necessary for successful interaction with the dialog itself?
    3. Have you ever had to click the Back button on a browser, to see a previous page, just so you could then immediately click the forward button to use that information on the current page?
    4. Have you ever had to scroll up when viewing a document, just so you could see information above necessary to understand the information below?

    If you answer yes to any of these questions, then you may be the victim of information presented in serial.

    Examples of information in serial include:

    • Audio podcasts
    • PowerPoint presentations
    • Stacked modal dialogs
    • Wizards
    • A long paragraph of text.

    So far in covering the science behind great user interfaces, a number of comparisons have been presented using information in parallel. Some examples you may remember:

    A failure of efficiency in Why is Great UI so Hard to Achieve?:

    TwoPaths3

     

    A failure to match visual weight to information relevance, from a table in Microsoft Word, from Great UI, Clarity, and Information Relevance:

     BothTablesSideBySide3

    An illustration showing how adjusting too many elements of emphasis (e.g., saturation, size, and contrast) can lead to visual noise, in Great User Interfaces, Clarity, and Emphasis:

     VisualWeightRansom2

    Let's compare information in parallel with information in serial, to see how they differ.

    Information in parallel is more effective than information in serial, because it can be exploited by our eyes which are excellent at quickly moving back and forth. All layers are simultaneously visible, and the viewer has full control over the pacing and the sequence in which information is understood. Information in serial, on the other hand, burdens the viewer with pacing challenges (information may arrive too quickly or too slowly). Viewers also have no control over the sequence of presented information when arriving in serial.

    That last paragraph was an example of information in serial.

    Here's the same data presented in parallel:

    A viewer can... Serial Parallel
    Control the pacing of information: No Yes
    Control the sequence of information: No Yes
    See all information simultaneously: No Yes

    Notice when information is presented in parallel, our eyes can move back and forth, easily and quickly comparing data in the columns at a pace that feels right.

    One note about pacing and information in serial: Sometimes UI provide viewers with a mechanism to control pacing, as in the example of a wizard dialog that allows you to navigate forward or backward through its pages. This all comes at the expense of introducing an interaction mechanic (e.g., reaching for the mouse to click the Next or Back buttons) that is likely to be far less efficient than simply shifting gaze, which is all it takes to control pacing with information in parallel.

    So the goal is to take a serial interface (e.g., stacked modal dialogs) and flatten that out. How can we do this?

    Well, it isn't easy. It often requires careful thought applied to layout as well as an understanding of what information is essential and needs to be readily available, and what information is less important. Sometimes it requires innovation, in the form of new user interface elements. Let's look at an example from the world of software development.

    Let's say your challenge was to create a user interface that assisted developers in reordering parameters in a method declaration. Using traditional software-building tools, you might crank out a modal dialog that looks something like this screen shot taken from Visual Studio 2008:

    ReorderParametersVS

    There are good things here, and there are also some undesired consequences. Let's talk about the good first. There are two:

    1. The dialog appears under the method you are trying to rename, so that the source code you are trying to change, as well as the dialog itself, are both in view. This is information in parallel.
    2. The Preview method signature box shows the impact of your changes before you apply them. This is also information in parallel.

    Next, the issues:

    1. The dialog hides a significant amount of otherwise visible code in the document. When a window hides information below it, this is information in serial.
    2. The user model changes. Developers are used to working with the keyboard and source code, but now developers have to adapt to a new unfamiliar model, one that uses a grid, buttons and mouse clicks to manipulate code.

    So, how do we flatten this down? Is there a way to remove the dialog completely, yet still allow interaction with a preview before changes are committed? The answer is yes. Here's how we do it:

    1. Move the UI elements down to the lowest layer of the UI (the source code, in this example).
    2. Introduce a similar state-like ability for the interactive phase (where developers can reorder the parameters without reaching for the mouse and without having to adapt to an unfamiliar representation of method signatures).

    That UI might look something like this:

    ReorderParametersInRefactor2

    On the good side, we've removed the modal dialog that blocked out so much of the code. And we've introduced an interactive phase that allows parameters to be reordered without reaching for the mouse (efficiency gains) while keeping a familiar representation of the data (clarity gains). On the downside, in keeping with the familiar representation of the code and losing the dialog, this in-source interactive phase will no doubt be an unexpected surprise the first time developers encounter it. So we compensate, improving discoverability by adding the shortcut hints window in the lower-left.

    So yes, flattening the UI can take some effort, and even the introduction of new UI elements or states. However the gains in efficiency and clarity (and sometimes even discoverability), can make it all worth it.

  • Great User Interfaces, Clarity, and Emphasis

    In our previous post in this series, we established an important guideline to achieving clarity:

    Visual weight should match information relevance.

    The essence of this guideline, is don't overdo it. So as we discuss ways to control emphasis, remember that the goal is to be subtle, to emphasize with what Edward Tufte refers to as the smallest effective difference.

    Contrast

    The easiest, and most powerful way to control emphasis is with contrast. Here's another screen shot from Adobe Lightroom:

    LabelsAndData

    Notice the less relevant labels on the left are rendered in a slightly lower contrast than the more important data on the right.

    Saturation

    Color can convey meaning, especially when used sparingly. In a sea of mostly black and white data, deeply saturated information will stand out.

    Trillian, an instant messaging client, has an interesting option to render non-focused windows in a semi-transparent state, or in gray scale. Here's an example. On the left, the window is focused. On the right, unfocused.

    TrillianBlackAndWhite

    Opacity

    Opacity is related to saturation and contrast. As you increase transparency of graphical elements, both saturation and contrast will drop. Opacity can also be used to indicate elements that are partially available.

    Below are two screen shots from Visual Studio with Refactor! Pro loaded. The arrow in red explains the command that just executed. On the left the arrow is fully opaque, distracting, and hard to ignore. On the right the red arrow is partially transparent, much easier to ignore, and yet the text is also readable if needed.

    Opacity

    When we initially shipped Refactor! Pro, the arrows were fully opaque, and we received many complaints from customers that the arrows were too distracting. We changed the default opacity for the next release, and the complaints dropped to zero (and we even received several emails praising their usefulness). So by controlling opacity we can take a distracting UI element that contains useful information (but perhaps is rarely needed) and turn it into something useful that feels right.

    Font

    Use a bold font to draw gaze into essential or important meaning, as in the tool tip below, that describes the Change Case button in Microsoft Word:

    Bold2

    In the checkboxes below, notice how you can quickly find the control you need just by visually scanning for the important words describing the functionality you need:

    Bold

    Being able to emphasize portions of control text is important, and it's something you can do with DevExpress controls. Julian Bucknall shows you how in his post, Using DevExpress controls to get a great looking UI.

    Size

    Size can be useful for attracting gaze. You can combine larger size with a lower contrast to attract the eyes and let them move on to more relevant data below, as in the screen shot from the DXCore options dialog, showing the Recent Files options page:

    SizeOptionsDialog

    Here, the title of the options page, "Recent Files" is in a larger font, which makes it easier to see for users encountering this page for the first time. But the contrast between this title text and the background is also low, making it easy for repeat users of this page to ignore the title and move on to the more important elements of the UI.

    For another example, take a look at the the "Image Settings" text in the gray-scale screen shot in the discussion on Fonts, above.

    Wrapping Up

    The essence of clarity, from a design and emphasis point of view, is to selectively match a subset (generally one) of the following traits to information relevance:

    • Contrast
    • Saturation
    • Opacity
    • Size

    I've left out Font because to some degree it's a subset of Size, and just as I don't want you to create UI with low-relevance information rendered in high contrast, I also want to make sure you don't create UI that so strictly follows this guideline to the point that you're no longer following the guideline and instead creating UI that approximates a ransom note.

     VisualWeightRansom2

    The goal, after all, is clarity.

    For example, imagine a tree list of information. One could make the argument that sibling and cousin nodes have different levels of information relevance than their respective parents or children, and therefore should be each rendered in a corresponding font size and contrast. That line of thinking could produce something like the following, which shows all references to the decimal type in a C# project:

    References

    The problem here is that by going too granular with grouping and dividing of information relevance, and also by applying too many levels of emphasis (e.g., font size as well as contrast), with good intentions and the goal of clarity we actually end up introducing some visual noise in the form of the varying font sizes and contrast. And that noise interferes with the essence of what we're trying to achieve. So just like the first guideline essentially says "Don't overdo it." A second, perhaps equally important guideline to consider is "Don't overdo it (or go too granular) when matching emphasis to information relevance."

  • Great UI, Clarity, and Information Relevance

    Let's create a 3x4 table in word....

    NewTableInWord

    And let's fill the table with data....

       ArialTableInWord

    Now, at this point, we might pause to ask "What's wrong?". But before we answer, let's make the observation that in the table above, there are a number of graphic elements working together to convey information. There is the data, rendered as text:

       ArialTableInWord_DataOnly

    And there are lines separating the data:

       ArialTableInWord_LinesOnly

    Note that in Word, both the lines separating the data, and the data itself, are both rendered with essentially the same visual weight. The thickness of the line matches the thickness of the strokes used to render the text, and the contrast of the lines also matches the contrast of the text.

    However, it seems that the lines and the data, in terms of relevance to the viewer, are far from equal. A viewer is far more interested in the data than the lines separating the data.

    Let's look at the same table in Excel:

    SameTableInExcel2

    Notice that when creating the same table inside Excel, something interesting has happened. The lines are rendered in a much lower contrast than the data itself.

    Which table do you find easier to read?

     BothTablesSideBySide3

    On a whim, let's create a version of this table where we do the opposite of what's done in Excel. Namely, let's reduce the visual weight of the important data, while increasing the visual weight of the much less relevant separating lines:

    TableReversContrast2

    How easy is this to read, compared to the table from Microsoft Excel, above?

    So, two important points to take away:

    1. Not all information is equally relevant to the viewer.
    2. We have control over the degree to which we can emphasize information.

    And from these two realizations, you can reach the third, perhaps most important guideline to achieving clarity in your UI:

    Visual weight should match information relevance.

    This guideline is incredibly important, easy to follow, and yet violated frequently. My contention, as set forth in the post that explored the question of why great UI was so hard to achieve, is that the rampant violations can be explained by a lack of training.

    Here's a screen shot of Visual Basic source code as seen in Visual Studio 2008:

     SourceCodeInVisualBasic2

    The horizontal lines carry little information relevance. And even though they are rendered above in a medium gray, their contrast and corresponding visual weight far outweigh their intended purpose.

    Here's the same image with one minor change:

    SourceCodeInVisualBasic_Redesign

    See how much easier it is to get your eyes into the relevant data.

    Here's a screen shot from Adobe Lightroom:

    AdobeLightroom

    Notice how the labels on the left, being less relevant than the data to their right, are rendered in a lower contrast, allowing your eyes to quickly get to the relevant information.

    In near-future posts, we'll talk more about ways to control emphasis and dive deeper into clarity.

  • Why is Great UI so hard to achieve?

    Filling up my car with fuel this morning, I'm suddenly hit in the face with more evidence that the vast majority of user interfaces fail to satisfy those three important elements of great UI, clarity, efficiency, and discoverability.

    One of the common mistakes you see in the physical world are buttons on a single device designed as if they will all be pressed with the same frequency. You can tell this because the buttons are all the same size, and in some cases the buttons you have to hit most frequently are two small and located in places that require more thought and more precision to hit than they should.

    Speaking of buttons and precision, here's part of the interface I was looking at:

    FuelGradeButtons2

    On the left you have the button from a fuel pump that selects the most popular grade of gasoline in the United States. On the right sits the button for the less popular, more expensive grade of petrol. The erosion on the left is like a chart showing both frequency of hits as well as signaling where people wanted to hit the button. It is clear that humans who interacted with the button on the left wanted to work with a button far larger than the designed "Push Here" area afforded.

    Here's a redesign of this interface, keeping with the essence of the original:

     FuelGradeButtons_Redesign2

    Here are the changes (we'll explain the rationale behind these changes in future posts):

    • "GRADE SELECT" converted to the less-noisy "Grade Select".
    • The unnecessary underline below "Grade Select" has been removed.
    • The redundant "Grade Select" text has been removed.
    • The tabs holding the "Grade Select" text have been removed.
    • Button area increased and low-contrast border added.
    • Contrast of the "Push Here" labels has been lowered.
    • Contrast between the Regular and Plus text, and their backgrounds has been increased.

    So, while there is plenty of evidence that some buttons are pressed more than others, there isn't much evidence that designers actually understand that.

    One of my favorite large buttons is the space bar on a modern keyboard. Of course that originates from an ancient machine known as the typewriter. Unfortunately, the QWERTY layout that dominates today takes a step far, far away from efficiency by unbelievably moving frequently-accessed keys into harder to reach spots, reportedly because early adopters of typewriters with pre-QWERTY layouts were so proficient they would frequently jam the typebars of the machine together. I can't tell you how much I would like to be in on that meeting back in 1874 when they decided to move the keys around so they were actually harder to hit. That one decision had to be one of the most expensive user interface mistakes ever made, in terms of lost productivity multiplied over time. That space bar likely survived only because hitting it simply advanced the paper one character to the left -- and did not increase the risk of typebars getting wedged together.

    But it did survive and it's big and easy to hit with either thumb, so I like it. :-)

    You need to get to that cross walk over there in the distance on the right. Which path do you take?

    SideWalkAndPath

    On the left, the designers' intention is highlighted in red. On the right, the more efficient path is outlined in blue:

    TwoPaths3

    There is evidence humans prefer efficiency to flat hard ground.

    You're watching TV. You need to change the channel, and you need to change the volume.

    Which remote control would you rather hold in your hand?

    RemoteControls

    Which remote would you rather adjust volume on?

            CompareVolumeButtons 

    On the white remote control on the right, the channel-changing buttons on top are far away from the volume-changing buttons at the lower-right.

    ChannelVolumeButtons

    Also, these horizontal and vertical orientations are inconsistent and require some mental training before it feels natural. And the visual weight (shape, size, contrast) of these buttons is identical to nearly all the other buttons on the control, including the rarely-pressed buttons labeled Language and Clock.

    You're inside an elevator in the N-terminal at Seatac airport. You need to get on your plane. There are three buttons to select your destination:

    • CONCOURSE (CC)
    • RAMP (*R)
    • TRAIN (TRN)

    SeatacElevatorButtons1 

    Which button do you press? That RAMP button with the star next to it looks mighty appealing. After all, I must walk down a ramp to get onto my plane. And haven't I seen stars on elevator buttons used to indicate the main floor or lobby? And what's a concourse, anyway? I need to get to my gate, and it's in a terminal.

    Here's a wider picture of that elevator control panel:

    SeatacElevatorButtons2

    Apparently there was sufficient ambiguity between the RAMP level (some obscure mid-level where passengers apparently do not want to be) and the CONCOURSE level (where you can board your plane) to justify the addition of the hardware-equivalent of software's floating tool tips. Of course, these tool tips are completely invisible to blind folks who are likely to all be congregating on that floor marked with the star R on it.

    So, there is evidence that designers of hardware (and certainly software) are not paying attention to important matters.

    Now back to the original question -- why is great UI so hard to achieve? While a number of factors impact the ultimate outcome, I would suggest the most important influencer is training. It seems that most developers and designers creating interfaces for human consumption simply lack a small bit of knowledge that could make a huge difference.

    How big of a difference? Well, for starters, let's go back to the gas station where I bought fuel this morning. Swiping my credit card, I'm presented with a message on screen, that looks like this:

    Is this a debit card?

    Of course I am unable to proceed until the question is answered. I happen to be using a credit card, not a debit card, so I know the answer is no. However it is not immediately clear how I'm supposed to communicate this status to the machine.

    So next I move my gaze down to scan the keypad. After reading labels on a half-dozen buttons, I find the buttons labeled Yes and No at the bottom-right of the keypad (much farther away from the message than they need to be). I should point out that the question (e.g., "debit card?") changes from station to station -- sometimes the machine wants to know if I'm using a credit card, and sometimes the machine asks me to find buttons labeled Credit and Debit and then press the right one. Remembering that the question this time was asking if I was using a debit card, I click the No button, and return my gaze to the screen above, to see if I am now permitted to put fuel in my vehicle.

    All told, this sucks up about ten seconds of life each time I use my card. And not only at gas stations, but grocery stores and many other places of business. Every time I try to give my money to the merchants, they just try to slow me down. This is not in their best interest, nor is it in mine.

    Poorly-designed UI acts as a frictional force against the economy. How much does it cost? Well, let's start multiplying those ten seconds out across all the millions of transactions occurring every day across all of these machines, and you have a picture of an amazing amount of time wasted. Multiply that time by average salary of those affected and you have a way to estimate the cost of this design choice.

    But why ask the debit card question in the first place? Personally, I never carry any debit cards. For me, the answer will always be the same. Well, it would always be the same if the questions were always the same. And surely we live in a world where technology can identify on some end (the gas station, the bank, etc) which way my card swings.

    So, bad UI surrounds and the reason for this failure may simply be lack of training. And I'm betting when you first saw the question you were thinking it was something else. :-)

    The test for this assertion is simple. Follow the series, get the training, and watch how dramatically that improves your ability to design great interfaces.

    In the next post, we'll dive into clarity.

  • The Essence of Great UI - An Overview

    I've decided to spend some time doing a brain dump of everything I know about great user experiences. Here's the first of what I hope will be a fun and inspiring journey. I also expect this series will serve as a replacement for the book on great UI that I'm unlikely to ever write....

    The most important thing to start with is the knowledge that great UI is all about three things:

    • Clarity

    • Efficiency

    • Discoverability

    Clarity is the ease of understanding presented information.

    Efficiency is the ease of control and movement through the data. There are obvious physical components to this, and there are not-so-obvious mental components as well.

    Discoverability is the ease of finding and understanding that which lies below the surface.

    So the challenge to creating a great UI is to maximize all three of these. However, this is rarely easy to do, as there are many paths we might take, and natural tensions that exist along those paths.

    In future posts, I'll show how to measure efficiency, clarity, and discoverability, and talk about interesting ways we can improve these elements of the user experience. And as always, I encourage interaction and ideas from you. Feel free to post a comment below, or email me directly at markm at the Dev Express domain.

    No series on great UI would be complete without a discussion about the value of great UI. And so in future posts we'll show how to measure the cost of poor UI. Today, I'll just leave you with an observation you may find interesting. There appear to be correlations among the following:

    CorrelationBetweenGreatUIandIncome2

    In the next post, we'll look at why great UI is so hard to achieve.

  • Moving up from CodeRush Xpress to CodeRush

    Considering an upgrade from CodeRush Xpress to the full version of CodeRush? This post is for you. For anyone who needs an overview of features shipping in CodeRush Xpress, see this page on MSDN:

    http://msdn.microsoft.com/en-us/vcsharp/dd218053.aspx

    The difference, in a nutshell: CodeRush packs in 150 more refactorings, code template technology (think Visual Studio's code snippets on steroids), more coding tools, more visualization tools, more navigation tools, and many more useful features. Additionally the full version of CodeRush also includes support for languages other than C#, like ASP.NET, XAML, Visual Basic, Javascript, and C++. And because the full version of CodeRush is the primary focus of DevExpress' IDE Tools team, the latest greatest features & refactorings will always show up there first.

    For clarity, features that are part of CodeRush Xpress appear here in a light red font, followed by a corresponding list of additional features only available in the full version of CodeRush.

    Refactorings

    CodeRush Xpress includes the following refactorings:

    • Add/Remove Block Delimiters
    • Combine Conditionals (merge nested "If" statements)
    • Compress to Lambda Expression
    • Compress to Ternary Expression
    • Convert to Auto-implemented Property
    • Convert to Initializer (use object/collection initialize when possible)
    • Create Backing Store (converts Auto-implemented Property to standard Property with get and set)
    • Decompose Initializer
    • Decompose Parameter
    • Expand Lambda Expression
    • Expand Ternary Expression
    • Extract Method to Type
    • Flatten Conditional
    • Introduce Local (introduce variable)
    • Inline Delegate
    • Inline Temp (inline variable)
    • Make Explicit
    • Make Implicit
    • Move Type to File
    • Name Anonymous Method
    • Name Anonymous Type
    • Reverse Conditional (invert "if")
    • Split Conditional (split complex "If" statements)
    • Use StringBuilder
    • Use String.Format

    The full version of CodeRush includes all of the preceding, plus the following additional refactorings, grouped by category:

    ASP.NET

    • Add AssociatedControlID Attribute
    • Add RunAt Attribute
    • Add Validator
    • Convert Color to HEX
    • Convert Color to Named Color
    • Convert Color to RGB
    • Convert to Pixels
    • Convert to Points
    • Convert to Skin
    • Extract ContentPlaceHolder
    • Extract ContentPlaceHolder (and create master page)
    • Extract Script
    • Extract Style (Class)
    • Extract Style (id)
    • Extract UserControl
    • Make ID Unique
    • Merge Styles
    • Move Style Attributes to CSS
    • Move Style Attributes to External CSS
    • Move Style Attributes to Theme
    • Move Style Attributes to Theme (use SkinID)
    • Move to Code-behind
    • Set CssClass
    • Split Style
    • Surround with Update Panel
    • Toggle ViewState

    Changing Signatures

    • Create Overload
    • Introduce Parameter Object
    • Make Extension
    • Make Member non-Static
    • Make Member Static
    • Promote to Parameter
    • Reorder Parameters
    • Safe Rename

    Conditionals

    • Case to Conditional
    • Compress to Null Coalescing Operation
    • Conditional to Case
    • Expand Null Coalescing Operation
    • Initialize Conditionally

    Contracts

    • Add Contract

    Dead Code

    • Remove Empty Handler
    • Remove Parameter
    • Remove Private Setter
    • Remove Redundant Call
    • Remove Redundant Conditional
    • Remove Redundant Constructor
    • Remove Redundant Delegate Creation
    • Remove Redundant Destructor
    • Remove Redundant Qualifier
    • Remove Redundant Type Specification

    Declaration and Initialization

    • Move Declaration Near Reference
    • Move Initialization to Declaration
    • Remove Assignments to Parameter
    • Split Initialization from Declaration
    • Split Temporary Variable

    Expressions

    • Convert to Is Nothing
    • Inline Constant
    • Inline Recent Assignment
    • Inline Result
    • Introduce Constant
    • Introduce Constant (local)
    • Introduce Result Variable
    • Replace with Constant
    • Replace with Local
    • Simplify Expression
    • Use Equality Operator

    Formatting

    • Break Apart Arguments
    • Break Apart Parameters
    • Create Multi-variable Declaration
    • Line-up Arguments
    • Line-up Parameters
    • Split Multi-variable Declaration

    Interfaces

    • Add to Interface
    • Extract Interface

    Loops and Blocks

    • Consolidate Using Statements
    • Create With Statement
    • For to ForEach
    • ForEach to For
    • Inline With Statement
    • Introduce ForEach Action
    • Introduce Using Statement
    • Lock to Try/Finally
    • Split With Statement
    • Using to Try/Finally

    Macros (C++)

    • Inline Macro
    • Use Const
    • Use typedef

    Moving/Extracting Methods

    • Extract Function (outside of class)
    • Extract Method
    • Extract Property
    • Move Method to Header
    • Move Method to Source File
    • Pull Member Up
    • Replace Temp with Query

    Properties and Fields

    • Collapse Accessors
    • Create Setter Method
    • Encapsulate Field
    • Encapsulate Field (read-only)
    • Expand Accessors
    • Introduce Setter Guard Clause
    • Method to Property
    • Property to Method
    • Remove Setter Guard Clause

    Renaming

    • Rename (works on local variables, functions, methods, fields, properties, parameters, types, namespaces, namespace aliases, CSS styles, XML/HTML/XAML tags, global variables, and C++ macros)

    Resource Files

    • Extract String to Resource
    • Extract String to Resource (replace all)
    • Extract to XAML Resource
    • Extract to XAML Resource (replace all)
    • Extract XML Literal to Resource
    • Replace with XAML Resource

    Strings

    • Concatenate Strings
    • Inline Format Item
    • Introduce Format Item
    • Split String
    • Use Environment.NewLine
    • Use IsNullOrEmpty
    • Use String.Compare

    Types, Typecasting & Namespace References

    • Boolean to Enum
    • Convert to Built-in Type
    • Convert to System Type
    • Encapsulate Downcast
    • Inline Alias
    • Introduce Alias
    • Introduce Alias (replace all)
    • Move Type to Namespace
    • Optimize Namespace References
    • Remove Type Qualifier
    • Remove Type Qualifier (replace all)
    • Rename File to Match Type
    • Rename Type to Match File
    • Replace with Alias
    • Use Explicit Notation
    • Use Short Notation

    Visibility

    • Reduce Visibility
    • Widen Scope
    • Widen Scope (promote constant)
    • Widen Scope (promote to field)

    XML, HTML, and XAML

    • Add End Tag
    • Break Apart Attributes
    • Change Tag
    • Extract to XAML Template
    • Line-up Attributes
    • Remove Attribute
    • Remove End Tag
    • Remove Tag
    • Reorder Attributes
    • Surround with Tag

    Other CodeRush Features

    Clipboard Tools

    CodeRush Xpress includes the following clipboard tools:

    • Smart Cut/Copy
    • Paste Replace

    The full version of CodeRush includes the clipboard tools listed above, as well as the following additional clipboard tools:

    Intelligent Paste

    Intelligent paste modifies the text from the clipboard before inserting it into the editor. Here's a sampling of some of the intelligent pastes defined for C#:

    Name / Description Sample Clipboard/Paste
    Typecast. Converts an "is" test to a typecast myVar is MyClass
    (MyClass)myVar
    Declaration. Converts a statement that creates a new object into a declaration. myVar = new MyClass();
    private MyClass myVar;
    Assignment. Converts a relational expression to an assignment. aaa != bbb
    aaa = bbb;
    Expression. Converts an assignment to an expression when pasted inside parens. aaa = bbb;
    aaa == bbb
    New Method. Creates a new method declaration from a method call when pasted on an empty line outside a method or property. MyMethod();
    public void MyMethod()
    {
    }

    Clipboard History

    The Clipboard History tracks up to 64 of your most recent clipboard cut and copy operations, syntax-highlights them using the same colors you use in Visual Studio, and lets you easily paste them into Visual Studio.

    ClipboardHistory 

    Code Providers

    Code Providers make intelligent changes to your code. Just select or place the caret on the code you want to change, and press the CodeRush/Refactor! key.

    CodeRush Xpress includes the following code providers to declare elements based on usage:

    • Types

      • Declare Class
      • Declare Delegate
      • Declare Enum
      • Declare Enum Element
      • Declare Interface
      • Declare Struct
    • Members
      • Declare Constructor
      • Declare Event Handler
      • Declare Getter
      • Declare Method
      • Declare Property
      • Declare Property (auto-implemented)
      • Declare Property (with backing field)
      • Declare Setter
    • Variables
      • Declare Field
      • Declare Local
      • Declare Local (implicit)

    The full version of CodeRush includes all the declaration features listed above, as well as the following:

    • Add Else Statement
    • Add Missing Constructors
    • Add Setter
    • Create Descendant
    • Create Descendant (with virtual overrides)
    • Create Implementer
    • Create Method Stub
    • Invert Selection
    • Mirror Code
    • Remove Region Directives
    • Remove Setter
    • Rotate 90 Degrees

    Add Missing Constructors and Create Descendant are two of my favorites in that list. There is also Rotate 90 Degrees, which works on a selection of code...

     Rotate 90 Degrees

    Rotate 90 Degrees turns code operating in two-dimensional space across the axis. In the example above a copy of a method which distributes controls evenly left-to-right becomes a new method that distributes controls evenly top-to-bottom.

    In addition to the code providers listed above, you also get Intellassist to suggest in-scope identifiers while you type, and auto-completion for parentheses and brackets.

    Code Templates

    Code templates are a huge part of the efficiency gains offered by the full version of CodeRush. Code templates are like Visual Studio's code snippets on steroids. They're extensible, more powerful, easier to use, and include their own editor. They also offer significant gains to developers working in more than one language, or to developers transitioning from one language to another.

    To see CodeRush templates in action, check out this video by CodeRush customer Kim Major. Also, we've published a number of CodeRush training videos that cover templates, including this introduction.

    Also, we've created a shortcuts sheet that includes a number of template and feature shortcuts to make it easier to exploit this powerful technology.

    CodeRush Shortcuts

    CodeRush Xpress does not include Code Template technology.

    Selection Embedding

    • try...catch
    • try...finally
    • try...catch/finally
    • #region...#endregion
    • #if...#endif
    • while ()
    • do...while ()
    • using ()
    • lock ()
    • block - {}
    • BeginUpdate...EndUpdate
    • WaitCursor
    • To string
    • Comment box

    EmbedSelection

    CodeRush Xpress does not include Selection Embedding technology.

    Code Issues

    Code Issues are a new technology currently available in beta in the 3.2 version of CodeRush. This technology continually scans your entire source code base in a background thread, looking for potential issues, code smells, compiler errors, and/or design issues. The full version of CodeRush ships with the following code issues, arranged by category:

    Dead Code
    • Can remove type qualifier
    • Empty case statement
    • Empty event handler
    • Empty namespace declaration
    • Lambda parameter has redundant type specification
    • Redundant base constructor call
    • Redundant base qualifier
    • Redundant destructor
    • Redundant else statement
    • Redundant field initialization
    • Redundant finally block
    • Redundant namespace reference
    • Redundant private setter
    • Redundant this qualifier
    • Redundant type qualifier
    • Unused member
    • Unused setter
    • Unused type parameter
    • Unused variable
    Errors
    • Abstract member cannot be declared in non-abstract class
    • Abstract member cannot be marked as sealed
    • Abstract member cannot be marked as virtual
    • Abstract member cannot be private
    • Abstract member cannot declare a body
    • Anonymous method cannot have 'params' parameter
    • Array elements cannot be of static type
    • Cannot create an instance of abstract class
    • Cannot create an instance of interface
    • Cannot create an instance of static class
    • Cannot declare instance member in a static class
    • Cannot declare variable of static type
    • Cannot inherit from sealed type
    • Cannot inherit from special class 'System.ValueType'
    • Cannot inherit from static class
    • Cannot override inherited sealed member
    • Cannot yield in the body of a catch clause
    • Cannot yield in the body of a finally clause
    • Cannot yield in the body of a try block with a catch clause
    • Constant cannot be marked static
    • Constructor cannot call itself
    • Constructor must declare a body
    • Control cannot leave the body of a finally clause
    • Delegate cannot be marked static
    • Destructor must declare a body
    • Extension method cannot have a parameter array used with 'this' modifier
    • Extension method must be defined in a non-generic static class
    • Extension method must be defined in a top level static class
    • Extension methods cannot extend System.Type
    • Extern member cannot declare a body
    • Generic class cannot derive from Attribute
    • Indexer cannot be static
    • Interface cannot contain constructors
    • Interface events cannot have add or remove accessors
    • Interface expected
    • Interface member cannot have a definition
    • Keyword "base" is not valid in a static member
    • Keyword this/Me is not valid in a static member
    • Lambda expression cannot have 'params' parameter
    • Member cannot be sealed because it is not an override
    • Member must declare a body because it is not marked abstract or extern
    • Member names cannot be the same as their enclosing type
    • Method must have a return type
    • Only class types can contain destructors
    • Operator cannot be abstract
    • Operator cannot have 'params' parameter
    • Operator must be declared static and public
    • Operator must declare a body
    • Overloaded unary operator takes one parameter
    • Override member cannot be marked as new
    • Override member cannot be marked as virtual
    • Override member cannot change access rights
    • Parameter modifier 'this' should be the first parameter of extension method
    • Partial method cannot have access modifiers or the virtual, abstract, override, new, sealed, or extern modifiers
    • Partial method cannot have out parameters
    • Partial method must be declared within a partial class or partial struct
    • Property cannot have void type
    • Protected member cannot be declared in struct
    • Protected member in sealed type will be private
    • Sealed class cannot be abstract
    • Static class cannot be abstract
    • Static class cannot be sealed
    • Static class cannot contain protected member
    • Static constructors cannot have access modifiers
    • Static constructors must be parameterless
    • Struct cannot contain parameterless constructor
    • The params parameter must be a single dimensional array
    • The params parameter must be the last parameter in a formal parameter list
    • Try statement without catch or finally
    • Undeclared element
    • Virtual member cannot be declared in sealed class
    • Virtual member cannot be declared in structures
    • Virtual member cannot be private
    Hints
    • Can combine initialization with declaration
    • Can implement base type constructors
    • Can initialize conditionally
    • Can inline temporary variable
    • Delegate can be replaced with lambda expression
    • Environment.NewLine can be used
    • ForEach Action can be called
    • Initializer can be used
    • Member can be static
    • Nested code can be flattened
    • Null coalescing operation can be used
    • Partial class has only single part
    • Partial method has only single part
    • Property can be auto-implemented
    • Redundant constructor
    • Redundant delegate creation
    • Redundant sealed modifier
    • Redundant String.Format call
    • String.Compare can be used
    • String.Format can be used
    • Ternary expression can be used
    • Type can be moved to separate file
    • Type name does not correspond to file name
    Smells
    • Case statement has incorrect range of integers expression
    • Complex Member
    Warnings
    • Catch block is empty
    • Default branch is missing
    • Member is not implemented
    • Undisposed local

    CodeRush Xpress does not include Code Issues technology.

    Other Features

    The following features are exclusive to the full version of CodeRush and Refactor! Pro (and are not available in CodeRush Xpress):

    • Visualization
      • Code Metrics
      • Flow Break Evaluation
      • Region Painting
      • Visibility Indicators
    • Code Generation/Modification
      • Cycle Member Visibility
      • One-key selection comment toggle
    • Navigation
      • Bookmarks
      • Browse Recent Files
      • "Jump to" menu
      • Markers (drop, swap, collect & paste)
      • References window
      • Step into Member (for debugging)

    Want the full version of CodeRush? Get it here.

  • Source Documents for the CodeRush Quick Start Sheet

    If you recently downloade the CodeRush shortcuts and templates quick start sheets in PDF format...

    CodeRush Shortcuts

    but would like to change the content of these sheets to match your actual shortcut bindings, you can now get the original Quick Start Sheets here. This is a PowerPoint 2007 file (Microsoft has a free PowerPoint plug-in so you can save it to PDF).

  • Talk about CodeRush and Refactor! Pro

    Noticed these blog posts recently:

    Seems like we're seeing more and more plug-ins for CodeRush and Refactor! Pro. Thanks to everyone who's helping grow the community.

  • New CodeRush Plug-in: Jump to Implementer

    I received a tech support question yesterday asking if there was an easy way to find all the implementers of a particular interface. While CodeRush has a nice Find All References window and a neat Tab to Next Reference feature, it doesn't filter out references to the interface that are used in variable declarations and expressions. So as a result both of those approaches produce more noise than the customer wanted. There is also a Jump to Implementers feature available when you right-click an interface declaration and choose Jump to..., however that feature requires that the caret be on the interface declaration, and is not available on interface references elsewhere in the code.

    Fortunately the DXCore framework that CodeRush, Refactor! Pro, and a host of powerful third-party plug-ins are built upon makes adding this feature very easy.

    And that's what we'll build right now. This plug-in is going to rely upon functionality that ships with CodeRush, specifically the Jump To menu which is available though a right-click in the editor.

    The steps listed here assume you have CodeRush and Refactor! Pro installed.

    Creating the Plug-in Project

    Inside Visual Studio, from the DevExpress menu, select "New Plug-in...". If you don't have a DevExpress menu or you don't see a "New Plug-in..." menu item, contact support@devexpress.com.

    NewDXCorePlugInProject 

    Specify the name of the project and the solution. I'm calling both the project and the solution "CR_JumpToImplementer". Click OK.

    The DXCore Plug-in Project Settings dialog appears.

    DXCorePluginProjectSettings

    Click OK to accept the default settings.

    Next the plug-in project will open in Visual Studio.

    Working with the NavigationProvider

    Since we're adding a navigation feature, and we want it to be part of the Jump to menu, we need to drop a NavigationProvider onto the form. The NavigationProvider is a relatively new control, and so it may not appear on the DXCore page of the Toolbox. If you don't see it, activate the DXCore control page, right-click the ToolBox and select "Choose Items..."

    ChooseItems

    In the Filter TextBox enter "navigation" and then place a checkmark next to the NavigationProvider entry.

    AddNavigationProviderToToolBox

    And click OK. Now you'll have the NavigationProvider control on your DXCore page of the ToolBox.

    Click the NavigationProvider control...

    ClickNavigationProvider

    and then click the design surface to add one of these to your plug-in.

    NavigationProviderDropped

    NavigationProvider Properties

    Property Value
    (Name) navJumpToImplementer
    Description Jumps to the selected implementer of this interface.
    DisplayName Interface Implementer
    ProviderName InterfaceImplementer

    The ProviderName identifies our NavigationProvider to the DXCore, and the DisplayName tells the DXCore the text to display in the Jump to menu. The Description text is placed in a hint that explains what will happen when this menu item is selected.

    NavProvProperties

    NavigationProvider Events

    Next, on the Properties grid click the Events button.

    ClickEvents

    There are two important events we'll need to handle. The first is CheckAvailability, which gives us a chance to find out if our Jump to Implementer navigation feature should be shown in the menu. In our handler for this event, we can check to see if the token at the caret is a reference to an interface.

    CheckAvailability

    Double-click the CheckAvailability event to generate a handler for it.

    HandleCheckAvailability

    Next, we'll write code to determine if the active element (the identifier or reference at the caret) is a reference to an interface. When I need to write code like this in a plug-in, I always create some sample code first that represents the code I want the plug-in to work on, then I move the caret through that sample code and see what it looks like in the Expressions Lab.

    The Expressions Lab is a DXCore plug-in that reveals the structure of the code in the editor. It's a direct view into the language-independent parse tree that the DXCore generates with its background compilers. If you don't have the Expressions Lab installed yet, you can download a copy and learn more about it here.

    Note: You don't need to install the Expressions Lab to finish writing the plug-in, but it is useful in exploring parse trees and especially comes in handy if you want to write plug-ins that work with source code.

    After installing (you will need to restart Visual Studio), bring up the Expressions Lab from the DevExpress menu by selecting Tool Windows | Diagnostics | Expressions Lab.

    SelectExpressionsLab

    Next, let's write some simple code to explore. I started with this (added to the end of my Plugin1.cs file):

    public interface IMyInterface
    {
    }

    public class MyClass :
    IMyInterface
    {
    }
     

    Placing the caret on the second reference to IMyInterface (the one after MyClass) yields the following:

    InterfaceExpression

    The Expressions Lab shows the value of this element's ElementType property is LanguageElementType.TypeReferenceExpression. We can use this information to start building the code that determines if the active element (the element at the caret) is a reference to an interface. Although ElementType is an enum property, the names of its elements correspond to actual class names inside the DevExpress.CodeRush.StructuralParser namespace. For example, the TypeReferenceExpression is a class that represents a reference to a type (e.g., a class, interface, struct, etc.). The ElementType property is provided on all LanguageElements for performance and other technical reasons.

    A few other things we'll need to know before writing this code:

    • All LanguageElements have a GetDeclaration method. This returns an IElement representing the declaration.
    • The ElementType of an interface declaration is LanguageElementType.Interface. You can verify this in the Expressions Lab by moving the caret onto the IMyInterface declaration.

    So armed with this knowledge, our CheckAvailability event handler looks like this:

    private void navJumpToImplementer_CheckAvailability(object sender, CheckContentAvailabilityEventArgs ea)
    {
      if (ea.Element == null
    )
        return
    ;
      ea.Available = GetInterfaceDeclaration(ea.Element) != null
    ;
    }

    Here we're simply setting the Available property to true or false based on whether we've found an interface declaration for the active element (ea.Element). GetInterfaceDeclaration looks like this:

    private static IElement GetInterfaceDeclaration(LanguageElement element)
    {
      IElement
    declaration;

      if (element.ElementType == LanguageElementType
    .TypeReferenceExpression)
        declaration = element.GetDeclaration();
     
    else  // We might be on an interface declaration....
       
    declaration = element;

      if (declaration != null && declaration.ElementType == LanguageElementType
    .Interface)
        return
    declaration;

      return null
    ;
    }

    This code checks to see if the ElementType is a type reference, and if so finds the declaration for that interface. If the type of the active element is not a type reference, there is a chance that the type is an Interface, and so the active element is assigned to the declaration local variable. The code in the second if clause checks to see if the declaration variable is in fact a declaration for an Interface. And so if the element passed in is an interface or a reference to an interface, this method will return the declaration of that interface.

    So far so good. If we were to run our plug-in right now and test it, we could right-click on interfaces and references to interfaces, select "Jump To...", and then see our "Interface Implementer" menu item. Before we test, however, let's write some code to respond to the menu item being selected....

    As previously mentioned, there are two events that you must handle when creating a NavigationProvider. The first event is CheckAvailability, which tells the DXCore if our NavigationProvider's Display Text should appear as a menu item in the Jump to menu. The second event we need to handle is Navigate. This event is called when our menu item is selected.

    Navigate

    The Navigate event handler is where I want to find all the interface implementers and present those in a window, allowing the developer to select a desired implementer to jump to. So let's add the Navigate event handler.

    Activate the Plugin1.cs [Design] surface. Select the navJumpToImplementer control. In the Properties grid (on the Events page) double-click the Navigate event to generate a handler.

    HandleNavigateEvent

    There are two things we need to know before we write this code:

    • When we call GetDeclaration on a reference to a type, (as opposed to a reference to a variable or member), the result will be an ITypeElement.
    • ITypeElement has method called GetDescendants, which returns an array of ITypeElements that descend from or implement that type.

    Knowing this, we can start with code like the following:

    private void navJumpToImplementer_Navigate(object sender, DevExpress.CodeRush.Library.NavigationEventArgs ea)
    {
      IElement
    declaration = GetInterfaceDeclaration(ea.Element);
      ITypeElement iTypeElement = declaration as ITypeElement
    ;
      if (iTypeElement != null
    )
      {
        ITypeElement
    [] directImplementers = iTypeElement.GetDescendants();
        if (directImplementers != null && directImplementers.Length > 0
    )
        {
         
    // TODO: Create the form to show implementers of this interface.
        }
       
    else
          MessageBox.Show("There are no implementers of the X interface in this solution.");
      }
    }

    In the code above we call our GetInterfaceDeclaration method built earlier, and attempt to cast the result to an ITypeElement. If the cast is successful, we call the GetDescendants method to get a list of all the implementers of this interface.

    Notice the string passed to MessageBox.Show is incomplete. that was done intentionally so you could try a cool refactoring we use whenever we need to create strings with substituted parts. The refactoring is called Introduce Format Item.

    Select the "X" inside that string, and press the CodeRush/Refactor! key (Ctrl+`). You'll see something like this:

    IntroduceFormatItem

    Press Enter to apply the refactoring, and you'll get a new String.Format call. All you need to do is replace the "X" string with declaration.Name, like this:

    MessageBox.Show(String.Format("There are no implementers of the {0} interface in this solution.", declaration.Name));

    Introduce Format Item is the most efficient way to create strings with substituted parts.

    Next, let's create the UI for selecting the implementer to jump to.

    Selecting an Implementer

    In the Solution Explorer, right-click the project and select Add | "Windows Form...".

    AddWindowsForm

    Give this form a meaningful name (like "FrmPickDescendant" -- I'm thinking we might list more than just interface implementers here):

    AddDescendantPicker

    Click "Add".

    With the new form active in the designer, change the following values in the Properties grid:

    Property Value
    MinimizeBox False
    ShowInTaskbar False
    Text Select an Implementer

    While we start with a flat list of all interface implementers, I'm thinking about eventuall showing a full hierarchy, one that shows descendents of the classes that implement the interface (the descendants inherit the implementation so they too are implementers). I want to use a TreeView instead of a ListBox to show the items. So drop a TreeView onto the new form.

    SelectTreeView

    Click the TreeView's smart tag and click the Dock in parent container task.

    TreeViewSmartTag

    This will dock the TreeView entirely inside the form.

    DockedTreeView

    Change the following properties:

    Property Value
    (Name) trvDescendants

    Back in Plugin1.cs, let's add the following code inside our Navigate event handler:

    private void navJumpToImplementer_Navigate(object sender, DevExpress.CodeRush.Library.NavigationEventArgs ea)
    {
      IElement declaration = GetInterfaceDeclaration(ea.Element);
      ITypeElement iTypeElement = declaration as ITypeElement;
      if (iTypeElement != null)
      {
        ITypeElement[] directImplementers = iTypeElement.GetDescendants();
        if (directImplementers != null && directImplementers.Length > 0)
        {

          FrmPickDescendant frmPickDescendant = new FrmPickDescendant(directImplementers);
          if (frmPickDescendant.ShowDialog(CodeRush.IDE) == DialogResult.OK)
          {
           
    // TODO: Jump to the selected Interface implementer.
         
    }
        }
       
    else
          MessageBox.Show(String.Format("There are no implementers of the {0} interface in this solution."
    , declaration.Name));
      }
    }

    This code creates a new instance of the FrmPickDescendant class, passing the array of ITypeElements to its constructor. FrmPickDescendant does not yet have a constructor matching this signature, so place the caret on this constructor call, press the CodeRush/Refactor! key (Ctrl+`), and select Declare Constructor, like this:

    DeclareConstructor

    Using the target picker, select a location for the new constructor and press Enter.

    SelectConstructorLocation

    The construction code should look something like this:

    ConstructorAdded

    Press the copy key (Ctrl+C or Ctrl+Insert) to copy the directImplementers parameter to the clipboard. Press Enter to accept this suggested parameter name and move the caret onto the first line inside the constructor. Remove that throw statement if it exists (versions of CodeRush later than 3.2 will not by default add this throw statement to newly-built constructors).

    Next we want to iterate through all the passed-in implementers and add each one to the TreeView. So we need a foreach statement. CodeRush includes a template to make this easy, and this template works with identifiers on the clipboard (we have directImplementers on the clipboard right now). So on that same empty line enter "fe"....

    EnterFeTemplate

    Then press the space bar to expand that template. This will produce the following:

    AfterForEachExpansion

    Notice that ITypeElement in the screen shots above is not formatted as a class. We need add a namespace reference to the file.

    Place the caret on the ITypeElement reference, bring up the smart tag, and choose "using DevExpress.CodeRush.StructuralParser;".

    UsingStructuralParser

    Now inside this foreach loop, add the following line of code:

    trvDescendants.Nodes.Add(new TreeNode(iTypeElement.Name));

    This will create a new node and set its Text property to the name of each type. Note however that in a complex solution, I may have two or more types with the same name, living in different namespaces. I like the simplicity of listing the types without their corresponding namespace, so we'll need some way to jump to the selected implementer. So for now let's use the Tag property of the TreeNode to store a reference to the implementing element.

    With the caret somewhere on on the new TreeNode call, use CodeRush's Selection Increase feature (Num+ or Ctrl+Shift+W) to select the entire "new TreeNode(iTypeElement.Name)" expression....

    NewTreeNodeSelected

    Press the CodeRush/Refactor! key (Ctrl+`).

    IntroduceLocal

    Select Introduce Local and press Enter.

    Accept the default name of "newTreeNode" (you can press Enter to break the green links if you like), and add the following line to assign the iTypeElement reference to the TreeNode's Tag:

    foreach (ITypeElement iTypeElement in directImplementers)
    {
      TreeNode newTreeNode = new TreeNode
    (iTypeElement.Name);
      newTreeNode.Tag = iTypeElement;
      trvDescendants.Nodes.Add(newTreeNode);
    }
     

    Since we added a custom constructor to this Form, we need to make sure we get a call to InitializeComponent in, otherwise the reference to trvDescendants will be null when this code executes. So lets add a call to ": this()" at the end of this new constructor, like this (note -- versions of CodeRush after 3.2 are likely to add this call automatically):

    public FrmPickDescendant(ITypeElement[] directImplementers): this()
    {
     
    foreach (ITypeElement iTypeElement in directImplementers)
      {
        TreeNode newTreeNode = new TreeNode
    (iTypeElement.Name);
        newTreeNode.Tag = iTypeElement;
        trvDescendants.Nodes.Add(newTreeNode);
      }
    }

    There's one more feature I want to add to this form, and that's the ability to respond to Enter and Escape to accept or cancel the destination. So let's add the following method (parts borrowed from the Clipboard History plug-in):

    protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
    {
      const int WM_KEYDOWN = 0x100
    ;
      if
    (msg.Msg == WM_KEYDOWN)
      {
        switch
    (keyData)
        {
          case Keys
    .Enter:
            this.DialogResult = DialogResult
    .OK;
            return true
    ;

          case Keys
    .Escape:
            this.DialogResult = DialogResult
    .Cancel;
            return true
    ;
        }
      }
      return base.ProcessCmdKey(ref
    msg, keyData);
    }
     

    Testing

    Now let's run and test what we have so far. From the Debug menu, select Start Debugging. A second instance of Visual Studio will launch. To test this, either open a solution that contains classes that implement interfaces, or create your own. I'm using the following simple sample code to test:

    namespace TestApp1
    {
      public interface
    IPerson
     
    {
        string Name { get; set
    ; }
        decimal Salary { get; set
    ; }
        void AdjustSalary(decimal
    amount);
      }

      public class Person :
    IPerson
      {
        public string Name { get; set
    ; }
        public decimal Salary { get; set
    ; }
        public void AdjustSalary(decimal
    amount)
        {
          Salary += amount;
        }
      }
    }

    Now, right-click one of the IPerson references in the code, and select "Jump to..."

    SelectJumpTo

    The Jump to menu will appear.

    JumpToInterfaceImplementer

    Select "Interface Implementer". Our "Select an Implementer" form will appear.

    SelectAnImplementer

    OK, so there's clearly some UI work to be done to improve the experience.  However the desired behavior (at least what we've coded so far) appears to be working. Let's make the test code a bit more complex by changing it to the following: 

    namespace TestApp1
    {
      public interface
    IPerson
     
    {
        string Name { get; set
    ; }
        int Age { get; set
    ; }
      }

      public class Person :
    IPerson
     
    {
        public string Name { get; set
    ; }
        public int Age { get; set
    ; }
      }

      public class Child:
    IPerson
     
    {
        public string Name { get; set
    ; }
        public int Age { get; set
    ; }
      }

      public class Adult :
    IPerson
     
    {
        public string Name { get; set
    ; }
        public int Age { get; set
    ; }
      }

      public interface
    IEmployee
     
    {
        decimal Salary { get; set
    ; }
        void AdjustSalary(decimal
    amount);
      }

      public class Employee : Person,
    IEmployee
     
    {
        public decimal Salary { get; set
    ; }
        public void AdjustSalary(decimal
    amount)
        {
          Salary += amount;
        }
      }

      public class Manager :
    Employee
     
    {
      }
    }

    Now right-click one of the IPerson references, bring up the Jump to... menu and select Interface Implementer. You should see something like the following:

    SelectAnImplementer2

    In general we appear to be in pretty good shape. We've actually written very little code, and yet already we have the ability to list all direct implementers of a particular interface. Also, because of the DXCore's language-independent parse trees, this functionality will work in all languages supported by the DXCore that also support interfaces, including C#, C++, and Visual Basic. This plug-in will also work in languages that we support in the future without a recompile. That's cool.

    So we can list all implementers of an interface, but we're not listing descendants of the direct implementers (consider Employee in the example code above). And of course we still need to add the code to actually move the caret to the selected implementer when the Enter key is pressed. So let's take care of those two things.

    Jumping to Selected Implementers

    Shut down the instance of Visual Studio we're debugging, and return to the CR_JumpToImplementer project.

    First, let's add a property to the FrmPickDescendant form to return the selected node. My first try looks like this:

    public ITypeElement SelectedElement
    {
     
    get
     
    {
        TreeNode
    selectedNode = trvDescendants.SelectedNode;
        if (selectedNode == null
    )
          return null
    ;

        return selectedNode.Tag as ITypeElement
    ;
      }
    }

    Next, let's exploit that property inside our Navigate event handler back in PlugIn1.cs, with the following code:

    private void navJumpToImplementer_Navigate(object sender, DevExpress.CodeRush.Library.NavigationEventArgs ea)
    {
      IElement
    declaration = GetInterfaceDeclaration(ea.Element);
      ITypeElement iTypeElement = declaration as ITypeElement
    ;
      if (iTypeElement != null
    )
      {
        ITypeElement
    [] directImplementers = iTypeElement.GetDescendants();
        if (directImplementers != null && directImplementers.Length > 0
    )
        {
          FrmPickDescendant frmPickDescendant = new FrmPickDescendant
    (directImplementers);
          if (frmPickDescendant.ShowDialog(CodeRush.IDE) == DialogResult
    .OK)
          {
            ITypeElement
    selectedElement = frmPickDescendant.SelectedElement;
            if (selectedElement != null && selectedElement.Files.Count > 0
    )
            {
              CodeRush.File.Activate(selectedElement.Files[0
    ].Name);
              CodeRush.Caret.MoveTo(selectedElement.Ranges[0
    ].Start);
            }
          }
        }
       
    else
          MessageBox.Show(String.Format("There are no implementers of the {0} interface in this solution."
    , declaration.Name));
      }
    }

    ITypeElements have a Files property, accessed here, which contains a list of files where the type is declared. In the case of partial classes, this list might include more than one file. We're using the first file in that list. ITypeElements also have a Ranges property that stores where the type starts and ends inside the source code. That property is also indexed (and this index is synchronized with the index of the Files property). So, for example, the values in Ranges[0] correspond to the file in Files[0]. So the new code above gets the selected element, activates the corresponding file and then moves the caret to the start of that type's declaration.

    Adding Descendants of Direct Implementers

    Let's go back to the FrmPickDescendant form to add the code to list all the implementers of an interface, even those that descend from other classes.

    After a bit of refactoring, I've replaced our form's constructor with the following (the signature is the same -- only the body has changed):

    public FrmPickDescendant(ITypeElement[] directImplementers): this()
    {
      AddImplementers(trvDescendants.Nodes, directImplementers);
    }

    AddImplementers looks like this, and includes a recursive call:

    private void AddImplementers(TreeNodeCollection nodes, ITypeElement[] implementers)
    {
      foreach (ITypeElement iTypeElement in
    implementers)
      {
        TreeNode newTreeNode = new TreeNode
    (iTypeElement.Name);
        newTreeNode.Tag = iTypeElement;
        nodes.Add(newTreeNode);

       
    // Now add descendants...
       
    ITypeElement
    [] descendants = iTypeElement.GetDescendants();
        if (descendants != null && descendants.Length > 0
    )
          AddImplementers(newTreeNode.Nodes, descendants);
      }
    }

    Note that it's theoretically possible to hit an infinite loop here, if a given nodes structure were to arrive in a corrupted state (something that won't compile), for example the case where two classes descend from each other. While this case is unlikely to come up ever, it's a good idea to write some defensive code to stop this. I'll leave that as an exercise for the reader, offering the hint that I would track the full names of each type added, and in the event of duplicates I would exit before recursively calling AddImplementers.

    Testing the Latest Changes

    Great. Now let's run and test again....

    In the test app (if you've been using the sample test code from before), I suggest applying the Move Type to File refactoring for each type, and applying Rename File to Match Type on the last type remaining in Class1.cs. Doing so produces the following files, as seen from Solution Explorer:

    SolutionExplorer

    Now right-click an IPerson reference somewhere in the code and select "Jump to...". Select "Interface Implementer".

    JumpToInterfaceImplementer2

    The Select an Implementer window appears:

    SelectAnImplementer3

    The root nodes of the TreeView are the direct implementers of the interface (Child, Person and Adult all implement IPerson). Now we're also listing descendants of these implementers as child nodes.

    Select an implementer and press Enter. The corresponding file should activate and the caret should move to the start of the class that implements this interface.

    Excellent!

    Now, if you're familiar with existing navigation conventions in CodeRush, you have probably realized by now that this experience is not quite as polished as the CodeRush navigation experience. The recommended guidelines for navigation are essentially as follows:

    1. Drop a Marker (so we can get back easily with Escape)
    2. Activate the file
    3. Move the caret (or the selection) to the desired location
    4. Display a locator beacon if there is no selection (so devs with large monitors can quickly see the new location of the caret)

    So far we have implemented steps 2 and 3. Let's add code for steps 1 and 4.

    Dropping a Marker

    Dropping a Marker at the active caret location is easy. Just call CodeRush.Markers.Drop and pass in the type of the marker desired. In the Navigate event handler inside the PlugIn1.cs file, add the following line of code:

    private void navJumpToImplementer_Navigate(object sender, DevExpress.CodeRush.Library.NavigationEventArgs ea)
    {
      IElement
    declaration = GetInterfaceDeclaration(ea.Element);
      ITypeElement iTypeElement = declaration as ITypeElement
    ;
      if (iTypeElement != null
    )
      {
        ITypeElement
    [] directImplementers = iTypeElement.GetDescendants();
        if (directImplementers != null && directImplementers.Length > 0
    )
        {
          FrmPickDescendant frmPickDescendant = new FrmPickDescendant
    (directImplementers);
          if (frmPickDescendant.ShowDialog(CodeRush.IDE) == DialogResult
    .OK)
          {
            ITypeElement
    selectedElement = frmPickDescendant.SelectedElement;
            if (selectedElement != null && selectedElement.Files.Count > 0
    )
            {

              CodeRush.Markers.Drop(MarkerStyle
    .System);
              CodeRush.File.Activate(selectedElement.Files[0].Name);
              CodeRush.Caret.MoveTo(selectedElement.Ranges[0
    ].Start);
            }
          }
        }
       
    else
         
    MessageBox.Show(String.Format("There are no implementers of the {0} interface in this solution."
    , declaration.Name));
      }
    }

    Displaying a Locator Beacon

    It turns out displaying a locator beacon is pretty easy as well. There's a control, aptly named LocatorBeacon that we can drop on the plug-in....

    Activate the PlugIn1.cs [Design] surface. From the DXCore page of the Toolbox, select the LocatorBeacon control and drop it on the plug-in.

    SelectLocatorBeacon

    Change the LocatorBeacon's Color property if you like. I'm changing it to MediumSeaGreen.

    Next let's add some code to our Navigate event handler to start the beacon animation. The LocatorBeacon has a Start method that accepts a TextView and a line and column position to be the center of the animation. The Navigate method already references the Start property, which holds line and column information, so let's refactor that a bit, introducing a local variable to use for the Start. After these changes my version of the Navigate event handler look like this:

    private void navJumpToImplementer_Navigate(object sender, DevExpress.CodeRush.Library.NavigationEventArgs ea)
    {
     
    IElement
    declaration = GetInterfaceDeclaration(ea.Element);
     
    ITypeElement iTypeElement = declaration as ITypeElement
    ;
     
    if (iTypeElement != null
    )
      {
       
    ITypeElement
    [] directImplementers = iTypeElement.GetDescendants();
       
    if (directImplementers != null && directImplementers.Length > 0
    )
        {
         
    FrmPickDescendant frmPickDescendant = new FrmPickDescendant
    (directImplementers);
         
    if (frmPickDescendant.ShowDialog(CodeRush.IDE) == DialogResult
    .OK)
          {
           
    ITypeElement
    selectedElement = frmPickDescendant.SelectedElement;
           
    if (selectedElement != null && selectedElement.Files.Count > 0
    )
            {
              CodeRush
    .Markers.Drop(MarkerStyle.System);
              CodeRush.File.Activate(selectedElement.Files[0
    ].Name);
              TextPoint start = selectedElement.Ranges[0
    ].Start;
              CodeRush
    .Caret.MoveTo(start);
              locatorBeacon1.Start(CodeRush
    .TextViews.Active, start.Line, start.Offset);
            }
          }
        }
       
    else
         
    MessageBox.Show(String.Format("There are no implementers of the {0} interface in this solution."
    , declaration.Name));
      }
    }

    And that wraps up our changes! There are still some minor UI changes I would like to make to the FrmPickDescendant class, including the following:

    • Smart positioning of the form near the caret
    • Support for double-clicking nodes to accept that node as a target
    • Changing the font size to match the size of the font in the editor
    • Adding more details, like the name of the interface and namespace hints associated with each node
    • Adding a mechanism to filter nodes in the TreeView

    However I'll save those changes for another day. For now I think you have an idea of how easy it is to create a NavigationProvider for CodeRush. Let me know if you have any questions, suggestions, or would like to see more step-by-step tutorials like this in the future.

  • CodeRush Shortcuts and Templates - Quick Start Sheets

    CodeRush shortcuts and templates quick start sheets are now available in PDF format. Click the image below to get them.

    CodeRush Shortcuts

5 6 7 8 9
10
11 12 13 14
LIVE CHAT

Chat is one of the many ways you can contact members of the DevExpress Team.
We are available Monday-Friday between 7:30am and 4:30pm Pacific Time.

If you need additional product information, write to us at info@devexpress.com or call us at +1 (818) 844-3383

FOLLOW US

DevExpress engineers feature-complete Presentation Controls, IDE Productivity Tools, Business Application Frameworks, and Reporting Systems for Visual Studio, along with high-performance HTML JS Mobile Frameworks for developers targeting iOS, Android and Windows Phone. Whether using WPF, ASP.NET, WinForms, HTML5 or Windows 10, DevExpress tools help you build and deliver your best in the shortest time possible.

Copyright © 1998-2017 Developer Express Inc.
All trademarks or registered trademarks are property of their respective owners