WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

MonthCalendar の Size

by WebSurfer 2017年11月4日 14:32

MonthCalendar

Windows Forms アプリケーション用に、カレンダーを表示してユーザーが日付を選択できる MonthCalendar コントロールがあります。

その Size プロパティから MonthCalendar のサイズを取得する際、タイミングによっては正しいサイズが取得できない、その場合でもデバッガで MonthCalendar を開いてその中身を見ると正しいサイズになるという不可解なことがありました。

具体的には、下のコードの Button_Click メソッドをデバッガでステップ実行させ、コメント (3) の行で止めて Size プロパティを見ると 178 x 155 となっているが、calendar の中身をデバッガで開いて見た後で Size プロパティを見ると 199 x 162 と正しい値になるというものです。

その理由を調べたので備忘録として書いておきます。なお、元の話は Teratail のスレッド「VisualStudioでデバッグ中にプロパティの値が変化する」です。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApplication2
{
  public partial class Form1 : Form
  {
    public Form1()
    {
      InitializeComponent();

      Button button = new Button();
      button.Click += Button_Click;
      this.Controls.Add(button);
    }

    private void Button_Click(object sender, EventArgs e)
    {
      Form f = new Form();
      Size size1 = f.Size;    // (1) W: 300, H: 300
      f.Size = new Size(200, 200);
      size1 = f.Size;         // (2) W: 200, H: 200

      MonthCalendar calendar = new MonthCalendar();
      Size size2 = calendar.Size;   // (3) W: 178, H: 155
      calendar.Size = new Size(200, 200);
      size2 = calendar.Size;        // (4) W: 178, H: 155
      f.Controls.Add(calendar);
      size2 = calendar.Size;        // (5) W: 178, H: 155
      f.Show();
      size2 = calendar.Size;        // (6) W: 199, H: 162

      f.ClientSize = calendar.Size;
    }
  }
}

上に書いた「不可解なこと」の理由は、多少想像が入っていますが、以下のようなことだと思われます。

  1. MonthCalendar のサイズを決定するのは使用されるフォントのみ。MonthCalendar.Size プロパティの設定では変えられない・・・コメント (4) 参照。
  2. new MonthCalendar() の時点ではフォントが不明なので MonthCalendar のサイズは未定。
  3. コメント (3) の時点で Size プロパティを見ると 178 x 155 となっているが、それはデフォルト値でフォントを反映した正しいサイズではない。
  4. コメント (3) の時点で calendar.Size の calendar にマウスカーソルを当てて開くと、その時点で calendar が初期化され、使用されるフォントに応じて正しいサイズが Size プロパティに設定される。
  5. その後で calendar.Size の Size にマウスカーソルを当てると正しいサイズ 199 x 162 が取得できる。
  6. デバッガで calendar を開いて見るということをしなければ、使用されるフォントに応じて正しいサイズが Size プロパティに設定されるのは、上記のコードでは f.Show(); の時点。
  7. その後であれば、calendar.Size で正しいサイズを取得でき、それを Form の ClientSize に設定してやれば上の画像の通り calendar がフォーム内にぴったり収まる。

上のことを書いた Microsoft の公式文書などは見つからないのですが、コードで検証した結果が上記の想像は正しいことを裏付けていると思います。

Tags: ,

.NET Framework

HttpWebRequest で WCF サービスを呼出

by WebSurfer 2017年3月26日 14:36

先の記事 WCF と jQuery AJAX では、JSON 文字列をデータとしてやり取りする WCF サービスのメソッドを、jQuery.ajax を使って呼び出してデータを取得する方法を書きました。

この WCF サービスのメソッドを HttpWebRequest / HttpWebResponse を利用して呼び出して JSON 文字列のデータを取得し、それを逆シリアル化して C# のオブジェクトに変換する方法を書きます。

ここでは例として先の記事の WCF サービスの GetCarsByDoors(int doors) メソッドを POST 要求してみます。

まず、JSON 文字列が逆シリアル化された際の C# のクラス / プロパティを書き、それらに DataContract / DataMember 属性を付与してデータコントラクトを定義します。

JSON 文字列から C# のクラス / プロパティの変換は json2csharp のような変換サービスを利用すると簡単にできると思います。

先の記事のコードでは、WCF サービスの GetCarsByDoors(int doors) メソッドの応答の JSON 文字列は "GetCarsByDoorsResult" でラップされるように設定されていますので、それを考慮して以下のようなデータコントラクト定義になります。

[DataContract]
public class RootObject
{
    // GetCarsByDoorsResult は WCF サービスメソッドに付与した
    // BodyStyle = WebMessageBodyStyle.WrappedRequest による
    // ラップの名前(ラップするのはセキュリティ対策)
    [DataMember]
    public List<Car> GetCarsByDoorsResult { get; set; }
}

[DataContract]
public class Car
{
    [DataMember]
    public string Make { get; set; }

    [DataMember]
    public string Model { get; set; }

    [DataMember]
    public int Year { get; set; }

    [DataMember]
    public int Doors { get; set; }

    [DataMember]
    public string Colour { get; set; }

    [DataMember]
    public float Price { get; set; }
}

HttpWebRequest を利用して WCF サービスの GetCarsByDoors(int doors) メソッドを要求し JSON 文字列をデータとして POST 送信します。ここでは例として "{\"doors\":5}" という 5 ドア車を要求する JSON 文字列を設定しています。

HttpWebResponse を利用して応答を取得し、DataContractJsonSerializer クラスを利用して応答ストリームに含まれる JSON 文字列を C# のオブジェクトにシリアル化します。

詳しくは以下のサンプルコードとそれに付与したコメントを見てください。

using System;
using System.Net;
using System.Text;
using System.IO;
using System.Runtime.Serialization;
using System.Collections.Generic;
using System.Runtime.Serialization.Json;

namespace ConsoleApplication7
{
  class Program
  {
    static void Main(string[] args)
    {
      // 指定した Uri を要求する HttpWebRequest を初期化
      HttpWebRequest endpointRequest =
          (HttpWebRequest)HttpWebRequest.
          Create("http://.../carservice.svc/GetCarsByDoors");

      endpointRequest.Method = "POST";
      endpointRequest.ContentType = 
          "application/json; charset=utf-8";

      // POST データの設定。とりあえず 5 ドアを要求してみる
      string postData = "{\"doors\":5}";

      Encoding encoding = Encoding.GetEncoding("utf-8");
      byte[] byte1 = encoding.GetBytes(postData);
      endpointRequest.ContentLength = byte1.Length;

      // POST データを書き込むストリームを取得
      using (Stream requestStream = 
             endpointRequest.GetRequestStream())
      {
        // POST データを要求ストリームに書き込み
        requestStream.Write(byte1, 0, byte1.Length);

        // WCF サービスメソッドからの応答を取得
        using (HttpWebResponse endpointResponse =
               (HttpWebResponse)endpointRequest.GetResponse())
        {
          // 応答のコンテンツを読むストリームを取得
          using (Stream responseStream = 
                 endpointResponse.GetResponseStream())
          {
            // JSON シリアライザの初期化
            DataContractJsonSerializer ser = 
              new DataContractJsonSerializer(typeof(RootObject));

            // 応答のコンテンツを逆シリアル化して C# の
            // オブジェクトを取得
            RootObject rootObject = 
              (RootObject)ser.ReadObject(responseStream);
                        
            foreach (Car car in rootObject.GetCarsByDoorsResult)
            {
              Console.WriteLine("Make:{0}, Model:{1}, Doors:{2}",
                                car.Make, car.Model, car.Doors);

            /*
            結果は:
            Make:Audi, Model:A4, Doors:5
            Make:Ford, Model:Focus, Doors:5
            Make:Renault, Model:Laguna, Doors:5
            Make:Toyota, Model:Previa, Doors:5
            */

            }
          }
        }
      }
    }
  }
}

DataContractJsonSerializer クラスを利用したシリアル化 / 逆シリアル化については、MSDN ライブらきの記事「方法 : JSON データをシリアル化および逆シリアル化する」が参考になると思います。

Tags: ,

.NET Framework

Chart Samples

by WebSurfer 2016年2月14日 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

About this blog

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

Calendar

<<  2017年11月  >>
2930311234
567891011
12131415161718
19202122232425
262728293012
3456789

View posts in large calendar