2011年5月27日 星期五

在WPF 中對所有的 UserControl 作樣式設定

需求

在WPF 中如何一次對所有的 UserControl 作樣式設定呢?

第一版

在 WPF 中,要對同一個 Control 作預設的樣式設定很簡單。只需要在 App.xaml 中設定即可。

App.xaml

<Application x:Class="WpfApplication1.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml">
  <Application.Resources>
    <Style TargetType="UserControl">
      <Setter Property="Background" Value="Gray" />
    </Style>
  </Application.Resources>
</Application>

 

在 Visual Studio 中,可以在 designer 看到 usercontrol 的樣式套用了指定的樣式,看起來是可以的樣子哦!(當然沒這麼簡單,不然就不用寫這一篇了)

image

執行起來畫面就是沒有套用到。

image

程式的部份,請看附件的 WpfApplicatioin1 這個專案。

第二版

不信邪的我,稍稍改動了一下,讓樣式直接指定 UserControl1。這樣就可以套用樣式了。

<Application x:Class="WpfApplication2.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:WpfApplication2"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
    <Style TargetType="{x:Type local:UserControl1}">
      <Setter Property="Background" Value="Gray" />
    </Style>
  </Application.Resources>
</Application>

這樣子只能套用到指定的 UserControl1,而非所有的樣式。原因是:樣式雖然可以像「繼承」樣子套用到指定的 Type,但不是繼承出來的 Type 都可以套用父類別的樣式。

程式的部份,請看附件的 WpfApplicatioin2 這個專案

第三版

這一版,自已加上了一個自訂的 MyUserControl 並繼承自 UserControl。之後,所有的 UserControl 都改繼承自訂的 MyUserControl。

在 App.xaml 中,樣式使用 BaseOn 這個關鍵。

<Application x:Class="WpfApplication3.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:WpfApplication3"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
    <Style TargetType="UserControl" x:Key="baseStyle">
      <Setter Property="Background" Value="Gray" />
    </Style>
    <Style TargetType="{x:Type local:MyUserControl}" BasedOn="{StaticResource baseStyle}" />
  </Application.Resources>
</Application>

滿心歡喜地以為這樣萬事 OK 了,結果還是失敗。使用 Snoop 來查一下,Style 並沒有值。

image

程式的部份,請看附件的 WpfApplicatioin3 這個專案

最後版本

不知道這是不是 bug。結果還是動用了 code behind 來解決事情。

public class MyUserControl : UserControl
{
public MyUserControl()
{
  this.Style = this.FindResource("baseStyle") as Style;
}
}

使用 Snoop 來看,終於有套用到 style 了

SNAGHTML27d2b2c

程式的部份,請看附件的 WpfApplicatioin4 這個專案

結論

這是 WPF 4 的 bug 嗎?不知道。只知道浪費了2小時的時間。

2011年5月26日 星期四

LINQ 的延遲執行(Deferred execution) 造成的奇特現象

LINQ 的一大強項,就是 deferred execution。所謂的 deferred execution,是指當下宣告時只是宣告未來要執行的命令,而實際的執行要等到列舉(enumerate) 時才真的開始。

範例

class Program
{
  static void Main(string[] args)
  {
    var stringArray = new string[] { "A", "B", "D", "D", "E"};

    //宣告執行的動作,未實際執行
    var q = from i in stringArray
            where i.StartsWith("D")
            select i;
    //列舉時才真的執行
    var count = q.Count();
    Console.WriteLine(count);
  }
}

 

這裡看來沒什麼問題。

問題範例

class Program
{
static void Main(string[] args)
{
  var stringArray = new string[] { "A", "B", "D", "D", "E"};
  var enumCount = 0;
  //宣告執行的動作,未實際執行
  var q = from i in stringArray
          select new {EnumCount = enumCount++, i};
  //列舉時才真的執行
  foreach (var item in q)
  {
    enumCount++;
    Console.WriteLine("{0}/{1}", enumCount, item.EnumCount );
  }
}
}

輸出

image

看到問題了嗎?原以為在 q 裡面的值應該是 0, 1, 2, 3, 4,結果輸出的卻是 0, 2, 4, 6, 8。

原因是在實際執行時(foreach loop 內)受到了 enumCount++這一行的干擾,讓原本的執行原意被扭曲了。

建議

LINQ 雖然好用,但在執行時千萬要注意 deferred execution 的現象。不該 deferred execution 時,就直接列舉吧。即 .ToList(), ToArray(), Sum() 等

2011年5月21日 星期六

使用 Oracle Entity Framework 的問題

使用ODAC 11.2.0.2.30 Beta for Entity Framework and LINQ to Entities (以下簡稱 Oracle EF)來查詢資料,發生了以下的錯誤。

情境

發票的主檔與明細檔。

這只是相當簡單的查詢CREATE TABLE MYSCHEMA.INV
(
INVOICENO NUMBER(38),
NAME CHAR(10 BYTE)
)

CREATE TABLE MYSCHEMA.INVDETAIL
(
INVDETAILID NUMBER(38),
INVNO NUMBER(38),
QTY NUMBER(38),
AMT NUMBER(38)
)

並且,以 InvNO 做為關聯

image

執行下面的 LINQ To Entity

var context = new Entities();
var q = from m in context.INVs
      select new
               {
                 m.INVOICENO,
                 Amt = m.INVDETAILs.Sum(x => x.QTY * x.AMT)
               };
Console.WriteLine(((ObjectQuery)q).ToTraceString());
var resutl = q.ToList();

發生錯誤

Oracle.DataAccess.Client.OracleException: ORA-00904: "Extent1"."INVOICENO": invalid identifier

產生的sql 還有些長,就不貼出來了。

已在 Oracle ODP.NET Forum 上發問了。

說實話,這樣的查詢其實相當普遍,然而 Oracle 在 Alfa 版時竟沒查出,而在Beta 版時給普羅大眾給測到這個 bug,實在有些……

2011年5月20日 星期五

DataGrid 在 StackPanel 下的效能問題

一個無意間,觸碰到了地雷。而這個地雷,是有原因的。

WPF 的 xaml 中,如果在 StackPanel 中含有 DataGrid,是常見的事。但是,如果 DataGrid bindind 到大量的資料時,就有些問題了。

程式

下面是我的 xaml

xaml

<Window x:Class="LargeDataInDataGrid.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" Loaded="Window_Loaded">
  <StackPanel>
    <Label Content="資料筆數" />
    <TextBox Name="txtRecNo" TextChanged="txtRecNo_TextChanged" Text="100" />
    <DataGrid Name="dgResult" ItemsSource="{Binding}"/>
  </StackPanel>
</Window>

 

程式運作如下圖

SNAGHTML575c70

現象

當資料只有100筆時,看起來很正常。但看一下工作管理員,天啊,要秏掉記憶體 72 MB。WPF 這麼秏 memory?

image

如果將筆數調整到 1000筆呢?剛改完數字,WPF 程式彷彿當掉一樣。hang 住個幾秒鐘後,看一下記憶體,增加到 187 MB了。

SNAGHTML5b7c97

image

原因

原來, StackPanel 的用途,是儘量地長出 Child control 所要使用的空間 (space)。DataGrid 要長出 100 筆資料的空間,就計算並配置所要的空間及記憶體。所以當 DataGrid 要長出1000筆時,記憶體的使用量就不得了。

但是,我們看不到這麼多筆啊?雖然看不到, StackPanel 仍然堅持配置記憶體呢!

我們將 StackPanel 換成 Grid來試試看。

<Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="30"/>
      <RowDefinition Height="30"/>
      <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Label Content="資料筆數"  />
    <TextBox Name="txtRecNo" Grid.Row="1" TextChanged="txtRecNo_TextChanged" Text="100" />
    <DataGrid Name="dgResult" ItemsSource="{Binding}" Grid.Row="2"/>
  </Grid>

 

換成 Grid 後,即使是1000筆,記憶體只用了 71 MB,明顯地少掉許多,程式運作時也不會有問題。

image

為什麼 Grid 可以運作正常呢?原來 Grid 運作時是有計算Row 的高度的。DataGrid 在顯示時 Row 高度明顯有限制,因此要求 DataGrid 有高度的限制。DataGrid 的 Height 預設是 Auto,不足時 ScrollBar 就會出現。此時 UI Virtulization 的機制開始運作,只需要顯示看的到的區域即可 ,因此記憶體使用量變少。

如果我還是需要在 StackPanel 使用 DataGrid 呢?只需要設定 DataGrid 的 Height 或 MaxHeight 就可以了。

<StackPanel>
      <Label Content="資料筆數" />
      <TextBox Name="txtRecNo" TextChanged="txtRecNo_TextChanged" Text="100" />
      <DataGrid Name="dgResult" ItemsSource="{Binding}" MaxHeight="500"/>
    </StackPanel>

2011年5月16日 星期一

讓 VS2010 連接 Team Foundation Server 加快

根據小朱大的 [TFS] Visual Studio 2010 Team Foundation Server 首次部署心得,找到了我長久以來的困擾解決方式。

這是一定要記下來的啦!下文是 copy 小朱大的文章部份內容

另外一個問題是,在連接 Team Foundation Server 時真的是超級無敵的慢 …,且不論是哪個 Visual Studio 的版本都會這樣,後來在網路上找到一個解法,在 Visual Studio 的 devenv.exe 的設定檔 devenv.exe.config 中,找到 <system.net>,然後加入下列的設定:

<system.net>
<defaultProxy enabled="false" />
<settings>
<ipv6 enabled="true"/>
</settings>
</system.net>

再重新打開 Visual Studio IDE,此時連接 TFS 的速度就會加快很多。

2011年5月4日 星期三

TOAD 的中文字,顯示出來都是亂碼

問題

最近常與 Oracle 打交道。然而,使用查詢工具 TOAD 查詢出來的結果都是亂碼。

insert 一筆資料,含有看起來是中文字的資料。到另一台看,不得了,中文字又是亂碼。

怎麼辦?

解決

當然搜尋一下了。不一會兒就出現一堆了。

解決的過程,是使用  regedit 找到 HKLM 下的 SOFTWARE\ORACLE,修改 NLS_LANG 為 TRADITIONAL CHINESE_TAIWAN.ZHT16MSWIN950

重新啟動 TOAD 就完工了。

image

2011年5月2日 星期一

Visual Studio 2010 愈來愈慢

歷史

不知為何,Visual Studio 2010 愈來愈慢了。

現在的 Extension Manager 實在好用,我所使用的 Extensions 也應有20 多個之譜。在經過 Service Pack 1 的更新後,不知哪一天,Visual Studio 2010 慢到比 Virtul Machine 中的 還慢。

今天是勞動節補假一天(感謝公司啊!),特地到公司加班。一打開 Visual Studio 2010,就頭痛了。慢!

現象

到底怎麼慢呢?有三大現象。

  1. Intellisense 不會自動顯示。例如:File.  正常來說,當我按了句號 . 後,會出現  System.IO.File 的 static methods,但我的環境卻必須再按 ctrl + j
  2. 在 solution 中,在 project 上的快顯功能表 「Properties」或直接按 alt + Enter時,超慢。有些專案甚至沒有回應,直到受不了強迫以 Task Manager kill process
  3. WPF 中,在xaml desinger 中為 control 命名時,x:Name=”  一開始打字就像慢動作一樣,一個一個字母慢慢顯示出來。

還有其他的怪問題,無法一一記錄下來。

解決

本來想找一天重灌電腦的。一想到如此的大工程還是不要隨意開工才好。上網用 Bing 搜尋了一下。下了兩個指令。

  1. devenv /ResetSettings
  2. devenv /resetaddin *

第一個指令下完後,還是不成功。第二個指令下完後,VS2010 彷彿減肥成功般飛快了起來。

應該是 addin 灌太多了吧!

記錄一下這個事件作為記念。

ASP.NET 4 的改變

每一次 ASP.NET 改版,都難免對前一個版本作一些修改。這樣的改變就會造成版本的不相容,對於版本升級來說不得不小心了。以下列出找到的改變。這一篇主要參考自原文的 http://www.asp.net/learn/whitepapers/aspnet4/breaking-changes

ViewState

Page 或 Control 的 ViewStateMode 目前多了 Inherit 的值,並且為預設值。在 ASP.NET 4 後,可以針對某個 control 關或開其 ViewState而不受 Parent 的影響。

controlRenderingCompatibilityVersion

ASP.NET 4 中的 control 所 Render 的 html 會與 ASP.NET 3.5 的結果不同。這會造成版本的相容性。因此,如果你的程式是由 V2.0 或 V3.5 使用Visual Studio 升級到V4.0 時,會在 web.config 中自動使用 controlRenderingCompatibilityVersion 為3.5。如下

<pages controlRenderingCompatibilityVersion="3.5" />

例如 asp:Menu. 在 V3.5 時所產生的 html 是一堆的table, tr, td。而 V4.0 時是 ul, li.

controlRenderingCompatibilityVersion="3.5" 時,Menu 的字型跑掉之外, html 是一堆的 table, tr, td。這是不被建議的。

image

 

controlRenderingCompatibilityVersion="4.0",改用 ul, li 來呈現。html簡化許多。

image

ClientID 的模式


ASP.NET 4 的預設模式為 Predictable, ASP.NET 3.5 的預設模式相當於 AutoID
<pages ClientIDMode="AutoID" />

HTMLEncode, UrlEncde 編碼單引號的方法不同


HtmlEncode ' => &#39;
UrlEncode ' => %27

預設的Hash

預設的Hash算法,ASP.NET 2.0 為 SHA1, ASP.NET 4.0 為 HMACSHA256

預設不使用 System.Web.Mobile.dll(即在 root web.config 不再找到)

ASP.NET Request Validation 的變更:
ASP.NET 2.0 只針對網頁 aspx 驗證
ASP.NET 4.0 針對所有 ASP.NET 資源的任何 Request, ,
  (包含 axd)
  httpHandler, httpModule, WebService

要使用舊的驗證方式
<httpRuntime requestValidationMode="2.0" />

ASP.NET 4 無法作為 2.0, 3.5 應用的子應用運行
solution.
在 parent web.config
<location path="" inheritInChildApplications="false">
</location>

 

ASP.NET 4 網站無法作為 SharePoint 站點的子站點運行

HttpRequest.FilePath 屬性中不再有  PathInfo 的值

Share with Facebook