in
Forums
Blogs
DevExpress.com
Client Center
Support Center
DevExpress Channel

Paul Kimmel's Blog

  • Disable Sorting in OLAP Mode for PivotGrid New in v2009 Volume 3

    By default the XtraPivotGrid (PivotGridControl) sorts field values (row and column headers) in ascending or descending order. OLAP cubes can define their own ordering. If you want to maintain the cube’s ordering then you can disable field ordering. When working with an OLAP cube if you set a pivot grid field’s SortMode to None then default ordering is turned off. New in v2009 volume 3 is the Clear Sorting feature. Clear Sorting, as well as the A-Z and Z-A, ordering let you re-order the data or clear ordering altogether.

    When you turn automatic sorting off by setting a field’s SortMode to None the PivotGridControl’s header context menus will show these additional header context menu items (see Figure 1). The following steps walk you through constructing the sample solution.

    image
    Figure 1: The new Clear Sorting context menu item shows up when automatic filtering is turned off for a field by setting SortMode to None.

    Constructing the XtraPivotGrid (PivotGridControl) Demo

    I am using the XtraPivotGrid version 2009 volume 3 for the demo. The data comes from the Northwind database and a .cub file was created using Microsoft Excel’s export cube feature. (DevExpress’ book Professional DevExpress ASP.NET Controls chapter 8 talks about how to export a cube file.) To create the demo follow these steps:

    1. Create WinForms project

    2. Add a PivotGridControl to the form and dock it

    3. Place the cube file in your project, somewhere that it is accessible

    4. Define a connection string using the Smart tags’ Choose OLAP Data Source wizard, or simply define the connection string in the PivotGridControl’s OLAPConnectionString property, similar to the following:

    Provider=msolap;Initial Catalog=Northwind;Cube Name=Northwind;Data Source=c:\devexpress\winforms\olapdemo\OlapDemoWinForms\Northwind.cub

    (If you define an OLAPConnectionString then the DataSource property is cleared and vice versa.)

    5. Right click on the PivotGridControl (or the Smart tags menu) and click on Run Designer to run the field’s designer (see Figure 2)

    6. Add six fields in the designer using the Add button. (The fields for the demo are City, Category name, Products, Country, Quantity, and Discount)

    7. You will need to set the field properties based on where you want the data to appear in the pivot grid. To create the result shown in Figure 3 use these settings:

    City – Caption is City, Area is FilterArea, FieldName is [Customers].[City].[City], and SortMode is None

    Category Name – Caption is Category Name, Area is RowArea, FieldName is [Categories].[Category Name].[Category Name], and SortMode is None

    Products – Caption is Products, Area is RowArea, FieldName is [Products].[Products].[Products], and SortMode is None

    Country – Caption is Country, Area is ColumnArea, FieldName is [Customers].[Country].[Country], and SortMode is None

    Quantity – Caption is Quantity, Area is DataArea, FieldName is [Measures].[Quantity], and SortMode is None

    Discount – Caption is Discount, Area is DataArea, FieldName is [Measures].[Quantity], and SortMode is None

    8. Close the designer. (The design-time view is shown in Figure 3.)

    9. Run the demo. The results at runtime (Figure 4) with the Clear Sorting context menu item is shown in Figure 1.

    The XtraPivotGrid is a great tool for exploring and arranging your data to figure out what it all means, collectively referred to as data mining.

    The Clear Sorting feature is also supported for the ASP.NET ASPxPivotGrid (and you can use the same .cub file, if you don’t have Microsoft Analysis Services handy.) If you are having trouble getting your hands on a cube file or following Excel’s instructions for creating one drop me a line and I will email you the one for the demo.

     

    image
    Figure 2: Add the fields using the designer (shown above).

    image
    Figure 3:  The design-time results based on the designer settings.

     

    image
    Figure 4: The PivotGridControl at runtime with the configured data.

  • Handling focus Row Change in XtraGrid

    A customer asked me yesterday: “I am frustrated. I looked in the Properties window event tab for the XtraGrid.GridControl and cannot find event that tells me when the focused row has changed?” I said I appreciate your frustration; let me help. So, as not to give him a doh! moment with the easy answer, i used the sample code he sent me, completed it and sent it back with a “its the FocusedRowChanged event, which you can find in the events view of the designer”.

    Like the guy that showed me how to change a fan belt—not by removing the radiator and fan as I tried to do at 15—but by loosening the alternator, its all about knowing the answer. From the designer implement the FocusedRowChanged event, grab the FocusedRowHandle from the event argument DevExpress.XtraGrid.Views.Base.FocusedRowChangedEventArgs e and ask the grid for the row, passing in the FocusedRowHandle. Listing 1 demonstrates the solution.

    There are support center posts about this kind of problem, which you can find by Googling for them, or by going directly to our support center or forums at http://community.devexpress.com/forums/. Of course, if you are frustrated it is always nice to reach out to support—they can often find existing posts quickly—or ping one of the evangelists. (The last choice gives us a reason to sling a little code every day.)

    Listing 1: Determining what the focused row is in FocsuedRowChanged event.

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

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

          BindingList<guiListItem> list = new BindingList<guiListItem>();

          for (int i = 0; i < 100; i++)
          {
            guiListItem item = new guiListItem(i, "Description " + i.ToString(), "item" + i.ToString());
            list.Add(item);
          }
          gridControl1.DataSource = list;
        }

        private void gridView1_FocusedRowChanged(object sender, DevExpress.XtraGrid.Views.Base.FocusedRowChangedEventArgs e)
        {
          //here
          guiListItem item = (guiListItem)gridControl1.DefaultView.GetRow(e.FocusedRowHandle);
          MessageBox.Show(item.Description);
        }
      }

      public class guiListItem
      {
        /// <summary>
        /// Initializes a new instance of the guiListItem structure.
        /// </summary>
        /// <param name="key"></param>
        /// <param name="description"></param>
        /// <param name="value"></param>
        public guiListItem(int key, string description, string value)
        {
          Key = key;
          Description = description;
          Value = value;
        }
        public int Key{get; set;}
        public string Description{get; set;}
        public string Value{get; set;}
      }
    }

  • Automatic Edit Type Switching New in v2009 Volume 3

    Some features are small and to the point. New features are being added all the time. The feature in this blog is short and to the point and it is a design time feature, so I combined it with another feature.

    Suppose you have a TextEdit control on a form and you set the TextEdit.Properties.Mask to ‘f’ for Full date/time (short time) using the Mask Editor (see Figure 1). You now also have the design time capability—a new feature in volume 3—of changing the actual design control while keeping layout and location the same. For example, to change the TextEdit control to a DateEdit control at design time click the TextEdit’s smart tags menu, click Change editor type (see Figure 2). All control types can be changed this way and you get to avoid the headache of reconstructing your very carefully constructed layout.

    image
    Figure 1: The Mask Editor let’s you pick from pre-defined edit masks for the TextEdit control.

     

    image
    Figure 2: Change control types in place on the fly at design time.

  • XtraCharts Swift Plot new in v2009 Volume 3

    The demo can be downloaded at http://www.devexpress.com/Support/Center/e/E1836.aspx. This is a zip file. You can add the .zip extension and extract it or download the Example Runner (from the above link).

    The ChartControl has a new feature referred to as Swift Plot. The benefit of the SwiftPlotDiagram class is render charts with a lot of data very quickly or to plot real-time data with a significant number of data changes. The first code sample in Listing 1 is from our Support Center —see Listing 1.

    image
    Figure 1: The ChartControl  diagramming in real-time (against .NET Framework 2.0).

    The first sample uses a ChartControl and Timer and plots 20 points at a time scrolling the plotted points from right to left at each timer interval. Points less then the minimum DateTime on the left of the chart are removed, simulating the real-time charting action.

    Listing 1: The ChartControl using the SwiftPlotDiagram class to simulate real-time charting.

    using System;
    using System.Windows.Forms;
    using DevExpress.XtraCharts;
    // ...

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

            const int interval = 20;
            Random random = new Random();
            int TimeInterval = 10;
            double value1 = 10.0;

            AxisRange AxisXRange {
                get {
                    SwiftPlotDiagram diagram = chartControl1.Diagram as SwiftPlotDiagram;
                    if (diagram != null)
                        return diagram.AxisX.Range;
                    return null;
                }
            }

            double CalculateNextValue(double value) {
                return value + (random.NextDouble() * 10.0 - 5.0);
            }

            void UpdateValues() {
                value1 = CalculateNextValue(value1);
            }

            private void timer1_Tick(object sender, EventArgs e) {
                Series series1 = chartControl1.Series[0];

                if (series1 == null)
                    return;
                DateTime argument = DateTime.Now;
                SeriesPoint[] pointsToUpdate1 = new SeriesPoint[interval];
                for (int i = 0; i < interval; i++) {
                    pointsToUpdate1[i] = new SeriesPoint(argument, value1);
                    argument = argument.AddMilliseconds(1);
                    UpdateValues();
                }
                DateTime minDate = argument.AddSeconds(-TimeInterval);
                int pointsToRemoveCount = 0;
                foreach (SeriesPoint point in series1.Points)
                    if (point.DateTimeArgument < minDate)
                        pointsToRemoveCount++;
                if (pointsToRemoveCount < series1.Points.Count)
                    pointsToRemoveCount--;
                series1.Points.AddRange(pointsToUpdate1);
                if (pointsToRemoveCount > 0) {
                    series1.Points.RemoveRange(0, pointsToRemoveCount);
                }
                if (AxisXRange != null) {
                    AxisXRange.SetMinMaxValues(minDate, argument);
                }

            }

        }
    }

    The chart from our sample program has a lot of jaggedy lines because the values are randomly generated. Real data may or may not produce an especially varied chart; it just depends on the data. The demo in Listing 2 uses stock quotes—the last trade price—from Yahoo! Finance. The example sends a quote request to Yahoo! every 1,000 milliseconds and updates the series points with the last trade price. (You can try this quoting system by typing in a URL similar to http://download.finance.yahoo.com/q?s=dell.) The request is sent from the Windows application using an HttpWebRequest, which means you will have to be online to run the demo, and it will help if the markets are open.

    To create the sample

    1. Drop a v2009 volume 3 ChartControl on a WinForm. The wizard will pop up. Pick the Swift Plot chart type and click Finish (see Figure 2)
    2. Click the ChartControl’s smart tag and select Series—see Figure 3—and add a Series item to the Series Collection Editor
    3. In the Series Collection Editor switch to the Properties tab and change the ArgumentScaleType to DateTime (shown in Figure 3)
    4. Close the Series Collection Editor
    5. Click on the SwiftPlotDiagram (center part of the control)
    6. Change DateTimeGridAlignment to Millisecond, DateTimeMeasureUnit to Millisecond, and DateTimeOptions.Format to Custom, using the format string ss:f to display seconds and sub-seconds (see Figure 4)
    7. Add and enable a timer.
    8. Add the code from Listing 2 and run the demo.

    Listing 2: Plotting the last trade price for stocks-quotes obtained from Yahoo! Finance.

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using DevExpress.XtraCharts;
    using System.Diagnostics;
    using System.Net;
    using System.IO;
    using System.Text.RegularExpressions;

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

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        private AxisRange AxisXRange
        {
          get
          {
            SwiftPlotDiagram diagram = chartControl1.Diagram as SwiftPlotDiagram;
            if (diagram != null)
              return diagram.AxisX.Range;
            return null;
          }
        }

        private void Update()
        {
          const string stock1 = "DELL";
          var quote = new { Stock = stock1, Quote = GetQuote(stock1) };
          var range = new { Stock = stock1, Quote = GetRange(stock1) };
          PlotSeries(chartControl1.Series[0], quote.Stock, quote.Quote, range.Quote);
        }

        private char[] charArray = new char[] { '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' };
        private void PlotSeries(Series series, string stock, string quote, string range)
        {
          if (series == null) return;

          series.Name = stock;
          DateTime argument = DateTime.Now;

          string str = quote.Split(',', '-')[1];
          string digits = str.Substring(str.IndexOfAny(charArray), str.IndexOf("</b>") - 4).Trim();

          string[] temp = range.Split('-', '"');
          double low = Convert.ToDouble(temp[1].Trim());
          double high = Convert.ToDouble(temp[2].Trim());

          SwiftPlotDiagram diagram = chartControl1.Diagram as SwiftPlotDiagram;
          diagram.AxisY.Range.MaxValue = high;
          diagram.AxisY.Range.MinValue = low - .5;
          double last = Convert.ToDouble(digits);

          SeriesPoint point = new SeriesPoint(argument, last);
          series.Points.AddRange(new SeriesPoint[]{point});
          argument.AddMilliseconds(1);

          if (series.Points.Count > 200)
            series.Points.RemoveRange(0, 100);
        }
        protected void Timer1_Tick(object sender, EventArgs e)
        {
          Update();
        }

        public string GetQuote(string stock)
        {
          try
          {
            return InnerGetQuote(stock, "l");
          }
          catch (Exception ex)
          {
            Debug.WriteLine(ex.Message);
            return "N/A";
          }
        }

        private string GetRange(string stock)
        {
          try
          {
            return InnerGetQuote(stock, "m");
          }
          catch (Exception ex)
          {
            Debug.WriteLine(ex.Message);
            return "N/A";
          }
        }

        private string InnerGetQuote(string stock, string format)
        {
          string url = @"http://quote.yahoo.com/d/quotes.csv?s={0}&f={1}";
          var request = HttpWebRequest.Create(string.Format(url, stock, format));

          using (var response = request.GetResponse())
          {
            using (var reader = new StreamReader(response.GetResponseStream(),
              Encoding.ASCII))
            {
              return reader.ReadToEnd();
            }
          }
        }

        private void timer1_Tick_1(object sender, EventArgs e)
        {
          Update();
        }
      }
    }

    In summary, the code works works by calling GetRange (trade range) and GetQuote at every timer tick, adjusting the intra-day trading range by setting the SwiftPlotDiagram’s AxisX,RangeMaxValue and AxisX.Range.MinValue and plugging the last trade price into the series.

    A longer explanation is that timer1_Tick calls Update. Update calls GetQuote and GetRange—hard coded for Dell—and calls PlotSeries. GetQuote and GetRange both form a query string that is sent via an HttpWebRequest to yahoo! Finance, passing this data to PlotSeries. PlotSeries sets the series name, parses the quote and range data. The range data adjust the AxisX min and max values, and the quote is placed in the Series Points collection. (After there are more than 200 series points a 100 points are removed.) Things like the string manipulation is necessary because Yahoo! Finance embeds some extra characters and HTML in the return data.

    the min and max range values are adjusted to zoom in because intra-day trade prices tend to change in small amounts. You can also set SwiftPlotDiagram.EnableZooming, which will turn on run-time zooming. Shift click will zoom in, Alt+click will zoom out by a factor of 3 and Ctrl + + and Ctrl + – will zoom in or out by a factor of 20%.

    image
    Figure 2: Select the Swift Plot chart type in the chart wizard.

     

    image
    Figure 3: Set ArgumentScaleType to DateTime.

    image
    Figure 4: Set DateTimeOptions to Custom and use the format string ss:f.

    image
    Figure 5: Dynamically set the range to focus in on small changes (or use the zoom feature).

  • Dynamic Lambda Expressions Don’t Use Name Lookup Rules

    Some of you are tool builders and extenders. You may more frequently write code generators using System.CodeDOM and System.Reflection.Emit, figure out clever ways to use functional construction for LINQ to XML or to write dynamic Lambda expression generators. Sometimes documentation is great or we Google and discover blogs like this one that help us and sometimes we have to figure out things through trial and error. (And, some of you some of you may just like to poke around and see what you can create instead of watching TV.) This quick holiday blog is for those of you—possibly a few—that may be interested in Lambda expressions and dynamic expression trees.

    Things like Lambda expressions can be codified as expression trees. A reason for doing this might be that you want to write an an extender that pulls apart a Lambda expression and convert it into something else. A dynamic Lambda expression is created by invoking functions on the System.Linq.Expressions.Expression class, invoking a function for each element of the Lambda expression that goes into the expression tree. For example,

    (s1, s2) => s1 == s2;

    represents an equality test expression that accepts two strings and test them for equality. Represented as a tree there are two input parameter strings, an equality test, and the input parameters are used to generate a bool return value. If you were to assign this expression to the generic delegate Func then the signature for the delegate would be Func<string, string, bool>. Writing these straight forwardly as an expression one might logically write

    LambdaExpression exp =
      Expression.Lambda<Func<string, string, bool>>(
      Expression.Equal(
      Expression.Parameter(typeof(string), "str1"),
      Expression.Parameter(typeof(string), "str2")),
      new ParameterExpression[] { Expression.Parameter(typeof(string), "str1"),
        Expression.Parameter(typeof(string), "str2")}
      );

    All of the elements are present:

    Expression.Lambda<Func<string, string, bool>> – tags the expression as a Lambda expression with the correct signature

    Expression.Equal – defines the equality test

    Expression.Parameter(typeof(string), "str1") – defines the left-hand-side of the equality test

    Expression.Parameter(typeof(string), "str2") – defines the right-hand-side of the equality test

    and,

    new ParameterExpression[] { Expression.Parameter(typeof(string), "str1"),
        Expression.Parameter(typeof(string), "str2")} – defines the two input arguments whose names match the method body names.

    Unfortunately, if you wrote the expression this way and compiled the expression you would get an InvalidOperationException (see Figure 1) with an error like “variable ‘str1’ of type System.String referenced from scope ‘’, but it is defined. Further, if you used the quick watch to look at the expression exp you would see the two parameters are present in the tree. There is a ‘justification’ for this and the correct code is shown in Listing 1.

    image
    Figure 1: This is a frustrating exception that does not tell you the solution.

    The reason the fragment above does not work is that “Lambda expressions are fully bound and are not in the business of implementing name lookup rules (which may differ from language to language) [Anders Hejlsberg]”. The expression tree treats the name as informational and uses the referenced object to match up parameters and their use in a method body. This literally means you have to use the same object created with the Expression method static calls, such as Expression.Parameter, for the input parameters and their use in the method body. (The CodeDOM actually supplies two separate node constructors for arguments and references, but the Expression builders do not.) Listing 1 shows how the same two ParameterExpression objects are used to identify the method arguments and their use in the equality test.

    In the example the expression tree is constructed, compiled, and invoked. You can use the debugger’s to QuickWatch feature to explore the LambdaExpression object or build and install the sample program ExpressionTreeVisualizer available online to extend Visual Studio and add a visualizer for expression trees. (Check out this link for more on the ExpressionTreeVisualizer http://msdn.microsoft.com/en-us/library/bb397975.aspx.)

    Listing 1: Correctly associating input parameters by using the same object references when building the expression tree.

    using System;
    using System.Linq.Expressions;

    namespace DynamicLambdas
    {
      class Program
      {
        static void Main(string[] args)
        {
          // Lambda expression tree don’t use name lookup rules

          ParameterExpression str1 = Expression.Parameter(typeof(string), "str1");
          ParameterExpression str2 = Expression.Parameter(typeof(string), "str2");

          LambdaExpression exp =
            Expression.Lambda<Func<string, string, bool>>(
            Expression.Equal(
            str1,
            str2),
            new ParameterExpression[] { str1, str2 }
            );
          Func<string, string, bool> compareStrings = (Func<string, string, bool>)exp.Compile();

          string s1 = "devexpress";
          string s2 = "Developer Express";
          Console.WriteLine("'{0}' {1} '{2}'",
            s1, compareStrings(s1, s2) ? "is the same string as" : "is not the same string as", s2);

          Console.ReadLine();

        }
      }
    }

  • PDF Password Security for XtraReports, new in v2009 volume 3

    In the document wars Adobe’s PDF format seems to have won. Because they give away their Adobe Reader for free it is probably the most widely distributed document format and reader except for maybe MS-Word documents. New in XtraReports Suite v2009, volume 3 is the ability to password protect documents exported in the PDF format.

    In short you can print an XtraReport and select various formats. If you select Print and Export from out demos (see Figure 1) or click the File|Export Document from the report previewer (see Figure 2) and pick the PDF format then the PDF Export Options dialog will displayed (see Figure 3). The PDF Export Options dialog has a Password Security input field. Click the ellipses to open the Password Security dialog (see Figure 4).Check Require a password to open the document and provide a password. The user will be required to repeat the password after closing the Password Security dialog (see Figure 6). If you want an additional password for changing the document then check “Restrict editing and printing of the document” and provide an additional password.

    When the user attempts to open a secured document in Adobe Reader the password prompt will be displayed, and the reader will be required to provide a password. The Adobe Reader title will indicated—with the word SECURED—in the title bar when a document is password protected. Programmatically exporting a password report to PDF format couldn’t be easier—design the report, create an instance of the XtraReport object, set the XtraReport.ExportOptions.Pdf.PasswordSecurityOptions.OpenPassword and set this property with the password value (see Listing 1). [Note: if you are going to embed password information in your code then you might want to use a tool like Dotfuscator to obfuscate your code, protecting it from a disassembler.]

    Listing 1: Two lines of code is all it takes to export a password protected PDF report created with XtraReports.

    using System;
    using System.Windows.Forms;

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

        XtraReport1 report = new XtraReport1();
        private void button1_Click(object sender, EventArgs e)
        {
          report.ExportOptions.Pdf.PasswordSecurityOptions.OpenPassword = "password";
          report.ExportToPdf("c:\\temp\\demo.pdf");

        }
      }
    }

     

    XtraReportsDemo
    Figure 1: The Products List demo report exposes the export feature from the Print and Export menu. 

     XtraReportsPreview
    Figure 2: The default previewer supports exporting (and mailing) from the File menu.

    image 
    Figure 3: The PDF Export Options dialog lets you specify a password for tthe PDF document to be exported.

    image
    Figure 4: Check the Require a password to open the document and provide a password in the Document Open Password field.

    image
    Figure 5: For a separate password for editing permissions check Restrict editing and printing of the document and provide an additional password. 

    image
    Figure 6: The user will be asked to confirm the password after you click OK on the Password Security dialog.

    image
    Figure 7: Adobe Reader will prompt the user and require the correct password before opening a password protected document. 

     

    image
    Figure 8: The protected document in Adobe Reader shows the the document is SECURED in the window title bar.

  • Defining Super ToolTips Programmatically and Managing the BackColor

    One pretty old principle in user interface design is to be consistent with your color schemes. The term that was most frequently used is trichromacy, or three colors. The basic idea is stick to three primary colors for the features of your GUIs. However, color in small doses—icons, tool tips, graphics, text highlighting, errors—can be useful in signifying important information or drawing attention to something unusual. And, of course, there are some instances where you may want to deviate from basic color schemes.

    To aid in helping you provide a consistent appearance the DevExpress WinForms controls use the DevExpress.LookAndFeel namespace and the concept of Style and Skin information for WinForms. This applies to the SuperToolTips too. If you don’t know a SuperToolTip behaves like a ToolTip, but permits you to define Title, Content, and Footer and associate graphics with each region. This results in a much richer tool tip experience for the end user—see Figure 1, from the DevExpress help documentation ms-help://MS.VSCC.v90/MS.VSIPCC.v90/DevExpress.NETv9.3/DevExpress.XtraUtils/clsDevExpressUtilsSuperToolTiptopic.htm.

    SuperTooltipSetup_ex
    Figure 1: A SuperToolTip from the DevExpress VS help documentation.

     

    The challenge is that if you elect to deviate from the LookAndFeel applied, for instance you want to change the BackColor of a single component to draw special attention to it, then you need to know that the Appearance.BackColor and Appearance.BackColor2 properties are ignored if a LookAndFeel.Style is being applied. Listing 1 shows you how to programmatically define a SuperToolTip and to get the Appearance.BackColor property to be applied. If you set Appearance.BackColor2 to Color.Empty then you get a solid color for the BackColor; if you provide an actual color for Appearance.BackColor2 then the second color acts as the end color for a gradiently-shaded background color.

    Listing 1: Create a SuperToolTip on the fly and override the LookAndFeel, enabling the BackColor property to be rendered.

          SuperToolTip tooltip = new SuperToolTip();
          // Create a tooltip item that represents a header.
          ToolTipTitleItem titleItem1 = new ToolTipTitleItem();
          titleItem1.Text = "Dynamic ToolTip";

          ToolTipItem item1 = new ToolTipItem();
          item1.Text = "This is the LabelControl Tip";
          // Add the tooltip items to the SuperTooltip.
          tooltip.Items.Add(titleItem1);
          tooltip.Items.Add(item1);
          tooltip.LookAndFeel.Style = DevExpress.LookAndFeel.LookAndFeelStyle.Flat;
          tooltip.Appearance.Options.UseBackColor = true;
          tooltip.Appearance.BackColor = Color.Green;
          //sTooltip1.Appearance.BackColor2 = Color.Empty;
          tooltip.Appearance.BackColor2 = Color.White;
          labelControl1.SuperTip = tooltip;

     

    The code creates a new SuperToolTip object and defines the title item. Next, a ToolTipItem is created and the title and item are added to the SuperToolTip.Items collection. Starting with the line tooltip.LookAndFeel.Style = DevExpress.LookAndFeel.LookAndFeelStyle.Flat the application of the default style is overridden. Picking the Flat style along with Options.UseBackColor = true, and providing background color values the background color settings will come through. Finally the SuperToolTip object is assigned to whatever control—the SuperTip property—should have this particular tip associated with it.

    If you want to uniformly change the LookAndFeel information then place a line of code similar to this one in the Form’s constructor for VB.NET or the Program.cs’s Main function in C#. Remember to add the semi-colon for the C# version.

    DevExpress.LookAndFeel.UserLookAndFeel.Default.SkinName = "Money Twins"

    This blog was posted in response to a question posted in our forums on 12/21 by Jassim Rahma--http://community.devexpress.com/forums/p/84237/288967.aspx#288967—and builds on our extensive help documentation, in this instance Visual Studio help document http://www.devexpress.com/Help/?document=XtraUtils/clsDevExpressUtilsSuperToolTiptopic.htm&levelup=true.

    Happy Holidays!

  • Using Multiple Where Clauses with LINQ

    LINQ, or Language INtegrated Query, is an all purpose query language that is added right in your C# or VB code. You can use LINQ to query collections, SQL DataSets, XML, entities, and LINQ is extensible into other technologies like SharePoint and Active Directory. LINQ is a general purpose query technology without a lot of power and some subtle features. One such feature is the where clause. The where clause is used to filter data a lot like where clauses are used in SQL.

    Where clauses in LINQ can have a single predicate, multiple predicates where each expression is combined with the Boolean And or Boolean Or operator. LINQ also supports multiple where clauses. You can use multiple where clauses in a single LINQ query to break up filtering in pieces. The utility in using multiple where clauses is that you can short circuit query processing, especially processor or IO expensive sub-operations, by adding additional where clauses that will stop processing part of the way through when a filter condition—a where predicate—fails.

    File IO can be system intensive. Suppose you were to use a LINQ query to process information in the file system—reading the files that match a specific search pattern like *.txt and that counts the words in those files. Searching the file system, reading the contents of the matching files, and counting the words each represents a relatively expensive IO operation. You could use a where clause with Boolean operators to perform the filtering in a single where statement, but by splitting the work up with light weight checks early you can reduce the total amount of query processing. (An additional technique that works well here, too, is to use the let clause. Let can be used to assign temporary values that can be stored and processed once in each iteration, but the data can be used multiple times.)

    In Listing 1, the file system is queried for text files. The firs where clause short circuits on empty files. For files with data all of the text is assigned to the temporary range variable content. The variable content is used to obtain the word count per file, and the second where clause us used to filter files that contain more than ten words. Finally, the project—the output data—contains the filename and the word count of each file that passes both tests.

    Listing 1: A LINQ query with multiple where clauses short circuits on empty files—represented by the first where clause.

    Imports System.IO
    Imports System.Text
    Imports Microsoft.VisualBasic.FileIO

    Module Module1

      Sub Main()
        Dim wordCount = From filename In Directory.GetFiles("C:\temp", "*.txt", System.IO.SearchOption.AllDirectories) _
                        Where FileSystem.GetFileInfo(filename).Length > 0 _
                        Let content = File.ReadAllText(filename) _
                        Let words = content.Split(",", ".", ";", ":", "!", ".", " ", "/", "?") _
                        Where words.Length > 10 _
                        Select New With {.File = filename, .WordCount = words.Length}

        For Each item In wordCount
          Console.WriteLine(item)
        Next
        Console.ReadLine()
      End Sub

    End Module

  • XtraRichEdit Bookmarks New in v2009 Volume 3

    New features abound. The XtraRichEdit Suite for v2009 volume 3 contains some new features, including bookmarks, hyperlinks, ruler support, multiple document formats, line numbering and selection drag and drop. This blog post shows you how to use the built-in Bookmark and Hyperlink dialogs and features of the RichEditControl. This is a no code solution and to use it you just need to complete some pretty straight forward steps.

    To reproduce the demo add a Windows Form to a Windows project. Follow these steps:

    1. From the Toolbox expand the DX.9.3: Rich Text Editor tab and drag and drop a RichEditControl onto the form
    2. From the DX.9.3: Navigation & Layout tab of the Toolbox add a BarManager component to the form
    3. Click the RichEditControl’s Tasks menu and click Create Links Bar; this will add the Bookmark and Hyperlink toolbar items to the form and a RichEditBarController component to the component’s section of the form
    4. Run the demo

    Add some text to the RichEditControl at runtime—see Figure 1. Select the text that represents the bookmark area—for example the word Bookmark just above the dialog—and click the Bookmark toolbar button (upper left). Provide a Bookmark name, for example, Bookmark and click Add—see Figure 2. Close the Bookmark dialog. To add a hyperlink in the text to the bookmark click the text for the hyperlink and click the hyperlink button or right-click and select Hyperlink from the RichEditControl’s runtime context menu. Add the text to display—which will be the selected text by default—provide a ScreenTip, in the Link to region select the “Place in this document” radio button and select the Bookmark from the drop down list—see Figure 3. The hyperlink will be visible at the selected location, and pressing Ctrl+Click will jump the focus from the hyperlink to the defined bookmark—see Figure 4.

    image
    Figure 1: Some text and graphics in the RichEditControl.

    image
    Figure 2: Click the Bookmark toolbar button to display the built-in bookmark definition.

    image
    Figure 3: Use the Hyperlink toolbar button or context menu to define a hyperlink in the text that will jump to the user-defined bookmark.

    image
    Figure 4: The finished document with the hyperlink; Ctrl+Click automatically takes the user to the defined bookmark location.

  • Wrapping Column Headers in the WinForms GridControl

    This blog post was written in response to support center topic Q241888: “how to wrap column caption”. There are a couple of quick steps. If you are in a hurry here is the short answer. For detailed instructions read on.

    1. Select the GridControl and click on Run Designer
    2. Select the Appearance Properties
    3. Se the ColumnPanelRowHeight to 50 (pixels for the default font; a little more if you have long headers/column names)
    4. Expand Appearance.HeaderPanel.Options and set UseTextOptions to True
    5. Expand Appearance.HeaderPanel.TextOptions and set Trimming to None or Default. (Default will add an ellipsis {e.g. Custom…} for long words
    6. Expand Appearance.HeaderPanel.TextOptions and set WordWrap to Wrap

    Here is the long and short of it.

    By default the GridControl column row is set to be approximately one row in height. By default long column names are elided {e.g. Custo… for Customer ID} if the form or grid is too small to display the full column name. Real estate on a form is like that. However, if the grid column name is elided then the ToolTip—displayed by hovering—will contain the full column name. If you want to display more of the column name or as much as possible then follow these steps:

    1. Add a GridControl from the Data tab to a Windows Form. (I am using version 9.3.2 for the example)
    2. Click on the GridControl smart tag, click Choose Data Source and pick Add Project Data Source
    3. Configure the Northwind Customers table (or any table you like)
    4. Click Run Designer (see Figure 1)
    5. In the Designer set the ColumnPanelRowHeight (in pixels) to 50—estimate the height in pixels; 50 should be large enough for two rows in the default font
    6. Expand the Appearance.HeaderPanel properties (in the designer)
    7. Using designer expand the sub-property Options (see Figure 2) and set UseTextOptions to True
    8. Expand the Appearance.HeaderPanel.TextOptions property and set Trimming to Default or None and WordWap to Wrap
    9. Close the designer and run the sample

    If you select WordWrap = Wrap and Trimming to Default then the grid will elide long names that don’t fit the column width and wrap shorter words, breaking on words. If you select WordWrap = Wrap and Trimming = None then the grid will wrap words based on individual words. This isn’t always possible, so you get wrapping in the middle of words if necessary.

    Figure 3 shows a combination of wrapped headers, elided words, and wrapping. This is ultimately a necessary step based on how much screen space is available. The code can only use the space available and make a best guess at how to display the headers based on the available space and options you have chosen. The good news is that the Tooltip (see Figure 4) shows the whole header name, and the user (at run-time can) can right-click the column headers and choose Best Fit, Best Fit (all columns), view the Tooltip by hovering the mouse, or expand individual columns. Reasonable length column names with grids using the maximum real estate space should be able to display full column headers. Where you may run into problems is with huge tables or long column names.

    If you want to figure out how to set the ColumnPanelRowHeight to an optimal height, dynamically, then you will need to determine the longest string, calculate the column width (roughly, and it can be changed by the user at runtime) and set the ColumnPanelRowHeight based on the font’s TextHeight. The code is Graphics.MeasureString(string, Font) which returns a SizeF type and the y property contains the string height for that font. Next, split the header by words and you should have an approximation of the number of rows, so height is SizeF.Y * rows. Again, this is a loose approximation and may still not be perfect when the user starts gerrymandering the layout at runtime.

    I’d be interested in hearing about scenarios that necessitate additional solutions or options.

    Happy Holidays!

    image
    Figure 1: Run the designer to help you configure GridControl options.

    image
    Figure 2: The designer will help you in experimenting with grid options.

    image
    Figure 3: Wrapped headers with default trimming.

    image
    Figure 4: Hovering the mouse displays the column header as a tooltip.

  • Right Justify Content in an ASPxComboBox

    This blog is written as a partial response to support ID Q241581—ASPxListBox – Right Justify Column. The example uses an ASPxComboBox, but the same approach should work with the ASPxListBox.

    The original query had a couple of parts: right justify numeric amounts and multi-list column sorting. The latter—supporting sorting by clicking on a column header is not supported. In this instance an ASPxGridView and ASPxListBox will basically provide the same functionality. To justify the data in a multi-column combobox (or listbox) the key is to not justify the first column even because this is using an incremental search. You can justify additional numeric columns using SQL. A couple of possible solutions are provided here.

    Option 1

    The ASPxComboBox has an ItemStyle.HorizontalAlign property. set this property to Right and all items will be displayed to the right. of course, if you want number aligned to the right then this won’t create the desired effect.

    Option 2

    To pad columns to the right embed right justification in the SQL statement. Assume you are selecting the ProductID. ProductName, and UnitPrice from the Northwind.Products table. You can set the column width of the UnitPrice column to 100 pixels and eyeball the width by playing with padding characters. The following SQL statement pads UnitPrice: given 100px width for UnitPrice padding to right for a total length of 20 looks about right in IE8 and Google Chrome. This may produce different results in different browsers, but you may find that it works for you.

    SelectCommand="SELECT ProductID, ProductName, RIGHT(REPLICATE(' ', 20) + CAST(UnitPrice AS varchar(20)), 20) AS UnitPrice
    FROM Products" onselecting="SqlDataSource1_Selecting">

    Option 3

    Implement an onselecting event handler for the SqlDataSource SelectCommand and dynamically write the SQL and adjust the padding based on the font. This may not work in all browsers because fonts may be rendered differently. Even if you find a great pixel-to-character width conversion function some fonts have different widths for each character, resulting in spotty results.

    protected void SqlDataSource1_Selecting(object sender, SqlDataSourceSelectingEventArgs e)
    {
      "SELECT ProductID, ProductName, RIGHT(REPLICATE(' ', {0}) +" +
      "CAST(UnitPrice AS varchar({0})), {0}) AS UnitPrice " +
      "FROM Products";

      double width = ((DevExpress.Web.ASPxEditors.WebColumnBase)
        (ASPxComboBox1.Columns[2])).Width.Value;

      // convert pixels to strength width here

      e.Command.CommandText = string.Format(sql, 20);

    }

    Option 4

    Run a solution and look at the generated source. The class rendered for the UnitPrice is dxeLTM (see Listing 1). This is a default class applied by the DevExpress renderer. (ListBoxItems are rendered as <td> tags with this class applied. Define a embedded style that provides a text-align attribute and add the !important declaration which increases the weight of a style rule. The embedded style in Listing 2 will right-align the UnitPrice.

    Listing 1: How the UnitPrice is rendered as HTML.

    <td class="dxeListBoxItem dxeFTM" style="width:120px;">1</td><td class="dxeListBoxItem dxeTM" style="width:120px;">Chai</td><td class="dxeListBoxItem dxeLTM" style="width:100px;">18.0000</td>

    Listing 2: An embedded style that right aligns the third column by class name, determined by examining the rendered HTML.

    <head id="Head1" runat="server">
        <title></title>

        <style type="text/css">
          .dxeLTM
          {
            text-align:right!important;
            border-color: Red!important;
          }
        </style>

    </head>

    I added the red border so you can see that the style is applied to just the UnitPrice column. Listing 3 contains the ASPX that will let you tinker with the possible solutions, and Listing 4 contains the supporting code-behind. There are some caveats to be applied here:

    Caveat 1: Calculating optimal width based on fonts may not render well due to different character widths in some font families.
    Caveat 2: Don’t right-align the first column using SQL padding because this will disrupt the Incremental Filtering mechanism.
    Caveat 3: Depending on embedded styles and rendered names may break in future releases and additional columns will have different class names. You will have to check the actual class name, especially if you add columns or update your version of DevExpress controls. Document tricky stuff like this carefully and post where everyone can figure out what is going on.

    The question you may have “should I do this”? The answer is you betcha. You can use hacks and kludges—sort of like adding duct tape—if the benefits give you that added cool factor and its the only solution available. However, the presence of physical duct tape is always evident to just about any practitioner, but code-duct-tape sometimes needs to be pointed out so document carefully. I plugged in little features in projects, especially if a customer demands I do so. It generally takes extra care, testing, and some careful documentation, but the customer is always right.

    Unfortunately too much of this kind of thing can blow schedules, complicate testing, result in behavior that is different across browsers, so plan accordingly.

    Listing 3: The ASPX that will let you explore variations on a solution.

    <%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>

    <%@ Register assembly="DevExpress.Web.ASPxEditors.v9.2, Version=9.2.8.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" namespace="DevExpress.Web.ASPxEditors" tagprefix="dxe" %>

    <%@ Register assembly="DevExpress.Web.ASPxGridView.v9.2, Version=9.2.8.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" namespace="DevExpress.Web.ASPxGridView" tagprefix="dxwgv" %>

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    <html xmlns="http://www.w3.org/1999/xhtml">
    <head id="Head1" runat="server">
        <title></title>

        <style type="text/css">
          .dxeLTM
          {
            text-align:right!important;
            border-color: Red!important;
          }
        </style>

    </head>

    <body>

        <form id="form1" runat="server">
        <div>
          <dxe:ASPxComboBox ID="ASPxComboBox1" runat="server" ClientInstanceName="Combo"
            DataSourceID="SqlDataSource1" EnableIncrementalFiltering="True"
            TextField="ProductName" ValueField="ProductID" ValueType="System.Int32"
            >
            <Columns>
              <dxe:ListBoxColumn Caption="ID" FieldName="ProductID" />
              <dxe:ListBoxColumn Caption="Name" FieldName="ProductName" />
              <dxe:ListBoxColumn Caption="Unit Price" FieldName="UnitPrice" Width="100px"  />
            </Columns>
          </dxe:ASPxComboBox>
          <asp:SqlDataSource ID="SqlDataSource1" runat="server"
            ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>"
            SelectCommand="SELECT ProductID, ProductName, RIGHT(REPLICATE(' ', 20) + CAST(UnitPrice AS varchar(20)), 20) AS UnitPrice
            FROM Products" onselecting="SqlDataSource1_Selecting">
          </asp:SqlDataSource>
        </div>
        </form>
    </body>
    </html>

    Listing 4: The code-behind containing the injected SQL based on list box item width. (You will have to provide a conversion forumla for pixels to text-width.)

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Drawing;

    public partial class _Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
        }
        protected void SqlDataSource1_Selecting(object sender, SqlDataSourceSelectingEventArgs e)
        {
          string sql =
            "SELECT ProductID, ProductName, UnitPrice " +
            "FROM Products";

          //"SELECT ProductID, ProductName, RIGHT(REPLICATE(' ', {0}) +" +
          //"CAST(UnitPrice AS varchar({0})), {0}) AS UnitPrice " +
          //"FROM Products";

          double width = ((DevExpress.Web.ASPxEditors.WebColumnBase)
            (ASPxComboBox1.Columns[2])).Width.Value;

          e.Command.CommandText = sql;// string.Format(sql, 20);

        }
    }

  • Which Content Types are Allowed in the ASPxUploadControl by Default

    This post is in response to support question Q241501 “Question Details: Upload default content types”. If you need a short answer then its all types by default. If you want a little holiday reading and to learn a couple of new skills the read the long answer.

    The DevExpress controls are rich controls and some times you have to dig a little for the answer. I spent the better part of the last 2,000 hours writing the Professional DevExpress ASP.NET Controls and haven’t come close to having all of these bits and details available for instant recall (but that’s why our support center is here and why the technical evangelists are here, to help).

    Add an ASPxUploadControl to a Web form. Add a button or use the ASPxUploadControl’s ShowUploadButton property to show the upload button. Define a ClientInstanceName for the ASPxUploadControl—I used uploader—and set the AutoPostBack to False. For the ASPxButton define client-side Click event and call uploader.UploadFile(). The control is ready to go.

    <ClientSideEvents Click="function(s, e) {
                   uploader.UploadFile();
                 }" />

    If you check out the help topic ms-help://MS.VSCC.v90/MS.VSIPCC.v90/DevExpress.NETv9.1/DevExpress.Web/CustomDocument5494.htm you will see that

    If this property (ASPxUploadControl.ValidationSettings.AllowedContentTypes) is not set (that is, it's set to an empty string), an end-user is allowed to upload files of all types. You can recognize the uploaded file's type by reading it from the System.Web.HttpPostedFile.ContentType property accessed via the UploadedFile.PostedFile property in this case.

    if you really want to see what is going on download a copy of Fiddler—at http://www.fiddler2.com/fiddler2/version.asp--and point it at your sample and you can explore what your web server sees. If you are using Cassini—the built-in server instead of IIS then change the URL to use the fiddler filter. For example, to point Fiddler at the demo below run the demo and the change the URL to

    http://ipv4.fiddler:5251/UploadControlOriginalFileName/Default.aspx

    Remember to set the port to whatever port Cassini is using. This is available from the development server which can be displayed from the taskbar in Windows. Using Fiddler for debugging was introduced in Chapter 2 of Professional DevExpress ASP.NET Controls in the section “Enabling Callback Compression”.

    image
    Figure 1: Fiddler is showing you how your web server is seeing interactions as depicted here.

    image
    Figure 2: The development server picks its own port each time it starts.

    Here is some sample ASPX (Listing 1) and the code-behind that will permit you to verify this information.

    Listing 1: Uploading any files is supported by default.

    <%@ Page Language="VB" AutoEventWireup="false" CodeFile="Default.aspx.vb" Inherits="_Default" %>

    <%@ Register assembly="DevExpress.Web.v9.2, Version=9.2.8.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" namespace="DevExpress.Web.ASPxUploadControl" tagprefix="dxuc" %>
    <%@ Register assembly="DevExpress.Web.ASPxEditors.v9.2, Version=9.2.8.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" namespace="DevExpress.Web.ASPxEditors" tagprefix="dxe" %>

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
        <title></title>
    </head>
    <body>
        <form id="form1" runat="server">
        <div>
          <dxuc:ASPxUploadControl ID="ASPxUploadControl1" runat="server" Height="27px"
            Width="208px" ClientInstanceName="uploader">
          </dxuc:ASPxUploadControl>
          <br />
          <dxe:ASPxButton ID="ASPxButton1" runat="server" Text="Upload" AutoPostBack="False">
          <ClientSideEvents Click="function(s, e) {
                    uploader.UploadFile();
                  }" />

          </dxe:ASPxButton>
        </div>
        </form>
    </body>
    </html>

    Listing 2: This code stores whatever kind of file you post on the server.

    Imports System.Diagnostics
    Imports System.IO

    Partial Class _Default
      Inherits System.Web.UI.Page

      Protected Sub ASPxUploadControl1_FileUploadComplete(ByVal sender As Object, _
                                                          ByVal e As DevExpress.Web.ASPxUploadControl.FileUploadCompleteEventArgs) _
                                                          Handles ASPxUploadControl1.FileUploadComplete

        EventLog.WriteEntry("Application", String.Format("Uploaded: {0}", e.UploadedFile.FileName))

        Dim ext As String = Path.GetExtension(e.UploadedFile.FileName)

        Using stream As FileStream = New FileStream( _
          Server.MapPath("~\Data\") + "uploadedfile" + ext, FileMode.CreateNew)

          stream.Write(e.UploadedFile.FileBytes, 0, e.UploadedFile.FileBytes.Length)

        End Using

      End Sub
    End Class

    Happy Holidays!

  • Obtaining the Original Client-side File Name for the ASPxUploadControl

    This post is in response to support issue Q241526=ASPxUpload Client Side Filename. Obtaining the client-side filename is a capability directly supported by the ASPxUploadControl. The relevant event is ASPxUploadControl.FileUploadComplete, and the event argument containing the original file content is DevExpress.Web.ASPxUploadControl.FileUploadCompleteEventArgs, the sup-property UploadedFile (see Figure 1).

    Here is the setup for a basic use of the ASPxUploadControl. The ASPX is contained in Listing 1 and the code-behind (this time in VB) is in Listing 2.

    1. Place an ASPxUploadControl on a web page
    2. Define a value for the ClientInstanceName; I used uploader.
    3. Set the ASPxUploadControl.AutoPostBack property to False
    4. Add the ClientSideEvent Click and define as follows:

    <ClientSideEvents Click="function(s, e) {
                    uploader.UploadFile();
                  }" />

    5. Add an ASPxButton and set the text to upload
    6. Implement the code-behind handler for the ASPxUploadControl.FileUploadComplete event using the properties window event tab
    7. Implement the code-behind as shown in Listing 2

    In the solution the original file name is stored in the event log—which is a form of auditing—and the original file extension is obtained. A FileStream is created and the bytes are written to a mapped folder on the server, in this case Data.

    Variations on this theme are easily implemented. You could stored the file in a database. You could log the original filename in a database. You have the option of displaying the content. See my blog entry Using the ASPxUploadControl and Displaying Image Thumbnails for an example that uploads images and creates thumbnail versions for viewing.

    Happy Holidays!

    image
    Figure 1: The UploadedFile property contains the client-side file information.

    image
    Figure 2: The original filename is stored in the event log as shown here.

    Listing 1: The ASPX for the solution.

    <%@ Page Language="VB" AutoEventWireup="false" CodeFile="Default.aspx.vb" Inherits="_Default" %>

    <%@ Register assembly="DevExpress.Web.v9.2, Version=9.2.8.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" namespace="DevExpress.Web.ASPxUploadControl" tagprefix="dxuc" %>
    <%@ Register assembly="DevExpress.Web.ASPxEditors.v9.2, Version=9.2.8.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" namespace="DevExpress.Web.ASPxEditors" tagprefix="dxe" %>

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
        <title></title>
    </head>
    <body>
        <form id="form1" runat="server">
        <div>
          <dxuc:ASPxUploadControl ID="ASPxUploadControl1" runat="server" Height="27px"
            Width="208px" ClientInstanceName="uploader">
          </dxuc:ASPxUploadControl>
          <br />
          <dxe:ASPxButton ID="ASPxButton1" runat="server" Text="Upload" AutoPostBack="False">
          <ClientSideEvents Click="function(s, e) {
                    uploader.UploadFile();
                  }" />

          </dxe:ASPxButton>
        </div>
        </form>
    </body>
    </html>

    Listing 2: The code-behind for the solution.

    Imports System.Diagnostics
    Imports System.IO

    Partial Class _Default
      Inherits System.Web.UI.Page

      Protected Sub ASPxUploadControl1_FileUploadComplete(ByVal sender As Object, _
                                                          ByVal e As DevExpress.Web.ASPxUploadControl.FileUploadCompleteEventArgs) _
                                                          Handles ASPxUploadControl1.FileUploadComplete

        EventLog.WriteEntry("Application", String.Format("Uploaded: {0}", e.UploadedFile.FileName))

        Dim ext As String = Path.GetExtension(e.UploadedFile.FileName)

        Using stream As FileStream = New FileStream( _
          Server.MapPath("~\Data\") + "uploadedfile" + ext, FileMode.CreateNew)

          stream.Write(e.UploadedFile.FileBytes, 0, e.UploadedFile.FileBytes.Length)

        End Using

      End Sub
    End Class

  • Hiding and Showing a Custom Command Button in ASPxGridView

    There is a query in our knowledgebase about hiding or showing a custom command button in an ASPxGridView ID Q241050. This blog walks you through adding custom command buttons and using the ASPxGridView’s CustomButtonInitialize to hide or show custom command buttons in the grid. the key is to use the multi-state variable DevExpress.Web.ASPxClasses.DefaultBoolean.False instead of just False for example to set the visibility of the button. Here are the steps for adding the custom command button, followed by the ASPX in Listing 1 and the code-behind in Listing 2. (You can use any datasource you want for the example; I used the Northwind database.)

    1. Add an ASPxGridView to a web form
    2. Use the smart tags option to choose a data source and configure the Northwind.Customers table
    3. After the database is configured click the smart tags menu a second time and click the Columns editor
    4. Click the Add an item button and select Command Column. (I Moved the Command Column to the 0th index position.)
    5. Add some buttons to the column; use the <CustomButtons> tag to define custom buttons. Here is the markup and its shown in place in Listing 1.

    <dxwgv:GridViewCommandColumn VisibleIndex="0">
                     <EditButton Visible="True" />
                     <NewButton Visible="True" />
                     <CustomButtons>
                         <dxwgv:GridViewCommandColumnCustomButton Text="Create a Copy" ID="Copy" />
                     </CustomButtons>

    6. Open the Properties window for the ASPxGridView and generate a CustomButtonInitialize event for the grid
    7. Add the code-behind shown in Listing 2 to set the visibility based on some criteria.

    In the example all CustomerIDs starting with “A” change the command button with the ID=”Copy” visibility property to False. The key is to use the three-state DevExpress.Web.ASPxClasses.DefaultBoolean type to set the Visible property of the event arg named e. You can use any criteria you want for the solution just remember to use DefaultBoolean to set the e.Visible property.

    image
    Figure 1: Add a custom command button using the Columns editor form.

    Listing 1: The ASPX for the sample, containing custom command buttons.

    <%@ Page Language="VB" AutoEventWireup="false" CodeFile="Default.aspx.vb" Inherits="_Default" %>

    <%@ Register assembly="DevExpress.Web.ASPxGridView.v9.2, Version=9.2.8.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" namespace="DevExpress.Web.ASPxGridView" tagprefix="dxwgv" %>
    <%@ Register assembly="DevExpress.Web.ASPxEditors.v9.2, Version=9.2.8.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" namespace="DevExpress.Web.ASPxEditors" tagprefix="dxe" %>

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
        <title></title>
    </head>
    <body>
        <form id="form1" runat="server">
        <div>
          <dxwgv:ASPxGridView ID="ASPxGridView1" runat="server"
            AutoGenerateColumns="False" DataSourceID="SqlDataSource1"
            KeyFieldName="CustomerID">
            <Columns>
                <dxwgv:GridViewCommandColumn VisibleIndex="0">
                     <EditButton Visible="True" />
                     <NewButton Visible="True" />
                     <CustomButtons>
                         <dxwgv:GridViewCommandColumnCustomButton Text="Create a Copy" ID="Copy" />
                     </CustomButtons>
              </dxwgv:GridViewCommandColumn>
              <dxwgv:GridViewDataTextColumn FieldName="CustomerID" ReadOnly="True"
                VisibleIndex="1">
              </dxwgv:GridViewDataTextColumn>
              <dxwgv:GridViewDataTextColumn FieldName="CompanyName" VisibleIndex="2">
              </dxwgv:GridViewDataTextColumn>
              <dxwgv:GridViewDataTextColumn FieldName="ContactName" VisibleIndex="3">
              </dxwgv:GridViewDataTextColumn>
              <dxwgv:GridViewDataTextColumn FieldName="ContactTitle" VisibleIndex="4">
              </dxwgv:GridViewDataTextColumn>
              <dxwgv:GridViewDataTextColumn FieldName="Address" VisibleIndex="5">
              </dxwgv:GridViewDataTextColumn>
              <dxwgv:GridViewDataTextColumn FieldName="City" VisibleIndex="6">
              </dxwgv:GridViewDataTextColumn>
              <dxwgv:GridViewDataTextColumn FieldName="Region" VisibleIndex="7">
              </dxwgv:GridViewDataTextColumn>
              <dxwgv:GridViewDataTextColumn FieldName="PostalCode" VisibleIndex="8">
              </dxwgv:GridViewDataTextColumn>
              <dxwgv:GridViewDataTextColumn FieldName="Country" VisibleIndex="9">
              </dxwgv:GridViewDataTextColumn>
              <dxwgv:GridViewDataTextColumn FieldName="Phone" VisibleIndex="10">
              </dxwgv:GridViewDataTextColumn>
              <dxwgv:GridViewDataTextColumn FieldName="Fax" VisibleIndex="11">
              </dxwgv:GridViewDataTextColumn>
            </Columns>
          </dxwgv:ASPxGridView>
          <asp:SqlDataSource ID="SqlDataSource1" runat="server"
            ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>"
            SelectCommand="SELECT * FROM [Customers]"></asp:SqlDataSource>
        </div>
        </form>
    </body>
    </html>

    Listing 2: The code-behind sets the Visible property; in this example hiding buttons for CustomerIDs start with “A”.

    Partial Class _Default
      Inherits System.Web.UI.Page

      Protected Sub ASPxGridView1_CustomButtonInitialize(ByVal sender As Object, _
        ByVal e As DevExpress.Web.ASPxGridView.ASPxGridViewCustomButtonEventArgs) _
        Handles ASPxGridView1.CustomButtonInitialize

        Dim val As Object = ASPxGridView1.GetRowValues(e.VisibleIndex, _
                                                       "CustomerID")

        If (val(0) = "A") Then
          If (e.ButtonID = "Copy") Then
            e.Visible = DevExpress.Web.ASPxClasses.DefaultBoolean.False

          End If
        End If

      End Sub

      Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _
        Handles Me.Load

      End Sub
    End Class

  • Variation: Filtering with Multiple Columns in an ASPxComboBox in an ASPxGridView

    There are a lot of ways to skin a cat. In the prior blog--http://community.devexpress.com/blogs/paulk/archive/2009/12/15/filtering-with-multi-column-aspxcombobox-in-aspxgridview.aspx--with a similar title I implemented some JavaScript that searched all of the items and all of the columns in a multi-column ASPxComboBox tucked away in an ASPxGridView EditItem Template. The prior post just used a single character typed and cycled through the items with that first character. Here is a revision—all of the steps for constructing the filtering capability can be used from the prior post—that allows the user to type in multiple characters and then searches for a match based on those characters.

    Assume for example that you have an ID column and a Name column. In this solution add KeyUp and KeyDown events for the ASPxComboBox. On KeyDown the event calls Clear which clears the stored input text for the first key and stores all alpha-numeric key values in a client-side variable. A regular expression is used to test the input characters. In the KeyUp event handler a timer is set to call CompareText after 500 milliseconds. CompareText clears the timer and then invokes the search—FindValidItem. FindValidItem cycles through all of the items in the ASPxComboBox and all of the column data individually. The input text stored in the local field currentText is compared to the first n-characters in the ASPxComboBox column data. If a match is found then that item in the ASPxComboBox is selected and the local field is set back to an empty string in preparation for the next search.

    Listing 1 contains the definition of the client-side attributes to set for the ASPxComboBox, and Listing 2 contains the JavaScript that stores the input and performs the search.

    Listing 1: Add these attributes and code to the ASPxComboBox.

    <ClientSideEvents KeyUp="function(s,e) {
                       timeout = window.setTimeout(CompareText, 500);
                       }"
                       KeyDown="function(s,e){ Clear(s,e); }"
                   />

    Listing 2: Add this script to the script block section of the ASPX page.

    var currentText = "";
    var timeout = null;
    var clear = false;

    function CompareText() {
      // clear the timer and perfomer the search
      if (timeout != null)
        window.clearTimeout(timeout);
      FindValidItem(Combo);
    }

    function Clear(s, e) {
      // clear the stored text and input editor
      if (!clear) {
        clear = true;
        currentText = "";
        Combo.GetInputElement().value = "";
        Combo.SetSelectedIndex(-1);
      }
      // store alphanumeric characters
      var ch = String.fromCharCode(e.htmlEvent.keyCode);
      var regex = /^([a-zA-Z0-9]+)$/;
      if(regex.test(ch))
        currentText = currentText + ch;
    }

    function FindValidItem(Combo) {
      // don't search an empty string
      if (currentText == "") return;
      // check all items
      for (var i = 0; i < Combo.GetItemCount(); i++)
        // check all columns
        for (var j = 0; j < Combo.GetItem(i).texts.length; j++) {
        var str = Combo.GetItem(i).texts[j];
        if (currentText.toUpperCase() == str.substr(0, currentText.length).toUpperCase()) {
          if (Combo.GetSelectedIndex() == i) continue;
          Combo.SetSelectedIndex(i);
          Combo.SetCaretPosition(Combo.GetItem(i).text.length);
          // cleanup stored characters
          currentText = "";
          clear = false;
          return;
        }
      }
      currentText = "";
    }   

More Posts Next page »
Copyright © 1998-2010 Developer Express Inc.
ALL RIGHTS RESERVED