2008年11月26日 星期三

應用程式如何得知 "控制台" --> "地區語言選項" 的改變

系統事件的使用者喜好設定變更時可以藉由 命名空間Microsoft.Win32 的 SystemEvents.UserPreferenceChanged 事件進行通知,

以下程式是註冊該事件

Visual Basic
AddHandler SystemEvents.UserPreferenceChanged, AddressOf SystemEvents_UserPreferenceChanged DisplayCultureInfo()
C#
SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(SystemEvents_UserPreferenceChanged);

它的事件參數 UserPreferenceChangedEventArgs 的 Category 屬性會傳入變更的事件類別, 如果是 "地區語言選項" 的改變, 是 UserPreferenceCategory.Locale

事件程序如下:

Visual Basic
Sub SystemEvents_UserPreferenceChanged(ByVal sender As Object, ByVal e As Microsoft.Win32.UserPreferenceChangedEventArgs)
        If e.Category = UserPreferenceCategory.Locale Then
            DisplayCultureInfo()
        End If
    End Sub
C#
static void SystemEvents_UserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e)
{
if (e.Category== UserPreferenceCategory.Locale )
DisplayCultureInfo();
}

Thread.CurrentThread.CurrentCulture 會回傳目前執行緒的文化特性, 只有應用程式載入初期會寫入這個值, 所以控制台改變, 這個值並不會跟著異動, 若要取得控制台最新的文化特性, 必須先將目前的快取資料清除, 呼叫ClearCachedData 方法. 當快取被清除時, 將會自動取得控制台最新的設定.

Visual Basic
    Sub DisplayCultureInfo()
        Thread.CurrentThread.CurrentCulture.ClearCachedData()
        Dim UsersCulture As CultureInfo = Thread.CurrentThread.CurrentCulture
        Dim region As New RegionInfo(UsersCulture.Name)
        Console.WriteLine(region.DisplayName)
        Console.WriteLine(region.EnglishName)
        Console.WriteLine(region.NativeName)
        Console.WriteLine(region.CurrencyEnglishName)
        Console.WriteLine(region.CurrencyNativeName)
    End Sub
C#
private static void DisplayCultureInfo()  
{
Thread.CurrentThread.CurrentCulture.ClearCachedData();
CultureInfo UsersCulture = Thread.CurrentThread.CurrentCulture;
RegionInfo region = new RegionInfo(UsersCulture.Name);
Console.WriteLine(region.DisplayName);
Console.WriteLine(region.EnglishName);
Console.WriteLine(region.NativeName);
Console.WriteLine(region.CurrencyEnglishName);
Console.WriteLine(region.CurrencyNativeName);
}

2008年11月9日 星期日

ASP.NET 專案的 Profile

如果你使用Visual Studio的 File --> New Web Site 來設計專案時, Web.config定義好Profile 的屬性並存檔之後, 在程式中使用Profile 會有Intellisence 自動列表相關屬性.

WebSite模式是將profile的properties 建立成一個Strongly Type, 請看下圖:

建立成一個Strongly Type

可是如果使用File --> New Project , 建立ASP.NET Web Application時, Web.config定義好Profile 的屬性並存檔之後, 在程式中使用Profile, Intellisence 所列表的資訊竟與前者完全不同, 而且並沒有定義好的屬性... 真是傷腦筋!!

用Object Browser 完全看不到Strongly type 的Profile類別...

2

這難道就不能用Profile 的功能了嗎? 答案: 並不是...

只是寫起來真的很麻煩, 必須使用HttpContext.Current.Profile的SetPropertyValue及GetPropertyValue存取屬性內容.

例如, 修改FirstName 的屬性

HttpContext.Current.Profile.SetPropertyValue ("FirstName", "Lisa")

讀取FirstName的屬性

Dim FName As String = HttpContext.Current.Profile.SetPropertyValue ("FirstName").ToString()

 

Ps.這是上課時同學發現Profile 沒有IntelliSense, 仔細看一下Soluction Explorer 才發現是專案模式而不是Web Site模式....但還是感到相當意外, 竟然在專案模式下的Profile不是Strongly type..., 只好用原始方式找答案.

Ps.用VS2005與VS2008 的Project 模式的Profile 都不是Strongly type.

ASP.NET快取之SQL相依性

除了原來的CacheDependency 針對File相依性決定移除Cache之外, ASP.NET 2.0 新增了SQL 相依性的功能, 是針對SQL Server 的Table 或是查詢進行變更時, 移除Cache 的策略. 它提供了兩種方式,

  1. 輪詢模式
  2. 查詢通知

輪詢模式

只支援SQL Server 7.0 (含)以上的版本, 由ASP.NET主動定期向SQL Server詢問Table 是否遭到異動, 以決定是否移除快取.

使用輪詢模式必須執行以下動作:

  1. 執行aspnet_regsql, 在資料庫及資料表設定異動檢查

    Aspnet_regsql –S 伺服器名稱 –E –d 資料庫 -ed –t 資料表 –et

  2. asp.net 的web.config 設定輪詢的時間及連線字串
    <caching>
    <sqlCacheDependency enabled="true" pollTime="1000">
    <databases>
    <add name="NWDB" connectionStringName="NorthwindConnectionString" pollTime="1000"/>
    </databases>
    </sqlCacheDependency>
    </caching>


  3. asp.net 的網頁要用到SQL Cache Dependency 時指定web.config 的SqlCacheDependencies 的項目名稱

    <%@ OutputCache Duration="秒數" VaryByParam="參數清單" SqlDependency = "資料庫快取相依性名稱:資料表" %>


    <%@ OutputCache Duration="3600" VaryByParam="None" SqlDependency = "NWDB:Categories;NWDB:Products" %>




查詢通知


只支援SQL Server 2005以上的版本, 由ASP.NET向SQL Server 註冊事件, SQL Server 只要發現查詢有異動, 便主動通知有註冊的服務.


使用查詢通知必須執行以下動作:



  1. 在SQL Server 啟用通知功能

    ALTER DATABASE Pubs SET ENABLE_BROKER ;


  2. 在ASP.NET的Global.asax, Application_Start 時向SQL Server註冊事件

    Application_Start事件啟動接聽程式


    SqlDependency.Start("連線字串")


    在Application_End事件停用接聽程式


    SqlDependency.Stop("連線字串")


  3. 網頁要用到時, 在相關的SqlCacheDependency 設定 CommandNotification

    <%@ OutputCache Duration="秒數" VaryByParam="參數清單" SqlDependency = "CommandNotification" %>


2008年11月8日 星期六

內嵌DropDownList, 欄位值為Null時怎麼辦

ASP.NET 2.0 小技巧

在GridView在編輯模式時內嵌DropDownList, 當對應的那筆資料欄位值為Null時怎麼辦?

這是在上ASP.NET 課程時同學問的問題.

 

欄位Null值時那個DropDownList 的SelectedValue無法對應到對的值, 所以網頁就會出現錯誤. 怎麼解決呢?

如果那個欄位不能設計成不可為Null值, 那只好在DropDownList裡多加一個可以給SelectedValue對應到的值.

作法是:

當Null值被對Building 到SelectedValue時, 那個值實際上是一個空字串.

所以可以在DropDownList的Items 加上一個項目, Text 為"請選擇", Value為空值.

<asp:ListItem Value="">請選擇</asp:ListItem>

而且必須設定DropDownList的AppendDataBoundItems屬性為"True", 整個TemplateField看起來像這樣:

<asp:TemplateField HeaderText="CategoryID" SortExpression="CategoryID">   
    <EditItemTemplate>
        <asp:DropDownList ID="DropDownList1"  runat="server"
            DataSourceID="SqlDataSource2" DataTextField="CategoryName"
             DataValueField="CategoryID" SelectedValue='<%# Bind("CategoryID") %>'
           AppendDataBoundItems="True">
            <asp:ListItem Value="">請選擇</asp:ListItem>
        </asp:DropDownList>
        <asp:SqlDataSource ID="SqlDataSource2" runat="server"
            ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>"
            SelectCommand="SELECT [CategoryID], [CategoryName] FROM [Categories]">
        </asp:SqlDataSource>
    </EditItemTemplate>
    <ItemTemplate>
        <asp:Label ID="Label1" runat="server" Text='<%# Bind("CategoryID") %>'></asp:Label>
    </ItemTemplate>
</asp:TemplateField>

就可以了.

1

2008年11月4日 星期二

WPF 的Command 是什麼?

WPF 隨手筆記

先別管Command 是什麼?

首先我們先回到傳統Windows Forms 的觀念裡, 如果你需要一個功能存檔功能, 你會怎麼設計?

一定很多人馬上舉手說, 我知道: "寫在Button 的 Click 事件", 或是有人會說 "建立一組選單, 在選單項目的事件寫上存檔的功能" ...等.

OK, 那如果同時要提供快捷鍵, 工具箱也有對應功能呢? 當然這在Windows Forms裡不難辦到.

 

相同的問題在WPF 裡要怎麼設計, 可以用相同的方式來做.

不過WPF提供了更聰明重用性更高的 Command, Command 的觀念是將這些可能用Button, Menu, ToolBar, 快捷鍵呼叫的功能統一定義在Command 裡, 以上面這個例子, 我可以設計一個叫Save 的Command, 然後呢, 如果需要用在某個Button, 或是任何控制項上以及快捷鍵, 那麼就將Command 與控制項或快捷鍵做 Binding , 這樣畫面設計上就會具有很大的彈性.

WPF為省去開發者寫一堆程式也內建了不少的Command, 例如常用的Copy, Paste, Cut...等功能. 如果沒有內建Command 再自行實作ICommand介面.

什麼是Dependency Property

WPF 隨手筆記

中文譯為依存屬性, 是WPF 引進的新屬性.

為何需要Dependency Property?

先看看一般.NET 物件, 一般Windows 控制項的屬性皆為Instance Property, 以Button為例, 若Button有96個Property, 那麼建立一個Button物件, 就得佔用96Property 所需的空間, 當然Window Forms 2.0的Control屬性或許沒有那麼多, 也就不會考慮佔用太多記憶體空間的問題.

但是WPF 的控制項可以提供相當高彈性的畫面設計, 相對的所需的屬性就會很多, 可又未必每個屬性都會用的到, 若全部設計成Instance 的Property, 那WPF 應用程式的記憶體最低需求將會很可觀.

於是WPF 引進Dependency Property 的觀念, 將一些Property 從控制項的Instance Property(也就是傳統作法)抽離 改為Dependency Property.

那到底什麼是Dependency Property?

Dependency Property 是一個存在System.Windows.DependencyObject所延伸的類別的靜態屬性, 那些不絕對需要用的屬性在需要用時就將它放置在這個DependencyObject之中, 以提升記憶體的可用性.

在使用WPF Controls 你可以不用太在意屬性是Dependency Property或Instance Property, 因為它的用法就像一般Property 的使用方式一樣, 那些記憶體配置的細節都被包裝在類別之中了.

Dependency Property 帶來的好處

除了節省記憶體空間之外, 因為處理成Dependency Property, 在屬性值變更時它提供了變更告知的功能.

Dependency Property 沒有改變設定屬性的程式寫法, 確意外的改變了傳統事件的寫法, 例如, 傳統上若要判知滑鼠進入控制項範圍之內, 會在MouseEnter事件上進行處理.

// 當滑鼠移到按鈕上時將前景顏色改為藍色
void Button_MouseEnter(object sender, MouseEventArgs e)
{
Button b = sender as Button;
if (b != null) b.Foreground = Brushes.Blue;
}
// 當滑鼠移出按鈕時將前景顏色改回黑色
void Button_MouseLeave(object sender, MouseEventArgs e)
{
Button b = sender as Button;
if (b != null) b.Foreground = Brushes.Black;
}





有了Dependency Property 若要得知滑鼠進入控制項範圍除了原本的MouseEnter事件之外, 另可從IsMouseOver(依存)屬性得知, 也就是說, 連帶的若要在移入時變更背景色, 除了寫傳統事件程式外, 還有另一個更簡單的選擇, 那就是使用XAML的Trigger 觀念.






<Button Margin="186.6,148,277.4,216" x:Name="button">  
<Button.Style>
<Style TargetType="{x:Type Button}" >
<Style.Triggers>
    <Trigger Property="IsMouseOver" Value="True">
        <Setter Property="Foreground" Value="Blue" />
    </Trigger>
</Style.Triggers>
</Style>
</Button.Style>
OK
</Button>



你不用擔心這段Trigger 元素沒有描述滑鼠移出時改回原來的色彩, 因為它會很聰明的自動改回去.







參考"Windows Presentation Foundation 新一代使用體驗開發實務"

(此書譯自原文:Windows Presentation Foundation UIeashed) 一書的第三章

2008年11月3日 星期一

一個控制項繫結多個欄位

WPF隨手筆記

WPF 的資料繫結允許多個欄位與一個控制項繫結, 使用以下步驟說明:

  1. 撰寫一類別, 實作IMultiValueConverter介面, 處理多個欄位連接成一個值的作業
        public class NameConverter : IMultiValueConverter {

    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
    string name;
    name = values[0] + " " + values[1];
    return name;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
    string[] splitValues = ((string)value).Split(' ');
    return splitValues;
    }

    }



  2. 在Windows.Resource宣告這個類別為資源
        <Window.Resources>    
            <c:NameConverter x:Key="NameConverter"/>
            <ObjectDataProvider x:Key="NorthwindDataSetDS" ObjectType="{x:Type WpfDataBinding:NorthwindDataSet}" d:IsDataSource="True"/>
        </Window.Resources>

        <Window.DataContext>
            <Binding Path="Employees" Mode="Default" Source="{StaticResource NorthwindDataSetDS}"/>
        </Window.DataContext>



  3. 在控制項要繫結的屬性上使用MultiBinding設定Converter及繫結的欄位
    <TextBox Margin="32,80,69.6,0" VerticalAlignment="Top" Height="48" TextWrapping="Wrap">    
                <TextBox.Text>
                    <MultiBinding Converter="{StaticResource NameConverter}"
                        ConverterParameter="FormatLastFirst">
                        <Binding Path="FirstName" />
                        <Binding Path="LastName" />
                    </MultiBinding>
                </TextBox.Text>
            </TextBox>




繫結Northwind的Employees Table, 執行結果:



1