2011年11月8日 星期二

App.Config 的轉換

在 Visual Studio 2010 中,可以針對 Web.config 進行轉換。

那 App.config 呢?好心人很多,現在也可以使用相同語法進行 app.conifg 的轉換。

在 Extension Manager 中找 SlowCheetah - XML Transforms

image

參考自 http://www.hanselman.com/blog/SlowCheetahWebconfigTransformationSyntaxNowGeneralizedForAnyXMLConfigurationFile.aspx

2011年10月31日 星期一

特殊符號的英文

from http://tw.knowledge.yahoo.com/question/question?qid=1606121207007

特殊符號的英文

~ Tilde (取代符號)
@ At sign, at (At 符號,At)
# Pound sign (井字號)
$ Dollar sign (錢符號)
^ Caret (插入號)
& Ampersand (And 符號)
* Asterisk (星號)
Backslash (反斜線)
[ Open bracket (左開式方括弧)
] Close bracket (右關式方括弧)
( Open parenthesis (左開式圓括號)
) Close parenthesis (右關式圓括號)
: Colon (冒號)
, Comma (逗號)
-- Double dash (雙破折號)
... Ellipsis (省略符號)
' Single quote (單引號)
" Quote (引號)
= Equals (等號)
+ Plus, plus sign (加,加號)
! Exclamation point (驚歎號)
> Greater than (大於)
< Less than (小於)
? Question mark (問號)
. Period, dot (句號,點)
; Semicolon (分號)
- Hyphen (連字號)
— Dash (破折號)
_ Underscore (底線)
| Vertical bar (垂直線)
{ Open brace (左開式大括號)
} Close brace (右關式大括號)
% Percent, percent sign (百分比,百分比符號)
/ Slash (斜線)
// Double slash (雙斜線)

2011年10月14日 星期五

WPF 中如何 Binding 到 UserControl

WPF 中,最好用的莫過於強大的繫結(Binding)能力了。也因此,以往常用的 WinForm 寫作方式也必須作個改變。這裡以 UserControl 的 Binding 為例。

Windows Form 中 UserControl

第一個例子是舊習慣的 UserControl 寫作方式。

首先有個 MyUserControl,功能為:使用 Count propery,顯示出多個人名的下拉選單,讓使用者選擇。並以 SelectedName 作為選擇項目的輸出。

public partial class MyUserControl : UserControl
  {
    public MyUserControl() { InitializeComponent(); }

    public int Count { get; set; }

    public string SelectedName
    {
      get { return cbNames.SelectedValue as string; }
    }

    private void UserControl_Loaded(object sender, RoutedEventArgs e)
    {
      var q = from i in Enumerable.Range(1, Count)
              select string.Format("Name{0}", i);
      cbNames.ItemsSource = q;
    }
  }

使用此 UserControl 時,只需要在 xaml 中加上 Count 的屬性,就可以顯示多個人名。

MainWindow.xaml

<Window x:Class="NormalUserControl.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525" xmlns:my="clr-namespace:NormalUserControl">
  <Grid>
    <my:MyUserControl HorizontalAlignment="Left" Margin="13,11,0,0" x:Name="myUserControl1" VerticalAlignment="Top" Count="3" />
    <Button Content="Ok" Height="23" HorizontalAlignment="Left" Margin="25,87,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
  </Grid>
</Window>

最後,在 Button Click event handler 中,如下的使用方式,可以知道使用者選擇了何人。
private void button1_Click(object sender, RoutedEventArgs e)
{
  MessageBox.Show(myUserControl1.SelectedName);
}

一切似乎運作的很好。這也是 Windows Form 的寫作方式。

有個問題來了,如果這個 UserControl 的 Count 值,是繫結自另一個控制項呢?

如下:

<Window x:Class="NormalUserControl.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525" xmlns:my="clr-namespace:NormalUserControl">
  <Grid>
    <my:MyUserControl HorizontalAlignment="Left" Margin="13,11,0,0" x:Name="myUserControl1" VerticalAlignment="Top" Count="{Binding}" />
    <Button Content="Ok" Height="23" HorizontalAlignment="Left" Margin="23,108,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
    <TextBox x:Name="txtCount" Margin="160,12,247,265" Text="3" />
  </Grid>
</Window>
我們想到將 Count 的值繫結自一個 txtCount 的 TextBox,然而 Visual Studio IDE 卻告訴我,A 'Binding' cannot be set on the 'Count' property of type 'MyUserControl'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject. image

WPF : Binding 到 Control

在 WPF 中,DependencyObject 的屬性如果要被繫結的話,該屬性必須是 DependencyProperty.

修改的方式也不難。首先在MyUserControl.xaml.cs 中使用 dependencyProperty 的 snippet,作一個 Count 的 dependencyProperty, 取代原先的 Count property

image

public static readonly DependencyProperty CountProperty =
  DependencyProperty.Register("Count", typeof(int), typeof(MyUserControl), new PropertyMetadata(default(int))
  );

public int Count
{
  get { return (int)GetValue(CountProperty); }
  set { SetValue(CountProperty, value); }
}

再執行一次,程式可以正常編譯了。

PropertyChangedCallback

執行程式,修改 TextBox為另一個數字後發現,Binding 只有第一次執行時才會繫結,其他時間都不會。為什麼?

原因是目前的實作,並沒有告訴它「當資料變化時該怎麼執行」。

以下是修改後的結果。

public static readonly DependencyProperty CountProperty =
  DependencyProperty.Register("Count", typeof(int), typeof(MyUserControl), 
  new UIPropertyMetadata(default(int), OnCountChanged));

private static void OnCountChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
  var count = (int)e.NewValue;
  var q = from i in Enumerable.Range(1, count)
          select string.Format("Name{0}", i);
  var userControl = o as MyUserControl;
  var comboBox = userControl.FindName("cbNames") as ComboBox;
  comboBox.ItemsSource = q;
}

以上的重點在於,註冊 DependencyProperty 時,告訴 WPF,當 Count 的值有變化時,請執行 OnCountChanged.

UserControl 輸出的 Binding

上面談完了將值 Binding 到 UserControl,UserControl 可以讀值,並顯示 UI。那輸出的值是否也可以 Binding 呢?如下面的程式,我們發現 UserControl 的SelectedName 雖然可以讀出,但也不能做 DataBinding。

<Window x:Class="NormalUserControl.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525" xmlns:my="clr-namespace:NormalUserControl">
  <Grid>
    <my:MyUserControl HorizontalAlignment="Left" Margin="13,11,0,0" x:Name="myUserControl1" VerticalAlignment="Top" Count="{Binding ElementName=txtCount, Path=Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
    <Button Content="Ok" Height="23" HorizontalAlignment="Left" Margin="23,108,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
    <TextBox x:Name="txtCount" Margin="160,12,247,265" Text="4" />
    <Label Content="{Binding SelectedName, ElementName=myUserControl1}" Height="28" HorizontalAlignment="Right" Margin="0,101,247,0" Name="label1" VerticalAlignment="Top" />
    <Label Content="Your Select" Height="28" HorizontalAlignment="Left" Margin="150,101,0,0" Name="label2" VerticalAlignment="Top" />
  </Grid>
</Window>

此時,要解這個問題,SelectedName仍然需要 dependencyproperty。

public string SelectedName
{
  get { return (string)GetValue(SelectedNameProperty); }
  set { SetValue(SelectedNameProperty, value); }
}

public static readonly DependencyProperty SelectedNameProperty =
    DependencyProperty.Register("SelectedName", typeof(string), typeof(MyUserControl),
    new UIPropertyMetadata(null));

 

光這樣是不夠的。ComboBox 的 SelectedItem 並不會自動更新這我們剛剛加上的 SelectedName。

此時在建構子上加如下的繫結(只不過是用程式加的)

var binding = new Binding("SelectedName") { Source = this };
  cbNames.SetBinding(ComboBox.SelectedItemProperty, binding);

 

結論

當 UserControl 遇到 Binding 時,會發生許多原先在 Windows Form 上遇不到的事。正因為 WPF 的繫結非常強大,要駕馭它也要相當的功夫。

2011年9月29日 星期四

Windows 8 的複製檔案

windows 8 在複製檔案時,資訊豐富多了。

image

並且,可以暫停動作。這樣一來,可以讓其他重要的複製先完成。

好想把這個功能放到 Windows 7 哦。

2011年8月18日 星期四

在 Windows 7 上開發 Windows Mobile 6.1 安裝步驟

最近因專案的緣故,需要開發 Windows Mobile 6.1 的應用程式。

這是我第一個 Mobile 的應用程式,沒想到必須使用舊的系統來開發。(心聲:真的有點老舊的系統,好想直接開發 WM 7,就可以使用 Silverlight 了)

無論如何,Visual Studio 2010 是無法開發這麼老舊的系統了。因此必須回頭灌 VS2008,沒想到,過程有點辛苦。

首先,我用了 Hyper-V 上的虛擬機 (Windows 2003)來開發,沒想到 Windows Mobile Emulator 無法模擬 PDA 上的網路。想了一下,在虛擬機上再虛擬一個網路,果然有些不對勁。

以下,是我在實體機(Windows 7)上的安裝步驟。

  1. 安裝 Visual Studio 2008
  2. 安裝 Team Explorer 2008
  3. 安裝 Visual Studio 2008 Service Pack 1
  4. 安裝 VSTS 2008 相容 2010 套件(2~4 的步驟是為了使用 TFS 2010 )
  5. 安裝 drvupdate-amd64.exe (Windows Mobile Device Center for Vista)
  6. 安裝 Windows Mobile 6 Professional SDK Refresh.msi
  7. 安裝 Windows Mobile 6 Professional Images (CHT).msi
  8. 由於我已經安裝了 Windows Virtual PC,而VS2008 上的PDA需要安裝舊版的 Virual PC 2007。為了解決兩個版本不能同時安裝的問題,見 http://www.brianpeek.com/post/2009/05/02/windows-virtual-pc-and-the-microsoft-device-emulator.aspx

2011年8月16日 星期二

使用 ExceptionCallHandler 進行例外處理

緣由

當我們開發應用程式時,難免(一定)會有bug。無論是使用者輸入不正確的資料,環境/平台因素等,只要有 bug 就是開發人員的錯。

開發人員難道只有被罵的份嗎?當然不能坐以待斃。我們開發人員必須留下錯誤的痕跡,以快速地找到錯誤的原因。

因此,我們都必須寫像下面的 Code

try
{
  //執行商業邏輯
}
catch (Exception)
{
  //記錄例外資訊到檔案或資料庫
  Console.WriteLine("對不起!發生錯誤,請洽...");
}

要如何進行例外的記錄(log)呢?

解決1:使用 EHAB

解決的方法,我都是使用 Enterprise Library 中的Exception Handling Application Block。

程式碼的部份很簡單。只需要呼叫ExceptionPolicy.HandleException 如下:

public virtual void Go2()
{
  try
  {
    throw new NotImplementedException();
  }
  catch (Exception ex)
  {
    ExceptionPolicy.HandleException(ex, "Policy");
  }
}

但config 設定的話,新手初看就很複雜了。這裡我並不想多做介紹,有興趣的話可以看這一篇。

這裡有個問題,如果每一段程式都需要 try…cache 再來處理例外的話,程式碼就變的很難看。

解決2:集中一個地方處理例外

要在同一個地方處理例外其實很常見。例如在 ASP.NET 上可以在 Global.aspx.cs 中的 Application_Error 中處理

protected void Application_Error(object sender, EventArgs e)
{
    //處理 Application 所有拋出的 exception
    Exception exception = Server.GetLastError();
    ExceptionPolicy.HandleException(exception, "LogAllInFile");
}

雖然集中在一個地方處理很方便,但能做的事情也很有限,只能使用相同的 Policy 來處理例外。並且一旦到如 Global.asax 這裡來處理時,就失去了各自處理的彈性。

有沒有一個方便的解決方法,既可以各自處理,又不用每個地方寫 try..cache 這樣的 Code 呢?

解決3:使用 ExceptionCallHandler

接下來就是今天的主題了。PIAB 可以幫我們解決這樣的問題。第一步,是要讓物件可以被注入。因此物件的類別宣告不可以為sealed,Method 也必須是 virtual。

 public class Go
  {
    public virtual void Go1()
    {
      throw new NotImplementedException();
    }

第二步是在 Method 上進行 Attribute 的註記。如下:

[ExceptionCallHandler("Policy")]
public virtual void Go1()
{
  throw new NotImplementedException();
}

其中,”Policy”是Exception Policy 的名稱。

當然,不是只有這樣就可以搞定的。我們必須在實例化物件前開始進行複雜的 injection(注射)以達到我們的目的。第三步:

static void Main(string[] args)
{
  var container = new UnityContainer();
  container.AddNewExtension<EnterpriseLibraryCoreExtension>();
  container.AddNewExtension<Interception>();
  container.RegisterType<ICallHandler, ExceptionCallHandler>("ExceptionCallHandler",
      new InjectionConstructor(
          new ResolvedParameter(typeof(ExceptionPolicyImpl), "Policy")));

  container.RegisterType<Go>(
    new InterceptionBehavior<PolicyInjectionBehavior>(),
    new Interceptor<VirtualMethodInterceptor>());

  var g = container.Resolve<Go>();
  g.Go1();
}

討論

使用 ExceptionCallHandler 來解決似乎方便多了,因為可以在不同的 Method 上註記當錯誤發生時,使用不同的 exception policy 處理之。

但壞處是物件的 Method 必須都是 virtual,讓 IoC 有機會注入不同的行為。

Share with Facebook