2012年1月31日 星期二

PropertyGrid的[Category][Description][DisplayName][Editor]注意事項

PropertyGrid是一個特別的大元件,[Category][Description][DisplayName]這些都是配合的特殊語法,他有一些限制條件,就是必須放在Member的上一行。搭配.NET3.5的封裝寫法,可以很簡單的撰寫,如下例
         
[Category( "Font" ), Description( "Font Size" )]
public int FontSize { get; set; }
         
但是,如果你需要在 set 或 get 函式做一些判斷,那就不能用.NET3.5的簡易寫法了,你必須定義一個 Private 的 Member,然後透過封裝的 get, set 將 Member給外面使用,但,這時候就要注意,[Category][Description][DisplayName]這一行,必須要寫在 private 跟 public 的中間囉,否則你會得到這樣的訊息:
         
屬性 'xxxxxxxx' 在此宣告型別上無效。它只在 'class, method, property, indexer, event' 宣告上有效
         
         
---錯誤寫法---
[Category( "Font" ), Description( "Size of the font" ), DisplayName( "Font Size]
[Editor( typeof( System.Windows.Forms.Design.FolderNameEditor ), typeof( System.Drawing.Design.UITypeEditor ) )]
private  int _FontSize;
public  int FontSize
{
    get
    {
        return  _FontSize ;
    }
    set
    {
       if ( 0 < 
_FontSize )

       {
            _FontSize  = value
       }
    }
}











---正確寫法---

private  int _FontSize;
[Category( "Font" ), Description( "Size of the font" ), DisplayName( "Font Size]
[Editortypeof( System.Windows.Forms.Design.FolderNameEditor ), typeof( System.Drawing.Design.UITypeEditor ) )]
public  int FontSize
{
    get
    {
        return  _FontSize ;
    }
    set
    {
       if ( 0 < 
_FontSize )

       {
            _FontSize  = value
       }
    }
}

2012年1月12日 星期四

C# Dictionary的用法,以及查字典的用途

什麼是Dictionary?他是個很簡單的東西,概念跟字典有點類似沒錯,如果我有10組鑰匙,跟10組物品,且滿足兩個條件,一、每個鑰匙不重複,二、每個鑰匙配一個物品。(物品可以長的一樣,比方第一個物品跟第六個物品都是一樣的杯子,只要能夠不會讓鑰匙沒有搭配的物品,或兩把鑰匙搭配到同一個物品就行了。)那接下來,你問Dictionary,某一把鑰匙,他就會告訴你對應的物品是什麼。我們看一下範例程式片段:
      
Dictionary<string, string> MyDic = new Dictionary<string, string>( );

// 建立字典
private void CreateDictionary( )
{
    MyDic.Add( "Name", "Jack" );
    MyDic.Add( "Blog", "Jack’s Blog" );
    MyDic.Add( "Group", "KTV Group" );
}

// 查字典
private String FindInDictionary( String FindMe )
{
    if ( true == ( MyDic.ContainsKey( FindMe ) ) )
    {
        return MyDic[ FindMe ];
    }
    else
    {
        return "Not Found";
    }
}

// 巡整個字典
private void ShowAllInDictionary( )
{
    foreach ( var OneItem in MyDic )
    {
        Console.WriteLine( "Key = " + OneItem.Key + ", Value = " + OneItem.Value );
    }
}

除了上面那種先宣告再Add的方式之外,也可以用底下的方式來直接宣告並產生內容
Dictionary<string, string> dctNewWay =
        new Dictionary<string, string>()
        {
            {"Key1", "AAAA"}, {"Key2", "BBBB"},
            {"Key3", "CCCC"}, {"Key4", "DDDD"}
        };


另外,值得一提的是,KeyValue並不限定只能用String,什麼型別都可以,所以你也可以把Class塞到Value,然後其中一個屬性拿出來當作Key,就可以方便的確認有沒有重複的Key,不用塞到SQL才知道。

2012年1月11日 星期三

WPF的BitmapImage佔用資源的處理方式

我們經常使用Image控制項來載入圖片,簡單的作法,直接在IDE開發環境中,在Image的屬性頁面設定Source屬性即可。但,我們有時候會需要將圖檔動態的讀入,怎麼寫?可想到程式碼如下:

  // 開檔

  String FilePath = @"C:\temp\001 (1).jpg";
  FileStream FStream = new FileStream( FilePath, FileMode.Open );


  // 將資料讀入 BitmapImage 變成圖檔資源
  BitmapImage BitMap = new BitmapImage( );
  BitMap.BeginInit( );
  BitMap.StreamSource = FStream ;
  BitMap.EndInit( );


  // 放到 Image 元件中,顯示圖片
  ImageControl.BeginInit( );
  ImageControl.Source = BitMap;
  ImageControl.EndInit( );
   // FStream.Close( );     不可Close,不然圖片不會出來





可是這樣會遇到一個問題,載入圖檔之後,原本的圖檔會無法刪除,或被別人使用,因為資源被Stream鎖住了,如果你在程式中把FStream給Close了,那麼,執行的時候就看不到圖了(什麼機制嘛!)所以要改成底下的作法,把檔案讀到記憶體裡面


  // 開檔
  String FilePath = @"C:\temp\001 (1).jpg";
  FileStream FStream = new FileStream( FilePath, FileMode.Open );


  // 轉成 Binary
  BinaryReader BinReader = new BinaryReader( FStream );
  byte[] BinaBytes = BinReader.ReadBytes( (int)FStream.Length );


  // 資料變成 MemoryStream (如此檔案就跟此程式無關連了,可關閉、刪除)
  MemoryStream MStream = new MemoryStream( BinaBytes );
  BinReader.Close( );
  FStream.Close( );


  // 將資料讀入 BitmapImage 變成圖檔資源
  BitmapImage BitMap = new BitmapImage( );
  BitMap.BeginInit( );
  BitMap.StreamSource = MStream ;
  BitMap.EndInit( );


  // 放到 Image 元件中,顯示圖片
  ImageControl.BeginInit( );
  ImageControl.Source = BitMap;
  ImageControl.EndInit( );
  //MStream.Close( );     不可Close,不然圖片不會出來








這樣,檔案可以更動了(因為FileStream關閉啦),可是,MemoryStream一樣無法關閉,不然圖片不會出來,這樣還是沒有解決問題,根本的作法,是預先載入,設定BitmapCacheOption,這樣,就可以回到第一個作法,簡單作出來。



  // 開檔
  String FilePath = @"C:\temp\001 (1).jpg";
  FileStream FStream = new FileStream( FilePath, FileMode.Open );


  // 將資料讀入 BitmapImage 變成圖檔資源
  BitmapImage BitMap = new BitmapImage( );
  BitMap.BeginInit( );
  BitMap.StreamSource = FStream ;
  BitMap .CacheOption =  BitmapCacheOption.OnLoad;
  BitMap.EndInit( );
  FStream.Close();  



  // 放到 Image 元件中,顯示圖片
  ImageControl.BeginInit( );
  ImageControl.Source = BitMap;
  ImageControl.EndInit( );


2012年1月10日 星期二

WPF的Main去哪裡了?我怎麼取得執行參數?

由於WPF把main()藏了起來,包裝起來,在編譯過程才會產生,所以原本透過main(string[] args)來取得參數的方法變得無法使用了(或說我不知道該怎麼用),怎麼辦呢?可以透過以下的變通方式來用,雖然麻煩點,但是知道方法之後,也沒有多難。




首先,要在App.xaml.cs動手腳,增加以下程式碼,就可以取得




        // override 的目的是為了取得傳入的參數
        protected override void OnStartup( StartupEventArgs e )
        {
            public static String Args0;


            if ( e.Args != null && e.Args.Count( ) > 0 )
            {
                Args0 = e.Args[ 0 ].ToString( );
            }
            base.OnStartup( e );
        }  




然後在啟動的視窗(可以在App.xmal當中看到呼叫了哪個視窗,他就會是啟動視窗,比方如果是 StartupUri="WinProgress.xaml" 那WinProgress 就是啟動視窗 
在該視窗程式碼(.cs)當中就可以用App.Args0取得傳入的參數了。




那參數如果有兩個呢?很簡單,就這樣子做:




        // override 的目的是為了取得傳入的參數
        protected override void OnStartup( StartupEventArgs e )
        {
            public static String Args0;
            public static String Args1;
            if ( e.Args != null && e.Args.Count( ) > 1 )
            {
                Args0 = e.Args[ 0 ].ToString( );
                Args1 = e.Args[ 1 ].ToString( );
            }
            base.OnStartup( e );
        }  




反而比較要注意的是,如果你透過程式要呼叫上面那個程式,想把參數傳進去,要怎麼透過Process()來傳多個參數呢?,要用個小技巧,Process.Start( "Filename", "Arg1 Arg2" );在Start()這個函式的第二個參數,傳入的自串中間用空格隔開,就會變成兩個參數了XD

C# 嵌入檔案,包在EXE裡

圖檔可以透過加入Resource的方式來使用,那其他檔案呢?有一種方便的嵌入方式可以把想要的檔案夾在你產生的EXE中,從專案中選擇「加入」、「現有項目」、然後從檔案對話框當中更改檔案類型為*.*,點選你想要的檔案,可加入專案中。

  




接下來,在方案總管中選擇該檔案,在屬性視窗將「建置動作」改為「內嵌資源」,這樣就可以讓程式碼引用,程式碼如下。










                    // 從專案組件讀入檔案到磁碟
                    Assembly Asmb = Assembly.GetExecutingAssembly( );
                    Stream ManifestStream = Asmb.GetManifestResourceStream( Asmb.GetName( ).Name + ".Update_Downloader.exe" );


                    // 讀入檔案
                    byte[] StreamData = new byte[ ManifestStream.Length ];
                    ManifestStream.Read( StreamData, 0, (int)ManifestStream.Length );


                    // 存到磁碟
                    FileStream FileStm = new FileStream( ExeFileName, FileMode.Create );
                    FileStm.Write( StreamData, 0, (int)ManifestStream.Length );
                    FileStm.Close( );

2012年1月9日 星期一

C#角度看XML格式讀取

XML以程式語言的角度來看的目的是什麼?就是為了知道怎麼讀寫,怎麼解析內容


讀寫的方式有很多種,XmlReader, XmlDocument......能控制的角度,能使用的函式也有不同
在此先介紹 XmlDocument


先將 XmlDocument 產生物件之後,可呼叫 Load() 來載入 XML 檔,如果用 LoadXml() 則可以由字串來讀入,也就是一個傳入路徑,一個傳入String,之後便透過節點的方式,指定要讀取哪一個階層的節點們,然後用迴圈逐一的將內容讀取出來,以下為簡單的範例程式
               
 XmlDocument XmlDoc = new XmlDocument( );
 XmlDoc.Load( @"C:\XmlFileName.XML" );
 XmlNodeList NodeLists = XmlDoc.SelectNodes( "Root/MyLevel1" );


 foreach ( XmlNode OneNode in NodeLists )
 {
     String StrAttrName = OneNode.Attributes.Name;
     String StrAttrValue = OneNode.Attributes[ "
 MyAttr1 " ].Value; 
     String StrAttrValue = OneNode.InnerText
;

  }

如此便可將 <MyLevel1> 這一層的節點都抓出來,下面這個XML範例來抓應該會抓到五個,然後讀取每一個的 MyAttr1 屬性的內容以及Node的InnerText。(備註:但是這樣的作法,是固定的沒有彈性的,必須每個<MyLevel1>節點都要具備MyAttr1,否則就會有Exception,而某個Node如果有多個Attribute則不會讀取到)。同樣的,如果用XmlNodeList NodeLists = XmlDoc.SelectNodes( "Root/MyLevel1/MyLevel2" );就可以讀取這層的四個節點了。 接下來,我們看看XML的格式



<?xml version="1.0" encoding="utf-8"?>
<Root>
   <MyLevel1 MyAttr1 = "aaaaa" > WXYZ 
       <MyLevel2 MyAttr10 = "UUUUUUUUU" />
       <MyLevel2 MyAttr10 = "KKKKKKKKK>QWER</MyLevel2>
   </MyLevel1>
   <MyLevel1 MyAttr1 = "aaaaa" > WXYZ 
       <MyLevel2 MyAttr10 = "VVVVVVVVV" />
       <MyLevel2 MyAttr10 = "PPPPPPPPP>QWER</MyLevel2>
   </MyLevel1>
   <MyLevel1 MyAttr1 = "bbbbb" MyAttr2 = "bbbbb"/>
   <MyLevel1 MyAttr1 = "UUUUUUUUU" />
</Root>





Root是根節點,一個檔案只能有一個,接下來,每個<>所括住的都是一個節點,並且分層次存在,層次的分法,是相同的標記如果用<>與</>來分兩行來包住其他的<>節點,那這些被包起來的,就會是下一層。而每個節點可以有Attributes,每個屬性可以存放一個數值(或字串),而如果節點的表示法用 用<>與</>來表示,且中間夾了數值(或字串),則這個稱為InnerText。(備註:另外還有InnerXml,這個是包含子節點都算進去,有點複雜,也可先不理會),所以用程式來讀取其Attributes的Value,或是讀取InnerText,在轉成需要的格式,就是XML的讀取應用方式了,








接下來,挑戰一下有難度的格式吧!!





<?xml version="1.0" encoding="utf-8"?>
<Root>
   <MyLevel1 MyAttr1 = "aaaaa" > WXYZ 
       <MyLevel2 MyAttr100 = "第二層01" />
       <MyLevel2>IN_002</MyLevel2>
   </MyLevel1>
   <MyLevel1 MyAttr1 = "bbbbb" > WXYZ 
       <MyLevel2 MyAttr100 = "第二層02" MyAttr101 = "第二層002" />
       <MyLevel2>IN_003</MyLevel2>
   </MyLevel1>
   <MyLevel1 MyAttr1 = "ccccc" > WXYZ 
       <MyLevel3 MyAttr100 = "第二層03" />
       <MyLevel3>IN_004</MyLevel3>
   </MyLevel1>
   <MyLevel1 MyAttr1 = "ddddd" > WXYZ 
       <MyLevel2 MyAttr100 = "第二層04" />
       <MyLevel2>IN_005</MyLevel2>
   </MyLevel1>
   <YouLevel1 MyAttr1 = "ddddd" > WXYZ 
       <MyLevel2 MyAttr100 = "第二層05" />
       <MyLevel2>IN_006</MyLevel2>
   </YouLevel1>
   <MyLevel1 MyAttr1 = "bbbbb" MyAttr2 = "bbbbb"/>
   <MyLevel1 MyAttr10 = "UUUUUUUUU" />
   <MyLevel1>IN_007</MyLevel1>
</Root>











不管他格式多複雜,用以下的方式,就可以讀取節點的每個Attribute,以及InnerText了,且,不會去嘗試讀取不存在的Attribute,所以不會發生Exception。




foreach ( XmlNode OneNode in NodeLists )
{
String StrNodeName = OneNode.Name.ToString();
foreach ( XmlAttribute Attr in OneNode.Attributes )
{
String StrAttr = Attr.Name.ToString( );
String StrValue = OneNode.Attributes[ Attr.Name.ToString( ) ].Value;
String StrInnerText = OneNode.InnerText;
}
}






再來,就是怎麼選擇要讀取哪一層,如果用以下的方式會讀取在Root底下的MyLevel1底下的MyLevel2,對,MyLevel3就不會被讀取了。還有YouLevel1底下的MyLevel2也不會被讀取,
所以會讀取到6個節點。
XmlNodeList NodeLists = XmlDoc.SelectNodes( "Root/MyLevel1/MyLevel2" );





如果用以下的方式會讀取在Root底下的MyLevel1 ,則會讀取到7個節點
XmlNodeList NodeLists = XmlDoc.SelectNodes( "Root/MyLevel1" );


2012年1月6日 星期五

C# 透過HTTP抓檔案回來

在Window視窗上放一個Button,一個ProgressBar,一個TextBlock(用來顯示ProgressBar的數值)
利用以下的函式,可以完成


        private void Btn_http_Click( object sender, RoutedEventArgs e )
        {
            // 非同步
            WebClient MyWebClient = new WebClient( );
            MyWebClient.DownloadFileCompleted += new AsyncCompletedEventHandler( Http_Completed );
            MyWebClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler( Http_ProgressChanged );
            MyWebClient.DownloadFileAsync( new Uri( "http://www.joyplux.net/xplayer/download/JoyeDM/JoyeDMDownload_Setup.exe" ), @"c:\temp\JoyeDM.exe" );
        }


        private void Http_ProgressChanged( object sender, DownloadProgressChangedEventArgs e )
        {
            PgBar_Http.Value = e.ProgressPercentage; //控制ProcessBar顯示進度
            TxtBk_Percent.Text = e.ProgressPercentage + "%";
        }


        private void Http_Completed( object sender, AsyncCompletedEventArgs e )
        {
            MessageBox.Show( "Download completed!" );
        }

C# 取得檔案版本資訊

FileVersionInfo.GetVersionInfo(@"C:\WINDOWS\NOTEPAD.EXE").FileVersion.ToString();