Different Ways to Display Images in ASPxGridView

March 27, 2011

Suppose you have a C# class for storing images, such as

public class MyClass
{
  public int ID { get; set; }
  public byte[] MyImage { get; set; }
}

which is a wrapper class for a table storing images as binary blobs (MS SQL image or varbinary(max), Oracle BLOB etc.)

I found 4 different ways (that is, “simple ways”; certainly there are countless other more complex alternatives) to display the binary data as images, using DevExpress’ ASPxImage and ASPxBinaryImage controls inside a ASPxGridView:

<dx:ASPxGridView ID="gridImages" runat="server"
  AutoGenerateColumns="False" KeyFieldName="ID">
  <Columns>
    <dx:GridViewDataTextColumn Caption="ID" FieldName="ID">
    </dx:GridViewDataTextColumn>

using a query like

var images = database.Query<MyImages>().OrderBy(r => r.ID);
gridImages.DataSource = images;
gridImages.DataBind();

Using GridViewDataBinaryImageColumn

    <dx:GridViewDataBinaryImageColumn FieldName="MyImage">
    </dx:GridViewDataBinaryImageColumn>

Using ASPxBinaryImage inside a DataItemTemplate

    <dx:GridViewDataColumn FieldName="MyImage" >
      <DataItemTemplate>
        <dx:ASPxBinaryImage ID="img" runat="server"
          Value='<%# Eval("MyImage") %>'>
        </dx:ASPxBinaryImage>
      </DataItemTemplate>
    </dx:GridViewDataColumn>

ASPxBinaryImage uses the Value property to store a byte array, which the DevExpress framework translates into <img src> where src requests a DevExpress-implemented URL to serve the byte stream.

If you prefer ASP:Image or ASPxImage to ASPxBinaryImage, you need to write an .ashx handler to retrieve the image data:

public class MyImageHandler : IHttpHandler
{
  public void ProcessRequest(HttpContext context)
  {
    int id = 0;
    int.TryParse(context.Request["id"], out id);

    var image = database.FirstOrDefault<MyClass>(c => c.ID == id);

    if (image == null)
    {
      context.Response.Clear();
      return;
    }

    context.Response.ContentType = "image/png";
    context.Response.BinaryWrite(image.MyImage);
    context.Response.End();
  }

This .ashx handler is invoked by setting the ImageUrlFormatString of a GridViewDataImageColumn

    <dx:GridViewDataImageColumn FieldName="ID">
      <PropertiesImage ImageUrlFormatString="myimage.ashx?id={0}">
      </PropertiesImage>
    </dx:GridViewDataImageColumn>

or by setting the ImageUrl property of an ASPxImage:

    <dx:GridViewDataColumn >
      <DataItemTemplate>
        <dx:ASPxImage runat="server" ID="imgTemplate"
          ImageUrl='<%#  "myimage.ashx?id=" + Eval("ID")  %>'>
        </dx:ASPxImage>
      </DataItemTemplate>
    </dx:GridViewDataColumn>
  </Columns>

Client-Side Confirmation on ASPxButton with CausesValidation=true

March 25, 2011

If you use DevExpress and its ASPxButton, you can add client-side confirmation of a button press by setting the button’s ClientSideEvents.Click property to contain Javascript code like this:

MyButton.ClientSideEvents.Click =
  @"function(s,e) {
      e.processOnServer = confirm('execute this action?');
    }";

If the button’s CausesValidation property is set, however, this event handler will be called even if client-side validation raises validation errors. In this case, the confirmation dialog should not be displayed.

This can be solved by calling the ASPxClientEdit.ValidateGroup() client API before displaying the confirmation dialog:

MyButton.ClientSideEvents.Click =
  @"function(s,e) {
      e.processOnServer =
        ASPxClientEdit.ValidateGroup('" + MyButton.ValidationGroup + @"') 
        && confirm('execute this action?');
    }";

Looking up Localized Error Messages

March 22, 2011

About 3 years ago, I complained

Now wouldn’t you expect all the funny messages that Windows (or *any* operating system, for that matter) generates, to be searchable on the web? Plus statements by the respective manufacturer about their meaning and possible solutions?

At least the first question seems to be solved in the meantime by a site called FindErr which claims to contain ~8,500 MS SQL Server messages, ~11,500 .Net Framework messages, and ~5,500 Windows messages in about 30 languages (depending on category).

What I am missing at first glance is the list of translations for each English error message.


Listing Installed Versions of .Net Framework in C#

March 22, 2011

The internets know many answers to the question how to get a list of all installed versions of the .Net Framework. (Hint: registry path HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NET Framework Setup\NDP)

Looking for some kind of “official” confirmation, I finally found this article in the MS Knowledge Base, How to determine which versions and service pack levels of the Microsoft .NET Framework are installed.

Converting this information into code

  • Open the registry key and loop through its subkeys
static void Main(string[] args)
{
  Console.WriteLine(".Net Framework Versions");

  var hkNDP = Registry.LocalMachine.OpenSubKey(
    @"SOFTWARE\Microsoft\NET Framework Setup\NDP", false);
  WriteKey(hkNDP, "");
  hkNDP.Close();

  Console.ReadLine();
}
  • For the subkeys, detect Version, SP, and Install values, and loop through subkeys
static void WriteKey(RegistryKey hk, string relPath)
{
  if (relPath != "")
    relPath += "/";

  foreach (var keyname in hk.GetSubKeyNames())
  {
    var key = hk.OpenSubKey(keyname, false);

    var keySP = key.GetValue("SP");
    var keyVersion = key.GetValue("Version");

    if (keyVersion != null)
      Console.WriteLine(relPath + keyname +
        ": Version " + keyVersion.ToString() +
        ((keySP != null) ? " SP " + keySP.ToString() : "") +
        (1.Equals(key.GetValue("Install")) ? " installed" : ""));

    WriteKey(key, relPath + keyname);

    key.Close();
  }
}

On Windows 7 (German), this results in the following output

.Net Framework Versions
v2.0.50727: Version 2.0.50727.4927 SP 2 installed
v2.0.50727/1031: Version 2.0.50727.4927 SP 2 installed
v3.0: Version 3.0.30729.4926 SP 2 installed
v3.0/Setup: Version 3.0.30729.4926
v3.0/Setup/1031: Version 3.0.30729.4926 SP 2 installed
v3.0/Setup/Windows Communication Foundation: Version 3.0.4506.4926
v3.0/Setup/Windows Presentation Foundation: Version 3.0.6920.4902
v3.5: Version 3.5.30729.4926 SP 1 installed
v3.5/1031: Version 3.5.30729.4926 SP 1 installed
v4/Client: Version 4.0.30319 installed
v4/Client/1033: Version 4.0.30319 installed
v4/Full: Version 4.0.30319 installed
v4/Full/1033: Version 4.0.30319 installed

Migrating HttpHandlers from IIS 6 to IIS 7

March 13, 2011

I developed a set ASP.Net applications with VS 2005/2008 which recently needed to be installed on IIS7. Everything worked “out of the box” except for one application that implemented a combined HTML and WEBDAV browser on files stored in a database using an HttpHandler.

The application is configured as belonging to the DefaultAppPool (.Net 2.0, integrated pipeline).

Integrated Pipeline requires all registered HttpHandlers to be configured in the system.webServer/handlers section rather than in system.web/httpHandlers.

The following steps made the application run (and debug) again:

  • Move the original configuration
<system.web>
  <httpHandlers>
    <clear />
    <add path="DebugAttach.aspx" verb="DEBUG" type="System.Web.HttpDebugHandler" />
    <add verb="*" path="*" type="MyNamespace.MyHttpHandler, MyAssembly" />
  </httpHandlers>
</system.web>

to

<system.webServer>
  <handlers accessPolicy="Read, Execute, Script">
    <add name="DebugAttach" path="DebugAttach.aspx" verb="DEBUG"
      type="System.Web.HttpDebugHandler" />
    <add name="MyHandlerName" path="*" verb="*"
      type="MyNamespace.MyHttpHandler, MyAssembly" resourceType="Unspecified" />
  </handlers>
</system.webServer>
  • Notice the “name” parameter in the handler entries
  • Enable the CGI-exe (CgiModule) and ISAPI-dll (IsapiModule) handler mappings

Success!

If you still experience errors, enable the Failed Request Tracing for the application in IIS Manager, and look at the generated trace files in C:\inetpub\logs\FailedReqLogFiles.


Analyzing Files and Directories used in Visual Studio Solution

March 9, 2011

The latest version of my application VS Solution Dependency Visualizer extracts information of all files included in projects and solution folders. This information is summarized to display the list of directories used in single projects or the whole solution.

Another new feature is to display the projects referencing a selected assembly.

Dependency charts are now displayed inside the application, and can be saved as .png or .dia files from the dependency chart viewer window.

The complete feature list

  • Installs into context menu of .sln files
  • Hierarchical view of projects in solution folders
  • Select projects to be included in dependency analysis
  • Command line supports multiple .sln file parameters
Solution
  • Display projects in solution
  • Display assemblies referenced by projects (including GAC and system assemblies)
  • Display used directories
Solution Folders
  • Display contained files
  • Display used directories
Projects
  • Display referenced projects and assemblies
  • Display referencing projects
  • Display files in project
  • Display used directories
Dependency Chart
  • Display dependency chart in separate window
  • Display dependencies between projects
  • Display assembly dependencies of projects
  • Display dependencies between solution folders
  • Zoom and full-size
  • Save dependency chart as .png
  • Save dependency chart as .dia for editing

The latest release (version 0.50) of VS Solution Dependency Viewer is available for download here.


DevExpress Linq to XPO and NULL values

March 2, 2011

DevExpress’ Linq To XPO has some known difficulties handling NULL values in .Where() conditions.

I came across them using code like this:

string Name = NullableString( [some input value] );
string Street = NullableString( [some input value] );

with NullableString() defined as

string NullableString(string s)
{
  if (string.IsNullOrEmpty(s))
    return null;
  return s.Trim();
}

and the query

var q = new XPQuery<Addresses>(session)
  .Where(a => a.Name == Name && a.Street == Street)
  .FirstOrDefault();

The resulting SQL statement looks like this:

Executing sql 'select * from (
  select N0."ID" as F0,N0."NAME" as F1,N0."STREET" as F2 
  from "ADDRESSES" N0 
  where ((N0."NAME" = :p0) and (N0."STREET" = null)) 
  where RowNum <= 1' with parameters {....}

This result surprised me (comparing null values using ‘=’ instead of IS NULL), especially since the DevExpress support center (Q100139, B94052)  indicates that NULL value handling has been fixed years ago.

Fortunately, another article in the support center provides an explanation and a workaround:

When the LINQ expression is processed by XPO, the value of the variable isn’t yet recognized and we can’t determine whether the variable has a value.

The workaround consists of checking each of the parameters for NULL, and comparing to the literal null value:

var q = new XPQuery<Addresses>(session)
  .Where(a => some not-null conditions);

if (Name == null)
  q = q.Where(a => a.Name == null);
else
  q = q.Where(a => a.Name == Name);

if (Street == null)
  q = q.Where(a => a.Street == null);
else
  q = q.Where(a => a.Street == Street);

Executing this Linq query generates the following statement:

Executing sql 'select * from (
  select N0."ID" as F0,N0."NAME" as F1,N0."STREET" as F2 
  from "ADDRESSES" N0 
  where ((N0."NAME" = :p0) and N0."STREET" is null) 
  where RowNum <= 1' with parameters {...}

just as you’d expect. Note that the combination of .Where() conditions is correctly translated into AND operations.

Another entry in the support center suggests that there won’t be a native solution:

We don’t see another solution, that can be implemented at the XPO level, without risk of introducing another bug. We have discussed this issue again, and come to the decision to not change anything.

 


Merge and Minify as Visual Studio Build Tasks

March 1, 2011

During development, a web project typically contains several JavaScript and CSS files.

Current best practice is to merge these files into single files (.js and .css respectively) and minify the merged files to reduce the number of requests between client and web server.

This can be achieved using two MSBuild tasks:

To invoke the tasks, you need to edit your web project’s .csproj file. Either open it in a text editor, or from Visual Studio, unload the project, and edit the project file while the project is unloaded from the solution.

First, you need to register both tasks in the csproj file (insert right before the </Project> tag at the end of the file):

<Import Project="$(MSBuildExtensionsPath32)\Microsoft\MicrosoftAjax\ajaxmin.tasks" />
<Import Project="path\to\FrogMSBuild\FrogMSBuild.tasks" />

MS Ajax Minifier comes with an installer, and installs itself under the Program Files\MSBuild directory. FrogMSBuild can simply be copied to a directory of your choice.

The tasks run after build:

<Target Name="AfterBuild">

Define the JavaScript files to be merged:

  <ItemGroup>
    <Scripts_Frog_JSFile Include="Scripts\foo.js;Scripts\bar.js;Scripts\etc.js;" />
  </ItemGroup>

Invoke the FileCombinator task defined in FrogMSBuild:

  <FileCombinator SourceFiles="@(Scripts_Frog_JSFile)" 
    TargetFile="Scripts\Merged.js"
    CombinationMode="Javascript" RemoveSourceFiles="false" />

Define the JavaScript files to be minified:

  <ItemGroup>
    <JS Include="Scripts\Merged.js" Exclude="**\*.min.js" />
  </ItemGroup>

Invoke the Minification task

  <AjaxMin JsSourceFiles="@(JS)" 
    JsSourceExtensionPattern="\.js$" JsTargetExtension=".min.js" />
<!-- CssSourceFiles="@(CSS)" 
  CssSourceExtensionPattern="\.css$" CssTargetExtension=".min.css" / -->
</Target>

This example deals with JavaScript only.

Load the web project again in Visual Studio, and the next build will generate merged and minified .js and .css files.


Follow

Get every new post delivered to your Inbox.