- Part 1 - FileImage_Edit FieldTemplate.
- Part 2 - FileImage_Edit FieldTemplate.
- Part 3 - FileUpload FiledTemplate.
FileUpload and FileUpload_Edit FiledTemplates
I thought this would complement the DBImage and FileImage FieldTemplates and so I thought what would you want to be able to do:
- Upload a file to a specified folder.
- Download the said file once uploaded.
- Display an image for the file.
- Control the download capability via attributes and user Roles.
- Handle errors such as wrong file type or when file is missing from upload folder.
The FileUpload Attributes
In this example I’m creating on attribute to hold all the parameters to do with FileUpload.
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] public sealed class FileUploadAttribute : Attribute { /// <summary> /// where to save files /// </summary> public String FileUrl { get; set; } /// <summary> /// File tyoe to allow upload /// </summary> public String[] FileTypes { get; set; } /// <summary> /// image type to use for displaying file icon /// </summary> public String DisplayImageType { get; set; } /// <summary> /// where to find file type icons /// </summary> public String DisplayImageUrl { get; set; } /// <summary> /// If present user must be a member of one /// of the roles to be able to download file /// </summary> public String[] HyperlinkRoles { get; set; } /// <summary> /// Used to Disable Hyperlink (Enabled by default) /// </summary> public Boolean DisableHyperlink { get; set; } /// <summary> /// helper method to check for roles in this attribute /// the comparison is case insensitive /// </summary> /// <param name="role"></param> /// <returns></returns> public bool HasRole(String[] roles) { if (HyperlinkRoles.Count() > 0) { var hasRole = from hr in HyperlinkRoles.AsEnumerable() join r in roles.AsEnumerable() on hr.ToLower() equals r.ToLower() select true; return hasRole.Count() > 0; } return false; } }Listing 1 – FileUploadAttribute
You will notice in the Listing 1 that all the properties are using c# 3.0’s new Automatic Properties feature less typing; just type prop and hit tab twice and there is you property ready to be filled in.
The second thing you will see is the HasRoles method on this attribute, which takes an array of roles and checks to see if HyperlinkRoles property has any matches. It does this by joining the two arrays together in a Linq to Object query and then selects true for each match in the join. I’m sure this is more readable that the traditional nested foreach loops, it’s certainly neater :D.
The FileUpload FieldTemplate
This FiledTemplate will show the filename and associated icon.
Figure 1- File and associated Icon
<%@ Control Language="C#" AutoEventWireup="true" CodeFile="FileUpload.ascx.cs" Inherits="FileImage" %> <asp:Image ID="Image1" runat="server" /> <asp:Label ID="Label1" runat="server" Text="<%# FieldValueString %>"></asp:Label> <asp:HyperLink ID="HyperLink1" runat="server"></asp:HyperLink> <asp:CustomValidator ID="CustomValidator1" runat="server" ErrorMessage=""> </asp:CustomValidator>
Listing 2 – FileUpload.ascx file
As you can see from Listing 1 there are Image, Label and Hyperlink controls on the page. The Label and Hyperlink are mutually exclusive if the conditions are right then a Hyperlink will show so that the file can be downloaded else just a Label will show with the filename.
using System; using System.IO; using System.Linq; using System.Web.DynamicData; using System.Web.Security; using System.Web.UI; using Microsoft.Web.DynamicData; public partial class FileImage : FieldTemplateUserControl { public override Control DataControl { get { return Label1; } } protected override void OnDataBinding(EventArgs e) { base.OnDataBinding(e); //check if field has a value if (FieldValue == null) return; // get the file extension String extension = FieldValueString.Substring( FieldValueString.LastIndexOf(".") + 1, FieldValueString.Length - (FieldValueString.LastIndexOf(".") + 1)); // get attributes var fileUploadAttributes = MetadataAttributes.OfType<FileUploadAttribute>().FirstOrDefault(); String fileUrl = fileUploadAttributes.FileUrl; String displayImageUrl = fileUploadAttributes.DisplayImageUrl; String displayImageType = fileUploadAttributes.DisplayImageType; // check the file exists else throw validation error String filePath; if (fileUploadAttributes != null) filePath = String.Format(fileUrl, FieldValueString); else // if attribute not set use default filePath = String.Format("~/files/{0}", FieldValueString); // show the relavent control depending on metadata if (fileUploadAttributes.HyperlinkRoles.Length > 0) { // if there are roles then check: // if user is in one of the roles supplied // or if the hyperlinks are disabled // or if the file does not exist // then hide the link if (!fileUploadAttributes.HasRole(Roles.GetRolesForUser()) fileUploadAttributes.DisableHyperlink !File.Exists(Server.MapPath(filePath))) { Label1.Text = FieldValueString; HyperLink1.Visible = false; } else { Label1.Visible = false; HyperLink1.Text = FieldValueString; HyperLink1.NavigateUrl = filePath; } } else { // if either hyperlinks are disabled or the // file does not exist then hide the link if (fileUploadAttributes.DisableHyperlink !File.Exists(Server.MapPath(filePath))) { Label1.Text = FieldValueString; HyperLink1.Visible = false; } else { Label1.Visible = false; HyperLink1.Text = FieldValueString; HyperLink1.NavigateUrl = filePath; } } // check file exists on file system if (!File.Exists(Server.MapPath(filePath))) { CustomValidator1.ErrorMessage = String.Format("{0} does not exist", FieldValueString); CustomValidator1.IsValid = false; } // show the icon if (!String.IsNullOrEmpty(extension)) { // set the file type image if (!String.IsNullOrEmpty(displayImageUrl)) { Image1.ImageUrl = String.Format(displayImageUrl, extension + "." + displayImageType); } else { // if attribute not set the use default Image1.ImageUrl = String.Format("~/images/{0}", extension + "." + displayImageType); } Image1.AlternateText = extension + " file"; // if you apply dimentions from DD Futures var imageFormat = MetadataAttributes.OfType<ImageFormatAttribute>().FirstOrDefault(); if (imageFormat != null) { // if either of the dims is 0 don't set it // this will mean that the aspect will remain locked if (imageFormat.DisplayWidth != 0) Image1.Width = imageFormat.DisplayWidth; if (imageFormat.DisplayHeight != 0) Image1.Height = imageFormat.DisplayHeight; } } else { // if file has no extension then hide image Image1.Visible = false; } } }
Listing 3 – FileUpload.ascx.cs file
In Listing 3 you can see that everything goes on in the OnDataBinding event handler.
The FileUpload_Edit FieldTemplate
<%@ Control Language="C#" AutoEventWireup="true" CodeFile="FileUpload_Edit.ascx.cs" Inherits="FileImage_Edit" %> <asp:PlaceHolder ID="PlaceHolder1" runat="server" Visible="false"> <asp:Image ID="Image1" runat="server" /> <asp:Label ID="Label1" runat="server" Text="<%# FieldValueString %>"></asp:Label> <asp:HyperLink ID="HyperLink1" runat="server"></asp:HyperLink> </asp:PlaceHolder> <asp:FileUpload ID="FileUpload1" runat="server" /> <asp:CustomValidator ID="CustomValidator1" runat="server" ErrorMessage=""> </asp:CustomValidator>
Listing 4 – FileUpload_Edit.ascx file
In Listing 4 the PlaceHolder control is used to hide the Image, Label and Hyperlink when in insert mode or when there is no value to be shown.
using System; using System.Collections.Specialized; using System.IO; using System.Linq; using System.Web.DynamicData; using System.Web.Security; using System.Web.UI; using Microsoft.Web.DynamicData; public partial class FileImage_Edit : FieldTemplateUserControl { public override Control DataControl { get { return Label1; } } 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.Substring( FieldValueString.LastIndexOf(".") + 1, FieldValueString.Length - (FieldValueString.LastIndexOf(".") + 1)); // get attributes var fileUploadAttributes = MetadataAttributes.OfType<FileUploadAttribute>().FirstOrDefault(); String fileUrl = fileUploadAttributes.FileUrl; String displayImageUrl = fileUploadAttributes.DisplayImageUrl; String displayImageType = fileUploadAttributes.DisplayImageType; String filePath; // check the file exists else throw validation error if (fileUploadAttributes != null) filePath = String.Format(fileUrl, FieldValueString); else // if attribute not set use default filePath = String.Format("~/files/{0}", FieldValueString); // show the relavent control depending on metadata if (fileUploadAttributes.HyperlinkRoles.Length > 0) { // if there are roles then check: // if user is in one of the roles supplied // or if the hyperlinks are disabled // or if the file does not exist // then hide the link if (!fileUploadAttributes.HasRole(Roles.GetRolesForUser()) fileUploadAttributes.DisableHyperlink !File.Exists(Server.MapPath(filePath))) { Label1.Text = FieldValueString; HyperLink1.Visible = false; } else { Label1.Visible = false; HyperLink1.Text = FieldValueString; HyperLink1.NavigateUrl = filePath; } } else { // if either hyperlinks are disabled or the // file does not exist then hide the link if (fileUploadAttributes.DisableHyperlink !File.Exists(Server.MapPath(filePath))) { Label1.Text = FieldValueString; HyperLink1.Visible = false; } else { Label1.Visible = false; HyperLink1.Text = FieldValueString; HyperLink1.NavigateUrl = filePath; } } // check file exists on file system if (!File.Exists(Server.MapPath(filePath))) { CustomValidator1.ErrorMessage = String.Format("{0} does not exist", FieldValueString); CustomValidator1.IsValid = false; } // show the icon if (!String.IsNullOrEmpty(extension)) { // set the file type image if (!String.IsNullOrEmpty(displayImageUrl)) { Image1.ImageUrl = String.Format(displayImageUrl, extension + "." + displayImageType); } else { // if attribute not set the use default Image1.ImageUrl = String.Format("~/images/{0}", extension + "." + displayImageType); } Image1.AlternateText = extension + " file"; // if you apply dimentions from DD Futures var imageFormat = MetadataAttributes.OfType<ImageFormatAttribute>().FirstOrDefault(); if (imageFormat != null) { // if either of the dims is 0 don't set it // this will mean that the aspect will remain locked if (imageFormat.DisplayWidth != 0) Image1.Width = imageFormat.DisplayWidth; if (imageFormat.DisplayHeight != 0) Image1.Height = imageFormat.DisplayHeight; } } else { // if file has no extension then hide image Image1.Visible = false; } } protected override void ExtractValues(IOrderedDictionary dictionary) { // get attributes var fileUploadAttributes = MetadataAttributes.OfType<FileUploadAttribute>().FirstOrDefault(); String fileUrl; String[] extensions; if (fileUploadAttributes != null) { fileUrl = fileUploadAttributes.FileUrl; extensions = fileUploadAttributes.FileTypes; if (FileUpload1.HasFile) { // get the files folder String filesDir = fileUrl.Substring(0, fileUrl.LastIndexOf("/") + 1); // resolve full path c:\... etc String path = Server.MapPath(filesDir); // get files extension without the dot String fileExtension = FileUpload1.FileName.Substring( FileUpload1.FileName.LastIndexOf(".") + 1).ToLower(); // check file has an allowed file extension if (extensions.Contains(fileExtension)) { // try to upload the file showing error if it fails try { FileUpload1.PostedFile.SaveAs(path + "\\" + FileUpload1.FileName); Image1.ImageUrl = String.Format(fileUploadAttributes.DisplayImageUrl, fileExtension + ".png"); Image1.AlternateText = fileExtension + " file"; dictionary[Column.Name] = FileUpload1.FileName; } catch (Exception ex) { // display error CustomValidator1.IsValid = false; CustomValidator1.ErrorMessage = ex.Message; } } else { CustomValidator1.IsValid = false; CustomValidator1.ErrorMessage = String.Format("{0} is not a valid file to upload", FieldValueString); } } } } }Listing 5 - FileUpload_Edit.ascx.cs file
In Listing 5 the OnDataBinding event handler is pretty much the same as the FileUpload.ascs.cs file. Here its the ExtractValues method that does the work of uploading and displaying errors, i.e. if the file type of the file to be uploaded does not match a file type specified in the metadata or there is an error during the upload.
Helper Class FileUploadHelper
public static class FileUploadHelper { /// <summary> /// If the given table contains a column that has a UI Hint with the value "DbImage", finds the ScriptManager /// for the current page and disables partial rendering /// </summary> /// <param name="page"></param> /// <param name="table"></param> public static void DisablePartialRenderingForUpload(Page page, MetaTable table) { foreach (var column in table.Columns) { // TODO this depends on the name of the field template, need to fix if (String.Equals( column.UIHint, "DBImage", StringComparison.OrdinalIgnoreCase) String.Equals(column.UIHint, "FileImage", StringComparison.OrdinalIgnoreCase) String.Equals(column.UIHint, "FileUpload", StringComparison.OrdinalIgnoreCase)) { var sm = ScriptManager.GetCurrent(page); if (sm != null) { sm.EnablePartialRendering = false; } break; } } } }
This is just a modified version of the Dynamic Data Futures DisablePartialRenderingForUpload method the only difference is that I’ve added support for both my file upload capable FieldTemplates FileImage and FileUpload.
Finally Some Sample Metadata
[MetadataType(typeof(FileImageTestMD))] public partial class FileImageTest : INotifyPropertyChanging, INotifyPropertyChanged { public class FileImageTestMD { public object Id { get; set; } public object Description { get; set; } [UIHint("FileUpload")] [FileUpload( FileUrl = "~/files/{0}", FileTypes = new String[] { "pdf", "xls", "doc", "xps" }, DisplayImageType = "png", DisableHyperlink = false, HyperlinkRoles=new String[] { "Admin", "Accounts" }, DisplayImageUrl = "~/images/{0}")] [ImageFormat(22, 0)] public object filePath { get; set; } } }
Listing 7 – sample metadata
The FileUpload Project
Enjoy
31 comments:
Hi,
I was wondering how this would change if you also used your security / attributes process? I attempted to combine both techniques, but the partial rendering on the image does not seem to work in combination with your security technique.
Hi Fred, I'm not sure why there there would be a problem combining these two, but my first step to checking this from what you say would be to disable PartialRendering on the Site.Master page. and see what happens there. :D
Steve
Couldn't get this working, so did some research and it turns out that the FileUpload control doesn't work inside of an UpdatePanel of the AJAX framework. You have to add a PostBackTrigger according to this reference:
http://www.codeproject.com/KB/ajax/simpleajaxupload.aspx
Just wanted to mention, so that others aren't scratching their heads like I was. In addition, I encountered a null reference error in FileUpload_Edit.ascx.cs, line 52 on this conditional:
if (fileUploadAttributes.HyperlinkRoles.Length > 0)
The equivalent line in FileUpload.ascx.cs checks this instead:
if (fileUploadAttributes.HyperLink
roles != null)
Hi,
Firstly, thank you very much for the perfect article.
I have a problem. We use different application for administration and web ui. I mean there are two virtual directory of my website.
/Admin
/NormalWebSiteFolder
But they are different virtual directories. So admin will upload images to Admin/files but i don't want to user use admin/files for preview of the images.
is it possible to upload a different path of the web site from admin application?
How can i upload files to website "files" folder from different application (different virtual directory on the same iis) to web site.
Thanks in advance.
If you want a folder in the WebSite folder to have images uploaded by the Admin app the all you need to do is create a virtual folder in the Admin app the point to the folder in the website
Steve :D
Any way i can solve it no problem ...
There is an another problem. FileUpload1.Hasfile is always false. :) I select file and then insert item bu when i am debugging i see it hasn't got a file. :( Is that know issue?
This is a known issue with Update pannel try setting EnablePartialRendering="false" in the site.master see the DisablePartialRenderingForUpload method which is used in the page to disable PartialRender.
Steve :D
You are perfect. ;)
In Edit & Insert.aspx i called that methods and solved ;)
Thanks a lot steve
Your welcome, I don't think I made it clear enough in the article.
Steve :D
its don't runs on my project. HasFile property of FileUpload component is already returns is false.
how to correct this?
thanks
That sort of effect may occur in you have not implemented the FileUploadHelper class to disable partial updates on the page. To test to see if this is your issue go to you site.master page and find the ScriptManager tag and change the EnablePartialRendering="true" to EnablePartialRendering="false"
Hope this helps.
Steve :D
I am not sure if it is due to the changes in the latest DynamicData preview, but the validation logic with CustomValidator seems ineffective in ExtractValues() Method. It looks like the framework is past checking IsValid on all validation controls and setting CustomValidator1.IsValid = false is not doing anything. More over, throwing a validationException in this method does not work either, as the DynamicValidator seems to kick in only if a ValidationException is thrown during setting values on the Entities (OnPropertyChanging Events, for example). I fixed it by trapping the CustomValidator_ServerValidate Event and putting the validation logic (in this case, check for valid file extension) in the event handler.
Thanks Haider, it's a long while ago that I did this and I'm pretty sure that it all worked back then.
Steve
P.S. I'll be comming back to this when VS2010 Beta 2 comes out and redoing all my custom FieldTemplates.
Hi Steve, when i upload a file that is not in the extension list, this line give me an error of : Databinding methods such as Eval(), xpath() and Bind() can only be used in the context of a databound control
if(extensions.contails(fileExtension))
{....}
else
{
CustomValidator1.IsValid = false;
CustomValidator1.ErrorMessage = String.Format("{0} is not a valid file to upload", FieldValueString); // Error throws here
}
any idea?thanks in advance
It sounds like you have partial render turned on to test this turn it off in ther master page <asp:ScriptManager ID="ScriptManager1" runat="server" EnablePartialRendering="true" /> set EnablePartialRendering to false. If this fixes the issue you missed out a section of the article "Helper Class FileUploadHelper" you will need to look at previouse article in the series to see how to apply it in the idividual pages.
Steve :D
Thanks Steve,still not solved the problem,I had already turn it off in the master page : EnablePartialRendering="false"(otherwise the FileUpload1.HasFile always return false)
and set DisablePartialRenderingForUpload in my TabbedEditWithSubGrids class:
FileUploadHelper.DisablePartialRenderingForUpload(this, table);
FormView1.ItemTemplate = new DetailsSubGridsTemplate(table, Page);
I downloaded the solution in this article, i also got the error as well. I used System.Web.DynamicData.dll v3.5.0.0, thanks again
Hi Feng, I'll spin the project up tomorrow and get back top you.
Steve :D
Hi, Steve,i redownload the project in the article make sure i got the right code, the issue is still exists..the only thing i change is in web.config file and remove the authorization so i don't need to login
Hi Feng, I've just tested it on my PC and all seems to work fine insert works and the files are uploaded.
Steve :(
Hi, Steve i got the upload/file insert working fine. but when i try to insert a file with a funny extention, like abc.bak or abc.config, it will throw the exception :)
Hi Feng, you are correct, I th8ink there are some fundemental errors in this project, I will rework based on the FileImage in the previouse article as you cannot do validation in the Extract values event, it's all too late by then I think there needs to be an upload button to do the validation in.
Steve :(
Right, thanks Steve,i will implement that as well :)
Sorry About that Feng, but we all make mistakes from time to time :(
Steve
File Upload has a problem..It can upload a file,But File path not visible on that textbox.
Yes there are a number of issues with it, I'm doing a paying project and will re-do the article and then make this one point to the new one in a week or so :(.
I'm going to base the new one on an earlyer article for uploading images as I seem to have introduced several serious bugs here.
Steve :D
Hi Steve:
I can't download the source code. Can you send me this .zip project to lrscott83@gmail.com ?
Thanks ...
Steve, I have downloaded and expanded the zip file but I can't even open the project. There is no project or solution file. can you please send me the e-mail at
samirbarot@rogers.com
I hope your sample project is working project where I can see or test something before I implement.
Samir Barot
Hey I am bit new to DD i got ur project run it was nice i want to implment this in my project but donot know where to call Fileimage in project. I have added many Tables in my project and want to implement this on one entity of any one table. having lot problem some one can refer me book or help me out in this
Thanks
MSBH
it will only work in a Dynam,ic Data project.
Steve
Hi Steve
This is Ashish from India i'm a software engineer working on dynamic file upload control
i have gone through your post
but i want to know more if i need a file upload control like asp.net and upload button also then is it post correct for that as i'm not sure there is any file upload control in this
please help me
Hi Ashish, just sent you the field template in a zio file
Steve
Post a Comment