If you want to detect a refresh on an HTTP GET rather than only POSTs, here's a hacky work-around that, in modern browsers, mostly works.
Javascript:
window.onload = function () {
    // regex for finding "loaded" query string parameter
    var qsRegex = /^(\?|.+&)loaded=\d/ig;
    if (!qsRegex.test(location.search)) {
        var loc = window.location.href + (window.location.search.length ? '&' : '?') + 'loaded=1';
        window.history.replaceState(null, document.title, loc);
    }
};
C#:
public bool IsPageRefresh 
{
    get
    {
        return !string.IsNullOrEmpty(Request.QueryString["loaded"]);
    }
}
When the page loads, it will change add a QueryString parameter of loaded=1 without reloading the page (again, this--window.history.replaceState--only works in post-archaic browsers).  Then, when the user refreshes the page, the server can check for the presence of the loaded parameter of the query string.
Caveat: mostly works
The case where this doesn't work is when the user clicks the Address Bar and presses enter.  That is, the server will produce a false-positive, detecting a refresh, when odds are, the user actually meant to reload the page fresh.
Depending on your purposes, maybe this is desirable, but as a user, it would drive me crazy if I expected it to reset the page.
I haven't put too much thought into it, but it might be possible to write some magic in order to distinguish a refresh from a reset via the address bar using any/all of:
- SessionState(assuming- SessionStateis enabled) and the value of the- loadedQueryString parameter
- the window.onbeforeunloadevent listener
- keyboard events (detecting F5 and Ctrl + R to quickly change the URL back to removing the loadedQueryString parameter--though this would have a false-negative for clicking the browser's refresh button)
- cookies
If someone does come up with a solution, I'd love to hear it.