One request that comes up from time to time is to embed a checkbox inside of an ASP.NET grid control for boolean data. Unfortunately, one of the downsides to this is that the controls are "read only" - they don't let you check or uncheck them without putting the row into edit mode. That works, but in some cases this isn't all that great either. What do you do if you just want a checkbox on each row, that's "live" and causes a postback as you check/uncheck items? Thankfully this doesn't take much code but figuring out the first time can be really painful. To get the "live" checkbox, just wrap it inside of a template. (In the example code I created a new Web Form named "GridCheckbox"). <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="GridCheckbox.aspx.cs" Inherits="WebApplication1.GridCheckbox" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"> <title>Untitled Page</title> </head> <body> <form id="form1" runat="server"> <div> <asp:GridView runat="server" ID="grdList" AutoGenerateColumns="false" onrowdatabound="grdList_RowDataBound"> <Columns> <asp:TemplateField> <ItemTemplate> <asp:CheckBox runat="server" ID="chkItem" Checked='<%# Container.DataItem %>' AutoPostBack="True" OnCheckedChanged="CheckChanged"/> </ItemTemplate> </asp:TemplateField> </Columns> </asp:GridView> </div> </form> </body> </html> Make sure you set the "AutoPostBack" to true. Then set it's OnCheckedChanged event handler to call your server side code. Now let's add some code to the Page_Load to give our grid something to bind to: protected void Page_Load(object sender, EventArgs e) { bool[] items = { true, true, false, false, true }; if (!Page.IsPostBack) { this.grdList.DataSource = items; this.grdList.DataBind(); } } In my example code, I'm just binding to a simple boolean collection to keep things simple. Now we need an event handler to respond to our checkbox event: protected void CheckChanged(object sender, EventArgs e) { } The code doesn't do anything yet but at this point you might be wondering how we figure out which checkbox we checked - normally, your grid is bound to a different row in some table. Each checkbox is related to that specific row. How or where do we get that from? The grid isn't much help in this case because it's not raising the event. If we were using a Button we could set it's CommandName and/or CommandArgument properties to hold something like the primary key of the row in our table. The checkbox control doesn't have any such properties. ASP.NET controls have an InputAttributes collection where you can place name/value pairs - this information is passed to the client (and back to the server), so it makes this relatively painless. First, add an event to onRowDataBound of the grid (already shown in the ASPX above). Now add the event in our code-behind: protected void grdList_RowDataBound(object sender, GridViewRowEventArgs e) { } We can't reference the control directly by name, since it was embedded in the grid and ASP.NET has made sure it's uniquely named. Let's use the FindControl method to do this. I've also added the code to add a Value attribute and added a member called m_row. I'm just using this so we can have a different value on each and every row of the grid. In a real application this is where you'd pull the primary key value from your datasource (probably using e.Row.DataItem). private int m_row = 0; protected void grdList_RowDataBound(object sender, GridViewRowEventArgs e) { CheckBox chk = e.Row.FindControl("chkItem") as CheckBox; if (chk != null) chk.InputAttributes.Add("Value", this.m_row.ToString()); this.m_row++; } Now we're just about done - all that's left is retrieving this value back on the server when the Checkbox is checked or unchecked: protected void CheckChanged(object sender, EventArgs e) { CheckBox chk = sender as CheckBox; string val = ""; if (chk != null) val = chk.InputAttributes["Value"]; } So our completed codebehind page looks like this: using System; using System.Collections; using System.Configuration; using System.Data; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.HtmlControls; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; namespace WebApplication1 { public partial class GridCheckbox : System.Web.UI.Page { private int m_row = 0; protected void Page_Load(object sender, EventArgs e) { bool[] items = { true, true, false, false, true }; if (!Page.IsPostBack) { this.grdList.DataSource = items; this.grdList.DataBind(); } } protected void CheckChanged(object sender, EventArgs e) { CheckBox chk = sender as CheckBox; string val = ""; if (chk != null) val = chk.InputAttributes["Value"]; } protected void grdList_RowDataBound(object sender, GridViewRowEventArgs e) { CheckBox chk = e.Row.FindControl("chkItem") as CheckBox; if (chk != null) chk.InputAttributes.Add("Value", this.m_row.ToString()); this.m_row++; } } } Now when you run this every time you check or uncheck an item you should see the page post-back (set a breakpoint on CheckChanged). You can now update your data, or whatever else you need to do in response to this event.
This has hit me more than once already. If you create an HTTP Handler (ASHX) file, by default the context object reference (passed in to ProcessRequest) doesn't contain a reference to the Session object, so attempting to access context.Session will fail. The fix is to add a "using System.Web.SessionState" to the using section, then inherit from the IReadOnlySessionState interface. Ex. using System.Web.SessionState; public class MyAshx : IHttpHandler, IReadOnlySessionState { } This will give you read only access to the session state. If you need to add to the session, inherit from IRequiresSessionState instead.
I've finally gotten all set-up in Microsoft's Empowerment program. I wish I could say it went smoothly, but that definitely wasn't the case. I think I may have signed up at the worst possible time - they were in the process of revamping their website which caused me a bunch of different problems. I finally got tired of playing e-mail tag and called Partner support a few weeks ago. They had been telling me to complete the application process, but every time I tried to log back into the Empowerment site it just told me my application had been denied and it wouldn't let me sign back up (or even tell me WHY it had been denied). When I called they took a look at it then told me they needed to have someone else take a look at the problem. I received an e-mail a few days later claiming the problem should be resolved. I tried logging back in but had the same problem I had been having. I called them again two weeks ago Monday and while I was on the phone they were finally able to clear whatever flag was keeping me from being able to resubmit my application - they definitely added a few more required fields from when I initially signed up. Last Monday I figured I'd try logging back into the Partner site and see if they had any way of checking the status. I ended up trying to sign up again but this time I got to a page which wanted me to agree to some EULA, but the buttons were all disabled. Was that a good sign or a bad sign? I found out two days later (on Wednesday) when I received my Empowerment CD's and DVD's. Unfortunately, they don't include any kind of documentation about how to actually get your license keys. Since I've worked at a few places which had their Universal subscription (when it was available), I assumed I might just be able to log into the MSDN subscriber download section using my MS Live ID. I clicked on the "Associate your subscription with your WIndows Live ID" link but it also wanted me to enter my Benefit Access Number, Subscriber ID, or MSPP Technical Contact ID. Unfortunately I didn't receive anything labeled that way in the kit they sent me. I tried using my partner ID and my MCP ID, neither of which worked. I dug through the partner website and found that I actually had a "Technical ID" associated with my name. To find this - log into the partner site and click on the "Requirements & Assets" menu at the top, then select "Associated People" At the bottom of the screen that opens is a list of all people associated with your company and their technical ID. I tried entering this and it unfortunately didn't work. I thought (maybe) the site required a Live ID e-mail address instead of my other e-mail address, so I entered it instead and saved it. Then I tried signing in again. At that point the site complained that I had entered the wrong info. in too many times and to try again in 5 minutes. At that point I gave up. The next day I dug up the MSDN support number and gave them a call. They had me re-enter the information I had tried the night before, fiddled with a few things and suddenly it let me log in! I know someone who is planning on signing up as well and I'm curious to see if it goes smoother for them than it did for me - hopefully it does. Links: http://www.rcs-solutions.com/blog/2007/10/12/MicrosoftsEmpowerProgramForISVs.aspx http://www.rcs-solutions.com/blog/2007/11/20/EmpowermentProgramUpdate.aspx https://partner.microsoft.com/40011351 http://msdn2.microsoft.com/en-us/default.aspx
We ran into an interesting performance issue a few weeks back. We had migrated our primary fileserver over to a new machine (it was previously running on the same machine as our website). We expected some of the pages on our website to be a bit slower since the files were no longer on the same box (we have both data in VFP and in SQL Server 2005), but some of them were suddenly painfully slow. They went from 1-2 seconds to more than 7+ seconds. We talked about installing a dedicated network card to link two servers to help, but it still didn't seem like the performance should take that much of a hit. My boss spent some time tracing the problem and found it was really slow when we opened the company file from our accounting system (Sage Pro). I've talked about the performance of USE in the past, but it was still amazing to see how slow it was. He tried using a mapped drive and UNC mapping but the performance of each was about the same. This particular table is part of a database, so he tried prefixing the USE statement with the database name, ex. USE F:\PathTo\Data\MyDatabase!MyTable IN 0 - the difference was amazing. That 7+ seconds dropped back down to 1-2 seconds: a bit slower than it was when it was local, but still much faster than it had been. Resolving the database is apparently very slow.
I was just checking out the PPCGeeks website, checking up to see if anyone had any status updates on the Sprint Mogul PPC. I've had this phone since late August, and have been waiting for a promised upgrade which is supposed to finally enable the built-in GPS-A support and support for EVDO Rev A. It finally looks like it may actually see the light of day on Monday, according to this post. It would have been nice to have had it released on Friday so I'd have the weekend to get it installed and get my phone customized again, but as a developer, I can appreciate not doing a release on a Friday. I'm hoping Google Maps will support the built-in GPS. I've got an external blue tooth GPS I could use, but it's just not as convenient as something built-in. I'm not really looking forward to reinstalling all of my applications and customizing it with the HTC Touch interface (which is a MUCH nicer interface than the stock one), but thankfully this isn't something I have to do on a regular basis. Links: http://forum.ppcgeeks.com/showthread.php?t=20363 http://www.america.htc.com/support/mogul/software-downloads.html http://www.google.com/gmm
I just uploaded a new version of the VFP calendar. I've fixed the problem where the drop-down version of the calendar wouldn't collapse if you clicked away from it. I initially thought it would be a simple matter of just adding some code to the calendar form's LostFocus event. That appeared to work, but had the side effect of breaking the drop-down button. If the calendar was being displayed and you then tried to click on the drop-down button to hide the calendar, the LostFocus would fire (hiding the calendar), then the Click event of the button would fire which would redisplay the calendar. Not quite what I was expecting. I experimented with a few different ways of attempting to address this, but I just couldn't get the events to fire the way I wanted. Ultimately, I ended up adding code in the Click event to save off the last time since the calendar was hidden. If it is under the configured minimum interval, we don't do anything (the assumption is the the LostFocus just fired and hide the calendar). If the interval has been exceeded, the calendar is displayed as normal. A little weird, but relatively simple to code. I've also added another sample to show how the rcsDateTimePicker controls can be hooked together for Start/End date scenarios. The controls keep the start date from being later than the ending date. The parent (Start) / child (End) relationship is setup via two properties on the control: uParentPicker and uChildPicker. These properties are evaluated to resolve to an object reference to the proper control. Links http://www.rcs-solutions.com/downloads.aspx
I'm happy to say my winter cleaning give away was a success. Almost all of the books that were available have new owners (although they may still be waiting for them, since they were shipped at the book rate which can take forever), and my huge pile of computer equipment is also gone. I had a taker for the computer equipment within a day of posting it on my local Freecycle site. I thought he'd pick through the rubble, but he ended up taking the whole pile (including my Windows NT 3.51, NT 4.0, and Windows 2000 Resource Kits - those alone took up a full bookshelf). There are a few books still available, if anyone is interested. Links: http://www.rcs-solutions.com/blog/2007/12/13/WinterCleaning.aspx http://www.freecycle.org
I was just reading some of the threads over on the UT and ran across one where someone was looking for a drop-down calendar control. Someone suggested they look at the one that I've got in the downloads section on my site. Unfortunately, the version I've had up forever doesn't actually have a "drop down" version of the control. I've had an updated version for a long time now, but I've never gotten around to posting it (until now). This thread kicked me in the butt a little to update this on the site. Here's a screenshot of the updated control: Hopefully you find it useful. Links: http://www.universalthread.com http://www.rcs-solutions.com/Downloads.aspx
I've been spending a lot of time lately doing some winter cleaning. We're trying to free up some space in the basement for a play area for Brendan. It's amazing how much stuff you can collect. We've thrown away a LOT of stuff - I'm pretty sure the garbage guys hate us by now. Jenn mentioned that one of them didn't look too pleased when he tried to lift one of the bags we put out. We've been donating anything with think might still be useful, and we have people who drive through our subdivision on garbage days looking for interesting finds. More power to them, I say; I'd rather someone finds some use for this stuff instead of throwing it out. Besides, who's got the patience for a garage sale? And who really wants to deal with people trying to get half price for an item marked $1 that originally cost $50. Since I've been involved with computers for quite some time (and not all of it as a developer), I've managed to collect quite a collection of old computers. Old Pentiums, 486's, a few 386's, motherboards, cases, power supplies, an unbelievable amount of cables, network cards, video cards, etc. I'm planning on posting that stuff on our local freecycle site to see if anyone might be interested in it before tossing it. One of my regrets with a lot of this is that I didn't give it away sooner, while it still may have been of more use to someone. I guess that may have been why I kept it. A big part of this collection is a ton of books and magazines. I've whittled the magazines down to something manageable, but I still have way too many books. I'm sure I'll add more to the list as soon as I can convince myself that I really don't need them anymore, and once I have time to go through the ones still hiding in the basement (and hopefully before some of them aren't useful anymore). Here's a list of what's on the chopping block (you might be surprised; there are some good books here): - Apple II Plus/IIe Troubleshooting & Repair Guide, Robert C. Brenner. Sams. ISBN: 0-672-22353-8
- DNS and BIND 3rd Edition, Paul Albitz & Cricket Liu. O'Reilly. ISBN: 1-56592-512-2
XML Extensible Markup Language (w/CD), Elliotte Rusty Harold. IDG Books. ISBN: 0-7645-3199-9 The Unified Modeling Language User Guide, Booch, Rumbaugh, Jacobson. Addison-Wesley. ISBN: 0-201-57168-4 - The Visual FoxPro 3 Codebook (CD is missing), Yair Aan Griver. Sybex. ISBN: 0-7821-1648-5
Object Orientation in Visual Foxpro, Savannah Brentnall. Addison-Wesley. ISBN: 0-201-47943-5 Object Models: Strategies, Patterns, & Applications (Second Edition), Coad, North, Mayfield. Yourdon Press. ISBN: 0-13-840117-9 - Visual Basic 6 Business Objects, Rockford Lhotka. Wrox. ISBN: 1-861001-07-X
ASP.NET 2.0 Unleashed, Stephen Walther. Sams. ISBN: 0-672-32823-2 Hacker's Guide to Visual FoxPro 6.0, Granor, Roche. Hentzenwerke Publishing. ISBN: 0-96550-936-2 The Inmates Are Running The Asylum, Alan Cooper. Sams. ISBN: 0-672-31649-8 About Face: The Essentials of User Interface Design, Alan Cooper. IDG Books. ISBN: 1-56884-322-4 The Improvement Guide, Langley, Nolan, Nolan, Normal, Provost. Jossey-Bass. ISBN: 0-7879-0257-8 HTML: The Complete Reference (Second Edition), Thomas A. Powell. Osborne. ISBN: 0-07-211977-2 - Effective Techniques for Application Development w/VFP 6.0, Booth, Sawyer. Hentzenwerke. ISBN: 0-96550-937-0
What's New in Visual FoxPro 8.0, Granor, Hennig. Hentzenwerke. ISBN: 1-930919-40-9 CrysDev: A Developer's Guide to Integrating Crystal Reports, Craig Berntson. Hentzenwerke. ISBN: 1-930919-38-7 Advanced Object Oriented Programming w/VFP 6, Egger. Hentzenwerke. ISBN: 0-96550-938-9 Client/Server Applications w/VFP & SQL Server, Urwiler, DeWitt, Ley, Koorhan. Hentzenwerke. ISBN: 1-930919-01-8 C# Unleashed, Joseph Mayo. Sams. ISBN: 0-672-321-22-X Measuring and Managing Performance in Organizations, Robert D. Austin. Dorset House. ISBN: 0-932633-36-6 Windows 2000 Server Resource Kit (No CD), 8 books total
If anyone might be interested in this stuff (books or computer techno-rubble), drop me a line (I can take a pic. of the computer stuff). All of it free as long as you pick up the shipping cost. I hope I don't regret giving away some of this, these books have served me well <g> Links: http://www.freecycle.org
While I'm working, I tend to open a LOT of windows (and leave them open). That way I'm not wasting time navigating around to the same places again and again. It's almost physically painful for me to watch other users open something like Explorer, spend the time navigating to a folder to do something like copy a file, then close the window (to satisfy some notion of keeping their desktop "Clean", I guess). Then, two minutes later, open Explorer again, navigate to the SAME folder, to do something else. I've found if I've spent the time opening some app., a website, etc. I'm very likely to need to have access to the same window a few more times within a fairly short period of time. In addition, there are a bunch of windows I leave open all the time. Things like one or more DOS prompts, maybe a few instances of Explore, Firefox w/a bunch of tabs open in each, SQL Management Studio, VFP, Visual Studio, Excel, Outlook, etc. Besides using up memory, there is very little downside (and my desktop at work and at home have 4GB, so I usually have a bit of memory to spare). I also don't reboot very often; sometimes weeks at a time if I can help it.
However, I still occasionally notice that things sometimes start to get flaky. I'll try right-clicking on a table in SQL Management studio to browse a table and the context-menu doesn't appear. Or I'll open IE and some of the menus are missing. Or Windows Explorer isn't "painting" correctly. Whatever. It seemed to be related to the number of programs open - not necessarily the amount of free memory. I'd did some digging and it seems to be related to the desktop heap. I found a great link which goes into detail about it: http://blogs.msdn.com/ntdebugging/archive/2007/01/04/desktop-heap-overview.aspx I ended up downloading the dheapmon tool mentioned and installing it. It has to be installed from the command prompt and requires access to the symbol libraries. I don't have them installed on my machine, so I ended up using the environment variable mentioned in the CHM file. C:\>cd \kktools\dheapmon8.1\x86 C:\kktools\dheapmon8.1\x86>SET _NT_SYMBOL_PATH=symsrv*symsrv.dll*C:\Symbols*http://msdl.microsoft.com/download/symbols C:\kktools\dheapmon8.1\x86>dheapinst.exe C:\kktools\dheapmon8.1\x86>dheapmon -l C:\kktools\dheapmon8.1\x86>dheapmon Desktop Heap Information Monitor Tool (Version 8.1.2925.0) Copyright (c) Microsoft Corporation. All rights reserved. ------------------------------------------------------------- Session ID: 0 Total Desktop: ( 5824 KB - 8 desktops) WinStation\Desktop Heap Size(KB) Used Rate(%) ------------------------------------------------------------- WinSta0\Default 3072 91.8 WinSta0\Disconnect 64 4.5 WinSta0\Winlogon 128 10.1 Service-0x0-3e7$\Default 512 27.0 Service-0x0-3e4$\Default 512 11.9 Service-0x0-3e5$\Default 512 4.3 SAWinSta\SADesktop 512 0.5 __X78B95_89_IW\__A8D9S1_42_ID 512 0.5 ------------------------------------------------------------- C:\kktools\dheapmon8.1\x86>
It looked like the WinSta0\Default was the issue; I'm at 91.8% usage. I followed the docs and it looked like I should modify the Windows registry key located here: HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\SubSystems\ It was set to:
%SystemRoot%\system32\csrss.exe ObjectDirectory=\Windows SharedSection=1024,3072,512 Windows=On SubSystemType=Windows ServerDll=basesrv,1 ServerDll=winsrv:UserServerDllInitialization,3 ServerDll=winsrv:ConServerDllInitialization,2 ProfileControl=Off MaxRequestThreads=16
I changed it to:
%SystemRoot%\system32\csrss.exe ObjectDirectory=\Windows SharedSection=1024,4096,512 Windows=On SubSystemType=Windows ServerDll=basesrv,1 ServerDll=winsrv:UserServerDllInitialization,3 ServerDll=winsrv:ConServerDllInitialization,2 ProfileControl=Off MaxRequestThreads=16 (note that I changed the "3072" to "4096"). This should give me a bit more heap, so hopefully this will help. It looks like you have to reboot after making this kind of change, so it'll be a bit before I can tell whether it's really fixed the issue or not. I'm hopeful. If not, I'll probably try bumping it up a bit more. Links http://blogs.msdn.com/ntdebugging/archive/2007/01/04/desktop-heap-overview.aspx
I'm not sure when this was added (if it's just in VS 2008 or if it's been there since VS 2005), but I just noticed this while I was updating the website (I just added a download section). I created a new master page for the site, then updated the original home page with one that used the new master page. The master page is now home to the header, menu, and footer with a "hole" punched in the middle for my custom content. Then I added the download page and started adding files. You might not have noticed before, but the home page has a footer which moves with the browser; it basically sticks to the bottom by using a bit of CSS. This works well on the home page, and appeared to be OK while I was working on the download page. That is. up until the number of downloads forced the page to scroll. At that point the footer actually sat on top of the downloads listed, which isn't quite the effect I was hoping for. If I changed the footer style to float, it worked well for the download page but was pretty ugly for the home page (since it's a bit sparse the footer was like 1 inch below the header). I played around with the idea of embedding a div tag in my home page that had relative heights to force the content back to where it belonged. That didn't work out too nicely, so I briefly thought about using JavaScript to handle this instead. That seemed like overkill, so I nixed that idea. If the footer was defined in my home page content section, I could just override the CSS style for that one page. However, it's in the master page. I could define two CSS files instead, but I really didn't like the idea of having to maintain two stylesheets. That's when I noticed a new section in my master page - a content holder for the head section of the page: That made it really simple to just add an overriding style (with the same name as the one defined in my CSS file) into the home page content. I had to move the CSS link above the ContentPlaceHolder so that it was defined before my overriding style. Then I just added a <style> section inside of the header and I was good to go. This isn't earth shaking stuff, but it was a nice addition.
I ran across this in the comments section over at Coding Horror: "I'm bitter, angry and I hate you all. Isn't that proof that I've worked in the software development industry for a long time?" Links http://www.codinghorror.com
I mentioned in a previous post about signing up for the Microsoft Empowerment program. At the time, they mentioned it may take a while to hear from them. I figured I'd get at least a "hey, we got your application, thanks" e-mail. So far, not a thing. It's not really clear on how (if?) I can even check the status on this. This was the original signup link: https://partner.microsoft.com/40011351 (strange, my original post should have had this link as well - I wonder where it go lost). I just checked it again and they mention that the site is undergoing maintenance from 11/8 - 11/30. Great - I wonder if my application ended up in a black hole. I was really hoping to have the licenses taken care of by now, since some of my MS software demos are about to expire.
I thought it might be fun to convert an old VFP app. I wrote when I was playing around with web services inside of VFP to work inside of Visual Studio. It was a simple program which connected to a webservice which serves up a new Dilbert comic every day, then it displays it in the VFP desktop. I've been thinking about doing it for a while - my initial thought was to modify and/or customize the RSS feed which Visual Studio uses to display it's latest news. Unfortunately, I couldn't figure out how to get it to display images - it seems to reformat the feed and strip out any of the markup. A bunch of googling didn't turn up much either. I briefly looked at the Visual Studio SDK a while back but the licensing cost was prohibitive (something like $10K). I could use it for development and on my machine, but I wouldn't be able to distribute it w/o the license. However, Microsoft just recently changed all of that with the latest release; ran across this via Code Magazine's Focus issue on Visual Studio Extensibility. You can download the magazine for free from: http://www.code-magazine.com/focus/Index.aspx. I downloaded the SDK and got it installed with VS 2007 Beta 2 (yeah, yeah, VS 2008 was just released). Then I kind of lost momentum and didn't do much with it. This morning, while I had a bit of dead-zone time (that's the time between interruptions that you KNOW are going to occur on some days, and they're going to be spaced so close together that it's not worth getting involved in anything too mentally strenuous). I ran Visual Studio Integration Package wizard and had a stub project up in running in a few minutes (I picked the "Tool Window" option). It creates a MyControl.cs form with a button on it, so I just deleted the button and added a PictureBox in it's place. Then I just did almost a straight port of my VFP code which checks to see if it's already downloaded the comic (and if so, it checks the dates to see if a newer one should be available). If necessary, it downloads the file and dumps it into the PictureBox. They've set things up so you can just "run" this project and it will automatically start up a new instance of VS to test in, which is really nice. If you've never used a web service in VS, it's really simple - add a reference, enter the address of the web service and it builds a nice simple wrapper class for you. I guess I could have done this via WCF, but I really didn't want to complicate things. My first test seemed OK - I selected View > Other Windows and picked my new Dilbert A Day option. The toolbar window appeared with the latest comic - so far, so good. It docks like any other VS tool, so I docked it to the bottom of the screen. I closed my debugging instance of VS and restarted it to see if it still worked. Nope. No image. I ended up having to move the code which downloaded the new comic out of the control and into the class which instanciates the form (MyToolWindow.cs). Once I had that working, I decided it really needed to have a custom Dilbert icon. I snagged an image from the Dilbert site, cropped it and resized it down to 16x16. There are two resources embedded in the project which include a strip of icons. I was lazy, so I just replaced the second icon (which seems to be the default selected inside of the pre-generated code) in both of the BMP files. Here's what it ended up looking like docked at the bottom of VS: Here's it expanded: And the new menu option:  I'm pretty happy with how this came out - this took me literally 20 minutes to put together. Granted, this is hardly a taxing application - I could easily be swearing at the SDK if the example were any more complex. But they at least did a nice job to help get you up and running pretty quickly. OK, I admit it, this post is a bit of a tease. I had hoped to get this posted as a installable component w/source tonight. I figured I could get the installer working (ideally as a "ClickOnce" app, since this gives me an excuse to play with that, although I'm not even sure it can be deployed this way) relatively quickly. However, once I started looking at what was necessary for deployment I came across the fact that I need to get some sort of Package Load Key (PLK) from Microsoft, which requires me to sign up for the Microsoft Visual Studio Industry Partner Program website. That's actually pretty painless, and it looks like they've got a simple interface to request this key: At that point, it wants me to answer a bunch of questions about the download location, product logo, etc. that I wasn't really ready to deal with. The final deployment aspect of all of this also seemed to be missing in the docs I've read. I ended up having to do some more digging through the Code Focus issue and came across this link which has a video which covers deployment (yeah!). I'll have to watch the full video (it's around 20 minutes) before I can finish this thing up. I'll follow up with that in another post, along with a bit of code from the project and maybe a simple comparison of the VFP code vs the .NET code. I guess I'll also have to put up a downloads section to make it easier to find all of this stuff, especially if I need to provide all of that to generate a PLK. Let's hope this 20 minute quickie project doesn't turn into some huge odyssey - unfortunately, that seems to happen a lot in development, doesn't it? Links: http://www.code-magazine.com/focus/Index.aspx http://msdn2.microsoft.com/en-us/vstudio/aa700819.aspx http://www.vsipmembers.com/ http://msdn2.microsoft.com/en-us/vstudio/aa700829.aspx
This falls into the "hey, I didn't realize this was available" category. In the 2.0 version of the .NET framework, support was added for nullable types, I'm guessing primarily for improved interaction with databases. Previously, nulls were a real pain to deal with; now they're only a bit more work (the pains not completely gone, but it's not as bad anymore). Lets say you've got a date time field in your database, it can have an actual date filled in or it can be null. In prior versions, you had to rely on "magic" dates to do translations when loading and saving your data (eg. 1/1/1900 is really null). Now you can just define a nullable type by prefixing the type with a question mark ? DateTime? activeStart = (DateTime?)row["ActiveStart"]; That's not the point of this post though. They've added a nice operator ?? - so let's suppose I've got a calendar control that returns null if no date has been selected. I'd like to default to the current date if that's the case. I can write code like this: DateTime start = System.DateTime.Now; if (this.startCalendar.SelectedDate != null) start = this.startCalendar.SelectedDate; Or I can use the ?? operator: DateTime start = this.startCalendar.SelectedDate ?? DateTime.Now; That's much simpler code to understand (at least to me). I like this a bit better than the normal ternary "if" support, which I always find a bit difficult to read:
string sample = a==b ? "A=B" : "A<>B" Maybe it will grow on me eventually...
The question shouldn't be, "Can we do this?" but, "Should we do this?". I think companies sometime get so excited about the possibility of doing "X" that they forget to ask themselves if it's something that should even be done:
- What are the risks?
- Does the payoff justify the amount of work required?
- Are there more important things that should be done first?
- What is the simplest way of accomplishing our goal (it may not even include doing "X")?
- Is this our core business or should we look to an outside source?
- Does this even make sense?
I've been using Subversion along with TortoiseSVN (and VisualSVN inside of Visual Studio) for a few months now. It's been relatively painless, up until recently when I've attempted to really start using it for more than the standard "make changes, check it in". Unlike Visual SourceSafe where you check out a file and it gets locked until you check it back in (which is just the default; it can actually be changed to allow shared checkouts), Subversion lets everyone use all the files all the time, then merge on check in. I haven't attempted to try Subversion with any of my VFP projects yet, it's been strictly the .NET applications, so I haven't had to try to deal with merging the binary files created by VFP (eg. for forms, the SCX and SCT files, or for classes the VCX & VCT files). If you're enterprising, you can try out TwoFox from Christof Wollenhaupt. I'm leaving that hill for another day. I ended up moving around my repository to support the more standard folder structure, since I didn't do this when I first started out. So my folders are now in this format: branches tags trunk |--Project A |--Project B The other suggested route was to put the branches/tags/trunk folder inside the project folders themselves, ex.
Project A |--branches |--tags |--trunk This looked a little too confusing, especially since most of our projects are build into a single solution. To move everything around, I opened TortoiseSVN and went into the repository (right-click in Explorer, select Repo-Browser). Then I navigated to where I had my .NET development stuff, \Server\DotNetDev, right-clicked and selected Create Folder. I created the branches folder (which is meant, strangely enough, for any branches in the source code), then created the tags folder (this is equivalent to a label in VSS, although Subversion doesn't make a distinction between a branch and a tag), then the trunk folder (which is meant to hold the primary set of source code). Then I dropped down to a command prompt and moved everything from it's original location to it's new location: svn move -m "Relocating Project" svn://Server/DotNetDev/Project svn://Server/DotNetDev/trunk/Project I kept the Repo-Browser open and hit F5 to make sure the change took place (it did, so far, so good). The rest of this didn't go so smoothly. I needed to point my local copy of the source to the new repository location. It turns out there are two different options to do something like this: Relocate and Switch. I thought I might want Relocate, so inside of TortoiseSVN I right-clicked on my DotNetDev folder and selected TortoiseSVN > Relocate. Here are my notes of how this went: - Immediately see scary message about me doing this, did some Googling, decided I really DO want to relocate. - Get another error about it not being the root. Decided to try Switch instead. - Get another error about one of my projects not being in the same repository. - Realize I may have mistyped the new repo. name, try svn://Server/DotNetDev to svn://Server/DotNetDev/trunk instead. - Get errors about it not existing in that location for that revision. - Great. - Select the option to Update to Revision. - Suddenly panic that I'm going to lose my changes that haven't been checked into source control yet. Wish I had thought to make a backup first. - It doesn't seem to help. - Realize I had one folder which still pointed to it's original source on the web (I'm using an open-source project that I had grabbed via TortoiseSVN, so it's still pointing to that website) - Try to relocate it and get errors about relocating only changing the repository part of a URL (regardless of what I try to change) - Get irritated and go to lunch. - Get back from lunch. - Delete the .svn folder that was pointing to the wrong server (the open source one). - Make a backup of the directory where the .svn folder was. - Get new version via SVN checkout for that specific folder. - Try to use Switch. - Get error about needing to cleanup the project. - Cleanup. - Try to use Switch again. - Get a different error about some file not being able to be added to the repository because it already exists (it's the same folder I've been having problems with). - Erase the entire folder including the .svn folder this time. - Do a checkout on this folder again. - Finally get around to making a backup of all my files, just in case. - Try Switch again, it seems to do something and not give any errors. - Ran a diff on the folder I had problems with to check to see if I lost anything and/or grab files from my backup. - Fire up VS and force a rebuild to see what/if anything was broken. - All good (!) Relocate = Source is switched to a completely different server. Switch = Source is on the same server in a different directory. I'm not entirely sure why these are two different options. Fast forward two weeks. I decide to test out the idea of creating a private branch where I can make all my daily changes (and do a check in at the end of the day). I basically don't want to check in stuff that isn't complete, may break things for other developers, etc. But I want to make sure it gets checked in daily so it's backed up. Branching seemed like a good way of handling this. The initial branch seemed to go smoothly so I made changes over the next week or so and kept checking them in. Today I decided to merge the branch back into the trunk so we could get a good copy of the files on another dev. machine. It seemed to go about as well as moving the repository (I'll spare you all the ugly details).
Ultimately, I think the problems were related to the fact that I had a completely different mental model of how merging a branch should work: I though you would just select the option to merge, then select the branch you want to merge from (my local copy) and select the branch you want to merge to (the trunk), let it merge and resolve any conflicts (if any). The UI even looks like this is how it should work. It doesn't. I must have tried merging 20 different times, each time with different errors or issues. I read and re-read the docs and just didn't seem to understand how this was supposed to work. Here's what finally worked for me: I had to check in all of my changes into the branch I had been working with, then use "Switch" to switch back to the trunk copy (which does some scary things like delete source code in your local copy). Once I had a copy of the trunk, I did the Merge again, pointed the "From" option in TortoiseSVN to the trunk, not my local copy. Pointed the "To" to the branched copy I had been working on. Then I selected the last revision before the branch in the trunk and all of the revisions in my branch, and it seemed to merge OK. Then I committed my local copy back into the trunk. It appears you need to track the revision numbers manually (the check-in notes are probably the simplest) when merging branches back in; so, for example, if I had revisions 50-55 in a branch which I then merge into the main trunk, then make changes 56-60 in the branch again, I have to make sure I only merge revisions 56-60 into the trunk. Otherwise, I risk duplicating changes (essentially, Subversion doesn't track your merges). I looked at the roadmap and it appears that functionality is scheduled for build 1.5 (it's currently at build 1.4.5). That's definitely going to be a welcome enhancement. One thing I noticed in the docs is that they mention that new Subversion users have problems with the From/To issue, and with picking the correct revision to merge against. It's pretty clear they're forcing users to deal with implementation details that I think should be hidden, so that the mental model most of us have of all this actually matches what we need to do in the UI to accomplish things. Having to mention that most developers make mistake "X" or "Y" should be a red flag that maybe things need to be changed. The one saving grace of all this is that I haven't lost any changes yet. I'm starting to get in the habit of checking all my files in before I attempt to do anything out of the ordinary (which seems to be a good practice to get into). Links http://subversion.tigris.org http://tortoisesvn.tigris.org http://www.visualsvn.com http://www.foxpert.com/downloads.htm
One thing I've learned while using SQL Server is (whenever possible), perform as much as you can on the server. That means either stored procedures or SQL commands passed up to the server. It also means thinking about problems as sets, as opposed to a more procedural approach. Visual FoxPro makes it easy to blur the lines between SQL commands and normal procedural code (mixing and matching as you'd like), so you tend to start getting locked into thinking about data on those terms. While some of this translates directly to SQL Server (obviously, the SQL language itself), other things don't. I recently decided to add a new foreign key to a table; the structural specifics aren't particularly important so I won't go into too much detail about them. Essentially, I had a parent (A) and child table (B). There is a posting process which takes a snapshot (C) of some or all of the records in the child table for a specific parent. This snapshot includes a foreign key back to the original child record but didn't include a foreign key back to the parent (since it could be derived by joining to this child table). I began to realize that, because of the number of records involved (it could be as many as 250,000 records in the child (B) for a single parent record (A), which meant up to 250,000 records in our snapshot table) that most of the queries were really slow because of the extra hop involved in resolving this parent key. So, I decided to go ahead and add a foreign key into the snapshot table (C) to the parent (A). That also meant I needed to go back and fill in the new foreign key in the Snapshot (C) table. In VFP, you might just open the tables, SCAN through the Snapshot (C) table, do a SEEK into the Child (B) table for the foreign key back to the Parent (A) table and save it into the Snapshot (C) table. That works OK if the tables are local, what about with SQL Server? I guess you could take the same approach; pull a copy of the records from the Snapshot (C) and Child (B) tables locally, index them, then run through the same SCAN loop (or if you're really old school, set a relation and just do a REPLACE). But how long will that take - remember we're talking about pulling down up to 250,000 records in the Child (B) table, plus another 250,000 records from the Snapshot (C) table, then updating all of the records in Snapshot (C) table. Multiply that by the number of master records (in my case, only 20-30 of the master records had anywhere near 250,000 records but we're still talking millions of records). Suddenly the above approach doesn't look like such a good idea. Thankfully, SQL includes provisions in the UPDATE command to do exactly what I wanted to. It would still take a while to run (I clocked it around 25 minutes), but it was a significantly better solution than a SCAN loop. Here's what the query ends up looking like: UPDATE Snapshot SET Snapshot.fk_Parent = Child.fk_Parent FROM Child INNER JOIN Snapshot ON Snapshot.fk_Child = Child.PrimaryKey So what does this actually mean? We're basically saying, inside of our Snapshot (C) table set our new foreign key equal to the Parent (A) foreign key in the Child (B) table. The FROM/INNER JOIN defines how to match the record in the Snapshot (C) to the one in Child (B). Simple and fast.
Here's a link to an article about keys in SQL. Make sure you read the comments. I wonder where most people fall on this subject and the split (if any) between developers and DBA’s (and people have to fill both roles)? Personally, I don’t give a crap about whether identity keys are “exposed physical locators”. How about GUIDs or Stored Procedures which generate an incrementing number instead? I’ll be honest, I’ve never used them to identify the physical location. It’s just that I’ve found that “natural keys aren’t” (I think I first heard that from Jim Booth |