windows 8 在複製檔案時,資訊豐富多了。
並且,可以暫停動作。這樣一來,可以讓其他重要的複製先完成。
好想把這個功能放到 Windows 7 哦。
最近因專案的緣故,需要開發 Windows Mobile 6.1 的應用程式。
這是我第一個 Mobile 的應用程式,沒想到必須使用舊的系統來開發。(心聲:真的有點老舊的系統,好想直接開發 WM 7,就可以使用 Silverlight 了)
無論如何,Visual Studio 2010 是無法開發這麼老舊的系統了。因此必須回頭灌 VS2008,沒想到,過程有點辛苦。
首先,我用了 Hyper-V 上的虛擬機 (Windows 2003)來開發,沒想到 Windows Mobile Emulator 無法模擬 PDA 上的網路。想了一下,在虛擬機上再虛擬一個網路,果然有些不對勁。
以下,是我在實體機(Windows 7)上的安裝步驟。
當我們開發應用程式時,難免(一定)會有bug。無論是使用者輸入不正確的資料,環境/平台因素等,只要有 bug 就是開發人員的錯。
開發人員難道只有被罵的份嗎?當然不能坐以待斃。我們開發人員必須留下錯誤的痕跡,以快速地找到錯誤的原因。
因此,我們都必須寫像下面的 Code
try
{
//執行商業邏輯
}
catch (Exception)
{
//記錄例外資訊到檔案或資料庫
Console.WriteLine("對不起!發生錯誤,請洽...");
}
要如何進行例外的記錄(log)呢?
解決的方法,我都是使用 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 再來處理例外的話,程式碼就變的很難看。
要在同一個地方處理例外其實很常見。例如在 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 有機會注入不同的行為。
當我使用 Visual Studio 2010 建立一個 Unit Test 時,發生了下面的錯誤
While trying to generate your tests, the following errors occurred:
Value cannot be null.
Parameter name: key
找了一下,原因很簡單。我的 TestProject 已經有相同名稱的檔名了,但在不同的目錄。VS 2010 發現有相同的 class (Full name),就會發生這樣的錯誤。
解決方法呢?就是改檔名或改 namespace 即可。
這個標題下的有些古怪。
我的專案,原本都以 ASP.NET為主。排程的程式大都是 Console 的程式。然而最近接了一個 Client Application 的專案。
經過比較後,我選擇了較新的 WPF,而放棄了 WinForm。問題來了,專案中的成員會問到WPF中如何在DataGrid 取得使用者所選擇ComboBox 的SelectedIndex。
為了演示 WebForm 與 WPF 兩者的差異,我寫了這個 demo.
這個 Demo其實很簡單:設定10個 Emp 的性別。然而分別以 WebForm 與 WPF 做一次,再討論兩者的設計方式。
雖然是不同的應用程式,但有相同的資料,故,將兩者相同的程式放在 CommonLib 中.
using System.Collections.Generic;
using System.Linq;
namespace CommonLib
{
public class Emp
{
public int Id { get; set; }
public string Name { get; set; }
public int Gender { get; set; }
}
public class GenderStruct
{
public int Id { get; set; }
public string Name { get; set; }
}
public class MyDataSource
{
public List<Emp> GetAllEmps()
{
var q = from i in Enumerable.Range(1, 10)
select new Emp()
{
Id = i,
Name = string.Format("N{0}", i),
Gender = 1
};
return q.ToList();
}
public List<GenderStruct> GetAllGenders()
{
return new List<GenderStruct>
{
new GenderStruct { Id = 1, Name = "Boy"},
new GenderStruct { Id = 2, Name = "Girl"},
new GenderStruct { Id = 3, Name = "Gay"},
};
}
}
}
ASP.NET WebForm的設計其實真的很麻煩,但用習慣了,也就習以為常。下是在 aspx 中放入一個 GridView 以顯示員工的資料。使用者輸入性別再按下 button,網頁顯示所有員工的最後值。
Default.aspx
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
DataKeyNames="Id" onrowdatabound="GridView1_RowDataBound">
<Columns>
<asp:BoundField DataField="Id" HeaderText="Id" />
<asp:BoundField DataField="Name" HeaderText="Name" />
<asp:TemplateField HeaderText="Gender">
<ItemTemplate>
<asp:DropDownList runat="server" ID="ddl">
</asp:DropDownList>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<asp:Button ID="Button1" runat="server" onclick="Button1_Click" Text="Button" />
Default.aspx.cs
using System;
using System.Web.UI.WebControls;
using CommonLib;
namespace WebApplication1
{
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
var dataSource = new MyDataSource();
var emps = dataSource.GetAllEmps();
GridView1.DataSource = emps;
GridView1.DataBind();
}
}
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
var dataSource = new MyDataSource();
var allGender = dataSource.GetAllGenders();
if (e.Row.RowType == DataControlRowType.DataRow)
{
var ddl = e.Row.FindControl("ddl") as DropDownList;
ddl.DataSource = allGender;
ddl.DataValueField = "Id";
ddl.DataTextField = "Name";
ddl.DataBind();
}
}
protected void Button1_Click(object sender, EventArgs e)
{
//由 GridView1 讀取資料
foreach (GridViewRow row in GridView1.Rows)
{
var ddl = row.FindControl("ddl") as DropDownList;
var gender = ddl.SelectedIndex;
var id = int.Parse(row.Cells[0].Text);
string message = string.Format("Id:{0}, Gender:{1}", id, gender);
Response.Write(message + "<br />");
}
}
}
}
這一段程式寫的還真不怎麼樣,竟然還用 Response.Write 這種古老 ASP 的寫作方式?重點不是在演示如何取得資料,而是 ASP.NET 先天上的限制: Stateless。
由於 Web 的 Stateless 的特性,使得Server 端不會記得 Client 曾經輸入或給過的值。因此,當網頁第一次進入時取得所有 Emp 的資料,並在 GridView 的 RowDataBound 事件發生時,為每個性別欄位設定 DropDownList。
之後,使用者輸入完畢後,Submit 回到 Server後,emp 及 gender 的資料都已經不見了,只剩下 GridView 這個 Control 中的值能幫我們留下使用者最終的選擇。
在不得已的情況下,我們必須研究 GridView 的組成,如 GridView 的 Row,Row 下有 Cells,再使用 FindControl 取得 DropDownList…等等。![]()
在 WPF 中,由於是 Stateful,相同的功能,寫作方式相對 ASP.NET 大為進步。
MainForm.xaml
<DataGrid AutoGenerateColumns="False" Height="206" HorizontalAlignment="Left" Margin="12,12,0,0" Name="dataGrid1" VerticalAlignment="Top" Width="288" >
<DataGrid.Columns>
<DataGridTextColumn Header="Id" Binding="{Binding Path=Id}" />
<DataGridTextColumn Header="Name" Binding="{Binding Path=Name}" />
<DataGridTextColumn Header="Gender" Binding="{Binding Path=Gender}" />
<DataGridComboBoxColumn Header="Gender2" x:Name="cbGender" SelectedValuePath="Id" DisplayMemberPath="Name" SelectedValueBinding="{Binding Gender}" />
</DataGrid.Columns>
</DataGrid>
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="359,115,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
MainForm.xaml.cs
public partial class MainWindow : Window
{
private List<Emp> _emps;
private List<GenderStruct> _allGenders;
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
//取得資料並放到 Window 的變數中。因為Stateful,所以可以這樣做
var dataSource = new MyDataSource();
_emps = dataSource.GetAllEmps();
_allGenders = dataSource.GetAllGenders();
dataGrid1.ItemsSource = _emps;
cbGender.ItemsSource = _allGenders;
}
private void button1_Click(object sender, RoutedEventArgs e)
{
var emp = dataGrid1.SelectedItem as Emp;
var message = string.Format("Name: {0}, Gender: {1}", emp.Name, emp.Gender);
MessageBox.Show(message);
}
}
值得注意的是,在 Window Loaded 後,取得所有 Emp 及 Gender 資料後,直接放到 Window 的 private member 中,讓後續的資料繫結(databind) 作業直接存取window private member即可。
使用者輸入完畢後,更新後的資料「直接」反應到資料繫結的對象(即 _emps, _allGenders),不必像 WebForm一樣透過 Control 間接取得資料。
這個範例中,主要演示了 ASP.NET WebForm 與 WPF 的先天上的差異(Stateless vs Stateful),並且由於 WPF 雙向繫結的功能,使用者的輸入可以直接更新到資料來源,遠較WebForm 來的直覺與方便。
經由上次介紹的 設定 IIS 為自動啟動 (Auto-Start)後,原先在 TFS 的 Web 自動部署 反而失敗了。訊息如下:
C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.targets (3588): Web deployment task failed.((2011/7/5 上午 08:16:45) An error occurred when the request was processed on the remote computer.) (2011/7/5 上午 08:16:45) An error occurred when the request was processed on the remote computer. Filename: \\?\C:\Windows\system32\inetsrv\config\applicationHost.config Line number: 176 Error: Unrecognized attribute 'serviceAutoStartMode'
看來,目前 TFS 的 Publishing 尚不知道 AppFabric 的serviceAutoStartMode 屬性。只好在測試機上不使用 IIS Auto Start 的功能。