Archive for October, 2010
Getting the DataKey of a GridView bound inside a DataList
Posted by scottstoecker in .NET, ASP.NET, C# on October 29, 2010
You know you nest your GridViews inside your DataLists. Everybody is doing it, and peer pressure is something you are going to give in to. I know that when my friends started doing it, I couldn’t resist. But once you start doing that sort of thing, then you have to do things like get your DataKey values from nested GridView, and that is no longer as simple as it used to be.
First, a bit of HTML to show exactly what I’m doing, with the standard disclaimer that I took a bunch of “highly relevant to me but not to thee” items:
<asp:DataList ID="lstTest" DataKeyField="ID" DataSourceID="OC" OnItemCommand="lstTest_ItemCommand" runat="server"> <ItemTemplate> <asp:Panel ID="pnlPanel" runat="server"> <asp:Label ID="lblLocationName" Text='<%#Eval("Name")%>' runat="server" /> <asp:Label ID="lblLocOwner" Text='<%#Eval("OwnerName")%>' runat="server" /> <asp:GridView ID="grdDetails" OnRowCommand="grdDetails_OnCommand" DataKeyNames="ID" runat="server"> <Columns> <asp:BoundField DataField="UserName" /> <asp:ButtonField CommandName="IgnoreRequest" Text="Ignore Request" /> <asp:ButtonField CommandName="IgnoreUser" Text="Ignore User" /> </Columns> </asp:GridView> </asp:Panel> </ItemTemplate> </asp:DataList>
So each item in the DataList has its own GridView assigned to it. Beautiful. But when I click on the Ignore button inside the DataGrid, I can’t just say grdDetails.DataKeys[grdDetails.SelectedIndex].Value. Why? Because grdDetails doesn’t exist at the page level, only in the ItemTemplate of the DataList. Thus, type grdDetails. and expecting your methods, properties, and events goodness will bring bitter tears to your face. But not for long.
Let’s do some .NET-foo on it:
protected void grdDetails_OnCommand(object sender, GridViewCommandEventArgs e) { GridView gv = (GridView)sender; int Index = Convert.ToInt32(e.CommandArgument); if (e.CommandName == "IgnoreRequest") DataClass.IgnoreRequest(Convert.ToInt64(gv.DataKeys[Index].Value)); }
So I’m first casting the sender as a GridView, because that’s what the sender is. Since the GridViewCommandEventArgs .CommandArgument is actually the index of the GridView row (it’s tricky like that), I can then use that index an pull the value from the GridView’s DataKeys collection. Bingo boingo done.
Notifying users when a long running process is ready
Posted by scottstoecker in .NET, Win32, WPF on October 27, 2010
I was forced to delve into the world of Win32 coding to solve an issue of calling the SaveFileDialog method. I have a long-running WPF application that I wanted to get the user’s attention when it was finally ready for some love (i.e. the Save File dialog box appeared). I was having an issue where the app would quietly sit there until the user decided to check it – not the kind of passive relationship I was looking for. I needed to spice it up.
Luckily, I found the solution to shy application.
First, add a few DllImport statements to delve into the user32.dll file:
<Runtime.InteropServices.DllImport("user32.dll")> _ Private Shared Function ShowWindow(ByVal hwnd As Integer, ByVal nCmdShow As Integer) As Integer End Function <Runtime.InteropServices.DllImport("user32.dll")> _ Private Shared Function SetForegroundWindow(ByVal hwnd As Integer) As Integer End Function <Runtime.InteropServices.DllImport("user32.dll")> _ Private Shared Function IsIconic(ByVal hWnd As Integer) As Integer End Function
The meat of the code is this For Each loop. The only modification you will have to make is to change the MainWindowTitle to match the result of the window you want to give focus to:
'Give focus to the dialog box when it is ready For Each p As Process In Process.GetProcesses If p.MainWindowTitle = "Results" Then If IsIconic(p.MainWindowHandle.ToInt32) <> 0 Then ShowWindow(p.MainWindowHandle.ToInt32, &H1) Else SetForegroundWindow(p.MainWindowHandle.ToInt32) End If End If Next
If you are interested in the internals of the calls, you can check MSDN for the IsIconic, ShowWindow and SetForegroundWindow functions.
Deleting Excel worksheets with VB.NET and LINQ
Posted by scottstoecker in .NET, Excel, LINQ, VB.NET on October 22, 2010
I never claim that the way I do it is the best way…
I created code to insert a worksheet into Excel for each user in the query. But the focus of this post is deleting those default sheets that Excel starts with, Sheet1, Sheet2, and Sheet3. When I tried to delete them at the beginning of my code, I was graciously given this friendly COMException:
A workbook must contain at least one visible worksheet.
No worries, I’ll just do it at the end of the process. I came up with two ways. First, the easy, so un-cool way to do it: Just count backwards from the worksheet count:
Dim bkWorkBook As Excel.Workbook = DirectCast(oXL.Workbooks.Add(), Excel.Workbook) Dim shWorkSheet As Excel.Worksheet = DirectCast(bkWorkBook.Sheets("Sheet1"), Excel.Worksheet) 'Code to populate the worksheets Dim Counter As Integer = bkWorkBook.Sheets.Count CType(bkWorkBook.Sheets(Counter), Excel.Worksheet).Delete() CType(bkWorkBook.Sheets(Counter - 1), Excel.Worksheet).Delete() CType(bkWorkBook.Sheets(Counter - 2), Excel.Worksheet).Delete()
Oh, that’s too easy, though. Isn’t there some way I could use some cool LINQ extension method to do this? You betcha. This is the round-about way I came up with using OrderByDescending:
Dim SheetName As String = String.Empty Dim SheetIds As New List(Of Integer) For i As Integer = 1 To bkWorkBook.Sheets.Count SheetName = CType(bkWorkBook.Sheets(i), Excel.Worksheet).Name If SheetName = "Sheet1" OrElse SheetName = "Sheet2" OrElse SheetName = "Sheet3" Then SheetIds.Add(i) End If Next For Each Counter As Integer In SheetIds.OrderByDescending(Function(i As Integer) i) CType(bkWorkBook.Sheets(Counter), Excel.Worksheet).Delete() Next
By the way, I used the first, much simpler, method.
Yes, that 65,536 rows maximum in Excel is true
Posted by scottstoecker in VB.NET on October 21, 2010
I was exporting into Excel a list of users and the files they “owned” in a selected directory when I ran into an error at row 65,537 in the Excel sheet.
Dim shWorkSheet As Excel.Worksheet = DirectCast(bkWorkBook.Sheets("Sheet1"), Excel.Worksheet)
...
For Each File As System.IO.FileInfo In FileCollection(i - 1).FileList
shWorkSheet.Cells(RowCounter, 2).Value = File.FullName
shWorkSheet.Cells(RowCounter, 3).Value = File.Length
RowCounter += 1
Next
So 65,536 was the last row successfully added to the Excel sheet. That number sounded familiar, so I diligently checked and found an MSDN article on Excel. And there, plain as day, was:
A Microsoft Office Excel worksheet contains 65,536 rows and 256 columns.
So I guess I’ll be splitting my records between different sheets. At least it was an easy error to diagnose.
Return an ID from a LinkButton
Posted by scottstoecker in .NET, ASP.NET, C# on October 21, 2010
I needed to pass the ID of a record that was bound to an ASP.NET LinkButton to the code behind page. Though there are other ways to do it, this is the way that worked for me. First, the GridView control that held the LinkButton, having removed other controls for clarity:
<asp:GridView ID="grdLocations" DataKeyNames="ID" runat="server"> <Columns> <asp:TemplateField> <ItemTemplate> <asp:LinkButton ID="lnkViewLocation" CommandArgument='<%#Bind("ID")%>' OnClick="ViewLocation_Click" Text='<%#Bind("Name")%>' runat="server" /> </ItemTemplate> </asp:TemplateField> </Columns> </asp:GridView>
Secondly, the code behind. I just casted the sender object as a LinkButton and was able to get the CommandArgument from the LinkButton:
protected void ViewLocation_Click(object sender, EventArgs e) { LinkButton MyButton = (LinkButton)sender; Int64 LocationId = Convert.ToInt64(MyButton.CommandArgument.ToString()); }
XAML error – System.Windows.Baml2006.TypeConverterMarkupExtension
Posted by scottstoecker in WPF, XAML on October 14, 2010
Notice, if you will, this itty bitty block of code:
<ListBox Name="FilesList" ItemsSource="{Binding FileList}" Width="100%"> <ListBox.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding}" /> </DataTemplate> </ListBox.ItemTemplate> </ListBox>
While attempting to databind a custom collection class to a ListBox, I encountered an oddly named exception:
XamlParseException occurred Provide value on 'System.Windows.Baml2006.TypeConverterMarkupExtension' threw an exception.
What the heck is Baml2006?
Anyway, here is the offending line:
<ListBox Name="FilesList" ItemsSource="{Binding FileList}" Width="100%">
Guess which part is the problem? The ‘Width=”100%”‘ part. When I changed it to read ‘Width=”350″‘, the error went away. No worries, but a more helpful error message would have been nice.
I’ve also received the “BAML” error where there are other invalid characters in a property value. So don’t put those invalid characters in property fields, kids – that’s bad.
Using Sum operator in LINQ
Posted by scottstoecker in .NET, LINQ, VB.NET on October 13, 2010
I needed to populate a list of record with file names along with how large the files were and who owned the file. Then, I needed to find how much space a particular user was taking up on the shared drive. The user would then be tarred and feathered, but this was nothing my code was going to have to deal with.
So I have a class of three properties, though I’ve only included the two relevant ones. I also declare a List(Of Results) to store my wonderfully awesome data:
Public Class Results Private _fileSize As Int64 Public Property FileSize() As Int64 Get Return _fileSize End Get Set(ByVal value As Int64) _fileSize = value End Set End Property Private _userName As String Public Property UserName() As String Get Return _userName End Get Set(ByVal value As String) _userName = value End Set End Property ... Lots of other code omitted End Class Dim ResultsFile As List(Of Results)
The details of how I populate the ResultsFile variable aren’t important, but here is the LINQ statement I use to total the file sizes:
Dim TotalFileSize As Int64 = Aggregate Result In ResultsFile Where Result.UserName = UserName Into Sum(Result.FileSize)
Simple and sweet.
Intro to LINQ
Posted by scottstoecker in C#, LINQ on October 11, 2010
I hadn’t used LINQ much, and I wanted to learn a bit more about how it worked, because it seems very popular, and I didn’t want to be left out in the cold. Plus, the “=>” expression used by LINQ made me seem really smart… to myself.
First, a function to build and return some data:
private List BuildLocations() { List Locs = new List() { }; Locs.Add(new Location("Pritzker","Chicago","IL",1,1)); Locs.Add(new Location("Capitol", "Lansing", "MI", 2, 1)); Locs.Add(new Location("Amical", "Traverse City", "MI", 3, 2)); Locs.Add(new Location("Blackstone Hotel", "Chicago", "IL", 4, 1)); Locs.Add(new Location("Wayne Count Courthouse", "Detroit", "MI", 5, 3)); Locs.Add(new Location("Penobscot", "Detroit", "MI", 6, 3)); Locs.Add(new Location("Sears Tower", "Chicago", "IL", 7, 1)); return Locs; }
The first function I tackle was FindAll, which returns all matching elements. There are two ways to work it. First is to define a seperate function and specifying the criteria in the function, and passing this function into the FindAll method:
private static bool FindLocation(Location Loc) { if (Loc.City == "Chicago") return true; return false; }
I prefer the second method, defining the criteria in-line using the Where clause, as it is easier to dynamically change the criteria I’m searching for:
protected void btnFindAll_Click(object sender, EventArgs e) { List Locs = BuildLocations(); Response.Write(" Using a defined method"); List Results = Locs.FindAll(FindLocation); foreach (Location Temp in Results) Response.Write("Location = " + Temp.Name + " in " + Temp.City); Response.Write("Using an in-line method"); Results = Locs.FindAll(p => p.City == "Lansing"); foreach (Location Temp in Results) Response.Write("Location = " + Temp.Name + " in " + Temp.City); }
Next is FindLast, which returns the last element that matches the criteria:
protected void btnFindLast_Click(object sender, EventArgs e) { List Locs = BuildLocations(); Location Results = Locs.FindLast(p => p.UserId == 1); Response.Write(" Location = " + Results.Name + " in " + Results.City); }
The last function I tangle with is Exists. I work through two samples, mainly to satisfy my curiosity. The first verifies that matching data exists, and the second verifies that data does not exist:
protected void Button1_Click(object sender, EventArgs e) { List Locs = BuildLocations(); bool Results = Locs.Exists(p => p.State == "MI"); Response.Write("Do any records with a State of 'MI' exist? " + Results.ToString()); Results = Locs.Exists(p => p.State == "AL"); Response.Write("Do any records with a State of 'AL' exist? " + Results.ToString()); }
Two links to help out with your LINQ-learning:
http://msdn.microsoft.com/en-us/library/bfed8bca.aspx
http://msdn.microsoft.com/en-us/library/5kthb929.aspx
Creating a flashing taskbar icon using FlashWindow with XAML
Posted by scottstoecker in .NET, C#, WPF, XAML on October 8, 2010
Looking to notify your users when an application wants its needs met? A sure fire way is to have its taskbar icon flash. Here’s how.
First, import those namespaces, people:
using System.Windows.Interop; using System.Runtime.InteropServices;
Next, declare the Windows API FlashWindow method.
[DllImport("user32")] public static extern int FlashWindow(IntPtr hwnd, bool bInvert);
Notice the first parameter is a IntPtr, NOT an int, like I found at one of the websites I visited trying to get this working. An int will get you nowhere. IntPtr is your friend.
Now create a WindowInteropHelper by passing the WPF Window object that you are calling the code from. Pass the window’s Handle property to the FlashWindow method, along with a value of “true”, to tell it that you want the awesome flashing taskbar effect.
WindowInteropHelper wih = new WindowInteropHelper(ThisWindow); FlashWindow(wih.Handle, true);
There we go, a simple way to alert users when your application is done or has updated information.
Saving an Excel file to a user’s machine from ASP.NET page
Posted by scottstoecker in ASP.NET, C# on October 8, 2010
Want a super-simple way to allow users to save data as an Excel file on their local machine through ASP.NET? There is a nice article at http://www.developer.com/net/asp/article.php/3633891/ASPNET-Tip-Exporting-Data-to-Excel.htm.
I’ll be even simpler. First, you clear out all the HTML code from the aspx page, so that only the @Page tag remains:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="ExcelTest.aspx.cs" Inherits="TestSite.ExcelTest" %>
Yep, that’s the entire aspx page.
The code-behind file is equally simple. Just add the Response.ContentType tag, and a table to hold the Excel data:
protected void Page_Load(object sender, EventArgs e) { Response.ContentType = "application/vnd.ms-excel"; Response.Write("<table><tr><td>1</td><td>2</td><td>3</td></tr><tr><td>1</td><td>2</td><td>3</td></tr></table>"); }
When the user hits the page, they will prompted to open or save our Excel file filled with incredibly useful data.
Recent Comments