Friday, September 23, 2005

Theme & Personalization in .Net 2

If you want provide the feature to let user select their own theme. You may need to write your own ProfileProvider and save the setting in users table. Here are my study in Theme & Personalization.
1. Theme
Theme is for the look and feel of web pages. Themes can use CSS files also. Just put the CSS file under specific theme folder, the page will use it.
File structure:
[Web App Root]\App_Themes\Blue\blue.shin
Then you can appay this skin in several way:

//In Single Page

//In Page_PreInit Event
void Page_PreInit(object sender, System.EventArgs e)
{ this.Theme = "Blue";}
2. Personalization
Let's save the Theme setting in Profile. In web.config file, add
〈 profile defaultProvider="myProfileProvider"〉
〈 providers〉
〈 add name="myProfileProvider" type="Corp.Framework.Shared.Security.myProfileProvider, Corp.Framework.Shared" connectionStringName="ConnID"/〉
〈 /providers〉
〈 properties〉
〈 add name="Theme" defaultValue="Blue"/〉
〈 /properties〉
〈 /profile〉

//In Page_PreInit Event, you load user's setting from Profile
void Page_PreInit(object sender, System.EventArgs e)
{
this.Theme = Profile.Theme;
//Page.Theme = (string)HttpContext.Current.Profile["Theme"];
}
3. myProfileProvider
myProfileProvider inherits from the SettingsProvider abstract class, which inherits from the ProviderBase abstract class.
class myProfileProvider : ProfileProvider
{
#region Private Member
private string _applicationName;
#endregion // Private Member

#region Constructor
///
/// Default Constructor
///

public myProfileProvider()
: base()
{
}
#endregion // Constructor
#region Initialize
///
/// Initialize the Profile Provider Class
///

/// Provider name
/// NameValueCollection containing the provider configuration settings
public override void Initialize(string name, NameValueCollection config)
{
}
#endregion //Initialize

#region Get Application Name
public override string ApplicationName
{
get { return _applicationName; }
set { _applicationName = value; }
}
#endregion
#region GetPropertyValues
///
/// Get Property Values
///

/// Context
/// Collection of Property Value
public override SettingsPropertyValueCollection
GetPropertyValues(SettingsContext context,
SettingsPropertyCollection ppc)
{
// In Web form, user login by their email, here username pass the email
string email = (string)context["UserName"];
bool isAuthenticated = (bool)context["IsAuthenticated"];

SettingsPropertyValueCollection svc =
new SettingsPropertyValueCollection();

foreach (SettingsProperty prop in ppc)
{
SettingsPropertyValue pv = new SettingsPropertyValue(prop);
switch (prop.Name)
{
case "Theme":
pv.PropertyValue = GetTheme(email, isAuthenticated, (string)prop.DefaultValue);
break;
default:
throw new ProviderException("Unsupported property.");
}
svc.Add(pv);
}
UpdateActivityDates(email, isAuthenticated, true);
return svc;
}
#endregion //GetPropertyValues

#region SetPropertyValues
///
/// Set Perperty Values
///

/// Context
/// Collection of Property value
public override void SetPropertyValues(SettingsContext context,
SettingsPropertyValueCollection ppvc)
{

}
#endregion //SetPropertyValues


#region UpdateActivityDates
/// Updates the LastActivityDate and LastUpdatedDate values
/// when profile properties are accessed by the
/// GetPropertyValues and SetPropertyValues methods.
/// Passing true as the activityOnly parameter will update
/// only the LastActivityDate.
///

/// Current user's email
/// is Authenticated
/// Activity
private void UpdateActivityDates(string email, bool isAuthenticated, bool activityOnly)
{
DateTime activityDate = DateTime.Now;
}
#endregion //UpdateActivityDates


#region GetTheme
///
/// Retrieves theme from the database during the call to GetPropertyValues.
///

/// Current user's email
/// is Authenticated
///
private string GetTheme(string email, bool isAuthenticated, string defaultTheme)
{
if (!isAuthenticated)
{
return defaultTheme;
}
if (null == email)
throw new ArgumentNullException("email");
// check if username is empty
if (email.Length == 0)
throw new ArgumentException("Argument is empty", "username");
try
{
// create the object manager
IUsersManager usersManager = ManagerFactory.CreateManager(Utilities.PortalConnStringID);
// Validate
IUsers user = usersManager.RetrieveByemail(email);
if (user.IsEmpty)
return defaultTheme;

if (string.IsNullOrEmpty(user.theme))
return defaultTheme;
else
return user.theme;
}
catch
{
throw;
}

}
#endregion
}

Variables in ASP.NET Page

今天花了一个多小时在Debug这个程序
var items = [];
for (int i=0; i〈items.length; i++)
...

浏览器告诉我在line **上Expected ;

知道什么错误了吗? 原来是int i的问题,一会儿用C#,一会儿用Javascript, 把int i和var i混了。害得我好久都没找到问题所在。

客户支持:
今天又花了一天的时间在查一个客户反应的问题:已经有5天时间他们收不到任何Order! 他们的用户网上购物时的信用卡都被Decline!

让他们把Verisign的帐号给我们,上去看了之后,发现所有的Transaction在Verisign上都查不到。而我们这一边则显示Transaction Fail.

以前用VB做的Dll运行几天后总会罢工,这次还以为是同样的问题,重新注册过,还是不行。

在源码取得Credit Card配置前和把信用卡送到Verisign处理之后插入几行跟踪信息,最后才发出问题在他们在5天前改了Verisign的密码,但没有相应在我们的网站上更新密码,所以显示的具体Transaction是“User Authentication Fail".

还好错误不在我们。

Visual Studio 2005 的ReportViewer

Team里用Visual Studio 2005做开发一有三个月了,在这三个月里已经重装了Visual Studio 2005 三次了。先是June 的CTP(Community Technology Preview)版本,发现ReportViewer控件在Design模式下,不管是Win Form还是Web Form属性显示是空的,然后回到Beta 2, 属性显示正常了,但是在Web上Paging和Zoom时会出错,这时刚好要给客户Demo, 只好当夜换成Aug 的CTP版本。这次Paging和Zoom都没有问题,而且还多了导出到PDF和Excel的功能。最后还是有Bug, 当Refresh时ObjectDataSource不会保持ViewState!
这个问题出在我们是程序动态给ObjectDataSource赋值,代码如下:
///
/// Function to create report using ReportViewer in UserControl
///

/// Full path name of .rdlc file
/// The datasource name use in .rdlc file
/// The class name in which provide a method to retrieve data for report
/// The method name in a class to retrieve data for report, this method can return a IList as datasource for report
/// Parameter collection to pass to the method
/// Parameters to pass to report (.rdlc)
public void CreateReport(string reportRdlc, string datasourceName, string dsTypeName, string dsSelectMethod, ParameterCollection dsParamas, Microsoft.Reporting.WebForms.ReportParameter[] repParams)
{
//ObjectDataSource ObjectDataSource1 = new ObjectDataSource(dsTypeName, dsSelectMethod);
hid_dataSourceTypeName = dsTypeName;
hid_dataSourceSelectMethod = dsSelectMethod;
ObjectDataSource1.TypeName = dsTypeName;
ObjectDataSource1.SelectMethod = dsSelectMethod;
//ObjectDataSource1.ID = datasourceName;
ObjectDataSource1.SelectParameters.Clear();
foreach (Parameter p in dsParamas)
{
ObjectDataSource1.SelectParameters.Add(p);
}
ReportViewer1.Attributes.Add("hid_dataSourceTypeName", dsSelectMethod);
ReportViewer1.Attributes.Add("hid_dataSourceSelectMethod", dsSelectMethod);
this.ReportViewer1.LocalReport.DisplayName = reportRdlc.Replace(".rdlc","");
this.ReportViewer1.LocalReport.ReportPath = reportRdlc;

this.ReportViewer1.LocalReport.DataSources.Clear();
Microsoft.Reporting.WebForms.ReportDataSource reportDatasource1 = new Microsoft.Reporting.WebForms.ReportDataSource();
reportDatasource1.Name = datasourceName;
//reportDatasource1.DataSourceId = datasourceName;
reportDatasource1.DataSourceId = ObjectDataSource1.ID;
this.ReportViewer1.LocalReport.DataSources.Add(reportDatasource1);
this.ReportViewer1.LocalReport.SetParameters(repParams);
this.ReportViewer1.LocalReport.Refresh();
}

最后跟踪发现Refresh回到服务器之后,用ReportViewer的Refresh之后,ObjectDataSource的TypeName和SelectMethod的值就丢了。只好给他加个补丁:
protected void ReportViewer1_PreRender(object sender, EventArgs e)
{
ObjectDataSource1.TypeName = hid_dataSourceTypeName;
ObjectDataSource1.SelectMethod = hid_dataSourceSelectMethod;
}
public string hid_dataSourceTypeName
{
get { return Convert.ToString(ViewState["hid_dataSourceTypeName"]); }
set { ViewState["hid_dataSourceTypeName"] = value; }
}
public string hid_dataSourceSelectMethod
{
get { return Convert.ToString(ViewState["hid_dataSourceSelectMethod"]); }
set { ViewState["hid_dataSourceSelectMethod"] = value; }
}
不知道大家有没有其它办法。