public void FormatPicture(HttpPostedFileBase file) {
WebImage image = new WebImage(file.InputStream);
if (image.Width > 85 || image.Height > 85) {
int w = image.Width;
int h = image.Height;
if (w > h) {
w = 85;
h = image.Height * w / image.Width;
}
else {
h = 85;
w = image.Width * h / image.Height;
}
image.Resize(w, h);
}
image.Save(PictureFilepath);
}
The HttpPostedFileBase file comes from the view model and contains the uploaded image from the file control. In this example the image is resized to a maximum of 85 pixels, with the smaller dimension interpolated, and stored.
10 March 2014
MVC - Resize an image
It turns out there is a helper class in the System.Web namespace for treating images. This class, WebImage, makes things like resizing e.g. a user profile picture when it is uploaded very easy:
MVC - Download a file from an action
It is often usefull for files to be downloaded through code, and not directly. In the case of MVC, the download would go via an action. In the following example I download a users profile picture that way, while the actual file is not accessible directly:
[Authorize]
public ActionResult Picture() {
if (App.GetCurrentUser().HasPicture) { //Finds out if the current user has a picture in App_Data/UserPictures.
io.FileStream stream = io.File.OpenRead(App.GetCurrentUser().PictureFilepath);
byte[] data = new byte[stream.Length];
stream.Read(data, 0, data.Length);
stream.Close();
stream.Dispose();
return File(data, MediaTypeNames.Application.Octet, App.GetCurrentUser() + ".jpg");
}
else {
return null;
}
}
The file is read into memory and the streamed back as an action result of type File. This principle also allows for files to be generated on the fly without them ever being stored on disc.
06 February 2014
MVC - Multiple submit buttons with distinct actions
In standard MVC, a view (page) can only have one action. You retrieve (GET) the view and submit it (POST). Therefore only one POST-action can be triggered, and if you want multiple buttons in a single view you need to dive into the form values to differentiate between them and use a switch or something inside the POST-action.
It turns out there is a way to have multiple submit buttons that each trigger a distinct action though. You can put an attribute on the two POST-actions that lets MVC pick the right one depending on which button you clicked. It is important to know though that you can't have the standard submit action you would normally have:
[HttpPost]
public ActionResult Index() {
}
Remove this action if you have it. It will cause a conflict between this standard action and the one for the submit button.
The attribute
First you need to create the following attribute class. I called it Submit:public class SubmitAttribute : ActionNameSelectorAttribute {
public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo) {
if (actionName == methodInfo.Name)
return true;
else
return controllerContext.RequestContext.HttpContext.Request[methodInfo.Name] != null;
}
}
The actions
Second, create the actions for - in this example - two submit buttons:[Submit]
[HttpPost]
public ActionResult SaveProjectFilter(ProjectModel model) {
}
[Submit]
[HttpPost]
public ActionResult DeleteProjectFilter(ProjectModel model) {
}
Notice I decorated both actions with my Submit attribute.
The buttons
Third, put these two submit buttons in the view:<input type="submit" name="SaveProjectFilter" value="@Dictionary.Get("Save")" class="icon-save" />
<input type="submit" name="DeleteProjectFilter" value="@Dictionary.Get("Delete")" class="icon-delete" />
Notice how the buttons' names match their respective actions. This is the key to the whole thing!
This is actually it. If you click any of the two buttons the proper action will execute.
04 November 2013
ASP.NET - A basic authentification system
The simplest login system for ASP.NET (MVC) works by simply doing something like this:
HttpContext.Current.Session["User"] = user;If Session["User"] yields NULL, your not logged in, otherwise you are. While this works, it has an annoying consequence: while you're working on the application, and you rebuild, the session gets lost and you have to log in every time you want to test. This can be remedied like this: First, add the following to the system.web section of the Web.config:
<authentication mode="Forms"> <forms cookieless="UseCookies" loginUrl="/Home/Login" name="BFWauth" timeout="10512000" slidingExpiration="true" /> </authentication>Second, in the same code that assigns Session["User"], add this:
System.Web.Security.FormsAuthentication.SetAuthCookie(user.Id.ToString(), false);The user ID you put in there will be persisted even when the session is reset. Third, to know whether the user is logged in use:
if(HttpContext.Current.User.Identity.IsAuthenticated) { ...
The user ID is available here:
int userId = HttpContext.Current.User.Identity.Name.ToInt(); //ToInt() is an extension of mine. User user = GetUser(userId); //This one's obvious.You can now rebuild your application without being logged out. Life just got a little bit better.
09 September 2013
CSS - Multilevel dropdown menu
The following is my slightly different version of the so-called suckerfish menu, as implemented here. It's a basic multilevel dropdown menu made from an unordered list using CSS:
HTML
<ul class="menu">
<li>
<a href="">Addresses<a>
<ul>
<li>
<a href="">New<a>
<ul>
<li>
<a href="">Address<a>
<a href="">Selection<a>
</li>
</ul>
</li>
</ul>
</li>
</ul>
CSS
.right { float: right; } /* Used to have the menu on the right side. */
ul.menu,
ul.menu ul { margin: 0; padding: 0; list-style-type: none;
list-style-position: outside; position: relative; line-height: 2em; }
ul.menu ul { position: absolute; width: 12em; top: 2em; display: none; }
ul.menu li { float: left; position: relative; }
ul.menu li ul a { width: 12em; float: left; border-top: solid 1px #888; }
ul.menu li ul ul { top: auto; left: 12em; border-left: solid 1px #888; }
ul.menu li:hover ul ul,
ul.menu li:hover ul ul ul,
ul.menu li:hover ul ul ul ul { display: none; }
ul.menu li:hover ul,
ul.menu li li:hover ul,
ul.menu li li li:hover ul,
ul.menu li li li li:hover ul { display: block; z-index: 99; }
ul.menu a:link,
ul.menu a:active,
ul.menu a:visited { display: block; padding: 0px 10px; border-right:
solid 1px #888; color: #fff; text-decoration: none; background-color: #000; }
ul.menu a:hover { background-color: #888; }
ul.right a:link,
ul.right a:active,
ul.right a:visited { border-right: none; border-left: solid 1px #888; }
18 June 2013
VB6 - Dynamic cursor
For a drawing application I needed to be able to set the cursor dynamically to reflect the current size and color of the brush. I was working in VB6 (love it!) and with GDI, and the following does what I wanted. It uses a number of Windows API methods which signatures can be found on MSDN. All methods called in the code below are API methods.
First, some constants and the type IconInfo that is needed for the Windows API:
Private Const GCL_HCURSOR = -12 Private Const BLACKNESS = 66 Private Const WHITENESS = 16711778 Private Const BLACK_BRUSH = 4 Private Const BLACK_PEN = 7 Private Const DC_BRUSH = 18 Private Const DC_PEN = 1 Private Type IconInfo IsIcon As Boolean Hotspot As PointL MaskBitmap As Long ColorBitmap As Long End TypeYou need to create a color bitmap of your cursor and a mask bitmap for it's transparancy. The following code creates a round cursor of size pixels and the color color.RgbColor.
'Create color bitmap Dim colorDc As Long: colorDc = CreateCompatibleDC(mHdc) mCursorInfo.ColorBitmap = CreateCompatibleBitmap(mHdc, size, size) Dim prevColorBmp As Long: prevColorBmp = SelectObject(colorDc, mCursorInfo.ColorBitmap) PatBlt colorDc, 0, 0, size, size, BLACKNESS SelectObject colorDc, GetStockObject(DC_BRUSH) SelectObject colorDc, GetStockObject(DC_PEN) SetDCBrushColor colorDc, color.RgbColor SetDCPenColor colorDc, color.RgbColor Ellipse colorDc, 0, 0, size, size SelectObject colorDc, prevColorBmp DeleteDC colorDc 'Create mask bitmap Dim maskDc As Long: maskDc = CreateCompatibleDC(mHdc) mCursorInfo.MaskBitmap = CreateCompatibleBitmap(mHdc, size, size) Dim prevMaskBmp As Long: prevMaskBmp = SelectObject(maskDc, mCursorInfo.MaskBitmap) Rectangle maskDc, -1, -1, size + 1, size + 1 SelectObject maskDc, GetStockObject(BLACK_BRUSH) SelectObject maskDc, GetStockObject(BLACK_PEN) Ellipse maskDc, 0, 0, size, size SelectObject maskDc, prevMaskBmp DeleteDC maskDcThen you need to tell Windows to use these as cursor. If I created a cursor before, I destroy it and it's bitmaps to free the memory:
'Create cursor
mCursorInfo.Hotspot.X = size / 2
mCursorInfo.Hotspot.Y = size / 2
mCursor = CreateIconIndirect(mCursorInfo)
If mCursor <= 0 Then
Debug.Print "Could not create dot."
Else
origCursor = SetCursor(mCursor)
SetClassLong mHwnd, GCL_HCURSOR, mCursor
If prevCursor > 0 Then
If DestroyIcon(prevCursor) = False Then Debug.Print "Could not delete dot."
DeleteObject prevInfo.ColorBitmap
DeleteObject prevInfo.MaskBitmap
End If
End If
25 April 2013
Extension - Adding a parameter to an ODBC command; the easy way
I find that adding parameters to an OdbcCommand is not very straightforward. That's why I wrote myself an extension to make it easier:
public static void AddParameter(this OdbcCommand command, string name, OdbcType type, object value, bool isNull = false) {
if (command != null && name.HasValue()) { //"HasValue()" is another extension which simply returns ''!IsNullOrEmpty''.
if (!name.StartsWith("@"))
name = '@' + name;
if (value == null || isNull)
value = DBNull.Value;
command.Parameters.Add(new OdbcParameter(name, type));
command.Parameters[name].Value = value;
}
}
It takes care of the '@', replaces NULL by DBNull and adds the parameter in two steps as it is supposed to.
It has a parameter isNull, in which you can say things like "if the int value = 0 then it constitutes NULL".
Subscribe to:
Posts (Atom)