Friday 21 October 2011

Axialis Software Ribbon & Toolbar Stock Icons - Basic Set Offer

are offering 30%-off discount to my readers for their new Windows Ribbon & Toolbars Stock Icons Basic Set, they are provided in sizes 48x48, 32x32, 24x42, 16x16, different states (normal, hot, disabled) and RGBA / RGB color depths. Called "Basic", the first set contains 1108 unique icons. Including all sizes and derivatives, more that 29,000 images are provided.

Get the offer here

This offer will last 30 days up to Nov 20:

These look great and will be ideal for Visual Studio LightSwitch 2011 toolbars

Share this post :

Monday 8 August 2011

Visual Studio LightSwitch 2011 Arrives (Yes, I know it arrived a while ago!)

Well I have finally had the time to indulge in Visual Studio LightSwitch 2011 RTM (henceforth LS), and it brings to Silverlight development what Dynamic Data (henceforth DD) brought to Web Forms with bells on.

It’s faster to develop an app with LS than it is with DD and that’s a fact. I decided to develop an app that I had need to run my little company but had been putting off for the last two years for lack of time. You know how a builder always has work to do on his own home never completed, well no more with LS I built it in just over one day see Figure 1.

My App in one day

Figure 1 – My App in one day

All I have to add is the Invoice report which I am going to use DevExpressXtraReports for Light Switch

And the good news is that most of the Silverlight control venders are jumping in there with everything from How to add our controls to custom installs for LS;

And there are tones of starter kits on Visual Studio Gallery.

The forums are very active;

Blogs I’ve found so far;

I don’t expect this is an exhaustive list but it’s very encouraging and I will be joining the band waggon and build some of my own extensions and of course blog about it here.

P.S. I have not left DD this is just some new fun that will certainly compliment DD

P.P.S. I still have loads of stuff to put on NuGet so keep an eye on me here.

Wednesday 13 July 2011

Monday 4 July 2011

Custom Field Templates On NuGet

In this article I am going to build some custom field templates;

  1. File Upload field template
  2. Image Upload field template
  3. Image Select field template
  4. Date picker field template

CustomFieldTemplates

Figure 1 – Our proposed field templates

So first of all we need specify what we want each to do.

File Upload Field Template Requirements
  • Be able to upload and file type (PDF, ZIP, DOC, XLS etc.).
  • Restrict types to be uploaded.
  • access uploaded file via hyperlink (enable or disable this).
  • Store in folder in site.
  • Replace old file if new file replaces it during edit (you would need to maintain deleting items using some business logic as the field template is not called during the deletion process).
Image Upload Field Template Requirements
  • Be able to upload and image type (jpeg, gif, png, etc.).
  • Restrict which image types can be uploaded (i.e. restricts to jpeg or png etc.).
  • Store in folder in site.
  • Replace old file if new file replaces it during edit (you would need to maintain deleting items using some business logic as the field template is not called during the deletion process).
Image Select Field Template Requirements
  • Choose from a set of images .
  • Use images a folder in site.
Ajax Date picker
  • Select a date using a popup date picker from the Ajax Control toolkit.

At another time we will do one based on the jQuery UI date picker.

Building the Field Templates

We could just pass the information we want using the UIHint attribute and it’s control parameters collection but there is no design time intellisense, so we will build an attribute that all three field template can use. We will call it the Upload attribute it will need to store the following information;

  • Folder to store images
  • Folder to store file icons
  • Size to display Icon or Image (Height and Width)
  • Whether to display an hyperlink or not.
  • Acceptable file type for image and file upload.
  • Image extension for icons.
The Upload Attribute
/// <summary>
/// Upload attribute defines values for the upload
/// field templates
/// </summary>
/// <remarks></remarks>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public class UploadAttribute : Attribute
{
    #region Properties
    /// <summary>
    /// Gets or sets the height to display the image, 
    /// if only one of the two dimensions are specified
    /// then the aspect ration will be retained.
    /// </summary>
    /// <value>The height.</value>
    /// <remarks></remarks>
    public int Height { get; set; }

    /// <summary>
    /// Gets or sets the width to display the image, 
    /// if only one of the two dimensions are specified
    /// then the aspect ration will be retained.
    /// </summary>
    /// <value>The width.</value>
    /// <remarks></remarks>
    public int Width { get; set; }

    /// <summary>
    /// Gets or sets the uploads folder.
    /// </summary>
    /// <value>The uploads folder.</value>
    /// <remarks></remarks>
    public String UploadFolder { get; set; }

    /// <summary>
    /// Gets or sets the icons folder.
    /// </summary>
    /// <value>The icons folder.</value>
    /// <remarks></remarks>
    public String ImagesFolder { get; set; }

    /// <summary>
    /// Gets or sets a value indicating whether [show hyperlink].
    /// </summary>
    /// <value><c>true</c> if [show hyperlink]; otherwise, <c>false</c>.</value>
    /// <remarks></remarks>
    public Boolean ShowHyperlink { get; set; }

    /// <summary>
    /// Gets or sets the file types.
    /// </summary>
    /// <value>The file types.</value>
    /// <remarks></remarks>
    public String[] FileTypes { get; set; }

    /// <summary>
    /// Gets or sets the image extension.
    /// </summary>
    /// <value>The image extension.</value>
    /// <remarks></remarks>
    public String ImageExtension { get; set; }
    #endregion

    /// <summary>
    /// Initializes a new instance of the <see cref="T:System.Attribute"/> class
    /// setting the Height to 50 and the folder to "~/images".
    /// </summary>
    /// <remarks></remarks>
    public UploadAttribute()
    {
        // set a default value of 50 and constrain aspect ratio.
        Height = 50;
        // set default images folder
        UploadFolder = "~/images";
    }
}

Listing 1 – Upload Attribute

The attribute in Listing 1 handles all the properties we want so we can build each of the field templates.

Upload File Field Template

To start off expand the DynamicData folder to see the FieldTemplate folder and right click and choose Add->New Item… you can now choose “Dynamic Data Field” (this will create a new field template pretty much the same as the Text field template) enter the name “UploadFile” and you will see your new blank field template.

Dynamic Data Field

Figure 2 – Dynamic Data Field

I have decided that I want nothing to show apart form the File Upload control until there is a file uploaded

WithoutData WithData
Figure 3 – Insert mode Figure 4 – Edit mode
In Figures 2 & 3 we can see both modes we only see an image and link/text when data is already present. So I have put the mark-up for this in a place holder.
<asp:PlaceHolder 
    ID="PlaceHolder1" 
    runat="server" 
    Visible="false">
    <asp:Image ID="Image1" runat="server" />&nbsp;
    <asp:Label ID="Label1" runat="server" Text="<%# FieldValueString %>" />
    <asp:HyperLink ID="HyperLink1" runat="server"><%# FieldValueString %></asp:HyperLink>
</asp:PlaceHolder><br />
<asp:FileUpload 
    ID="FileUpload1" 
    runat="server" 
    CssClass="DDTextBox" 
    Width="150px"/>

Listing 2 – Upload File field template mark-up

Next we remove the current validators and replace with a custom validator.

<asp:CustomValidator 
    ID="CustomValidator1" 
    runat="server" 
    ControlToValidate="FileUpload1" 
    onservervalidate="CustomValidator1_ServerValidate">
</asp:CustomValidator>

Listing 4 – Custom validator

In the code behind we need to setup the validator and the fileupload

protected void Page_Load(object sender, EventArgs e)
{
    CustomValidator1.Text = "*";
    SetUpValidator(CustomValidator1);

    // get attributes
    uploadAttribute = MetadataAttributes.GetAttribute<UploadAttribute>();
    if (uploadAttribute == null)
    {
        // no attribute thrw an error
        throw new InvalidOperationException("FileUpload must have valid uploadAttribute applied");
    }
    else
    {
        // add tooltip describing what file types can be uploaded.
        FileUpload1.ToolTip = String.Format("Upload {0} files", uploadAttribute.FileTypes.ToCsvString());
    }
}

Listing 5 – Page_Load

Here we setup the custom validator and get the Upload attribute, if the attribute is missing we throw an InvalidOperationException and if the attribute is there we set the FileUpload’s tooltip to a helpful message.

protected void CustomValidator1_ServerValidate(object source, ServerValidateEventArgs args)
{
    if (FileUpload1.HasFile)
    {
        // get files name
        var fileName = FileUpload1.FileName;

        // get files extension without the dot
        String fileExtension = FileUpload1.GetFileExtension();

        // check file has an allowed file extension
        if (!uploadAttribute.FileTypes.Contains(fileExtension))
        {
            args.IsValid = false;
            CustomValidator1.ErrorMessage = String.Format("{0} is not a valid upload file type (only {1} are supported).",
                FileUpload1.FileName, 
                uploadAttribute.FileTypes.ToCsvString());
        }
    }
    else if (Column.IsRequired && String.IsNullOrEmpty(Label1.Text))
    {
        args.IsValid = false;
        CustomValidator1.ErrorMessage = Column.RequiredErrorMessage;
    }
}

Listing 6 – CustomValidator1_ServerValidate method

Listing 6 is the Custom validator method here all we are concerned with is making sure we have a valid file to upload, so we check the file type via its file extension and if it matched one from out Upload attribute the file can be uploaded but if not we invalidate the page and add a custom error to our Custom validator. Also if the field is a required field then we check to see if there is a file present either from an edit or from the FileUpload control.

protected override void OnDataBinding(EventArgs e)
{
    base.OnDataBinding(e);

    //check if field has a value
    if (FieldValue == null)
        return;

    // when there is already a value in the FieldValue
    // then show the icon and label/hyperlink
    PlaceHolder1.Visible = true;

    // get the file extension
    String extension = FieldValueString.GetFileExtension();

    if (uploadAttribute.ShowHyperlink)
    {
        Label1.Visible = false;
        // open in new window
        HyperLink1.Target = "_blank";
        HyperLink1.Text = FieldValueString.GetFileNameTitle();
        HyperLink1.NavigateUrl = VirtualPathUtility.AppendTrailingSlash(uploadAttribute.UploadFolder) + FieldValueString;
    }
    else
    {
        HyperLink1.Visible = false;
        Label1.Text = FieldValueString;
    }

    // show the icon
    if (!String.IsNullOrEmpty(extension))
    {
        // set the file type image
        if (!String.IsNullOrEmpty(uploadAttribute.ImagesFolder))
        {
            // get file type image from folder specified in
            Image1.ImageUrl = String.Format("{0}{1}.{2}",
                VirtualPathUtility.AppendTrailingSlash(uploadAttribute.ImagesFolder),
                extension,
                uploadAttribute.ImageExtension);
        }

        Image1.AlternateText = extension + " file";

        // set width
        if (uploadAttribute.Width > 0)
            Image1.Width = uploadAttribute.Width;

        // set height
        if (uploadAttribute.Height > 0)
            Image1.Height = uploadAttribute.Height;
    }
    else
    {
        // if file has no extension then hide image
        Image1.Visible = false;
    }
}

Listing 7 – OnDataBinding event

In the OnDataBinding event Listing 7 we first of all check to see if the FieldValue is null if it is we just return. If it’s not null then we show the place holder. New we have to determine if we are showing an Hyperlink or a Label for the name of the file. And lastly we check to see if we can show a icon to represent the uploaded file type.

Note: The OnDataBinding event is the only place where we have access to the FieldValue in the field template code behind.
protected override void ExtractValues(IOrderedDictionary dictionary)
{
    // make sure file is valid
    if (FileUpload1.HasFile && Page.IsValid)
    {
        // make sure we have the folder to upload the file to
        var uploadFolder = Server.MapPath(VirtualPathUtility.AppendTrailingSlash(uploadAttribute.UploadFolder));
        if(!Directory.Exists(uploadFolder))
            Directory.CreateDirectory(uploadFolder);

        // upload the file
        FileUpload1.SaveAs(uploadFolder + FileUpload1.FileName);

        // update the field with the filename
        dictionary[Column.Name] = ConvertEditedValue(FileUpload1.FileName);
    }
}

Listing 8 – ExtractValues method

We make sure we have a file to upload and the there was no error from the custom validator, check that the upload folder exists and create it if not. We then save the file to the upload folder and update the data field.

Upload Image Field Template
// show the uploaded image
Image1.ImageUrl = String.Format("{0}{1}", 
    VirtualPathUtility.AppendTrailingSlash(uploadAttribute.UploadFolder), 
    FieldValueString);

// add alternate text
Image1.AlternateText = FieldValueString.GetFileNameTitle();

Listing 9 – OnDataBinding event

There only a few minor differences between UploadImage and UploadFile and they are in the section that displays the icon, in the UploadImage instead of displaying an Icon representing the file type we display the actual image.

Select Image Field Template

Here we are using a RadioButtonList and most of the work is done in the Page_Load Listing 10

protected void Page_Load(object sender, EventArgs e)
{
    // get attributes
    var uploadAttribute = MetadataAttributes.GetAttribute<UploadAttribute>();
    if (uploadAttribute == null)
        throw new InvalidOperationException("FileUpload must have valid uploadAttribute applied");

    SetUpValidator(RequiredFieldValidator1);
    SetUpValidator(DynamicValidator1);

    // if no images folder return
    var dirInfo = new DirectoryInfo(Server.MapPath(uploadAttribute.ImagesFolder));
    if (!dirInfo.Exists)
    {
        RadioButtonList1.Visible = false;
        return;
    }

    if (RadioButtonList1.Items.Count == 0)
    {
        // get a list of images in the ImageUrlAttribute folder
        var imagesFolder = ResolveUrl(uploadAttribute.ImagesFolder);
        var files = dirInfo.GetFiles(String.Format("*.{0}", uploadAttribute.ImageExtension));

        foreach (FileInfo file in files)
        {
            // size image to uploadAttribute
            var imgString = new StringBuilder();

            imgString.Append(
                String.Format("<img src='{0}' alt='{1}' ",
                    imagesFolder + file.Name,
                    file.Name.GetFileNameTitle()
               ));

            if (uploadAttribute.Width > 0)
                imgString.Append(String.Format("width='{0}' ", uploadAttribute.Width));

            if (uploadAttribute.Height > 0)
                imgString.Append(String.Format("height='{0}' ", uploadAttribute.Height));

            imgString.Append(" />");

            // embed image in the radio button
            var listItem = new ListItem(imgString.ToString(), file.Name);
            listItem.Attributes.Add("title", file.Name.GetFileNameTitle());

            this.RadioButtonList1.Items.Add(listItem);
        }
    }
}

Listing 10 – Page_Load

Here we get a list of file filtered by the file extension supplied by the Upload attribute the we populate the RadioButtonList with this list. it get a little clever as we add some html mark-up into the Text value of each ListItem added to the RadioButtonList showing the Image to choice.

Note: Setting the images folder up you will need to place your selection of images for the user to choose from into the folder and name them appropriately as the file name will be used for the tool tip and be stored in the DB as the value for the selection.
Date Picker  Field Template

All I have done here is add an Ajax Control Toolkit CalendarExtender to the page and set it up.

<asp:TextBox 
    ID="TextBox1" 
    runat="server" 
    CssClass="DDTextBox" 
    Text="<%# FieldValueEditString %>" 
    Columns="20">
</asp:TextBox>
<ajaxToolkit:CalendarExtender 
    ID="TextBox1_CalendarExtender" 
    runat="server" 
    Enabled="True"
    TargetControlID="TextBox1">
</ajaxToolkit:CalendarExtender>

Listing 11 – Date with Calendar Extender

private readonly static String DATE_FORMAT = CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern

protected void Page_Load(object sender, EventArgs e)
{
    TextBox1_CalendarExtender.Format = DATE_FORMAT;
    TextBox1.ToolTip = Column.Description;

    SetUpValidator(RequiredFieldValidator1);
    SetUpValidator(RegularExpressionValidator1);
    SetUpValidator(DynamicValidator1);
    SetUpCustomValidator(DateValidator);
}

Listign 12 – Page_Load

All I’ve had to do is make sure the CalendarExtender matches the localised date time format, we now get a nice data picker.

Sample Metadata
[MetadataTypeAttribute(typeof(File.FileMetadata))]
public partial class File
{
    internal sealed class FileMetadata
    {
        public int ID { get; set; }
        public string Description { get; set; }

        [Upload(
            FileTypes = new String[] { "doc", "xls" },
            UploadFolder = "~/FileUploads/",
            ImagesFolder = "~/images/",
            ShowHyperlink = true,
            ImageExtension = "png",
            Height = 30)]
        [UIHint("UploadFile")]
        public string FileName { get; set; }

        [Upload(
            FileTypes = new String[] { "png", "jpg", "jpeg" },
            UploadFolder = "~/ImageUploads/",
            ShowHyperlink = true,
            ImageExtension = "png",
            Height = 30)]
        [UIHint("UploadImage")]
        public string ImageName { get; set; }

        [Upload(
            ImagesFolder = "~/ImageSelection/",
            ImageExtension = "png",
            Height = 30)]
        [UIHint("SelectImage")]
        public string Selection { get; set; }

        [DataType(DataType.Date)]
        public DateTime DateCreated { get; set; }

    }
}

Listing 13 – sample metadata

Download NuGet Package

Dynamic Data Custom Field Templates - 1.0

Tuesday 28 June 2011

Autocomplete ForeignKey Field Template Now on NuGet

I have just added the ForeignKey field template based on the old Futures Autocomplete filter to NuGet at NotAClue.DynamicData.AutocompleteForeignKey 

NotAClue.DynamicData.AutocompleteForeignKeySample

Autocomplete in action

All you need is to add a UIHint attribute to the to set the field template,

Note: only works on Foreign Key columns

you can als0 set the number of characters you need to type before autocomplete kicks in.

[UIHint("Autocomplete", null, "MinimumPrefixLength",2)]
public Supplier Supplier { get; set; }

By setting the MinimumPrefixLength in the UIHint’s ControlParameters.

Monday 27 June 2011

My Classic Cascading Filters and Field Templates on NuGet

I’ve just released my classic Cascading Filters and Field Templates on NuGet for ease of install supports both Entity Framework and Linq to SQL.

DynamicDataCascade100x100

Dynamic Data Filters and Field Templates

Sample Metadata
[MetadataTypeAttribute(typeof(Vehicle.VehicleMetadata))]
public partial class Vehicle
{
    internal sealed class VehicleMetadata
    {
        public int Id { get; set; }
        public int ManufacturerId { get; set; }
        public int VehicleTypeId { get; set; }
        public int ModelId { get; set; }
        public int StyleId { get; set; }

        [Display(Order = 0)]
        public String Name { get; set; }

        /// <summary>
        /// Note for cascade to work correctly
        /// fields must be in cascade order Parent->Child
        /// </summary>
        [FilterUIHint("CascadingForeignKey")]
        [UIHint("CascadingForeignKey")]
        [Filter(Order = 0)]
        [Display(Order = 1)]
        public Manufacturer Manufacturer { get; set; }

        [FilterUIHint("CascadingForeignKey")]
        [UIHint("CascadingForeignKey")]
        [Cascade("Manufacturer")]
        [Filter(Order = 1)]
        [Display(Order = 2)]
        public VehicleType VehicleType { get; set; }

        [FilterUIHint("CascadingForeignKey")]
        [UIHint("CascadingForeignKey")]
        [Cascade("VehicleType")]
        [Filter(Order = 2)]
        [Display(Order = 3)]
        public Model Model { get; set; }

        [FilterUIHint("CascadingForeignKey")]
        [UIHint("CascadingForeignKey")]
        [Cascade("Model")]
        [Filter(Order = 3)]
        [Display(Order = 4)]
        public Style Style { get; set; }

    }
}
Note: For cascading to work each child must follow in order Parent->Child note the Ordering of both filters and fields to facilitate the cascade.

Next up are hierarchical cascade field templates

Thursday 23 June 2011

Links to Sample Down :(

It would appear that Live.com have done some reorganisation of my SkyDrive for me Disappointed smile and now ALL the links to my samples are broken, you can see my public folder here Public

Sorry for this but it is out of my control.

Anyway on a positive note I plan to have all my old samples update to .Net 4 and on NuGet as we go forward, I will post a pole with a list of proposed samples to get a vote what people want most.

Wednesday 22 June 2011

Filter NuGet Package updated

Thirteen Dynamic Data Custom Filters has been updated to Dynamic Data 15 Custom Filters and the PackageId is now “DynamicData.CustomFilters”, makes more sense than “DynamicData.ThirteenFilters” especially it now had 15 filters instead of 13.

I have added

  • GreaterThanOrEqual
  • LessThanOrEqual
Sample Metadata
[MetadataTypeAttribute(typeof(Product.ProductMetadata))]
public partial class Product
{
    internal sealed class ProductMetadata
    {
        [FilterUIHint("MultiForeignKey")]
        public Category Category { get; set; }

        public Nullable<int> CategoryID { get; set; }

        public bool Discontinued { get; set; }

        public EntityCollection<Order_Detail> Order_Details { get; set; }

        public int ProductID { get; set; }

        public string ProductName { get; set; }

        public string QuantityPerUnit { get; set; }

        public Nullable<short> ReorderLevel { get; set; }

        public Supplier Supplier { get; set; }

        public Nullable<int> SupplierID { get; set; }

        [FilterUIHint("GreaterThanOrEqual")]
        public Nullable<decimal> UnitPrice { get; set; }

        [FilterUIHint("LessThanOrEqual")]
        public Nullable<short> UnitsInStock { get; set; }

        public Nullable<short> UnitsOnOrder { get; set; }
    }
}

Much fun I expect there will be more soon.

Tuesday 21 June 2011

More NuGet

I have just put up the the first instalment of my Dynamic Data Extensions this version (V1.0) we have

  • HideColumnIn attribute see A New Way To Do Column Generation in Dynamic Data 4
  • ShowColumnOnlyIn attribute does what it says on the tin shows column in particular page templates
  • Filter attribute (I have only implemented the Order property as yet) this allows you to set the filter order.

Dynamic Data Extensions

Dynamic Data Extensions

Search for DynamicData.Extensions on NuGet

Example Metadata

[MetadataTypeAttribute(typeof(Order.OrderMetadata))]
public partial class Order
{
    internal sealed class OrderMetadata
    {
        public string CustomerID { get; set; }
        public Nullable<int> EmployeeID { get; set; }
        public Nullable<int> ShipVia { get; set; }

        [ShowColumnOnlyIn(PageTemplate.List)]
        [ScaffoldColumn(true)]
        public int OrderID { get; set; }

        public Nullable<DateTime> OrderDate { get; set; }

        [HideColumnIn(PageTemplate.List | PageTemplate.Insert)]
        public Nullable<DateTime> RequiredDate { get; set; }

        [HideColumnIn(PageTemplate.List | PageTemplate.Insert)]
        public Nullable<DateTime> ShippedDate { get; set; }
        public Nullable<decimal> Freight { get; set; }
        public string ShipName { get; set; }
        public string ShipAddress { get; set; }
        public string ShipCity { get; set; }
        public string ShipCountry { get; set; }
        public string ShipRegion { get; set; }
        public string ShipPostalCode { get; set; }

        [Filter(Order=2)]
        public Customer Customer { get; set; }
        [Filter(Order = 1)]
        public Employee Employee { get; set; }
        [Filter(Order = 3)]
        public Shipper Shipper { get; set; }

        public EntityCollection<Order_Detail> Order_Details { get; set; }
    }
}

Here you can see all three custom attributes in action.

  • The HideColumnInAttribute hides columns in page templates (note the use of the ‘|’ OR operator to allows multiple pages to be selected.
  • The ShowColumnOnlyInAttribute does the opposite and only shows columns in selected pages.
  • The FilterAttribute here sets the order to display the filters on the list page (Note there are other properties on Filter that are not yet implemented).

Next up on NuGet are both of my original cascade and my hierarchical cascading field templates.

Friday 10 June 2011

A Second NuGet Package for Dynamic Data

Dynamic Data 15 Custom Filters is now on NuGet  this package add thirteen filters to your Web Application Project see Five Cool Filters for Dynamic Data 4 I have added some more filters to that list;

  • AutoComplete – is ForeignKey filter but pre-filters results by what users has typed in.
  • Contains – equivalent to T-SQL LIKE ‘%text%’
  • DateFrom – All Dates greater than or equal to the selected date.
  • DateRange – A range of dates
  • DateTo – Less than or equal to the selected date.
  • EndsWith – Ends with entered text.
  • GreaterThan – Greater than entered value
  • LessThan – Less than entered value
  • MultiForeignKey – allows multiple ForeignKey values to be selected.
  • Range – a range of values
  • StartWith – Starts with entered text.
  • Where – is equal to entered text
  • WhereDropdownList – a dropdown list of values from column to filter by.

This has a few more exciting bits that NuGet offers Dependencies and Framework Assembly References

Dependencies, here this package there is dependency upon Ajax Control Toolkit

<dependencies>
    <dependency id="AjaxControlToolkit" version="4.1.50508" />
</dependencies>

Framework Assembly References – is requirement for an assembly reference from the GAC

<frameworkAssemblies>
    <frameworkAssembly assemblyName="System.Data.Entity" targetFramework="net40" />
</frameworkAssemblies>

This give great flexibility without resorting to PowerShell.

NuGet truly is awesome!

NuGet for Dynamic Data – First Package

I have just added my first NuGet package, and you may no know what NuGet is? so what is NuGet it’s a Package management add on for Visual Studio 2010 and above (maybe more as it is integrated with Windows PowerShell).

Install NuGet

Figure 1 – Install NuGet

See Installing NuGet and then you will need NuGet.exe Command Line and or NuGet Package Explorer v1.1 this is the GUI version (and unlike Scott Hanselman I like the GUI and try to avoid the DOS as much as possible I’m with Phil Haack on that see NuGet In Depth: Empowering Open Source on the .NET Platform on Channel9) and can do a lot of what the command line tool does.

You can get loads on information on NuGet Docs.

OK so I’m going to assume your all setup and ready to go.

Creating the Package

NuGet has some Package Conventions and we should follow them (I’ll not explain them just go and have a read as documentation can be updated from time to time) for out package lets first of all look at what we want to package.

For this Package I want to package up Scott Hunter’s old Sample for Displaying Images from the Database using Dynamic Data (DBImage) and it’s update Sample for Displaying Images Updated + Screencast note the latest version of this is in the old Dynamic Data Futures VS2008 SP1 RTM for DD 3.51 SP1.

Embedded image field template

Figure 2 – Embedded image field template

This consists of several parts;

  • Read-Only field template
  • Edit field template
  • Image Format attribute
  • Linq extension methods
  • Image http handler.
  • Web.Config settings

I have decided to keep things simple for out first NuGet Package and all the above will go into one package some of the above could be shared and as such we could spilt it between several packages and have dependencies but we will keep it simple for now.

so to build our package there are some things we need to know like where do we put our files;

Package folder structure

Figure 3 – Simple Package folder structure

As you can see I have broken the folder structure into three parts, the first part is quite simple, it’s the readme file which will give basic information about the package.

Content (files to be copied on install)

Here you can have three types of file;

The “.transform” files transform web.config or app.config files by adding/merging their content with the project’s configuration file.

While the “.pp” files are files that are transformed as they are copied to the project in the same folder structure they have in the package. These files are a little more complex than the configuration files, as they have Visual Studio project properties in the code and these are used to transform the code files during install ((see Configuration File and Source Code Transformations and the Specifying Source Code Transformations section for more info).

Lib Folder

This folder contains any DLLs that are required, not that the “net40” subfolder this indicates that the DLL supports .Net 4 you could have several version that support different versions of the .Net framework here we will just support .Net 4.

Next

Note this is just the basic of where you need to put files for your package to work.

now we have out files ready in a folder structure we need to create our “nuspec” file, this is an XML file containing details (the manifest) about the package you can manually create this or you can use NuGet Package Explorer to create this see Using A GUI (Package Explorer) to build packages once created you can manually edit the “nuspec” file manually if you like.

You then place your “nuspec” file in the root of the package folder structure or if you follow Using A GUI (Package Explorer) to build packages you can add them directly.

Once done you just publish and it works!

Next steps

The real power come with PowerShell scripts see Scaffolding – ASP.NET, NuGet, Entity Framework Code First and More with Steve Sanderson he shows what you can really do.

I am going to add more complex post here as I discover new and interesting feature of NuGet.

Thursday 31 March 2011

Custom Entity Templates – Dynamic Data 4

In Dynamic Data 4 we have Entity Templates these are great for customizing layout of tables (see Walkthrough: Customizing Table Layout Using Entity Templates).  At the moment you will have to create a Custom Entity template for each table, but not only that you will need to create a set of three (Template, Template_Edit and Template_Insert) and that can be useful to have different layout for each view.

But what if you want something different from the Default, (see Grouping Fields on Details, Edit and Insert with Dynamic Data 4, VS2010 and .Net 4.0 RC1 my first custom Entity Template which then had to replace the Default and also had to have all three defined) but not on all your tables just on a selected few?

I was inspired by Brian Pritchard’s post on the forum: How to reduce code duplication when using Entity Template's for only having to create a single template that would switch between Read-Only, Edit and Insert modes.

I want to go a little further:

  1. Override the Default Entity Template with some sort of UIHint attribute.
  2. An only have to specify one template to keep things DRY
  3. Detect if an Edit or Insert version of a template exists and use that.

So now we know out goal, let’s layout the ingredients for this recipe:

  1. An attribute to allow us to change the default Entity Template for any given Table.
  2. Custom entity factory to allow us to override normal entity behaviour like Brian Pritchard’s.
  3. A Custom dynamic Entity Template.

The Attribute

I thought I would be able to use UIHint at class level but alas we have to hand craft our own, I want some of the same feature of UIHint specifically the Control Parameters collection so that we can pass extra information into our custom Entity Templates with out creating a plethora extra attribute each specific to it’s one Entity Template. (I’ve used the Control Parameters collection before in Dynamic Data Custom Field Template – Values List, Take 2.

The Attribute is relatively straight forward, the only complication is the BuildControlParametersDictionary method which takes the Object array passed in using the params key word into a Key, Value Dictionary with some validation. Note we have also set this attribute to be only useable at Class level.

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class EntityUIHintAttribute : Attribute
{
    private IDictionary _controlParameters;

    public IDictionary ControlParameters
    {
        get { return this._controlParameters; }
    }

    /// 
    /// Gets or sets the UI hint.
    /// 
    /// The UI hint.
    public String UIHint { get; private set; }

    public EntityUIHintAttribute(string uiHint) : this(uiHint, new object[0]) { }

    public EntityUIHintAttribute(string uiHint, params object[] controlParameters)
    {
        UIHint = uiHint;
        _controlParameters = BuildControlParametersDictionary(controlParameters);
    }

    public override object TypeId
    {
        get { return this; }
    }

    private IDictionary BuildControlParametersDictionary(object[] objArray)
    {
        IDictionary dictionary = new Dictionary();
        if ((objArray != null) && (objArray.Length != 0))
        {
            if ((objArray.Length % 2) != 0)
                throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Need even number of control parameters.", new object[0]));

            for (int i = 0; i < objArray.Length; i += 2)
            {
                object obj2 = objArray[i];
                object obj3 = objArray[i + 1];
                if (obj2 == null)
                    throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Control parameter key is null.", new object[] { i }));

                string key = obj2 as string;
                if (key == null)
                    throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Control parameter key is not a string.", new object[] { i, objArray[i].ToString() }));

                if (dictionary.ContainsKey(key))
                    throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Control parameter key occurs more than once.", new object[] { i, key }));

                dictionary[key] = obj3;
            }
        }
        return dictionary;
    }
}

Listing 1 – EntityUIHintAttribute

Now we need a method to use this attribute to change the default Entity Template, this factory need to do two things:

  1. Change the Default Entity Template based on out new EntityUIHint attribute.
  2. Intercept the template mode so that single Entity Template can be used with out having to have three versions.
public class AdvancedEntityTemplateFactory : System.Web.DynamicData.EntityTemplateFactory
{
    public override string BuildEntityTemplateVirtualPath(string templateName, DataBoundControlMode mode)
    {
        var path = base.BuildEntityTemplateVirtualPath(templateName, mode);
        var editPath = base.BuildEntityTemplateVirtualPath(templateName, DataBoundControlMode.Edit);;
        var defaultPath = base.BuildEntityTemplateVirtualPath(templateName, DataBoundControlMode.ReadOnly); ;

        if (File.Exists(HttpContext.Current.Server.MapPath(path)))
            return path;

        if (mode == DataBoundControlMode.Insert && File.Exists(HttpContext.Current.Server.MapPath(editPath)))
            return editPath;

        if (mode != DataBoundControlMode.ReadOnly && File.Exists(HttpContext.Current.Server.MapPath(defaultPath)))
            return defaultPath;

        return path;
    }

    public override EntityTemplateUserControl CreateEntityTemplate(MetaTable table, DataBoundControlMode mode, string uiHint)
    {
        var et = table.GetAttribute();
        if (et != null && !String.IsNullOrEmpty(et.UIHint))
            return base.CreateEntityTemplate(table, mode, et.UIHint);

        return base.CreateEntityTemplate(table, mode, uiHint);
    }

    public override string GetEntityTemplateVirtualPath(MetaTable table, DataBoundControlMode mode, string uiHint)
    {
        var et = table.GetAttribute();
        if (et != null && !String.IsNullOrEmpty(et.UIHint))
            return base.GetEntityTemplateVirtualPath(table, mode, et.UIHint);

        return base.GetEntityTemplateVirtualPath(table, mode, uiHint);
    }
}

Listing 2 – AdvancedEntityTemplateFactory

Listing 1 shows us out AdvancedEntityTemplateFactory, we fulfil task 1. in the methods CreateEntityTemplate and GetEntityTemplateVirtualPath where we check for the presence of a EntityUIHintAttribute and if we find one then set the name of the template to the UIHint property.

Task 2. is dealt with in the BuildEntityTemplateVirtualPath where we check to see if the file exists, if so we just return the path as is, otherwise we strip out the  _Edit or _Insert from the path and return.

The last thing we need is to wire up the AdvancedEntityTemplateFactory in Global.asax.cs

DefaultModel.EntityTemplateFactory = new AdvancedEntityTemplateFactory();

just before the RegisterContext in RegisterRoutes method.

The Custom Entity Template

Multi Column Entity Template

Figure 1 – Multi Column Entity Template

This entity template will be a multi column temp[late designed to give you a little more screen for your money Big Grin I have decided to pass the main parameters in via the EntityUIHint attribute’s Control Parameters, in Figure 2 you can see them in pairs.

Control Parameters

Figure 2 – Control Parameters

“Columns”, 3 sets the number of column the MultiColumn entity template will show.

The next two pairs are the Title and Field CSS classes.

I’ve also decided to add the ability for some columns to span more then one cell in the table.

[AttributeUsage(AttributeTargets.Property)]
public class MultiColumnAttribute : Attribute
{
    /// 
    /// Gets or sets the column span.
    /// 
    /// The column span.
    public int ColumnSpan { get; private set; }

    public static MultiColumnAttribute Default = new MultiColumnAttribute();

    public MultiColumnAttribute() 
    { 
        ColumnSpan = 1;
    }

    public MultiColumnAttribute(int columnSpan)
    {
        ColumnSpan = columnSpan;
    }
}

Listing 3 – MultiColumnAttribute

The use of Default for when we use the DefaultIfEmpty method in Linq (see my Writing Attributes and Extension Methods for Dynamic Data more info) this allows us to get an attribute even if one is not specified so now with thi sline of code

var totalNoOfCells = metaColumns.Select(c => c.GetAttributeOrDefault<MultiColumnAttribute>().ColumnSpan).Sum();

we can get the total number of cell required with some columns having a span of more than on column see Figure 1.

Finally our Multi Column entity template is completed in Listing 4

public partial class MultiColumnEntityTemplate : System.Web.DynamicData.EntityTemplateUserControl
{
    private const string COLUMNS = "Columns";
    private const string TITLE_CSS_CLASS = "TitleCssClass";
    private const string FIELD_CSS_CLASS = "FieldCssClass";

    protected override void OnLoad(EventArgs e)
    {
        // get columns from table
        var metaColumns = Table.GetScaffoldColumns(Mode, ContainerType).ToList();

        // do not render any HTML table if there are no columns returned
        if (metaColumns.Count == 0)
            return;

        // default the HTML table columns and CSS class names
        int columns = 2;
        String titleCssClass = String.Empty;
        String fieldCssClass = String.Empty;

        // Get the CssClass for the title & Field from the attribute
        var entityUHint = Table.GetAttribute();
        if (entityUHint != null)
        {
            if (entityUHint.ControlParameters.Keys.Contains(COLUMNS))
                columns = (int)entityUHint.ControlParameters[COLUMNS];
            if (entityUHint.ControlParameters.Keys.Contains(TITLE_CSS_CLASS))
                titleCssClass = entityUHint.ControlParameters[TITLE_CSS_CLASS].ToString();
            if (entityUHint.ControlParameters.Keys.Contains(FIELD_CSS_CLASS))
                fieldCssClass = entityUHint.ControlParameters[FIELD_CSS_CLASS].ToString();
        }

        // start in the left column
        int col = 0;

        // create the header & data cells
        var headerRow = new HtmlTableRow();
        if (!String.IsNullOrEmpty(titleCssClass))
            headerRow.Attributes.Add("class", titleCssClass);
        var dataRow = new HtmlTableRow();
        if (!String.IsNullOrEmpty(fieldCssClass))
            dataRow.Attributes.Add("class", fieldCssClass);

        // step through each of the columns to be added to the table
        foreach (var metaColumn in metaColumns)
        {
            // get the MultiColumn attribute for the column
            var multiColumn = metaColumn.GetAttributeOrDefault();
            if (multiColumn.ColumnSpan > columns)
                throw new InvalidOperationException(String.Format("MultiColumn attribute specifies that this 
                    field occupies {0} columns, but the EntityUIHint attribute for the class only allocates {1} 
                    columns in the HTML table.", multiColumn.ColumnSpan, columns));

            // check if there are sufficient columns left in the current row
            if (col + multiColumn.ColumnSpan > columns)
            {
                // save this header row
                this.Controls.Add(headerRow);
                headerRow = new HtmlTableRow();
                if (!String.IsNullOrEmpty(titleCssClass))
                    headerRow.Attributes.Add("class", titleCssClass);

                // save this data row
                this.Controls.Add(dataRow);
                dataRow = new HtmlTableRow();
                if (!String.IsNullOrEmpty(fieldCssClass))
                    dataRow.Attributes.Add("class", fieldCssClass);

                // need to start a new row
                col = 0;
            }

            // add the header cell
            var th = new HtmlTableCell();
            var label = new Label();
            label.Text = metaColumn.DisplayName;
            //if (Mode != System.Web.UI.WebControls.DataBoundControlMode.ReadOnly)
            //    label.PreRender += Label_PreRender;

            th.InnerText = metaColumn.DisplayName;
            if (multiColumn.ColumnSpan > 1)
                th.ColSpan = multiColumn.ColumnSpan;
            headerRow.Cells.Add(th);

            // add the data cell
            var td = new HtmlTableCell();
            var dynamicControl = new DynamicControl(Mode);
            dynamicControl.DataField = metaColumn.Name;
            dynamicControl.ValidationGroup = this.ValidationGroup;

            td.Controls.Add(dynamicControl);
            if (multiColumn.ColumnSpan > 1)
                td.ColSpan = multiColumn.ColumnSpan;
            dataRow.Cells.Add(td);

            // record how many columns we have used
            col += multiColumn.ColumnSpan;
        }
        this.Controls.Add(headerRow);
        this.Controls.Add(dataRow);
    }
}

Listing 4 – MultiColumnEntityTemplate

Updated: MultiColumnEntityTemplate updated by Phil Wigglesworth who kindly found a bug and fixed it, the bug was shown when there were no enough columns left on a row, this caused a crash. So thanks again to Phil.
Also we will need some styles to make out new Multi Column entity template look good.

TR.SmallTitle
{
    background-color: #F7F7FF;
}
TR.SmallTitle TD
{
    font-size: 0.8em !important;
    font-weight: bold;
    background-color: #F7F7FF;
    padding: 2px !important;
}

Listing 5 – CSS styles

Hope this expand your use of Entity Templates.

P.S. I’ll do an updated version of the Grouping entity template.

Download

Sunday 16 January 2011

Setting the Initial Sort Order – Dynamic Data

This is just a quick note on a post I made on the Dynamic Data Forum to answer a question where setting the DisplayColumnAttribute should set the default sort order of the GridView on the List page but didn’t. Here’s how I solved it with a hint or two from Marcin Dobosz.

[MetadataType(typeof(Orders_Metadata ))]
[DisplayColumn("OrderID", "OrderID", true)]
public partial class Orders
{
    public class Orders_Metadata
    {       
        // ...
    }
}

Listing 1 – Example Metadata

Assuming you have a table with the DisplayColumnAttribute set you could put this on your List page: 

table = GridDataSource.GetTable();

// set default sort
if (!IsPostBack && table.SortColumn != null)
    GridView1.Sort(table.SortColumn.Name, table.SortDescending ? SortDirection.Descending : SortDirection.Ascending);

Listing 2 – Add to Page_Init (.Net 3.5)

// set default sort
if (!IsPostBack && table.SortColumn != null)
{
    var order = new OrderByExpression()
    {
        DataField = table.SortColumn.Name,
        Direction = table.SortDescending ? SortDirection.Descending : SortDirection.Ascending,
    };
    GridQueryExtender.Expressions.Add(order);
}

Listing 3 – Add to Page_Init (.Net 4)

public static T GetAttribute<T>(this MetaTable table) where T : Attribute
{
    return table.Attributes.OfType<T>().FirstOrDefault();
}

Listing 3 – you will also need this extension method see Writing Attributes and Extension Methods for Dynamic Data

Add your DisplayColumnAttribute (see Listing 1) to the table you want sorted (note: it must have the second string constant even if it’s the same name, as the second string it the one that the sort is done on, and the third value if true causes the sort to be descending). Then in Listing 2 you get the attribute using the extension method from Listing 3 and apply the relevant sort.

This is here mainly so I can find it again! HappyWizard

Thursday 13 January 2011

Adding a Multi-Column Search to the Default List page in Dynamic Data (UPDATED)

This will extend the default filters that Dynamic Data provides by adding a free text search box the will search multiple text fields on the same page. I have done this in a few projects now as one off but after this post Custom filters & searches within DD in Visual Web Developer 2010 on the Dynamic Data Forum I decided to document it here.

Multi Column Search

Figure 1 – Multi Column Search

Here in Figure 1 you can see the multi column search box, in this example the search is on FistName, LastName, Address, City and Country, it will even allow search across entities e.g. on Orders Employee.LastName etc. which make it very flexible.

Things we will need to do

  • Attribute to tell the system which columns to search on.
  • Add mark-up to show the UI on the List page.
  • Code to wire up the UI and QueryExtender.

The Attribute

This attribute will be added to the Class/Table not it’s columns and will have only one property Columns.

[AttributeUsage(AttributeTargets.Class)]
public class MultiColumnSearchAttribute : Attribute
{
    public String[] Columns { get; private set; }

    public MultiColumnSearchAttribute(params String[] columns)
    {
        Columns = columns;
    }
}

Listing 1 – MultiColumnSearchAttribute

and we apply it as in Listing 2 The Employee table show standard multi-column search but the Order table shows how we can search across entity boundaries you can see this in Figure 2.

[MetadataTypeAttribute(typeof(Employee.EmployeeMetadata))]
[MultiColumnSearch(
    "LastName", 
    "FirstName", 
    "Address", 
    "City", 
    "Country")]
public partial class Employee
{
    internal sealed class EmployeeMetadata
    {
        //...
    }
}

[MetadataTypeAttribute(typeof(Order.OrderMetadata))]
[MultiColumnSearch(
    "Employee.LastName", 
    "Employee.FirstName", 
    "Customer.CompanyName", 
    "Customer.ContactName", 
    "Shipper.CompanyName")]
public partial class Order
{
    internal sealed class OrderMetadata
    {
        //...
    }
}

Listing 2 – sample metadata

Cross Entity Search

Figure 2 – cross entity search

Adding UI Mark-Up

I have added the multi column search UI in between the validators and the QueryableFilterRepeater see Listing 3

<asp:DynamicValidator runat="server" ID="GridViewValidator" ControlToValidate="GridView1"
    Display="None" CssClass="DDValidator" />

<fieldset id="MultiSearchFieldSet" class="DD" runat="server" visible="false">
    <legend>Full Text Search</legend>
    <asp:TextBox ID="txbMultiColumnSearch" CssClass="DDTextBox" runat="server" />
    <asp:Button ID="btnMultiColumnSearchSubmit" CssClass="DDControl" runat="server" Text="Search"
        OnClick="btnMultiColumnSearch_Click" />
    <asp:Button ID="btnMultiColumnSearchClear" CssClass="DDControl" runat="server" Text="Clear"
        OnClick="btnMultiColumnSearch_Click" />
</fieldset>
<br />

<asp:QueryableFilterRepeater runat="server" ID="FilterRepeater">

Listing 3 – Multi Column Search UI

Code to wire up the UI and QueryExtender

/// <summary>
/// Setups the multi column search.
/// </summary>
private void SetupMultiColumnSearch()
{
    // get multi column search attribute
    var multiColumnSearch = table.GetAttribute<MultiColumnSearchAttribute>();

    if (multiColumnSearch != null)
    {
        var searchExpression = new SearchExpression()
            {
                DataFields = multiColumnSearch.Columns.ToCsvString(),
                SearchType = SearchType.Contains
            };
        
        // create control parameter
        var controlParameter = new ControlParameter() { ControlID = txbMultiColumnSearch.ID };
        
        // add control parameter to search expression
        searchExpression.Parameters.Add(controlParameter);

        // set context
        searchExpression.SetContext(GridQueryExtender, Context, GridDataSource);

        // add search expression to query extender
        GridQueryExtender.Expressions.Add(searchExpression);

        // make multicolumn search field set visible
        MultiSearchFieldSet.Visible = true;
    }
}

Listing 5 – SetupMultiColumnSearch method

In Listing 5 (which is called from the Page_Init method) we get the attribute and the SearchExpression the if both are not null we add the fields to the SearchExpressions DataFileds property, then we make the whole thing visible.

Note: Much thanks to David Ebbo who sorted out the issue I had had with adding the search expression in code rather than declaratively.

protected void btnMultiColumnSearch_Click(object sender, EventArgs e)
{
    var button = (Button)sender;
    if (button.ID == btnMultiColumnSearchClear.ID)
        txbMultiColumnSearch.Text = String.Empty;
}

Listing 6 – button event

And lastly we need an event to fire when the clear button is fired.

Note: I have both buttons wired-up to this handler as I also add session history here so that when you search on something and go to another page when you return you get the same search, but I also want it cleared if I click the clear button.

Download