<?xml version="1.0" encoding="utf-8"?>
<feed xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xml:lang="en-ie" xmlns="http://www.w3.org/2005/Atom">
  <title>Tim Mackey's Weblog</title>
  <link rel="alternate" type="text/html" href="http://tim.mackey.ie/" />
  <link rel="self" href="http://tim.mackey.ie/SyndicationService.asmx/GetAtom" />
  <icon>favicon.ico</icon>
  <updated>2010-06-17T09:44:58.435375+01:00</updated>
  <author>
    <name>Tim Mackey</name>
  </author>
  <subtitle>mostly.Net</subtitle>
  <id>http://tim.mackey.ie/</id>
  <generator uri="http://dasblog.info/" version="2.3.9074.18820">DasBlog</generator>
  <entry>
    <title>Nokia Ovi Suite Calendar Sync Cancelled error message</title>
    <link rel="alternate" type="text/html" href="http://tim.mackey.ie/NokiaOviSuiteCalendarSyncCancelledErrorMessage.aspx" />
    <id>http://tim.mackey.ie/PermaLink,guid,f165282b-2995-4b18-97f5-dd9cf1c8a6e2.aspx</id>
    <published>2010-06-17T09:44:58.435375+01:00</published>
    <updated>2010-06-17T09:44:58.435375+01:00</updated>
    <category term="General" label="General" scheme="http://tim.mackey.ie/CategoryView,category,General.aspx" />
    <author>
      <name>Tim Mackey</name>
    </author>
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">I have a nokia E52 and in general i'm very
happy with the Nokia Ovi Suite software.  But today it refused to synchronise
my calendar with a "Sync Cancelled" error message, other items synchronised fine. 
The solution was to reset the nokia profile/files on my computer.  Make sure
to close Outlook, Ovi Suite, and end the "nokiamserver.exe" process.  Then open
up explorer and browse to C:\Users\Name\AppData\Local and C:\Users\Tim\AppData\Roaming. 
You could delete these files, but just in case i renamed the "Nokia" and "Nokia Ovi
Suite" folders to "xNokia" and "xNokia Ovi Suite".  If anything goes wrong you
can always rename them back to their original names. 
<br />
Then open up Ovi Suite again and reconnect the phone.  It should synchronise
fine then, there must be some bug with the calendar synchronisation that can corrupt
the local database. 
<br /><p></p><img width="0" height="0" src="http://tim.mackey.ie/aggbug.ashx?id=f165282b-2995-4b18-97f5-dd9cf1c8a6e2" /></div>
    </content>
  </entry>
  <entry>
    <title>Excel interop with Asp.Net</title>
    <link rel="alternate" type="text/html" href="http://tim.mackey.ie/ExcelInteropWithAspNet.aspx" />
    <id>http://tim.mackey.ie/PermaLink,guid,4e0a72af-03e6-441d-bcd5-d8ed406d5209.aspx</id>
    <published>2010-06-11T18:32:37.92325+01:00</published>
    <updated>2010-06-11T18:32:37.92325+01:00</updated>
    <category term="Asp.Net" label="Asp.Net" scheme="http://tim.mackey.ie/CategoryView,category,AspNet.aspx" />
    <author>
      <name>Tim Mackey</name>
    </author>
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">Ran into a permissions problem today using <a href="http://www.codeproject.com/KB/office/fasterexcelaccesstoc.aspx">Excel
interop code</a> from within ASP.Net, which had worked fine from a windows forms application.<br />
Thanks to '<a href="http://www.mofeel.net/61-microsoft-public-dotnet-framework-interop/6941.aspx">Frosty</a>'
for his post which explains how to enable the appropriate permissions, reproducing
it here in case the link ever goes down:<br /><br /><blockquote>From command prompt / start-&gt;run... type dcomcnfg<br /><br />
Select Component Services-&gt;Computers-&gt;My Compter-&gt;DCOM Config<br /><br />
Scroll down and select Microsoft Excel Applicaton<br /><br />
Right click on Microsoft Excel Applicaton and select properties.<br /><br />
Select the Security tab<br /><br />
In Launch Permissions group box click Edit button.<br /><br />
Add the appropriate user for your particular situation. In my case, I<br />
selected MyDomainName\Domain Users.<br /><br />
Make sure that Allow check box is checked for your appropriate user.<br /><br />
Click OK<br /><br />
In Access Permissions group box click Edit button.<br /><br />
Add the appropriate user for your particular situation. In my case, I<br />
selected MyDomainName\Domain Users.<br /><br />
Make sure that Allow check box is checked for your appropriate user.<br /><br />
Click OK<br /><br />
Excel interop will now work via asp.net<br /></blockquote><p></p><img width="0" height="0" src="http://tim.mackey.ie/aggbug.ashx?id=4e0a72af-03e6-441d-bcd5-d8ed406d5209" /></div>
    </content>
  </entry>
  <entry>
    <title>Compact Framework - Using the Treeview to replace the ListBox</title>
    <link rel="alternate" type="text/html" href="http://tim.mackey.ie/CompactFrameworkUsingTheTreeviewToReplaceTheListBox.aspx" />
    <id>http://tim.mackey.ie/PermaLink,guid,caab5837-eebf-428b-a239-a5b266046f57.aspx</id>
    <published>2010-05-26T15:51:34.401+01:00</published>
    <updated>2010-05-26T15:54:46.839125+01:00</updated>
    <category term=".Net Compact" label=".Net Compact" scheme="http://tim.mackey.ie/CategoryView,category,NetCompact.aspx" />
    <author>
      <name>Tim Mackey</name>
    </author>
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">As any compact framework developer will
tell you, the out-of-the-box ListBox control is a bit crap because it doesn't support
horizontal scrolling.  what were they thinking??<br />
anyway, after several work-arounds to this problem, i finally realised that the treeview
control does support horizontal scrolling, and you can load up a treeview to look
just like a listbox, having all the nodes added at the top level, i.e. a "flat" treeview.<br />
the extra bonus with this approach is that Windows Mobile 6.5 has a brilliant touch
friendly Treeview control that you can finger-drag up and down and left and right
(at least the HTC HD2 has this, i can't vouch for other devices). 
<br /><br />
i've included a "Chooser" class i wrote to make user selections easy to code for. 
to use the class, use the following approach, the SelectedItem string is kept as a
static variable in the Form so you can close and dispose the form straight away, and
still catch the "return" value of the form. If you pass in the parameter to use Checkboxes,
you can access the CheckedItems static property which is a List&lt;string&gt;.<br /><pre>Chooser c = new Chooser("Select Item", new string[]{"Option 1", "Option 2", "Option 3"}, true, true); 
<br />
DialogResult d = c.ShowDialog();<br />
c.Dispose();<br />
if(d != DialogResult.Cancel)<br />
MessageBox.Show("You chose " + Chooser.SelectedItem);<br /></pre>here's the class then:<br /><pre>using System;<br />
using System.Collections.Generic;<br />
using System.ComponentModel;<br />
using System.Data;<br />
using System.Drawing;<br />
using System.Text;<br />
using System.Windows.Forms;<br />
using System.Threading;<br /><br />
namespace IbenzaMobileUtil<br />
{<br />
public partial class Chooser : Form<br />
{<br />
private MainMenu mainMenu1;<br />
private MenuItem mmOK;<br />
private MenuItem mmCancel;<br />
private Label lblCaption;<br />
public static string SelectedItem = null;<br />
public static List&lt;string&gt; CheckedItems = null;<br />
private TreeView treeView1;<br />
private bool selectInvokesClose;<br /><br /><br />
public Chooser()<br />
{<br />
InitializeComponent();<br />
}<br /><br />
private Chooser(string Caption, bool FullScreen, bool SelectInvokesClose)<br />
{<br />
InitializeComponent();<br />
CheckedItems = new List&lt;string&gt;();<br />
SelectedItem = null;<br />
this.selectInvokesClose = SelectInvokesClose;<br />
this.lblCaption.Text = Caption;<br />
this.mmOK.Enabled = false;<br /><br />
if (FullScreen)<br />
{<br />
this.WindowState = FormWindowState.Maximized;<br />
this.FormBorderStyle = FormBorderStyle.None;<br />
this.ControlBox = false;<br />
this.MinimizeBox = false;<br />
}<br />
}<br /><br />
public Chooser(string Caption, string[] Items, bool FullScreen, bool SelectInvokesClose)<br />
: this(Caption, FullScreen, SelectInvokesClose)<br />
{<br />
this.treeView1.BeginUpdate();<br />
foreach (string s in Items)<br />
this.treeView1.Nodes.Add(s);<br />
this.treeView1.EndUpdate();<br />
}<br /><br />
public Chooser(string Caption, string[] Items, bool FullScreen, bool CheckBoxes, bool
SelectInvokesClose, bool CheckAll)<br />
: this(Caption, Items, FullScreen, SelectInvokesClose)<br />
{<br />
this.treeView1.CheckBoxes = CheckBoxes;<br />
if (CheckBoxes &amp;&amp; CheckAll)<br />
foreach (TreeNode node in this.treeView1.Nodes)<br />
node.Checked = true;<br />
if(CheckBoxes)<br />
this.mmOK.Enabled = true; // don't require any user input<br />
}<br /><br />
private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)<br />
{<br />
this.mmOK.Enabled = true;<br />
if (selectInvokesClose)<br />
this.mmOK_Click(sender, EventArgs.Empty);<br />
}<br /><br />
#region Windows Form Designer generated code<br /><br />
/// &lt;summary&gt;<br />
/// Required designer variable.<br />
/// &lt;/summary&gt;<br />
private System.ComponentModel.IContainer components = null;<br /><br />
/// &lt;summary&gt;<br />
/// Clean up any resources being used.<br />
/// &lt;/summary&gt;<br />
/// &lt;param name="disposing"&gt;true if managed resources should be disposed; otherwise,
false.&lt;/param&gt;<br />
protected override void Dispose(bool disposing)<br />
{<br />
if (disposing &amp;&amp; (components != null))<br />
{<br />
components.Dispose();<br />
}<br />
base.Dispose(disposing);<br />
}<br /><br /><br />
/// &lt;summary&gt;<br />
/// Required method for Designer support - do not modify<br />
/// the contents of this method with the code editor.<br />
/// &lt;/summary&gt;<br />
private void InitializeComponent()<br />
{<br />
this.mainMenu1 = new System.Windows.Forms.MainMenu();<br />
this.mmCancel = new System.Windows.Forms.MenuItem();<br />
this.mmOK = new System.Windows.Forms.MenuItem();<br />
this.lblCaption = new System.Windows.Forms.Label();<br />
this.treeView1 = new System.Windows.Forms.TreeView();<br />
this.SuspendLayout();<br />
// 
<br />
// mainMenu1<br />
// 
<br />
this.mainMenu1.MenuItems.Add(this.mmCancel);<br />
this.mainMenu1.MenuItems.Add(this.mmOK);<br />
// 
<br />
// mmCancel<br />
// 
<br />
this.mmCancel.Text = "Cancel";<br />
this.mmCancel.Click += new System.EventHandler(this.mmCancel_Click);<br />
// 
<br />
// mmOK<br />
// 
<br />
this.mmOK.Text = "OK";<br />
this.mmOK.Click += new System.EventHandler(this.mmOK_Click);<br />
// 
<br />
// lblCaption<br />
// 
<br />
this.lblCaption.Dock = System.Windows.Forms.DockStyle.Top;<br />
this.lblCaption.Font = new System.Drawing.Font("Tahoma", 10F, System.Drawing.FontStyle.Bold);<br />
this.lblCaption.Location = new System.Drawing.Point(0, 0);<br />
this.lblCaption.Name = "lblCaption";<br />
this.lblCaption.Size = new System.Drawing.Size(240, 27);<br />
this.lblCaption.Text = "...";<br />
// 
<br />
// treeView1<br />
// 
<br />
this.treeView1.Dock = System.Windows.Forms.DockStyle.Fill;<br />
this.treeView1.Location = new System.Drawing.Point(0, 27);<br />
this.treeView1.Name = "treeView1";<br />
this.treeView1.ShowLines = false;<br />
this.treeView1.ShowPlusMinus = false;<br />
this.treeView1.ShowRootLines = false;<br />
this.treeView1.Size = new System.Drawing.Size(240, 241);<br />
this.treeView1.TabIndex = 12;<br />
// 
<br />
// Chooser<br />
// 
<br />
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);<br />
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;<br />
this.AutoScroll = true;<br />
this.ClientSize = new System.Drawing.Size(240, 268);<br />
this.ControlBox = false;<br />
this.Controls.Add(this.treeView1);<br />
this.Controls.Add(this.lblCaption);<br />
this.KeyPreview = true;<br />
this.Menu = this.mainMenu1;<br />
this.MinimizeBox = false;<br />
this.Name = "Chooser";<br />
this.Text = "Choose Item";<br />
this.Load += new System.EventHandler(this.Chooser_Load);<br />
this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.Chooser_KeyDown);<br />
this.ResumeLayout(false);<br /><br />
}<br />
#endregion<br /><br />
private void Chooser_KeyDown(object sender, KeyEventArgs e)<br />
{<br />
if ((e.KeyCode == System.Windows.Forms.Keys.Enter))<br />
this.mmOK_Click(sender, EventArgs.Empty);<br />
}<br /><br />
private void mmOK_Click(object sender, EventArgs e)<br />
{<br />
if (this.treeView1.SelectedNode != null)<br />
Chooser.SelectedItem = this.treeView1.SelectedNode.Text;<br />
if (this.treeView1.CheckBoxes)<br />
foreach (TreeNode n in this.treeView1.Nodes)<br />
if (n.Checked)<br />
CheckedItems.Add(n.Text); 
<br /><br />
this.DialogResult = DialogResult.OK;<br />
this.Close();<br />
}<br /><br />
private void mmCancel_Click(object sender, EventArgs e)<br />
{<br />
this.DialogResult = DialogResult.Cancel;<br />
this.Close();<br />
}<br /><br />
private void Chooser_Load(object sender, EventArgs e)<br />
{<br />
this.treeView1.Focus();<br />
this.treeView1.SelectedNode = null;<br />
this.treeView1.AfterSelect += new TreeViewEventHandler(treeView1_AfterSelect);<br />
}<br />
}<br />
}<br /></pre><p></p><img width="0" height="0" src="http://tim.mackey.ie/aggbug.ashx?id=caab5837-eebf-428b-a239-a5b266046f57" /></div>
    </content>
  </entry>
  <entry>
    <title>Hillwalking in the Lakeland Fells</title>
    <link rel="alternate" type="text/html" href="http://tim.mackey.ie/HillwalkingInTheLakelandFells.aspx" />
    <id>http://tim.mackey.ie/PermaLink,guid,0c4c5e87-645a-4f02-a4f2-a24598ac8728.aspx</id>
    <published>2009-09-19T15:49:34.818+01:00</published>
    <updated>2010-04-09T17:23:12.930875+01:00</updated>
    <category term="Outdoors" label="Outdoors" scheme="http://tim.mackey.ie/CategoryView,category,Outdoors.aspx" />
    <author>
      <name>Tim Mackey</name>
    </author>
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
        <img src="http://tim.mackey.ie/dev/grasmere_camp.jpg" />
        <br />
Just back from a two week trip in the Lake District in England, what a discovery. 
this photo was taken at sunrise at 500m on the last day.  unfortunately the first
10 days were mostly torrential rain, but it was worth it in the end. i want to go
back next year.  i posted some more info on the trip at the <a href="http://wainwright.proboards.com/index.cgi?board=general&amp;action=display&amp;thread=5081">wainwright
society forum</a> where i got some good advice before going.<br /><img src="http://tim.mackey.ie/dev/grasmere_sunrise.jpg" /><br /><p></p><img width="0" height="0" src="http://tim.mackey.ie/aggbug.ashx?id=0c4c5e87-645a-4f02-a4f2-a24598ac8728" /></div>
    </content>
  </entry>
  <entry>
    <title>Javascript dynamic anchor select menu</title>
    <link rel="alternate" type="text/html" href="http://tim.mackey.ie/JavascriptDynamicAnchorSelectMenu.aspx" />
    <id>http://tim.mackey.ie/PermaLink,guid,c947af7f-9cb4-4018-a856-c92855b04738.aspx</id>
    <published>2009-04-21T13:30:58.139+01:00</published>
    <updated>2009-04-21T13:35:56.499125+01:00</updated>
    <category term=".Net General" label=".Net General" scheme="http://tim.mackey.ie/CategoryView,category,NetGeneral.aspx" />
    <category term="Asp.Net" label="Asp.Net" scheme="http://tim.mackey.ie/CategoryView,category,AspNet.aspx" />
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">I got a request from a client for a 'mini'
navigation menu to appear on a page to allow the user to 'jump' to the various sections
of the page.  Maintaining a set of named anchor hyperlinks in a document (with
a javascript-based select menu) is not really an option for someone who doesn't know
HTML, which is most people who use content management.  In the wrong hands this
creates more trouble than it's worth.  
<br />
so i started thinking. the documents in question are well structured, using paragraphs
and headings, as enforced by the content management system.  i worked out this
solution which is exactly what i need, a maintenance free javascript jump menu, that
doesn't add clutter to the document with named anchors.  it assumes that the
all H2 tags should be displayed as links in the menu.  the javascript iterates
through the H2 tags and loads up the menu, with a ScrollTo function used when an item
is selected.<br /><br />
here's a <a href="/content/binary/jumpmenu.png">screenshot<br /><img style="border: 1px solid rgb(0, 0, 0);" src="http://tim.mackey.ie/content/binary/jumpmenu.png" border="1" width="420" height="257" /></a><br /><br />
The code for the HTML page:<br /><pre>&lt;script src="/JumpMenu.js" type="text/javascript"&gt;&lt;/script&gt;<br />
&lt;select id="jumpNavSelect" name="jumpNavSelect" onchange="JumpToHeading(this.selectedIndex)"&gt;<br />
&lt;option value=""&gt;On this page...&lt;/option&gt;<br />
&lt;/select&gt;<br /></pre>and the JumpMenu.js file<br /><pre>var array;<br /><br />
function CreateAnchorMenu() {<br />
array = document.getElementsByTagName("h2"); 
<br />
var src = document.getElementById('jumpNavSelect');<br />
// iterate through all H2 headings, add dropdown items for each. 
<br />
for (var i = 0; i &lt; array.length; i++) {<br />
var heading = array[i];<br />
var text = heading.firstChild.nodeValue;<br />
if (!text)<br />
continue;<br />
src[i+1] = new Option(text, i);<br />
}<br />
}<br /><br />
function JumpToHeading(HeadingIndex) {<br />
if (HeadingIndex == 0)<br />
return;<br />
var heading = array[HeadingIndex-1];<br />
ScrollToElement(heading);<br />
}<br /><br />
function ScrollToElement(theElement) {<br />
var selectedPosX = 0;<br />
var selectedPosY = 0;<br />
while (theElement != null) {<br />
selectedPosX += theElement.offsetLeft;<br />
selectedPosY += theElement.offsetTop;<br />
theElement = theElement.offsetParent;<br />
}<br />
window.scrollTo(selectedPosX, selectedPosY);<br />
}<br /><br />
window.onload = CreateAnchorMenu;<br /></pre><img width="0" height="0" src="http://tim.mackey.ie/aggbug.ashx?id=c947af7f-9cb4-4018-a856-c92855b04738" /></div>
    </content>
  </entry>
  <entry>
    <title>Asp.Net membership, correcting username case at login</title>
    <link rel="alternate" type="text/html" href="http://tim.mackey.ie/AspNetMembershipCorrectingUsernameCaseAtLogin.aspx" />
    <id>http://tim.mackey.ie/PermaLink,guid,943b3c7e-bf3e-4047-a672-e0d2eac43c31.aspx</id>
    <published>2009-03-24T16:38:48.709+00:00</published>
    <updated>2009-03-24T16:39:17.537625+00:00</updated>
    <category term="Asp.Net" label="Asp.Net" scheme="http://tim.mackey.ie/CategoryView,category,AspNet.aspx" />
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">if your application has users who like to
log in as "JOE BLOGGS" or "joe bloggs" when their username is actually "Joe Bloggs",
you might want to ensure that they login with the correct "case" of their username. 
It can cause discrepancies if you refer to the current Identity.User in your web application,
for example if you use the username anywhere in your database and run reports grouped
on the username, you will get multiple records for each variation on the case of the
username.  It is surprisingly unintuitive to solve this problem.  Firstly
you would think that Asp.Net membership would take care of this itself.  Then
you would think that you could determine the correct case of the username as follows:<br /><pre>protected void Login1_LoggedIn(object sender, EventArgs e)<br />
{<br />
string correctUsername = Membership.GetUser(this.Login1.Username).Username;<br />
FormsAuthentication.SetAuthCookie(correctUsername , true);<br />
}<br /></pre><p>
but this doesn't work because the "Username" property of the MembershipUser object
does not collect its value from the AspNetDB SQL database like you would expect, instead
it is filled with whatever you pass it when loading the user, this must be a bug but
i'm not bothered trying to convince MS.  instead, i came up with this solution
below, to directly load the AspNetUser object from a Linq DataSource of the AspNetDb
database (created using SqlMetal).<br /></p><pre>protected void Login1_LoggedIn(object sender, EventArgs e)<br />
{<br />
// correct the case of the username<br />
string Username = this.Login1.UserName;<br />
AspNetDb db = new AspNetDb();<br />
MembershipUser memUser = Membership.GetUser(Username); // load the MembershipUser
object to get the UserID<br />
Aspnet_User aspnetUser = db.Aspnet_Users.SingleOrDefault(z =&gt; z.UserId == new Guid(memUser.ProviderUserKey.ToString()));<br />
if(aspnetUser != null)<br />
Username = aspnetUser.UserName;<br />
FormsAuthentication.SetAuthCookie(Username, true);<br />
}<br /></pre><p></p><img width="0" height="0" src="http://tim.mackey.ie/aggbug.ashx?id=943b3c7e-bf3e-4047-a672-e0d2eac43c31" /></div>
    </content>
  </entry>
  <entry>
    <title>Crystal Reports date format (in code)</title>
    <link rel="alternate" type="text/html" href="http://tim.mackey.ie/CrystalReportsDateFormatInCode.aspx" />
    <id>http://tim.mackey.ie/PermaLink,guid,1f65c32e-7a99-4fe1-b58c-92c8241bcc13.aspx</id>
    <published>2009-02-27T17:47:52.5488056+00:00</published>
    <updated>2009-02-27T17:47:52.5488056+00:00</updated>
    <category term=".Net General" label=".Net General" scheme="http://tim.mackey.ie/CategoryView,category,NetGeneral.aspx" />
    <category term="Asp.Net" label="Asp.Net" scheme="http://tim.mackey.ie/CategoryView,category,AspNet.aspx" />
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
        <pre>Cstr(CDate({Table.Column}), "dd/MM/yyyy")</pre>
        <p>
        </p>
        <img width="0" height="0" src="http://tim.mackey.ie/aggbug.ashx?id=1f65c32e-7a99-4fe1-b58c-92c8241bcc13" />
      </div>
    </content>
  </entry>
  <entry>
    <title>A simple Auto-Save / Keep Alive feature for Asp.Net</title>
    <link rel="alternate" type="text/html" href="http://tim.mackey.ie/ASimpleAutoSaveKeepAliveFeatureForAspNet.aspx" />
    <id>http://tim.mackey.ie/PermaLink,guid,779ba072-7636-496e-a71c-0f09ea71f16e.aspx</id>
    <published>2009-02-19T16:18:50.46875+00:00</published>
    <updated>2009-02-19T16:18:50.46875+00:00</updated>
    <category term="Asp.Net" label="Asp.Net" scheme="http://tim.mackey.ie/CategoryView,category,AspNet.aspx" />
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">today i came across a situation where a
user was spending a long time filling out a form.  they went past the session
timeout and when they hit save they got booted back to the login screen, having lost
the previous 30 minutes of work.  i looked around the net and found a few options
with AJAX and what not but it doesn't make sense to keep renewing the session automatically
otherwise you might as well not have a timeout, which is there for security reasons
anyway.  in my case i don't actually want to save the information to the database
(it's a transaction) until the user hits the Save button, i just want to keep the
session alive.  
<br /><br />
so i came up with this idea to ask the user if they want to keep their session alive
with a javascript prompt every 15 minutes. if they leave their computer and don't
answer the prompt, the session will time out since they won't have interacted with
the web site (regardless of how they answer the prompt after the timeout).  if
they answer OK to the prompt it invokes a Ping() type web service to keep the session
alive.  this is the most lightweight way i could think of doing it.  i don't
want to post back to the page because that will interrupt the user and make them wait
for the page to reload. 
<br /><br />
there are 3 parts:<br /><h3>"KeepAlive" function<br /></h3><p>
I put this function in a 'Util' class so pages can easily turn on the 'Keep Alive'
functionality, simply call <font face="Courier New">Util.KeepAlive(this);</font></p><pre>public static void KeepAlive(Page p)<br />
{<br />
p.ClientScript.RegisterClientScriptInclude("KeepAlive", "/KeepAlive.js");<br />
}</pre><h3>Ping.asmx (add the web service to your root folder)<br /></h3><pre>[WebMethod]<br />
public void Ping()<br />
{<br />
HttpContext.Current.Response.Write("OK"); 
<br />
HttpContext.Current.Response.End(); // this makes the result easier to parse than
an XML web service message<br />
}</pre><h3>KeepAlive.js (put this file in your root folder)<br /></h3><pre>var timerID = 0;	     // used to track the timer function<br />
var interval = 1000*60*15; // 15 mins<br />
var KeepAliveUrl = '/Ping.asmx/Ping'; // replace your web service address here<br />
var xmlhttpKeepAlive;<br /><br />
function AutoSaveSubmit()<br />
{<br />
if(confirm('15 minutes of idle time has passed, do you want to keep your session active?'))<br />
InvokeWebService();<br />
}<br /><br />
function InvokeWebService() 
<br />
{ 
<br />
if (window.XMLHttpRequest)<br />
{<br />
xmlhttpKeepAlive=new XMLHttpRequest();<br />
xmlhttpKeepAlive.onreadystatechange = xmlhttpChangeKeepAlive;<br />
xmlhttpKeepAlive.open('GET',KeepAliveUrl + '?T=' + timerID,true);<br />
xmlhttpKeepAlive.send(null);<br />
}<br />
// code for IE<br />
else if (window.ActiveXObject)<br />
{<br />
xmlhttpKeepAlive=new ActiveXObject('Microsoft.XMLHTTP')<br />
if (xmlhttp)<br />
{<br />
xmlhttpKeepAlive.onreadystatechange = xmlhttpChangeKeepAlive;<br />
xmlhttpKeepAlive.open('GET', KeepAliveUrl + '?T=' + timerID, true);<br />
xmlhttpKeepAlive.send();<br />
}<br />
}<br />
return false;<br />
}<br /><br />
function xmlhttpChangeKeepAlive()<br />
{<br />
var text;<br />
if (xmlhttpKeepAlive.readyState == 4) 
<br />
{<br />
text = xmlhttpKeepAlive.responseText;<br />
if (xmlhttpKeepAlive.status==200) // OK<br />
{<br />
if(text == 'OK') // reset the timer 
<br />
timerID = setTimeout('AutoSaveSubmit()', interval); 
<br />
else<br />
alert('Your session has already expired.\nIf you have any information on this page
you will lose it if you try to save this page now. You have 2 options to avoid losing
the information on this screen.\n\nOption 1: open a new internet window and log in
again to the web site, then close that window and go back to this window, at which
point you will have a new session and you will be able to save the information.\n\nOption
2: Copy all the text you have typed on this page and paste it into another program
(such as Word or Notepad), then log in to the web site again (click the home page)
and come back to this page, and paste in the text again.\n\nTo prevent this happening
in the future, click OK on the 15 minute reminder box each time it appears, this will
keep your session active. If you do not answer the reminder within 10 minutes the
session may expire.');<br />
}<br />
else<br />
alert('Error: ' + xmlhttpKeepAlive.status + '. The session may have already expired,
please try to save the page now');<br />
}<br />
}<br />
window.setTimeout('AutoSaveSubmit()',interval); 
<br /></pre><p>
You might be wondering why i append the timer code/number to the url of the web service. 
The reason is because IE has a caching bug and will not actually send the XmlHttp
request unless the URL is different to what it has in its history, it will just return
the previous result. 
<br /></p><p></p><img width="0" height="0" src="http://tim.mackey.ie/aggbug.ashx?id=779ba072-7636-496e-a71c-0f09ea71f16e" /></div>
    </content>
  </entry>
  <entry>
    <title>SQL 2005 cannot drop user with owned schema</title>
    <link rel="alternate" type="text/html" href="http://tim.mackey.ie/SQL2005CannotDropUserWithOwnedSchema.aspx" />
    <id>http://tim.mackey.ie/PermaLink,guid,65639461-f6c9-4e62-90ba-f2bbf556c7b4.aspx</id>
    <published>2009-01-11T15:28:04.237125+00:00</published>
    <updated>2009-01-11T15:28:04.237125+00:00</updated>
    <category term="Database" label="Database" scheme="http://tim.mackey.ie/CategoryView,category,Database.aspx" />
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">i notice when i backup a database and restore
it to another server, the user IDs are different and i have to delete the users and
add them back in to the database.  however last time i tried this the drop failed
because the user owned a schema.  i right-clicked on the user's properties and
the tick box for 'db_owner' was shaded in a read only state.  so i couldn't delete
the user and couldn't remove their schema ownership.  took a while to figure
out that the way to do this is via the Database &gt; Security &gt; Schemas menu, then
change the schema owner back to itself, e.g. db_owner.  then the user is free
to be deleted.<br /><p></p><img width="0" height="0" src="http://tim.mackey.ie/aggbug.ashx?id=65639461-f6c9-4e62-90ba-f2bbf556c7b4" /></div>
    </content>
  </entry>
  <entry>
    <title>HowTo: disable the Excel prompt for opening formats different than the extension</title>
    <link rel="alternate" type="text/html" href="http://tim.mackey.ie/HowToDisableTheExcelPromptForOpeningFormatsDifferentThanTheExtension.aspx" />
    <id>http://tim.mackey.ie/PermaLink,guid,5de9f2ca-d589-4a84-9386-602f0b2a4dde.aspx</id>
    <published>2008-12-23T12:08:07.083125+00:00</published>
    <updated>2008-12-23T12:08:07.083125+00:00</updated>
    <category term="Asp.Net" label="Asp.Net" scheme="http://tim.mackey.ie/CategoryView,category,AspNet.aspx" />
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">many web sites export tabular information
from grids/tables into 'Excel' format. the file is not a true Excel format and actually
contains X/HTML.  if the file is opened in Excel 2007 you may get a prompt that
the file is in a different format than specified by the file extension.  if you
regularly work with files like this, there is a way to disable this prompt, add a
DWORD key called ExtensionHardening with value 0 to HKEY_CURRENT_USER\Software\Microsoft\Office\12.0\Excel\Security<br />
it works straight away, no need for a reboot.<br /><p></p><img width="0" height="0" src="http://tim.mackey.ie/aggbug.ashx?id=5de9f2ca-d589-4a84-9386-602f0b2a4dde" /></div>
    </content>
  </entry>
</feed>