WebSurfer's Home

トップ > Blog 1   |   Login
Filter by APML

Chart Samples

by WebSurfer 14. February 2016 16:15

先の記事 Chart で ASP.NET Web Forms アプリでグラフを表示するのに便利な Chart コントロールを紹介しましたが、MSDN のサイトからサンプルを入手できるので、入手方法その他備忘録として残しておいたほうがよさそうなことを書いておきます。

Chart Samples

まず入手先ですが、以下のページから Windows Forms アプリ用と ASP.NET Web Forms アプリ用のサンプルをダウンロードできます。

Samples Environments for Microsoft Chart Controls

Windows Forms 用のサンプルは上のページで[C# (5.6MB)]をクリック、ASP.NET Web Forms アプリ用なら[HTML (4.4MB)]の方をクリックしてください。両方入手したいなら[Full (10.0MB)]をクリックしてください。

「Samples Environments for Microsoft Chart Controls.zip」という名前の zip ファイルの中に、Windows Forms 用なら「Windows Forms Samples Environment for Microsoft Chart Controls\C#」というフォルダがあって、それに完全な Windows Forms アプリのソリューションとしてサンプルが含まれています。

ASP.NET Web Forms アプリ用なら「ASP.NET Samples Environment for Microsoft Chart Controls\HTML」というフォルダがあって、それに完全な ASP.NET Web Forms アプリの Web サイトプロジェクトとしてサンプルが含まれています。

それを適当なフォルダに解凍して Visual Studio(.NET4 なので 2010 以降のバージョン)で開いて実行すれば、基本の解説、いろいろなタイプのサンプルのデモ、それを作るための C# および VB.NET サンプルコード等が満載のアプリが動くはずです。

上の画像は Windows Forms アプリ用のサンプルを実行したときのものです。ASP.NET Web Forms アプリ用は同様なサンプルがブラウザ上に表示されます。

解説が英語であるのを厭わなければこのサンプルは非常に有益なものになると思います。というか、このサンプル無しでは Chart を使っての開発は難しいと言っても過言でなないと思います。

一つだけ注意事項があります。Windows Forms アプリのサンプルは、ソリューションのフォルダ名が zip を解凍したときのままですと以下の画像のように C# と言う名前になっており、これをそのままソリューションのフォルダとして使う場合は、フォルダ名 C# を CSharp などに書き換えないとうまく動かないということです。(最後が # で終わるのは不可)

フォルダ構成

ちょっと余計な話ですが、何故 '#' があるとダメなのかも調べたので、以下にその理由も書いておきます。

プログラムの中で Application.ExecutablePath プロパティを利用してパス名を得ていますが、.exe ファイルの出力パスが .\ と設定されているので、フォルダ名が C# のままですと得られるパス名の文字列は "C:\ ... \\Windows Forms Samples Environment for Microsoft Chart Controls\\C#/WinFormsChartSamples.exe" となってしまいます。(C# の直後が '\\' ではなくて '/' であることに注意)

その後、上記文字列からファイル名を除去するのに applicationPath.LastIndexOf('\\'); でインデックスを取得しているのですが、'\\' を期待しているところが '/' になっているのでうまく行きません。

C# を CSharp などに書き換えると "C:\ ... \\Windows Forms Samples Environment for Microsoft Chart Controls\\CSharp\\WinFormsChartSamples.exe" となって期待通りに行きます。

何故 C# の直後が '\\' ではなくて '/' なってしまうかと言うと Application.ExecutablePath のコードで '#' が fragment と見なされるからだそうです。

その詳細は stackoverflow の記事 Odd C# path issue にありますので興味があれば見てください。(手抜きですみません)

Tags:

.NET Framework

C# と VB.NET のフィールド初期化子の違い

by WebSurfer 6. February 2016 15:25

C# でフィールドをインスタンスメソッド等で初期化しようとすると「フィールド初期化子は、静的でないフィールド、メソッド、またはプロパティ 'xxx' を参照できません」(xxx はそのメソッド名)というコンパイルエラーになります。

ところが VB.NET ではコンパイラは問題なく通ってしまいます。実行上も、自分が調べた限りですが、期待通り動きました。(ただし、詳しく調べたわけではないので、どんなケースでも問題ないかは分かりませんが)

理由は、想像ですが、C# と VB.NET のコンパイラの違いによるものだと思われます。

(1) Why Do Initializers Run In The Opposite Order As Constructors? Part One、(2) Can VB.NET be forced to initialize instance variables BEFORE invoking the base type constructor?、(3) Field initializer differences between C# and VB.NET 等の記事を読んでみると:

  • C# の場合: まず最初に全ての initializers が derived ⇒ base の順で実行され、次に全ての constructor bodies が base ⇒ derived の順で実行される。
  • VB.NET の場合: constructor bodies が base ⇒ derived の順で実行される。その際、各 initializer は当該 constructor body が実行される直前に実行される。

という違いがあるようです。

知ってました? 実は自分はつい最近まで知らなかったです。(汗) 知っておくべきところは以上なんですが、それだけではブログの記事として面白くないので、無知な自分がハマった話を書いておきます。(笑)

その話は、以下のような VB.NET のコードを C# に書き換えるということから始まりました。

Public Class Sample
    Public Property Code As Integer()

    Public ReadOnly Property Codes As IList(Of Integer)
        Get
            Return Me._Codes.Value
        End Get
    End Property
    
    Private ReadOnly _Codes As New Lazy(Of IList(Of Integer))(
        Function()
            Return If(Me.Code, {}).ToList()
        End Function)
End Class

VB.NET をよく知らない自分は Telerik Code Converter などの変換サービスを利用しています。

自動変換されたコードをそのまま信じて使うのは問題ありと認識していますが、かなりのところまで変換してくれるので多少の手直しで済み、重宝しています。

今回の場合では、VB.NET のコードの If(Me.Code, {}) のところで {} は変換してくれなかったので、そこのみ new int[0] に直して Visual Studio のエディタに貼り付けました。(VB.NET のコードで {} は New Integer() {} と見なされるらしいです。実は、それも知らなかったのですが、さすがに C# で {} ではダメなのは気がつきましたので直しました)

そうすると、以下の画像の赤丸で示した部分でエラーが出ます。

Visual Studio に表示されたエラー

エラーメッセージは最初の方は:「ラムダ式 はデリゲート型ではないため、型 'System.Threading.LazyThreadSafetyMode' に変換できません」(コンパイルした時のエラーメッセージは「ラムダ式 はデリゲート型ではないため、型 'bool' に変換できません。」になります)

2 つ目は:「キーワード 'this' は��在のコンテキストでは使用できません」

となりました。それらのエラーメッセージから原因は分かりますでしょうか? 無知な自分は最初原因が分からず、半日ぐらいハマってしまいました。(汗)

結局原因は、Code がインスタンスプロパティなので(静的ではないので)それを使ってフィールドを初期化することはできないということだったんですが、それなら「フィールド初期化子は、静的でないフィールド、メソッド、またはプロパティ 'Code' を参照できません」というエラーメッセージを出して欲しかったです。(泣き言)

解決策は、変数 _Codes をコンストラクタで初期化するのがよさそうです。(Code プロパティに static キーワードを付与して静的プロパティにするのは止めた方がよさそうです)

Tags:

.NET Framework

全角数字を半角に変換

by WebSurfer 15. January 2016 17:58

全角 / 半角の変換には Microsoft.VisualBasic 名前空間の Strings.StrConv メソッドがよく使われているようですが、それは使わないで、全角数字を半角に変換する方法を書きます。

基本的には Regex.Replace メソッド (String, MatchEvaluator) を使って文字列の中の全角数字の部分を半角数字に置き換えるのですが、問題は引数の MatchEvaluator に設定するカスタムメソッドをどのように作るかです。

@IT の記事「全角英数字のみを半角に変換するには?」にその例がありますが、そこでは Strings.StrConv メソッドが使われています。

しかしながら、Strings.StrConv メソッドはローケルの違いとか XP 互換モードで期待した結果にならないという問題があるそうです。

数字だけなら辞書を作るのは簡単ですので、Strings.StrConv メソッドを使う代わりに、IEnumerable.Select 拡張メソッドと辞書を使ってカスタムメソッドを実装してみました。

以下にコード例を書きます。その中の Replacer というのが MatchEvaluator に設定するカスタムメソッドです。

Replacer のコードの中で、m.Value はマッチした全角数字の文字列(String 型。IEnumerable<char> を継承している)になります。Select 拡張メソッドは辞書を使って全角数字の文字列の各文字 (Char 型)を半角に置き換え、それを IEnumerable<char> 型のオブジェクトとして返します。

半角数字に置き換えられた IEnumerable<char> 型のオブジェクトを ToArray メソッドで Char[] に変換し、String(Char[]) コンストラクタで半角数字の文字列(String 型)を生成して Replacer メソッドの戻り値として返しています。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace ConsoleApplication
{
  class Program
  {
    // 辞書を作る
    public static Dictionary<char, char> dictionary =
      new Dictionary<char, char>() {
        {'0','0'},{'1','1'},{'2','2'},{'3','3'},
        {'4','4'},{'5','5'},{'6','6'},{'7','7'},
        {'8','8'},{'9','9'}
      };
        
    public static string Convert(string source)
    {
      Regex regex = new Regex("[0-9]+");
      return regex.Replace(source, Replacer);
    }

    public static String Replacer(Match m)
    {
      return new String(
        m.Value.Select(n => dictionary[n]).ToArray());
    }

    static void Main(string[] args)
    {
      String source = "0123456789Ab1C02dEfあい36";
            
      Console.WriteLine(source);
      Console.WriteLine(Convert(source));

      // Regex を使わなくても以下のようにして可能
      String replaced = new String(
        source.Select(
          n => (dictionary.ContainsKey(n) ? dictionary[n] : n)
          ).ToArray()
        );
      Console.WriteLine(replaced);
            
      /* 結果は:
      0123456789Ab1C02dEfあい36
      0123456789Ab1C02dEfあい36
      0123456789Ab1C02dEfあい36
      */
    }
  }
}

Tags: ,

.NET Framework

About this blog

2010年5月にこのブログを立ち上げました。その後 ブログ2 を追加し、ここは ASP.NET 関係のトピックス、ブログ2はそれ以外のトピックスに分けました。

Calendar

<<  December 2019  >>
MoTuWeThFrSaSu
2526272829301
2345678
9101112131415
16171819202122
23242526272829
303112345

View posts in large calendar