C#: Most Recently Used List


I wanted to add a button to one of the applications that I wrote that listed the last ten reports that a user has viewed so they could quickly and easily access recently viewed reports. I found a lot of information about how to create a Most Recently Used (MRU) list of files that have been opened but I couldn’t find anything built into the .NET Framework to create a custom list of objects where the most recently used item was at the beginning of the list.

I’m sure many of you are saying to yourself, “Why didn’t he just use a stack?”. The reason I didn’t use the already built Stack class is because I needed to implement two other features:

  • Restrict the size of the set
  • Ensure no duplicate items exist in the set

The Stack class found in the System.Collections namespace doesn’t enforce these two properties inherently and thus the need for a custom collection.

Note: I could have optimized this collection a bit more by adding each item to the end of the list and keeping a pointer to the location of the true first item but given the fact that I only will be storing a few items in the set, I didn’t take the time to implement it that way. I leave that optimization to you.

/// <summary>
/// Stores a set of items with the most recently added at item the front and the 
/// least recently added item at the end
/// </summary>
public class RecentSet<T> : IEnumerable<T>
{
    private List<T> _list;
    private int _size = -1;

    /// <summary>
    /// Creates a new RecentSet object.
    /// </summary>
    public RecentSet()
    {
        _list = new List<T>();            
    }
     /// <summary>
    /// Creates a new RecentSet object with a fixed size. The return set may be smaller than
    /// the specified size but it will never be larger
    /// </summary>
    /// <param name="size">The maximum size of the set</param>
    public RecentSet(int size)
    {
        _list = new List<T>();
        _size = size;
    }
 
    /// <summary>
    /// Creates a new RecentSet object initializing it with the indicated items. Note: 
    /// the initialized RecentSet will be in the order of parameter items.  If items are {1, 2, 3, 4},
    /// iterating through RecentSet will result in a list of {1, 2, 3, 4} not {4, 3, 2, 1}        
    /// </summary>
    public RecentSet(IEnumerable<T> items)
    {
        _list = items.ToList();
    }

    /// <summary>
    /// Creates a new RecentSet object with a fixed size initializing it with the indicated items. Note: 
    /// the initialized RecentSet will be in the order of parameter items.  If items are {1, 2, 3, 4},
    /// iterating through RecentSet will result in a list of {1, 2, 3, 4} not {4, 3, 2, 1}        
    /// </summary>
    public RecentSet(int size, IEnumerable<T> items)
    {
        _list = items.ToList();
        _size = size;

        TrimList();
    }

    /// <summary>
    /// Adds an item to the RecentSet
    /// </summary>
    public void Add(T item)
    {
        // If the item is already in the set, remove it
        int i = _list.IndexOf(item);
        if (i > -1)
            _list.RemoveAt(i);

        // Add the item to the front of the list.
        _list.Insert(0, item);

        TrimList();
    }

    public int Count
    {
        get { return _list.Count; }
    }

    private void TrimList()
    {
        // If there is a set size, make sure the set only contains that many elements
        if (_size != -1)
            while (_list.Count > _size)
                _list.RemoveAt(_list.Count - 1);
    }

    /// <summary>
    /// Returns the set in the form of a List
    /// </summary>
    public List<T> ToList()
    {
        return _list;
    }

    #region IEnumerable<T> Members

     public IEnumerator<T> GetEnumerator()
    {           
        return _list.GetEnumerator();
    }

    #endregion

    #region IEnumerable Members

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return _list.GetEnumerator();
    }

    #endregion
}

C#: MD5 Hash Method


I needed to create an MD5 hash of a string today that could be sent along with a web service request to a third party application. I’ve never done this before using .NET but it was exteremely simple! Here is the code:

/// <summary>
/// Gets the MD5 hash value for the passed in value parameter
/// </summary>
/// <param name="value">The string value to hash</param>
/// <param name="upperCase">Indicates whether or not the return value should be upper case</param>
/// <returns>The MD5 hash of the value parameter</returns>
public static string GetMD5Hash(string value, bool upperCase)
{
    // Instantiate new MD5 Service Provider to perform the hash
    System.Security.Cryptography.MD5CryptoServiceProvider md5ServiceProdivder = new System.Security.Cryptography.MD5CryptoServiceProvider();
            
     // Get a byte array representing the value to be hashed and hash it
    byte[] data = System.Text.Encoding.ASCII.GetBytes(value);
    data = md5ServiceProdivder.ComputeHash(data);

    // Get the hashed string value
    StringBuilder hashedValue = new StringBuilder();
    for (int i = 0; i < data.Length; i++)
        hashedValue.Append(data[i].ToString("x2"));

    // Return the string in all caps if desired
    if (upperCase)
        return hashedValue.ToString().ToUpper();

    return hashedValue.ToString();
}

C#: Word Mail Merge Bypass Header Record Delimiters Popup


One of the applications that I have written allows the user to create custom form letters in Microsoft Word. The user is able to create a Word document and populate it with mail merge fields and then I programmatically create the mail merge data source and perform the merge. Everything worked fantastic except when the user created a form letter that only contained one merge field. Under this circumstance, every time they attempted to perform the merge from within my application, Microsoft Word would pop up with the Header Record Delimiters window, shown below, asking the user to specify how the fields and the records were delimited.

With more than one field, Microsoft Word was able to automatically detect the tab that was inserted between fields and use it as the delimiter but with only one field, it could not. The solution to this problem is actually so simple that I felt a bit retarded that I didn’t think of it earlier. All you have to do is add another merge field in your data file so that there is never only one merge field! The second column of data in your merge file will not correspond to any merge field in the document but Word doesn’t complain about it and it is able to automatically detect the tab between fields.

So, if you only have one merge field, just setup your data file to look something like the following:

MergeField1 [DUMMY_FIELD]
Data1 NULL
Data2 NULL
Data3 NULL

Note: You obviously can put whatever you want for the data in the DUMMY_FIELD column. I just put the string NULL for kicks.

LINQ: Flatten a List of Lists


Today I needed to flatten a list of lists and really didn’t want write out a nested loop to iterate through each element in each list. After some searching I found that LINQ provides a very elegant solution to this problem. LINQ is a powerful set of extensions that were added to the .NET Framework that allows you to query and manipulate sets of data. The SelectMany extension method solves this problem very easily as shown below.

List<List<string>> listOfLists = new List<List<string>>();
listOfLists.Add(new List<string>() { "a", "b", "c" });
listOfLists.Add(new List<string>() { "d", "e", "f" });
listOfLists.Add(new List<string>() { "g", "h", "i" });

var flattenedList = listOfLists.SelectMany(x => x);

foreach (string s in flattenedList)
     Console.Write(s + " ");

// Output
// -----
// a b c d e f g h i

Justin Etheridge has a great blog post with awesome diagrams of how the SelectMany extension method works. Check it out for more information.

Posted in LINQ. Tags: , . 3 Comments »

ASP.NET AJAX ModalPopup to Confirm Delete


As mentioned in my last post I have been working with the ASP.NET AJAX Toolkit lately and more specifically the ModalPopupExtender control. On one of the pages I have a DataGridView with a delete ImageButton and I wanted to use the ModalPopup control to confirm that the user really wanted to delete the row from the DataGridView. This question has been asked many times on many different forums but the most common response seems to be, “That is completely overkill. Just use the confirm() javascript method”. While this may be a true statement, the confirm() method has some drawbacks one of which is that you can’t customize the window to the look and feel of your website.

Confirming an action is a very common scenario, one that will occur on many pages and even multiple times on one page. Such a situation obviously calls for a custom control and that is exactly what I did. After creating the custom control, which will be explained shortly, adding a confirmation box is accomplished by inserting the following markup into your page:

<enso:ConfirmBox runat="server" 
     Title="Confirm Delete" 
     Message="Are you sure you want to delete this product key?" 
     TargetControlId="btnDeleteProductKey"  />

This one simple line will create a confirmation box that can be completely customized to fit the look and feel of your site as shown below.

Now for the explanation.

To create a new user control:

  1. In the Solution Explorer of Visual Studio right click the project name, select Add, and click New Item…
  2. Select Web User Control
  3. Enter ConfirmBox.ascx in the Name textbox
  4. Copy and paste the following code into the ConfirmBox.ascx window.
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="ConfirmBox.ascx.cs" Inherits="TestWebApp.ConfirmBox" %>
<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="asp" %>

<script runat="server">
    public string Title { set { this.lblTitle.Text = value; } }
    public string Message { set { this.lblMessage.Text = value; } }
    public string TargetControlId { set { this.popupConfirmBox.TargetControlID = this.btnConfirm.TargetControlID = value; } }
    public int Width { set { this.panelConfirmBox.Width = Unit.Pixel(value); } }

    protected void Page_Load(object sender, EventArgs e)
    {
        this.btnPopupClose.OnClientClick = "$find('" + popupConfirmBox.ClientID + "').hide();";
    }
</script>
  
<asp:Panel ID="panelConfirmBox" runat="server" CssClass="modalPopup" Width="400px" Style="display:none;">
    <h5><asp:Label ID="lblTitle" runat="server" Text="[Title]"></asp:Label></h5>
    <div class="closeButton">
        <asp:LinkButton ID="btnPopupClose" runat="server" >x</asp:LinkButton>
    </div>    
    <div class="clearBoth"></div>
    
    <br />  
    <div class="modalMessage">
        <asp:Label ID="lblMessage" runat="server" Text="[Message]"></asp:Label>
    </div>
    <br />
    
    <div class="modalButtons">
        <asp:Button ID="btnYes" runat="server" Text="Yes" /> &nbsp; <asp:Button ID="btnNo" runat="server" Text="No" />
    </div>
</asp:Panel>                    
<asp:ModalPopupExtender Id="popupConfirmBox" runat="server" PopupControlID="panelConfirmBox" BackgroundCssClass="modalBackground" CancelControlID="btnNo" OkControlId="btnYes" ></asp:ModalPopupExtender>
<asp:ConfirmButtonExtender ID="btnConfirm" runat="server" DisplayModalPopupID="popupConfirmBox"> </asp:ConfirmButtonExtender>

You will need to change the Inherits property in the first line of the file to match the name of your application. Additionally, you will need to make sure you already have a reference to the AjaxControlToolkit.dll in your project.

Property Definitions:

  • Title – Sets the title of the confirmation box
  • Message – Sets the message of the confirmation box
  • TargetControlId – Sets the id of the control that should display the confirmation box when clicked
  • Width – Sets the width confirmation box

The only non-appearance property is the TargetControlId which as in all ASP.NET AJAX Toolkit controls is the id of the control that is to be extended. In our case, setting this property will set the TargetControlId property of both the ModalPopupExtender and the ConfirmButtonExtender. For these to work in conjunction, they must both have the same value for the TargetControlId property.

If you read my previous post, ASP.NET AJAX Toolkit ModalPopup Tips, I mentioned a few tricks I learned while using the ModalPopupExtender; one of which was how to add multiple cancel buttons. This can be accomplished with a bit of client-side javascript placed in the OnClientClick event of a LinkButton. In the Page_Load event of the control we are adding this javascript to the ‘x’ in the top right corner of the window. Note that we are accessing the ClientID property of the ModualPopupExtender as when the page is rendered, the control will no longer have an Id of ‘popupConfirmationBox’ but rather something like ”ctl00_contentMain_dataGridViewProductKeys_ctl02_ctl01_popupConfirmBox’.

There is nothing fancy to the configuration of the ModalPopupExtender. We simply set the panel that will appear when the TargetControlId is clicked and the cancel and ok control ids.

Next we introduce another ASP.NET AJAX Toolkit control called the ConfirmButtonExtender. As the name indicates, it is used to confirm an action taken by the user. The plain vanilla version of this control simply uses the javascript function confirm() as discussed earlier in this post. But, the developers were nice enough to allow us to specify a ModalPopup control to be displayed instead. We simply set the DisplayModalPopupID property to the Id of our ModalPopupExtender to let the control know to display the ModalPopup instead.

Before we can start using this control on any page we desire, we need to make a simple modification to the web.config to ensure the control is visible throughout the site. Open your web.config and find the pages section which is a child of the system.web node. Add something similar to the last line that appears in the controls section below. Note that the tagPrefix and tagName can be anything you desire but just make them something you can remember as they will be used to identify your control.

<system.web>
    <pages>
      <controls>
        <add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
        <add tagPrefix="asp" namespace="System.Web.UI.WebControls" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
        <add tagPrefix="enso" src="~/ConfirmBox.ascx" tagName="ConfirmBox"  />
      </controls>
    </pages>
</system.web>

Save the web.config file and build your project to make the control visible while using intellisense. Now navigate to the page on which you want to place the ConfirmBox control. In my case I wanted to add it to a DataGridView delete button. To do this I added a TemplateField to the DataGridView using the following markup.

<asp:TemplateField ShowHeader="false">
     <ItemTemplate>
          <asp:ImageButton ID="btnDeleteProductKey" runat="server" CommandName="Delete" ImageUrl="~/Styles/images/X.png" />
          <enso:ConfirmBox runat="server" Title="Confirm Delete" Message="Are you sure you want to delete this product key?" TargetControlId="btnDeleteProductKey"  />
     </ItemTemplate>
</asp:TemplateField>

Lastly you will need to make sure you have a ScriptManager on the page somewhere and a link to the stylesheet used to format your confirmation box. Click here for the stylesheet and here for the image used in my implementation.

Simply add the markup for the new control you created, specify the Title, Message, and TargetControlId and you are done! You have now implemented a confirmation box that you can placee anywhere on any page without having to duplicate code over and over!

Follow

Get every new post delivered to your Inbox.

Join 66 other followers