﻿<?xml version="1.0" encoding="utf-8"?><rss version="2.0"><channel><title>Dentaku</title><link>http://www.dentaku.com</link><description>The latest postings.</description><copyright>Copyright 2006 Dentaku.com. All rights reserved.</copyright><item><title>Take control over stylesheet order and media when using ASP.NET 2.0 themes</title><description>&lt;p&gt;Themes in ASP.NET 2.0 is a great concept, but it doesn't give you any control over how stylesheets are added to your pages. ASP.NET won't let you use multiple stylesheets with different media types in a arbitrary order. But you can easily fix it if you want to. I've put together an example which let's you use both themes and have full control over the stylesheet rendering.&lt;/p&gt;

&lt;p&gt;The way themes works is that ASP.NET includes all .css files found in the theme folder into you page. The stylesheeets are added as runat at server link elements (HtmlControls with tagname "link"). They're added ordered by their names. The links won't get any media-attribute.&lt;/p&gt;

&lt;p&gt;You can read more about the themes problems in Adam Kahtava's post: &lt;a href="http://adam.kahtava.com/journal/2006/11/09/The+Problems+With+Themes+Skins+And+Cascading+Style+Sheets+CSS++Where+It+All+Falls+Apart.aspx"&gt;The Problems with Themes, Skins, and Cascading Style Sheets (CSS) - Where it all Falls Apart&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;A simple example&lt;/h3&gt;
&lt;p&gt;You want your web pages to look nice when you print them. The way to do it is to add a stylesheet with media="print" and put it after the default stylesheet. If you don't use themes you could add this to the page's head:&lt;p&gt;

&lt;code class="aspx"&gt;
&lt;link type="text/css" href="StyleSheet.css" rel="stylesheet" media="all" /&gt;
&lt;link type="text/css" href="Print.css" rel="stylesheet" media="print" /&gt;&lt;/code&gt;

&lt;p&gt;Print.css could include rules for hiding elements like navigation and banners that should not be visible when you print it.&lt;/p&gt;

&lt;p&gt;If you use themes and the stylesheets are in the theme folder. The output for your page would be like:&lt;/p&gt;

&lt;code class="aspx"&gt;
&lt;link type="text/css" href="App_Themes/TheTheme/Print.css" rel="stylesheet" /&gt;
&lt;link type="text/css" href="App_Themes/TheTheme/StyleSheet.css" rel="stylesheet" /&gt;&lt;/code&gt;	

&lt;p&gt;If Print.css hides any element, that element won't be visible at all because the StyleSheet.css is rendered after Print.css.&lt;/p&gt;

&lt;h3&gt;Take control&lt;/h3&gt;
&lt;p&gt;I made a simple user control to put in the head. It's just a sub class to PlaceHolder so you can add any controls you like in it. I've assumed that stylesheets are written as  literal content and when the control prerenders it replaces &lt;span class="code"&gt;%Theme&lt;/span&gt; with the actual theme path. Put this code in the head of the page:&lt;/p&gt;
&lt;code class="aspx"&gt;
&lt;head runat="server"&gt;
    &lt;title&gt;…&lt;/title&gt;
    &lt;cc1:Styles ID="Styles1" runat="server" ThemeVariableName="%Theme"&gt;
        &lt;link rel="Stylesheet" type="text/css" href="%Theme/StyleSheet.css" media="all"/&gt;
        &lt;link rel="Stylesheet" type="text/css" href="%Theme/Print.css" media="print"/&gt;
    &lt;/cc1:Styles&gt;
&lt;/head&gt;&lt;/code&gt;	

&lt;h3&gt;How does it work?&lt;/h3&gt;

&lt;p&gt;The control does two things in the prerendering:&lt;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;b&gt;Disable default rendering&lt;/b&gt;&lt;br /&gt;
The page's header must be set &lt;span class="code"&gt;runat="server"&lt;/span&gt; when you use themes. Therefore it's possible to access it's control collection. You could easily remove any server side links from the head in the page's onPreRender event. But it's not possible to do that from a user control. So instead of removing the links, we make them invisible
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Replace %Theme with the actual theme path&lt;/b&gt;&lt;br /&gt;
Literal text within a PlaceHolder is added as LiteralControl's. I use a regular expression to replace all occurance of &lt;span class="code"&gt;%Theme&lt;/span&gt; in LiteralControls to the actual theme path. 
&lt;/li&gt;
&lt;/ol&gt;


&lt;h3&gt;Source code&lt;/h3&gt;
&lt;code class="csharp"&gt;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.Text.RegularExpressions;

namespace Dentaku.Web.UI {

    [DefaultProperty("ThemeVariableName")]
    [ToolboxData("&lt;{0}:Styles runat=\"server\"&gt;&lt;/{0}:Styles&gt;")]
    [Themeable(true)]
    public class Styles : PlaceHolder {

        [Bindable(true)]
        [Category("Appearance")]
        [DefaultValue("%Theme")]
        [Localizable(false)]
        [Description("Name of the variable that should be replaced by the actual theme path")]
        public string ThemeVariableName {
            get {
                String s = (String) ViewState["ThemeVariableName"];
                return ((s == null) ? "%Theme" : s);
            }

            set {
                ViewState["ThemeVariableName"] = value;
            }
        }
        
        /// &lt;summary&gt;
        /// Fix controls before render
        /// &lt;/summary&gt;
        protected override void OnPreRender(EventArgs e) {
            base.OnPreRender(e);

            if (this.Visible) {
                // Hide any server side css 
                foreach (Control c in this.Page.Header.Controls) {
                    if (c is HtmlControl &amp;&amp; ((HtmlControl) c).TagName.Equals("link", 
                                StringComparison.OrdinalIgnoreCase)) {
                        c.Visible = false;
                    }
                }

                // Replace ThemeVariableName with actual theme path
                Regex reg = new Regex(ThemeVariableName, 
                                      System.Text.RegularExpressions.RegexOptions.IgnoreCase);

                foreach (Control c in this.Controls) {
                    if (c is LiteralControl) {
                        LiteralControl l = (LiteralControl) c;
                        l.Text = reg.Replace(l.Text, this.ThemePath);
                    }
                }
            }
        }
        
        /// &lt;summary&gt;
        /// Get the theme path
        /// &lt;/summary&gt;
        public string ThemePath {
            get {
                return String.Format("{0}/App_Themes/{1}",
                                     this.Page.Request.ApplicationPath,
                                     this.Page.Theme);
            }
        }
    }
}&lt;/code&gt;

&lt;h3&gt;Skin it&lt;/h3&gt;
Do you need different number of stylesheets for each theme? You can use skin-files for the Styles control. But if you do, you might want to use skins for all themes. Any text within the Styles control in the page's header will be rendered along with any text set in the skin file. Example what to put in your skin file: 

&lt;code class="aspx"&gt;
&lt;cc2:Styles runat="server"&gt;
    &lt;link rel="Stylesheet" type="text/css" href="%Theme/StyleSheet.css" media="all"/&gt;
    &lt;link rel="Stylesheet" type="text/css" href="%Theme/Print.css" media="print"/&gt;
&lt;/cc2:Styles&gt;&lt;/code&gt;















</description><link>http://www.dentaku.com/2007/01/take-control-over-stylesheet-order-and-media-when-using-asp-net-2-0-themes.aspx</link><pubDate>2007-01-30 13:32:00</pubDate></item><item><title>Problem with themes in a precompiled "updateable" ASP.NET web</title><description>&lt;p&gt;If you precompile a web site with a theme set, the compiler adds the theme setting into the @Page directive on every ASPX page in the site. Probably not what you'd expect. On my last day at work before my vacation I stumbled upon this unexpected problem. The only thing I needed to do before leaving work was to my compile web site and deploy it. The web site was to be deployed in three versions with different themes. I spent the last frustrating hour trying to find out why all three web looked same theme after I had deployed them. I had changed the &amp;lt;pages theme="theme1" /&amp;gt;, and all three sites had different themes set. Everything looked right, but still it didn't work. I had compiled the site as an updateble web site. At last I found out that if I didn't set a theme in web.config before I compiled the web. I could change it afterwards. I didn't have the time to check what the real problem was. During my vacation, my co worker Johan Dewe was handling the project. So he had to deal with the problem when I was away. He wrote a post about this on his blog (in Swedish): &lt;a href="http://www.dewe.net/sharepoint/2006/07/theme-settings-i-webconfig-i.html"&gt;Theme settings i web.config i kombination med förkompilerad ASP.Net&lt;/a&gt;. He also found that K. Scott Allen wrote &lt;a href="http://odetocode.com/Blogs/scott/archive/2006/02/24/3019.aspx"&gt;Caveat With ASP.NET Precompilation and web.config Settings&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Johan also has a solution how to make deployment projects which fix the theme problem: &lt;a href="http://www.dewe.net/sharepoint/2006/07/search-and-replace-med-msbuild-och-web.html"&gt; Search and replace med MSBuild och Web Deployment Project&lt;/a&gt;. Because it's in Swedish I'll give you a rough translation. You have to use Web Deployment Project (WDP) och &lt;a href="http://msbuildtasks.tigris.org/"&gt;MSBuild Community Tasks&lt;/a&gt;. Download and install the MSBuild Community Tasks, update your WDP file and add this reference to the target file &amp;lt;Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets"/&amp;gt;
&lt;/p&gt;
&lt;h3&gt;Remove theme before compile&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Create a new WDP and open the project file.&lt;/li&gt;
&lt;li&gt;Make sure that a temporary folder is created for the source files by extend the elementet PropertyGroup:&lt;br /&gt;

      &amp;lt;EnableCopyBeforeBuild&amp;gt;true&amp;lt;/EnableCopyBeforeBuild&amp;gt;

&lt;/li&gt;
&lt;li&gt;Update BeforeBuild target by adding a task that removes the theme setting from the &amp;lt;pages&amp;gt;-element:&lt;br /&gt;
      &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;Target Name="BeforeBuild"&gt;&lt;br /&gt;
      &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;FileUpdate&lt;br /&gt;
      &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Files="$(CopyBeforeBuildTargetPath)\web.config"&lt;br /&gt;
          &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Regex="(\u003Cpages.*)(theme=\u0022.*?\u0022)"&lt;br /&gt;
          &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ReplacementText="$1" /&amp;gt;&lt;br /&gt;
      &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/Target&amp;gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So before the site is compiled the theme-setting will be removed and not added to every page.&lt;/p&gt;</description><link>http://www.dentaku.com/2006/08/problem-with-themes-in-a-precompiled-updateable-asp-net-web.aspx</link><pubDate>2006-08-23 23:22:00</pubDate></item><item><title>PostBackUrl + PostBack problem is a bug</title><description>&lt;p&gt;The PostBackUrl problem I wrote about before (&lt;a href="http://www.dentaku.com/2006/06/how-to-fix-autopostback-error-for-aspnet-pages-with-postbackurl-button.aspx" title="How to fix AutoPostBack and PostBack error for ASP.NET pages with PostBackUrl button"&gt;How to fix AutoPostBack and PostBack error for ASP.NET pages with PostBackUrl button&lt;/a&gt;) is a bug. Got a mail from Microsoft which confirms it. No wonder I didn't find anything about it on Google when I tried to solve the problem. My solution would probably be to change &lt;span class="code"&gt;__doPostBack&lt;/span&gt; function to always reset the action before submitting the form.&lt;/p&gt;
&lt;code class="javascript"&gt;
function __doPostBack(eventTarget, eventArgument) {
    if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
        theForm.action = "http://&lt;page's url&gt;";
        theForm.__EVENTTARGET.value = eventTarget;
        theForm.__EVENTARGUMENT.value = eventArgument;
        theForm.submit();
    }
}&lt;/code&gt;</description><link>http://www.dentaku.com/2006/06/postbackurl-postback-problem-is-a-bug.aspx</link><pubDate>2006-06-18 15:35:00</pubDate></item><item><title>Installing ASP.NET Providers on SQL Server 2000 without default collation</title><description>&lt;p&gt;
Do you need to install ASP.NET providers on a SQL Server with a non default collation? This blog is run on a SQL Server 2000 that uses "Finnish Swedish". So when I tried to install the ASP.NET providers for SQL Server I got the error: "Cannot resolve collation conflict for equal to operation.". To get it running you need to edit the script before running it. 
&lt;p&gt;
&lt;h3&gt;1. Generating the script&lt;/h3&gt;
&lt;p&gt;
Generate the script with &lt;span class="code"&gt;aspnet_regsql&lt;/span&gt; in your .NET 2.0 folder. In my case it's &lt;span class="code"&gt;C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727&lt;/span&gt;. Use this to create a file in C:&lt;br /&gt;
&lt;span class="code"&gt;aspnet_regsql -sqlexportonly "c:\i
nstall providers.sql" -A all -d &amp;lt;YourDataBaseName&amp;gt;&lt;/span&gt;&lt;br /&gt;
Using the -d option includes the name of your database in the script. 
&lt;/p&gt;
&lt;h3&gt;2. Fix the collation&lt;/h3&gt;
&lt;p&gt;
The stored procedures &lt;span class="code"&gt;aspnet_UsersInRoles_AddUsersToRoles&lt;/span&gt;
and &lt;span class="code"&gt;aspnet_UsersInRoles_RemoveUsersFromRoles&lt;/span&gt; uses temporay tables to store user names. To make the script work, search for &lt;span class="code"&gt;DECLARE @tbNames&lt;/span&gt; and add &lt;span class="code"&gt;COLLATE &amp;lt;your database collation&amp;gt;&lt;/span&gt; to row as shown below: 
&lt;/p&gt;
&lt;code class="sql"&gt;
DECLARE @tbNames  table(Name nvarchar(256) 
    COLLATE Finnish_Swedish_CI_AI_KS_WS NOT NULL PRIMARY KEY)
&lt;/code&gt;
&lt;h3&gt;3. Install the providers&lt;/h3&gt;
&lt;p&gt;
Open up the script in a SQL Query Analyzer and execute it. 
&lt;/p&gt;
&lt;h3&gt;4. Set the connection string&lt;/h3&gt;
&lt;p&gt;ASP.NET uses a default connectionstring named &lt;span class="code"&gt;LocalSqlServer&lt;/span&gt;, remove it and add it again in Web.config:&lt;/p&gt;
&lt;code class="xml"&gt;
&lt;connectionStrings&gt;
    &lt;remove name="LocalSqlServer"/&gt;
    &lt;add name="LocalSqlServer" 
        connectionString="Data Source=&lt;your server&gt;;
            Initial Catalog=&lt;database name&gt;;
            User Id=&lt;user&gt;;
            Password=&lt;password&gt;;" 
    providerName="System.Data.SqlClient"/&gt;
&lt;/connectionStrings&gt;
&lt;/code&gt;

</description><link>http://www.dentaku.com/2006/06/installing-asp-net-providers-on-sql-server-2000-without-default-collation.aspx</link><pubDate>2006-06-09 23:20:00</pubDate></item><item><title>How to fix AutoPostBack and PostBack error for ASP.NET pages with PostBackUrl button</title><description>&lt;p&gt;
I recently worked on a web page which used the new &lt;span class="code"&gt;PostBackUrl&lt;/span&gt; feature for buttons. On the same page I used an RadioButtonList with &lt;span class="code"&gt;AutoPostBack&lt;/span&gt; enabled. After testing the page something strange happened in FireFox. When I clicked on an item in the RadioButtonList I was sent to the &lt;span class="code"&gt;PostBackUrl&lt;/span&gt; page. After some debugging and testing the same page both in FireFox and Internet Explorer I found what was wrong. 
&lt;/p&gt;
&lt;p&gt;
When you click on a button with a &lt;span class="code"&gt;PostBackUrl&lt;/span&gt;, the ASP.NET JavaScript changes the page's form &lt;span class="code"&gt;action&lt;/span&gt; attribute to the &lt;span class="code"&gt;PostBackUrl&lt;/span&gt;. When the you go back to the previous page by clicking on the browser's back button, the &lt;span class="code"&gt;AutoPostBack&lt;/span&gt; on that page is now broken. This also breaks the normal postback for buttons with no value for &lt;span class="code"&gt;PostBackUrl&lt;/span&gt;. FireFox remembers the page's last state and in this case that the &lt;span class="code"&gt;action&lt;/span&gt; attribute is set to the &lt;span class="code"&gt;PostBackUrl&lt;/span&gt;. This is not a problem in Internet Explorer, it doesn't remember it's state when going back. The problem only occurs when the user clicks on the back button or on a JavaScript link which will cause the browser to go back. 
&lt;/p&gt;
&lt;p&gt;
The problem is the way &lt;span class="code"&gt;__doPostBack&lt;/span&gt; works. It relies on the &lt;span class="code"&gt;action&lt;/span&gt; attribute of the page's form. Whatever &lt;span class="code"&gt;action&lt;/span&gt; is set to. Of course it's possible to fix this in several ways. If you want you can try to change it on the server side. But it's really not necessary. A few lines of JavaScript will do the trick. 
&lt;/p&gt;

&lt;h3&gt;Solution&lt;/h3&gt;
&lt;p&gt;
Put this little piece of code directly after the &lt;span class="code"&gt;&amp;lt;form runat="server"&amp;gt;&lt;/span&gt; on your page.
&lt;/p&gt;
&lt;code class="aspx"&gt;
&lt;form id="form1" runat="server"&gt;
    &lt;script type="text/javascript"&gt;
        // PostBackUrl hack by Per Zimmerman | www.dentaku.com  
        var __oldAction = theForm.action;
        var __oldPostBack = __doPostBack;
        __doPostBack = function(eventTarget, eventArgument) {
            theForm.action = __oldAction;
            __oldPostBack(eventTarget, eventArgument);            
        }
    &lt;/script&gt;

    &lt;!-- rest of the page... --&gt;
&lt;/form&gt;
&lt;/code&gt;
&lt;p&gt;
You can put it anywhere after the form, but it's better that the code is run as soon as possible. The first time the page is loaded, the code stores the original &lt;span class="code"&gt;action&lt;/span&gt; and postback function in variables and then sets &lt;span class="code"&gt;__doPostBack&lt;/span&gt; to a new function. When the new &lt;span class="code"&gt;__doPostBack&lt;/span&gt; is called it first resets the &lt;span class="code"&gt;action&lt;/span&gt; attibute to the original value before it calls the old &lt;span class="code"&gt;__doPostBack&lt;/span&gt; function. You could set &lt;span class="code"&gt;action&lt;/span&gt; attibute to an empty string to get a normal postback, but I wanted to make sure the function use the value ASP.NET supplied. 
&lt;/p&gt;
&lt;p&gt;
&lt;h3&gt;Test pages&lt;/h3&gt;
I've uploaded test pages for you to try. 
&lt;ol&gt;
&lt;li&gt;&lt;a href="/AspNetDemos/PostBackUrl/Default.aspx" target="_blank"&gt;Default page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/AspNetDemos/PostBackUrl/JavaScriptHack.aspx" target="_blank"&gt;Hacked page&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/p&gt;
</description><link>http://www.dentaku.com/2006/06/how-to-fix-autopostback-error-for-aspnet-pages-with-postbackurl-button.aspx</link><pubDate>2006-06-03 23:11:00</pubDate></item><item><title>Hello World!</title><description>&lt;p&gt;My name is Per Zimmerman. I'm a proffessional web developer working mostly with solutions using ASP.NET, SQL Server, HTML, CSS and JavaScript. I have developed this blog engine. My plan is to write about issues about developing web and ASP.NET solutions. The blog is still work in progress and I'm planning to add more features.&lt;/p&gt;
</description><link>http://www.dentaku.com/2006/06/hello-world.aspx</link><pubDate>2006-06-02 00:49:00</pubDate></item></channel></rss>