Meet “Go To Implementator” DXCore plugin for Visual Studio

by Miha Markič 18. January 2010 14:54

The problem

One of the biggest annoyance when doing unit-test-friendly projects is that you have to deal with interfaces much more than you usually would. This isn’t bad by itself but it will effectively kill your F12 aka “Go To Definition”. In other words F12 won’t show you the code anymore but rather the interface definition of the method or property. Which is not what I like and I guess not what you like as well.

Let’s see an example:

imageWhen you F12 (or right click, Go To Definition) on DoTubo() you’ll get catapulted to the interface declaration of the method. Which might be what you want but mostly it isn’t. I’d like to see  the Tubo.DoTubo() method declaration where the code I am interested is. Specially because often an interface is really implemented just by one class, at least in design time.

image

This is what I’d like to see. And this is what you’d get if you weren’t using IAnnoy interface but rather Tubo type directly.

The solution

Good news is that now there is a way to use the go to method implementation. Even better news is that it is free. As a free lunch.

I’ve created a DXCore plugin named “Go To Implementator” that does exactly that. When over a interface’s method or property reference it will give you an option to go directly to (one of the) implementation. Sounds fine?

Installation

1. If you don’t have CodeRush already installed then do install its Express version which is free or even better, go with full version (which is not free but it is well worth it).

2. Download attached zip file and unpack its content into either [USER]\Documents\DevExpress\IDE Tools\Community\PlugIns or [Program files [x86]]\DevExpress [DX version, i.e. 2009.3]\IDETools\System\CodeRush\BIN\PLUGINS.

3. Run Visual Studio. Go to DevExpress/Options… menu and select IDE/Shortcuts node the left.

4. Create a new shortcut: click on the first icon under Shortcuts title. Click on Key 1 text box on the left and press F12 (you are telling which key to respond to). Pick “Go to interface implementation” command from Commands combo box. The last step is to click on checkbox Focus/Documents/Source/Code Editor on the usage tree list on right side – a green checkmark should appear. Note that you can bind this action (or any other) to a different shortcut or even a mouse shortcut or any other way supported by CodeRush.

image

Take also note that there is a “Parameters” text box. I’ll use it pass parameters to my action later on in the article.

Test & use

Create a console application and past in there this code:

class Program
{
static void Main(string[] args)
{
IAnnoy annoy = new Tubo();
annoy.DoTubo();
}
}

interface IAnnoy
{
void DoTubo();
}

class Tubo : IAnnoy
{
public void DoTubo()
{
}
}

Position the caret on DoTubo() method reference. There are two ways to invoke my plugin.

Context menu

Right click, there should be an submenu “Go To Implementator” in context menu:

image

Keyboard shortcut (F12)

Just press F12. But what if you are not on a interface method/property reference? The default “Go To Definition” will be called like it would be without this plugin.

Dealing with more than one interface implementation

So far there was only one interface implementation. But what happens if there are two or more classes that implement the interface?

Let’s add another implementation to the crowd:

class AnotherTubo : IAnnoy
{
public void DoTubo()
{
}
}

Now both Tubo and AnotherTubo implement the IAnnoy interface. Right click context menu should give both options in no particular sort order, like this:

image 

Let’s pick AnotherTubo class. Plugin will remember this choice and next time it will be placed on the top:

image

But what about F12?

If there is no default class assigned then it should present you a smart tag with options:

image

 

 

However, if a default is available it would go straight to the implementation rather then presenting this smart tag.

Customizing the action

Popup menu behavior is fixed and can’t be customized in current version. The action, one that you can bind to a keyboard shortcut or whatever else input CodeRush is supporting can be customized. There are two parameters (parameters are a comma delimited string that you pass to Parameters text box when you are creating/editing shortcut in DevExpress/Options… – see the 4. step in installation) you might use.

NoPassGoToDefinition

You can disable invoking Go To Definition when action doesn’t do anything. The default behavior is to pass through when action does nothing. So why would you need this option? In case you are using the action from non F12 shortcut or if you want the action to do its job and nothing else.

ShowPopupAlways

When there is a default class for action available no smart tag is shown by default. You can override this behavior by passing ShowPopupAlways parameter. Then smart tags menu will be shown always when there is more than one class suitable regardless the default value is present or not.

Here is an example of passing both parameters:

image

The conclusion

I hope you’ll find this plugin useful. I am starting to use it and it saves me a lot of clicking. And thanks to DXCore it was really easy to create it.

Let me know if you have ideas how to enhance it or anything else, all feedback is welcome.

1.0.0 (18.1.2010)

  Initial release

1.0.1 (19.1.2010)

  Bug fix: it worked only within project scope.

1.0.2 (19.1.2010)

  Too strict parsing used which might resulted in no go to implementor option at all.

1.0.3 (21.1.2010)

  Didn't work on partial classes.

1.0.4 (26.1.2010)

  Fixed navigational bug (thanks to Quan Truong Anh for finding the bug and creating a repro case)

GotoImplementator_v1_0_4.zip (9.61 kb)



Tags: , ,

.net | CodeRush | DevExpress | DXCore | DXCore plugin | Visual Studio

About DevExpress skinning and custom skins

by Miha Markič 15. January 2010 11:18

Here is the thing. DevExpress WinForms components support custom skinning. Out of the box there are plenty of skins you might use just by assigning a simple property with a name of the skin. Every DevExpress WinForms control will follow the skin settings and will look fancy and so will your application. That’s great. But if you want more advanced skinning you are in for troubles.

Let’s see my case. An application I am building for a customer of mine supports skinning. However I had to slightly modify out of the box skins with some adjustments and I’ve added few new glyphs which I use in my custom controls (they follow skinning UI as well since entire application does). Here is how I started. I’ve opened SkinEditor, a tool provided by DevExpress and created new skins based on their skins, i.e. MyCaramel from Caramel, etc. Once I had “my” skins I’ve adjusted some properties still using SkinEditor. Finally I’ve created a “skin resource” assembly. That’s all easily done via SkinEditor. So far so good. But there are problems ahead.

First problem – adding custom glyphs to skin

Since I have custom controls that have custom glyphs I had to add those glyphs to skins. After all they belong in the skin assembly since they will be also changed when skin changes. I could add them somewhere else, but that would be asking for troubles – better to have “grouped” resources in one place. But there is no way to add custom glyphs to my skin via SkinEditor. By design. Obviously nobody at DevExpress ever supposed that custom skins might be used for custom controls.

Second problem – updating the custom skin

Next, much more annoying problem, is updating custom skin to a newer DevExpress version. Even when a minor DevExpress version is released the out of the box skin definition might change a bit - here and there. So, the template you have built your custom skin from has changed but your custom skin still “sits” on the top of the old template version. It might even result in a runtime exception if you don’t upgrade the skin while application uses newer version of DevExpress components. And go wonder, SkinEditor doesn’t have an “upgrade custom skin” option. You have to recreate the original project (what a fun when you are dealing with 20+ skins – you have to add separately) and reapply all changes you might to out of the box skins. Eeek.

Third problem – skin size

If you use a lot of custom skins they will use a lot of space (each skin is about 500KB) even though you might not be using all of DevExpress controls and thus you don’t require full skins. The relatively big size might be a problem if you distribute your application via internet and even if you don’t your application uses more memory without any apparent benefit. SkinEditor doesn’t support removing of skin elements and even if you modify skin.xml definition (by removing unnecessary nodes) SkinEditor will add them again when you open the project next time.

Fourth problem – nor skin nor its assembly can’t be unloaded

Once the skin assembly is loaded to your application (main AppDomain) it can’t be unloaded. And once skin is registered it creates a hash table of all resources (a ton of Images – I am not 100% about this but it pretty much looks like it) and you can’t unloaded any of them. So, when you register a skin assembly it will remain loaded until the application is closed and all resources will be loaded to hash table in the form of images (souds like a sort of duplication to me). There is no way to load a skin from a custom AppDomain.

Solutions to problems

The first problem can be solved “manually”. I say manually, but you can pretty easily create some XML manipulation and file copy code. While SkinEditor doesn’t support adding custom glyphs you can still add them manually in two steps – save the graphics to the proper folder of the SkinEditor project and modify skin.xml file by adding proper XML nodes pointing to newly added glyphs. After some trial and error I was able to accomplish this task.

I’ll write about the solution to the second problem in a later post. I’ll also provide an utility that does a part of the job.

I have an idea how to solve the third problem but didn’t solve it yet nor I am sure whether it will work.

The fourth problem is the most hard to solve due to the current implementation. I am not sure whether it is even possible or whether does it make sense to invest much energy into this.

Conclusion

While skinning works pretty nicely in DevExpress controls its implementation is not the best one. Specially support for custom skinning isn’t very well thought and SkinEditor can be enhanced with these problems in mind.

The good news is that with little effort I’ve managed the overcome the most important issue – how to create slightly modified out of the box skins and how to update them to new versions (automatically). I’ll talk more about this solution in a later post.

What do you think? Do you use DevExpress skinning feature? Did you create your own skins?



Tags: , ,

DevExpress

Developer Express published roadmap for 2010

by Miha Markič 5. January 2010 22:19

First new “feature” is stepping down from three releases per year to two releases per year. Regardless how it sounds, it makes sense. A lot of sense. Developers and other staff spent a lot of time preparing releases and I don’t mean implementing new features but just preparing and testing the releases. From now one they will have more time for coding new features. As per DevEx clients this is good news as well – having to do major updates less time per year provides similar benefits. Bottom line is that we have more features, less work and possibly less bugs.

From the technologies perspective it looks like WPF and Silverlight are getting mainstream while WinForms is dropping down from first place (that said it isn’t a dead product, far from it, it is just not the most important anymore – I am very pleased to see this shift happening). ASP.NET MVC will get some beta versions (“we shall be testing the waters with beta versions of the data grid, menu, navigation bar, and tab control”) while ASP.NET is still the most important of the two.

Visual Studio 2010 is going to be supported big time including CodeRush for VS2010.

These are news in brief from what I can read. But don’t take my word for granted, read it yourself at http://www.devexpress.com/Home/Announces/roadmap-2010.xml.



Tags:

DevExpress | CodeRush

Adjusting DevExpress’ XtraTabControl’s page padding

by Miha Markič 16. November 2009 13:06

Look at the picture below, it is a DevExpress' XtraTabControl (WinForms) with a single page hosting a normal Button. Both XtraTabControl and Button are set as Dock = Dock.Fill. Somebody (read: my client) considers that there is just too much wasted space around them (annotated with arrows):

image

There are no properties to modify padding behavior but luckily DevExpress supports skinning and I solved it through the use of slightly modified skin based iMaginary in this case.

Preparation

Fire up SkinEditor (found in All Programs/Developer Express v2009 vol 2/Components/Tools) and create a new skin based on iMaginary out of the box skin. For more info on creating new skins see this help topic. Save the new project and build the skin assembly. Create a new Visual Studio project, reference the skin assembly and code similar to this (substitute SKINPROJECTNAME and SKINNAME with your names):

DevExpress.Skins.SkinManager.Default.RegisterAssembly(typeof(SKINPROJECTNAME).Assembly);!
XtraForm f = new XtraForm();
f.LookAndFeel.SkinName = "SKINNAME";
f.LookAndFeel.UseDefaultLookAndFeel = false;
XtraTabControl ctl1 = new XtraTabControl();
ctl1.Dock = DockStyle.Fill;
XtraTabPage page = new XtraTabPage { Text = "One" };
ctl1.TabPages.Add(page);
Button b = new Button { Text = "Dock.Fill" };
b.Dock = DockStyle.Fill;
page.Controls.Add(b);
f.Controls.Add(ctl1);

Now you have a test project that should output the window above.

Step 1 – The drawing of XtraTabControl the border

DevExpress controls use template images that are properly resized for most of the resizable drawing including XtraTabControl’s borders.

Find the TabPane node in the tree view on the right

image

and you’ll see the border template image:

image

Note that the shadows are drawn within this template, not programmatically. Red lines are borders that determine what part of the image is copied to provide dynamically size final image. First, close SkinEditor. Then find the template image on the disk ([SKIN PROJECT]/[SKIN NAME]/Tab/TabPane.png) and use on of the paint applications to get ride of the shades (by copy paste the parts without shades) and expand the usable area to get a result like below, reopen SkinEditor and load the skin project (I did move borders to 2,2,2,2 as well – you can drag them with mouse or by setting proper values on the left).

image

If you run the test application it will show painted borders as expected but the button will remain on the original position regardless of this change. For now only the drawing changed but not the behavior.

image

We are half way now. Obviously the page content margins have to be adjusted as well.

Step 2 – adjust page content margins

First instinct was to adjust Tab’s parameters of my skin in the SkinEditor, it has to be one of these I thought:

image

Left and RightContentIndent were the obvious choices.  But no, they have no effect in our case. So I resorted to what every developer does: I looked at the sources of the XtraTabControl. It turns out that the solution is rather simple yet not supported by SkinEditor for some reason. Instead I had to manually modify the skin’s XML definition you’ll find in [SKIN PROJECT]/[SKIN NAME]/skin.xml file.

Open the skin.xml and find the line that contains this content: SkinProduct="Tab" SkinName="[SKIN NAME]". It should look like:

<Skin4 SkinProduct="Tab" SkinName="SKIN NAME">

(don’t ask me why there is a node named Skin4 and others numbered from 1 on). So, within this node find the node named TabPane. Within TabPane node adjust the attributes of ContentMargins node to values you want. I.e. try this:

<ContentMargins Bottom="1" Top="1" Left="1" Right="1" />
The result is the 2nd picture below – compare both original and modified skin to see which one you prefer.

imageimage

This would be a lot more straightforward if ContentMargins were available through SkinEditor’s UI. I hope that SkinEditor will get more treatment in the future as it lacks other features as well.



Tags:

.net | DevExpress

Reversing for loops with CodeRush

by Miha Markič 17. September 2009 13:19

Imagine you have to delete a bunch of items from a list, something like this:

List<int> items = new List<int>();
...
for (int i = 2; i < items.Count; i++)
{
items.RemoveAt(i);
}

Will it work? Of course not because you are removing items while your are looping the entire list. That means sooner or later you’ll bump against out of index exception or items silently won’t be removed. And this is a common mistake we all do I suppose.

The solution is a pretty easy one – reverse the for loop, like this:

for (int i = items.Count - 1; i >= 2; i--)
{
items.RemoveAt(i);
}

However I find writing reverse loops harder than the former one. Not sure why but I guess that’s how I am used to do the maths – addition is always easier compared to subtraction.

Luckily there is CodeRush to the rescue. Just execute the Reverse For Loop code reformatting (not refactoring because you do change the meaning of the code with this one) and you are done.

cr

It works the other direction as well. Until today I didn’t even know that this trick exists. I just assumed it does and it sure did exist. This and a “million” of other features makes CodeRush really a must have coding tool.



Tags:

.net | CodeRush | DevExpress | Visual Studio

Suppress system menu when right clicking on DevExpress skinned form's icon

by Miha Markič 14. August 2009 16:36

Task

Today I've got a requirement from my customer to prevent showing the system menu when WinForm's icon (top left) is right-clicked. The form is painted by DevExpress skin. Here is how I've done it.

Solution

1. Intercept proper mouse messages

I had to intercept two mouse messages: WM_NCRBUTTONDOWN (0xa4) and WM_NCRBUTTONUP (0xa5). These two guys shows up when right click is performed on non client (NC) are of the form (where icon is located). That’s also the reason I couldn’t use Mouse* events which fire only for client area.

The interception is done by overriding Form's WndProc method and it looks something like this:

...
enum MouseMessage
{
WM_NCRBUTTONDOWN = 0xA4,
WM_NCRBUTTONUP = 0xA5
}
...
protected override void WndProc(ref Message msg)
{
switch ((MouseMessage)msg.Msg)
{
case MouseMessage.WM_NCRBUTTONDOWN:
case MouseMessage.WM_NCRBUTTONUP:
// code here
break;
}
base.WndProc(ref msg);
}

2. Finding icon bounds

If you are doing DevExpress form skinning it means that your form has to derive from XtraForm which does all of the skinning work for you. XtraForm holds a reference to FormPainter instance (which is responsible for painting the skin) through FormPainter property which in turn holds the icon bounds through its IconBounds property of Rectangle type. Now, the problem is that FormPainter.IconBounds property is protected and reflection has to be used to retrieve the value:

...
private PropertyInfo iconBoundsInfo;
...
public MyForm()
{
...
// store reference to IconBounds property to increase the performance
iconBoundsInfo = typeof(FormPainter).GetProperty("IconBounds", BindingFlags.Instance | BindingFlags.NonPublic);
...
}
protected override void WndProc(ref Message msg)
{
...
Rectangle iconBounds = (Rectangle)iconBoundsInfo.GetValue(FormPainter, null);
...
}

3. Figuring out if right click is within icon bounds and ignore such clicks

To get the click’s location I simply used Cursor.Position which is easier than retrieving the position from Message but less accurate – still enough for me.The position is in screen coordinates and they have to be transformed to form's coordinates. Form.PointToClient method won’t help because it transforms to form’s client area which border isn’t. The solution to this is rather simple – just Offset the position by Form’s –Left, –Top.

If click is positioned within icon bounds just return without delegating to base.WndProc. That’s it. Here is the complete code:

public IgnoreRightIconClickForm: XtraForm
{
enum MouseMessage
{
WM_NCRBUTTONDOWN = 0xA4,
WM_NCRBUTTONUP = 0xA5
}
private PropertyInfo iconBoundsInfo;

public IgnoreRightIconClickForm()
{
iconBoundsInfo = typeof(FormPainter).GetProperty("IconBounds", BindingFlags.Instance | BindingFlags.NonPublic);
}

protected override void WndProc(ref Message msg)
{
switch ((MouseMessage)msg.Msg)
{
case MouseMessage.WM_NCRBUTTONDOWN:
case MouseMessage.WM_NCRBUTTONUP:
// prevent showing system menu on window's icon rightclick
Rectangle iconBounds = (Rectangle)iconBoundsInfo.GetValue(FormPainter, null);
Point cursorPosition = Cursor.Position;
// offset to the raw window's coordinates (PointToClient transforms to window's client coordinates)
cursorPosition.Offset(-Left, -Top);
if (iconBounds.Contains(cursorPosition))
return;
break;
}
base.WndProc(ref msg);
}
}

Notes

This method works only for DevExpress skinned forms. If you want to use for normal forms the you should modify the icon bounds retrieval.



Tags:

.net | DevExpress

ClickOnce and DevExpress components deployment

by Miha Markič 27. June 2009 19:22

Let’s say you’ve build a WinForms application and put some DevExpress (or any other 3rd party controls installed in GAC I suppose) controls in there. At some point you have to deploy the application and let’s say you’ll use ClickOnce technology. Its easy, go to Project Properties/Publish tab, select where to publish and click Publish button. The next step is to try the application and of course you try it at your development computer – it works, no surprise there. However, when your client runs it on his computer it will most probably throw some sort of exception at the very beginning. Besides the obvious Murphy law involvement, why’s that? After all it worked on *development* computer.

There are two problems in this process.

1. ClickOnce doesn’t include 3rd party assemblies by default. This is probably true for all 3rd party assemblies that reside in GAC on development computer or in other words, those which are marked with CopyLocal=false (I admit it, I never researched in details). It should include those who are marked CopyLocal=true. Now, if you look at your Application Files in Publish tab of Project Properties you’ll see that all three DevExpress assemblies are marked with Publish Status = Prerequisite (Auto) flag – it means that your application expects those assemblies already installed on target machine:

pre

To include them in your deployment package change their Publish Status flag to Include. Like this:

post

All required assemblies will be published to client’s computer and the application will work, or at least will work as good or as bad as on development computer.

2. Your testing process isn’t good. You should always test deployment scenarios on non-development machine(s). VMWare Workstation is of great help. Besides the great virtualization desktop platform it also support integration in Visual Studio itself so you can pretty easily launch application in guest OS or even remotely debug it and even record the session. The other virtualization option on desktop is MS’ VirtualPC, an inferior application but free. You could use virtualization servers as well (MS HyperV,  VMWare Server, etc.).

That’s it.



Tags:

.net | Deployment | DevExpress | Visual Studio

Packing assemblies to a single file for Righthand.Dataset.Visualizer

by Miha Markič 23. June 2009 21:51

A while ago I’ve created Righthand.DataSet.Visualizer, an advanced DataSet visualizer. Today I’ve added support for displaying a single table as well. It wasn’t a big deal but I guess people will find it useful.

Now, there is one things I weren’t too happy about until today: I reference a lot of DevExpress assemblies and I have to redistribute all those assemblies along mine two (my visualizer comes in form of two assemblies). Which makes a lot of assemblies in total and even worse, if two visualizers use slightly different DevExpress versions you are in for a trouble.

So I’ve decided to pack everything to a single file. ILMerge, a free assembly merging tool from Microsoft, won’t work for me since it has problems with reference to Microsoft.VisualStudio.DebuggerVisualizers even though I don’t want to redistribute it. So I tried Xenocode Postbuild for .NET which does all sort of hacky things with .net assemblies, it even allows to create a setup that doesn’t require .net framework installed at the target machine. Among other features (obfuscation, optimization, etc.) it provides an assembly merging option that I successfully used in my case. Here are the required steps for my case:

1. Start Xenocode Postbuild for .NET, click on Application tab. Use Add… button to add required assemblies to pack together (you can add assemblies individually or pick most important ones and then use Scan Dependencies button to add referenced ones):

application

2. If you want only to pack assemblies then use Null – For test and debugging purposes or any other preset you want, just make sure you set other options appropriately.

presets

 

 

 

3. On the Protection tab I did uncheck all metadata obfuscation (since I am not after obfuscation here) by right clicking on the root node and selecting Unselect Tree menu item. I don’t use any Disassembler Suppression either. I left moderate code obfuscation (level 3 in scale 0..4), just for testing – this option shouldn’t cause any trouble since it should keep functionality the same. If there are problems with the later it means that the tool sucks heavily.

protection

4. Clear all checkboxes in Optimize tab.

5. On the Output tab I made sure that Single application executable option in Link and Code Generation group is selected and Righthand.DebugerVisualizer.Dataset.Visualizer assembly is the main one. I also selected .\Setup for the output folder.

output

6. By clicking Xenocode Application button the final, single file, output is written to the disk.

And that’s it. I got a single file with all required assemblies packed together. Just that easy. Note that I intentionally used only a fraction of Postbuild power.

If you use frequently Postbuild you should also consider using Final Builder tool, an automated build and release management tool that supports Postbuild out of the box (I am sure other such tools support Postbuild as well).

And finally, here is the updated visualizer:

RightHand.DebugerVisualizer.Dataset.Visualizer 0.9.14.zip (7.60 mb)

Let me know if there are any problems or if you have any improvement wish.



Using XtraChart ASP.NET control in an ASP.NET MVC project

by Miha Markič 13. May 2009 16:09

The problem

These days I am building an ASP.NET MVC project that requires charting. The problem is that there are no native ASP.NET MVC charting controls out there. The only one I am aware of is the one from Syncfusion . The problem with it is that it is in a beta and I didn’t have time to test it out. BTW, Syncfusion is the only 3rd party control provider (in beta though) for ASP.NET MVC AFAIK.

The solution

Anyway, back to my problem – charting in ASP.NET MVC. It is quite obvious that I have to use an ASP.NET control for the job. And since I already own and I am used to XtraCharts I’ve decided to give it a try (alternatives include use of Microsoft’s free ASP.NET Chart Control (which works in ASP.NET MVC for sure) and other ASP.NET charting controls might work as well). And guess what: XtraCharts works without many problems. As an image provider that is – I don’t need callbacks nor I think they are supported in this scenario.

Creating a test project

There are couple of extra steps necessary to use XtraCharts in ASP.NET MVC project. Here are steps necessary to create a test project.

Requirements: XtraCharts v9.1.

1. Drop a WebChartControl toolbox item (residing in DX.9.1.: Data Toolbox’ tab) on the Index view (I tested with the view (aspx) but it should work with partial view (ascx) as well). You’ll get this code:

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>
<%@ Register Assembly="DevExpress.XtraCharts.v9.1.Web, Version=9.1.3.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a"
Namespace="DevExpress.XtraCharts.Web" TagPrefix="dxchartsui" %>
<asp:Content ID="indexTitle" ContentPlaceHolderID="TitleContent" runat="server">
Home Page
asp:Content>
<asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
<h2><%
   1: = Html.Encode(ViewData["Message"]) 































































%>h2>































































<p>































































To learn more about ASP.NET MVC visit <a href="http://asp.net/mvc" title="ASP.NET MVC Website">http://asp.net/mvca>.































































p>































































<dxchartsui:WebChartControl ID="WebChartControl1" runat="server">































































dxchartsui:WebChartControl>































































asp:Content>
































It might happen that all the necessary assemblies won’t be referenced automatically. In this case add manually these (v9.1.x) assemblies:

  1. DevExpress.Web
  2. DevExpress.Charts.Core
  3. DevExpress.XtraCharts

2. Adjust WebChartControl’s settings either directly or through designer (yes, you can use its designer!). I’ll create a 3D pie chart with a single series bound to Name/Value pair. Disable both EnableCallbacks and EnableViewState.

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>
<%@ Register Assembly="DevExpress.XtraCharts.v9.1.Web, Version=9.1.3.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a"
Namespace="DevExpress.XtraCharts.Web" TagPrefix="dxchartsui" %>
<asp:Content ID="indexTitle" ContentPlaceHolderID="TitleContent" runat="server">
Home Page
asp:Content>
<asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
<h2><%
   1: = Html.Encode(ViewData["Message"]) 































































%>h2>































































<p>































































To learn more about ASP.NET MVC visit <a href="http://asp.net/mvc" title="ASP.NET MVC Website">http://asp.net/mvca>.































































p>































































<dxchartsui:WebChartControl ID="someChart" runat="server" EnableViewState="false" EnableCallbacks="false"































































DiagramTypeName="SimpleDiagram3D" Height="200px" Width="300px">































































<SeriesSerializable>































































<series argumentdatamember="Name" labeltypename="Pie3DSeriesLabel" name="Series 1"































































pointoptionstypename="PiePointOptions" seriesviewtypename="Pie3DSeriesView" valuedatamembersserializable="Value">































































<view hiddenserializablestring="to be serialized">view>































































<label hiddenserializablestring="to be serialized" linevisible="True" overlappingoptionstypename="OverlappingOptions">































































<fillstyle filloptionstypename="SolidFillOptions">































































<options hiddenserializablestring="to be serialized">options>































































fillstyle>































































label>































































<pointoptions hiddenserializablestring="to be serialized">pointoptions>































































<legendpointoptions hiddenserializablestring="to be serialized">legendpointoptions>































































series>































































SeriesSerializable>































































<SeriesTemplate LabelTypeName="Pie3DSeriesLabel" PointOptionsTypeName="PiePointOptions"































































SeriesViewTypeName="Pie3DSeriesView">































































<View HiddenSerializableString="to be serialized">































































View>































































<Label HiddenSerializableString="to be serialized" LineVisible="True" OverlappingOptionsTypeName="OverlappingOptions">































































<FillStyle FillOptionsTypeName="SolidFillOptions">































































<Options HiddenSerializableString="to be serialized">Options>































































FillStyle>































































Label>































































<PointOptions HiddenSerializableString="to be serialized">































































PointOptions>































































<LegendPointOptions HiddenSerializableString="to be serialized">































































LegendPointOptions>































































SeriesTemplate>































































<Diagram RotationMatrixSerializable="1;0;0;0;0;0.5;-0.866025403784439;0;0;0.866025403784439;0.5;0;0;0;0;1">































































Diagram>































































<FillStyle FillOptionsTypeName="SolidFillOptions">































































<Options HiddenSerializableString="to be serialized">Options>































































FillStyle>































































dxchartsui:WebChartControl>































































asp:Content>
































































3. Add the following registration on the top of the page:

<%@ Register Assembly="DevExpress.XtraCharts.v9.1, Version=9.1.3.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a"
Namespace="DevExpress.XtraCharts" TagPrefix="dx" %>

and change nodes of type to type . Otherwise you’ll have a naming conflict.

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>
<%@ Register Assembly="DevExpress.XtraCharts.v9.1.Web, Version=9.1.3.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a"
Namespace="DevExpress.XtraCharts.Web" TagPrefix="dxchartsui" %>
<%@ Register Assembly="DevExpress.XtraCharts.v9.1, Version=9.1.3.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a"
Namespace="DevExpress.XtraCharts" TagPrefix="dx" %>
<asp:Content ID="indexTitle" ContentPlaceHolderID="TitleContent" runat="server">
Home Page
asp:Content>
<asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
<h2><%
   1: = Html.Encode(ViewData["Message"]) 































































%>h2>































































<p>































































To learn more about ASP.NET MVC visit <a href="http://asp.net/mvc" title="ASP.NET MVC Website">http://asp.net/mvca>.































































p>































































<dxchartsui:WebChartControl ID="someChart" runat="server" EnableViewState="false" EnableCallbacks="false"































































DiagramTypeName="SimpleDiagram3D" Height="200px" Width="300px">































































<SeriesSerializable>































































<dx:series argumentdatamember="Name" labeltypename="Pie3DSeriesLabel" name="Series 1"































































pointoptionstypename="PiePointOptions" seriesviewtypename="Pie3DSeriesView" valuedatamembersserializable="Value">































































<view hiddenserializablestring="to be serialized">view>































































<label hiddenserializablestring="to be serialized" linevisible="True" overlappingoptionstypename="OverlappingOptions">































































<fillstyle filloptionstypename="SolidFillOptions">































































<options hiddenserializablestring="to be serialized">options>































































fillstyle>































































label>































































<pointoptions hiddenserializablestring="to be serialized">pointoptions>































































<legendpointoptions hiddenserializablestring="to be serialized">legendpointoptions>































































dx:series>































































SeriesSerializable>































































<SeriesTemplate LabelTypeName="Pie3DSeriesLabel" PointOptionsTypeName="PiePointOptions"































































SeriesViewTypeName="Pie3DSeriesView">































































<View HiddenSerializableString="to be serialized">































































View>































































<Label HiddenSerializableString="to be serialized" LineVisible="True" OverlappingOptionsTypeName="OverlappingOptions">































































<FillStyle FillOptionsTypeName="SolidFillOptions">































































<Options HiddenSerializableString="to be serialized">Options>































































FillStyle>































































Label>































































<PointOptions HiddenSerializableString="to be serialized">































































PointOptions>































































<LegendPointOptions HiddenSerializableString="to be serialized">































































LegendPointOptions>































































SeriesTemplate>































































<Diagram RotationMatrixSerializable="1;0;0;0;0;0.5;-0.866025403784439;0;0;0.866025403784439;0.5;0;0;0;0;1">































































Diagram>































































<FillStyle FillOptionsTypeName="SolidFillOptions">































































<Options HiddenSerializableString="to be serialized">Options>































































FillStyle>































































dxchartsui:WebChartControl>































































asp:Content>
































































4. Under Models folder create RhData class that will hold data:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace MvcApplication10.Models
{
public class RhData
{
public string Name { get; set; }
public int Value { get; set; }
}
}

5. Add class Index.aspx.cs next to Index.aspx file. This will serve as codebehind file. Also add Page_Load method, create and bind data to chart right in there. Note that you have to declare chart instance field as well since the designer won’t do it for you.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MvcApplication10.Models;
namespace MvcApplication10.Views.Home
{
public class Index: ViewPage
{
protected global::DevExpress.XtraCharts.Web.WebChartControl someChart;
protected void Page_Load(object sender, EventArgs e)
{
List data = new List();
data.Add(new RhData { Name = "One", Value = 1 });
data.Add(new RhData { Name = "Two", Value = 2 });
someChart.DataSource = data;
someChart.DataBind();
}
}
}

Note: The proper MVC to provide data way would be through the model, however for the sake of simplicity I took this shortcut. The binding itself has to be done in Page_Load event.

6. Wire up the code behind class to the view by adding CodeBehind attribute and modifying Inherits one.

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="MvcApplication10.Views.Home.Index" CodeBehind="Index.aspx.cs" %>

 

 

The result

If everything goes well you should see such output.

demo

This will do just fine until there is an MvcXtraChart control I guess.

XtraChartsMvc.zip (241.46 kb) (Updated) Note: Built with DevExpress v9.1.3.0. If you use other version then you should run DX' ProjectConverter tool or just do a search and replace of "9.1.3.0" with your version.

Shout it




Tags:

.net 3.5 | asp.net mvc | DevExpress

Strong typing routes in ASP.NET MVC

by Miha Markič 21. April 2009 22:43

Let’s say you have Test action in Home controller like this:

public ActionResult Test(int id, int tubo)
{
return View("Index");
}

And you want to create a hyperlink to that action on your view. So, did you ever wonder why do you have to write code like:

< %= Html.ActionLink("Classic Action", "Test", "Home", new { tubo=8, id=77 }, null) %>

[Note: I had to insert a space after each less than character (<) becaue of formatting issues in IE7. For some reason the code disappeared when there was no space after <. Firefox didn't show any such issues. If you try running the code contained in the code snippets you'll have to remove those spaces.]

(I won’t even consider lambda version because it is awfully slow). This version has two problems:

  1. It isn’t strong typed by any mean.
  2. The anonymous class holding id and tubo values isn’t the best performer either.

To solve the second issue you might opt for RouteValueDictionary instead of anonymous class, like this:

< %= Html.ActionLink("Classic Action", "Test", "Home", 
new RouteValueDictionary {  { "tubo", 8 } ,{ "id", 77 } }, null) %>

This version is less readable but slightly better performer.

The bigger problem is the first one, which wasn’t possible to overcome, unless you wanted to get rid of performance (by using lambdas). Until now, that is. I created a solution that would provide best of both worlds: strong typing and performances.

< %= Html.LinkTestOnHome("TestLink", 8, 77)%>

Note the advantages:

  1. Parameters are strongly typed
  2. The most performing call (currently to ActionLink) is handled internally (with possibility to improve).

Sounds good? If yes, read futher.

Instructions

The generator comes in a form of DXCore/CodeRush plugin and there is a single requirement: you’ll need the best Visual Studio productivity tool CodeRush. Or even better, this plugin requires only DXCore, which is a free subset of CodeRush (untested scenario at this point though). In other words, this is a free lunch albeit you should really take a look at CodeRush and all of its super features. You won’t regret it.

Here are the steps to test it out:

1. Unzip the attached MvcStrongTyping0.1.zip file into [Program Files]\Developer Express Inc\DXCore for Visual Studio .NET\2.0\Bin\Plugins folder (on a 64 bit Windows it should look like C:\Program Files (x86)\Developer Express Inc\DXCore for Visual Studio .NET\2.0\Bin\Plugins).

2. Start an instance of Visual Studio 2008 and open (or create a new) ASP.NET MVC project.

3. Add a root folder named Helpers in the ASP.NET MVC project if it doesn’t exist yet.

4. Create a file named UrlExtensions.cs in Helpers folder.

5. Open the file and/or make it the active document.

6. WARNING: Any content in active document gets deleted and replaced by new one during this operation. Click DevExpress\Create MVC Routes menu entry.

route1
7. Include [Rootnamespace].Helpers (check out UrlExtensions class’ namespace) namespace to the web.config file in the node:

<system.web>
...
<pages>
...
<namespaces>
...
<add namespace="[YourRootNamespace].Helpers"/>
< /namespaces>
< /pages>
...
< /system.web >

That’s it. There should be a lot of code generated to UrlExtenstions.cs file and you are able now to use both new methods (or their overloads):

< %= Html.LinkTestOnHome("TestLink", 8, 77)%>
< %= Url.ActionTestOnHome(8, 77) %>

Generated code

For the Test action there should be this code (shortened) present:

...
namespace [YourRootNamespace].Helpers
{
public static class UrlExtensions
{
...
#region Fixed params action for Home.Test
public static string ActionTestOnHome(this UrlHelper helper, object routeValues) ...
public static string ActionTestOnHome(this UrlHelper helper, object routeValues, 
string protocol)...
#endregion
#region Fixed params links for Home.Test
public static string LinkTestOnHome(this HtmlHelper helper, string linkText, 
object routeValues) ...
public static string LinkTestOnHome(this HtmlHelper helper, string linkText, 
object routeValues, object htmlAttributes) ...
public static string LinkTestOnHome(this HtmlHelper helper, string linkText, 
string protocol, string hostName, 
string fragment, object routeValues, object htmlAttributes) ...
#endregion
#region Variable params for action Home.Test
public static string ActionTestOnHome(this UrlHelper helper, int tubo)
public static string ActionTestOnHome(this UrlHelper helper, int tubo, 
RouteValueDictionary routeValues) ...
public static string ActionTestOnHome(this UrlHelper helper, int tubo, 
RouteValueDictionary routeValues, 
string protocol, string hostName) ...
#endregion
#region Variable params links for Home.Test
public static string LinkTestOnHome(this HtmlHelper helper, string linkText, int tubo) ...
public static string LinkTestOnHome(this HtmlHelper helper, string linkText, int tubo, 
RouteValueDictionary routeValues) ...
public static string LinkTestOnHome(this HtmlHelper helper, string linkText, int tubo, 
RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes) ...
public static string LinkTestOnHome(this HtmlHelper helper, string linkText, int tubo, 
string protocol, string hostName, string fragment, 
RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes) ...
#endregion
#region Variable params for action Home.Test
public static string ActionTestOnHome(this UrlHelper helper, int tubo, int id) ...
public static string ActionTestOnHome(this UrlHelper helper, int tubo, int id, 
RouteValueDictionary routeValues) ...
public static string ActionTestOnHome(this UrlHelper helper, int tubo, int id, 
RouteValueDictionary routeValues, string protocol, string hostName) ...
#endregion
#region Variable params links for Home.Test
public static string LinkTestOnHome(this HtmlHelper helper, string linkText, 
int tubo, int id) ...
public static string LinkTestOnHome(this HtmlHelper helper, string linkText, 
int tubo, int id, RouteValueDictionary routeValues) ...
public static string LinkTestOnHome(this HtmlHelper helper, string linkText, 
int tubo, int id, 
RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes) ...
public static string LinkTestOnHome(this HtmlHelper helper, string linkText, 
int tubo, int id, 
string protocol, string hostName, string fragment, 
RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes) ...
#endregion
}
}

That’s quite a lot of overloaded methods (and this is just an example for a single action!).
First note the name of the method that corresponds to the ActionOnController pair.
Next, there are basically two groups:
  • Fixed parameters – just strong typed links to the action (without any explicit action parameter)
  • Variable parameters – strong typed links with various combinations of action’s input parameters

Both groups are subdivided into two groups:

  • Actions – generates string representation of matching URL through UrlHelper.Action
  • Links – generates links through HtmlHelper.ActionLink method

Both internal calls might be optimized further and perhaps they will be in the future. At this point it is important that the methods work.

 

Implementation details

The magic of the autogenerated code comes from DXCore’s StructuralParser, which allowed me easy parsing of the project’s code (something that should be provided by Visual Studio from the beginning). And by easy I mean really easy and straightforward. I can’t praise it enough. On the other hand if it wasn’t for DXCore my plugin wouldn’t be even possible. At least not in time that I can allocate to this project.

So, all it took me was a couple of advices from Mark Miller (who is an incredibly helpful person btw) on where to start with parser and a bunch of basic operations of building a DXCore plugin. All in all it took me around 4 hours of my time. Thanks to my friend Miha for method naming suggestion. If it wasn’t for him the method names would be a lot worse. And thanks also to Whiletrue guys for their asp.net mvc performance presentation.

So, here it is. Let me know what do you think about it and don’t forget, this is an early, an experimental version of the plugin. All feedback is appreciated.

MvcStrongTyping0.1.zip (9.62 kb)

See also my previous article on strong typing the views.



Tags: , ,

.net 3.5 | asp.net mvc | DevExpress | VS Add In | CodeRush | DXCore | DXCore plugin

Miha Markič

About me
Righthand
 
Microsoft MVP
 
Developer Express' DXSquad
INETA Country Leader for Slovenia
INETA Country Leader for Slovenia

Slovene Developer Users Group Lead
Friends of Red-Gate
LLBLGenPro Partner

Miha currently works as a free lance consultant and software developer specialized in .net area.
He graduated in Computer and information science at the University of Ljubljana, Slovenia. He has accumulated experience in various programming languages such as Java, Visual Basic 3-6 (MCP), Visual C++, Delphi, C# and VB.Net through years.
He has experience in practically all (technical) stages of project development, including planning, framework development, user interface, business processes, as well as testing and documenting. He has worked on big and small projects in Slovenia and abroad (e.g. participated in completing level 3 IS for the Nucor steel plant, Hertford, USA).
Currently he enjoys programming in .net environment using C#. Since 2000 he has been active in Developer Express' DX Squad and has been ECDL trainer and tester. He also gives lectures on conferences and other events in Slovenia.

Shortcuts

Add to Technorati Favorites

Social networking

Most comments

Brandon Brandon
1 comments
us United States
Igor Brejc Igor Brejc
1 comments
si Slovenia
BV BV
1 comments
si Slovenia

Google friends

Recent Comments

Comment RSS