goo blog サービス終了のお知らせ 

中年サラリーマンのC#プログラミング勉強日記

プログラミング超初心者(おっさん)のC#勉強日記です。同じ境遇でがんばっている人がいたらコメントもらえるとうれしいです。

未来の自分にメールする

2015-11-08 21:55:18 | 日記
1年近く間を空けてしまった。。。
その間にVisual Studioは新しいバージョンが出たり、ずいぶん環境も変わったので、このブログでキャッチアップしていけたらと思う。
(ちゃんと更新しないと)

今回は未来の自分に向かってリマインドメールを送るコード。
本当はバックグラウンドで実行、とか考えないといけないのだろうけど、とりあえず今は無理しない。。。

■参考にしたのは下記(相変わらず参考にさせてもらっている。こういう人を目指したい。。。)
DOBON.NET SmtpClientクラスを使ってメールを送信する
http://dobon.net/vb/dotnet/internet/smtpclient.html

プログラムは大きく下の3パートに分けて考える。
(A)送信時間の設定(GUI)から
(B) メールの送信メソッド
(C) 時間が来たら処理(B)を実行するイベントハンドラ

Bについては、C#だし(簡単そうだから)Microsoftのhotmailを使ってやってみる。
CはツールボックスのTimerを使って、画面で入力した時間がきたら処理(メール送信)を行う。
という風につくってみる。

まず(A)の画面を作成する。

各コンポーネントの設定は下記のとおり
①(Name): dateTimePicker1
 加えてCustomFormatをyyyy/MM/ddに設定する。
②(Name):numericUpDownHour
 Maximumを11に設定
③(Name):numericUpDownMin
 Maximumを59に設定
④(Name):textBoxMessage
MultilineをTrueに設定
⑤(Name):label1
⑥(Name):button1
Text:送信予約
さらにTimerをForm1にドラッグする。→timer1(Intervalは100(ms))

つぎに、今回のプログラムでつかう変数を宣言(それぞれの変数の使用方法は後述)
DateTime targetDate;
int targetHour;
int targetMinute;
DateTime datetime_set;
bool isSend = false;


つづいて、メールを送る時刻を画面から取得する処理。
操作としては、画面上で時刻を選択(dateTimePicker1、numericUpDownHour、numericUpDownMin)しておいて、ボタン(button1)をクリックすると、送信時刻が設定され、タイマー(Timer1)が起動される感じにする。
(本当はdateTimePickerだけで時刻も指定したかったんだけど、どうしても上手くできずこの形になってしまった。)

private void button1_Click(object sender, EventArgs e)
{
targetDate = dateTimePicker1.Value;
targetHour = (int)numericUpDownHour.Value;
targetMinute = (int)numericUpDownMin.Value;

datetime_set = new DateTime(targetDate.Year, targetDate.Month, targetDate.Day, targetHour, targetMinute, 0;

//一応過去時間になってないか入力チェック
if(DateTime.Now.CompareTo(datetime_set)>0)
{
MessageBox.Show("未来の時間を入力してください");
}
else
{
MessageBox.Show(datetime_set.ToString() + "にメールを送信します");
isSend = true;
}

//ここでタイマーを開始して、以降の時刻をチェックする
timer1.Start();
}



つぎに(B)、メール送信のshotMailメソッドをつくる。

xxxx@hotmail.comがyyyy@gmail.comにメールを送るコード
送信に使うメール(xxxx@hotmail.com)のパスワードは『password』ってことにする。。
送るメッセージの題名は「自分からのメッセージ」内容はtextBoxMessageから取得
あとは、おまじないかな。hotmailのSMTPサーバへメールを送るためのクライアントをインスタンス化、ホスト名、ポート番号指定とか。。。

private void shotMail()
{
//MailMessageの作成
System.Net.Mail.MailMessage msg = new System.Net.Mail.MailMessage(
"xxxx@hotmail.com", "yyyy@gmail.com",
"自分からのメッセージ", textBoxMessage.Text);

System.Net.Mail.SmtpClient sc = new System.Net.Mail.SmtpClient();
//SMTPサーバーなどを設定する
sc.Host = "smtp.live.com";
sc.Port = 587;
sc.DeliveryMethod = System.Net.Mail.SmtpDeliveryMethod.Network;
//ユーザー名とパスワードを設定する
sc.Credentials = new System.Net.NetworkCredential("xxxx@hotmail.com", "password");

//SSLを使用する
sc.EnableSsl = true;

//メッセージを送信する
sc.Send(msg);

//後始末
msg.Dispose();
sc.Dispose();
}



つぎに(c)

タイマーは、画面を作ったときにIntervalを100(ms)の指定したので、この間隔で現在時刻を確認して、画面から指定した時間と一致したら shotMail()メソッドを実行してメールを送信するコードを作る。

以下。
private void timer1_Tick(object sender, EventArgs e)
{
DateTime datetime_now = DateTime.Now;
label1.Text = datetime_now.ToLongTimeString();
if (datetime_now.ToLongTimeString() == datetime_set.ToLongTimeString() && isSend==true)
{
shotMail();
label1.Text = "メールを送信しました";
isSend = false;
timer1.Stop();
}
}



これで完成。

1分後を指定して実行してみる。



1分後、label1が「メールを送信しました」に変わった。






メールをチェック!
届いてる!!!







1年間時間を空けて(空いてしまって)それなりにネタもたまったので、今度こそ2週に1回くらいは更新していこうと思います。。。




地図アプリを作ってみる。

2014-11-24 21:44:40 | 日記
 今日は、地図のアプリを作ってみる。本当はGoogleMapみたいなWebの地図を使ってみたかったのだけど、やる方法を見つけられなかった。仕方ないので下記サイトを参考にSharpMapというライブラリと国土地理院が出している地図データを使ってやってみる。アプリといってもただ地図を表示するだけだけど。
 基本的に参考にしたサイトに沿ってやっているだけだけど、古い記事だからなのか、ところどころ上手くいかない。そこの補正しながら進めてみた。

■参考情報
C#とSharpMapで地図を描画するアプリケーションを作る -シンプル編
http://news.mynavi.jp/articles/2008/06/23/sharpmap/
CodePlex SharpMap
http://sharpmap.codeplex.com/wikipage?title=Features&referringTitle=Home
地球地図日本のデータ(国土地理院)
http://www.gsi.go.jp/kankyochiri/gm_jpn.html#gm_jpn_dl

 まず、SharpMapのページの右側にある「download」をクリックしSharpMap.zipファイルをダウンロード。このダウンロードしたファイルを適当な場所に解凍する。VisualStudioから参照の追加で、GeoAPI.dllとSharpMap.dllを追加する(下図)。





 さらに、国土地理院のページから、全レイヤのShapeファイル(gm-jpn-all_u_2.zip)をダウンロードする。こちらもどこかに解凍し(意味は無いけど参考にしたサイトに合わせて)ShapeFilesというフォルダを作って、全ファイルをそこに移す。このフォルダをVisualStudioのソリューションエクスプローラ上のプロジェクト名のところにドラッグ&ドロップする。下図のようになる。





これで下準備は完了。

 アプリは、Form1ロード時に地図を読込んで表示するだけの簡単なものにする。大きな流れは、SharpMapで提供されているMAPインスタンス上に地図データをつくる→PictureBoxに貼り付け。MAPデータの作り方は、地図情報(Shapeデータ)をレイヤ状に重ねて、地図を描いていくもののよう。例えば、行政区レイヤを作成→その上に道路レイヤを重ねる→さらに河川レイヤ。。。というふうに地図を作るらしい。今回は最初行政区だけ作って、地図の外枠だけをつくる。参考にしたサイトは河川とかも重ねている。

では実装開始。まずForm1にPictureBoxを貼り付ける。


usingに下記を追加。
using SharpMap;
using SharpMap.Layers;
using SharpMap.Data.Providers;
using GeoAPI.Geometries;

Form1をダブルクリックしForm1_Loadのイベントハンドラを生成。
先ほどの記述の流れで、コーディング。
private void Form1_Load(object sender, EventArgs e)
{
//地図データをインスタンス化(サイズ、背景色を設定)
_Map = new Map(new Size(picJapan.Width, picJapan.Height));
_Map.BackColor = Color.LightBlue;
//行政区を読込んで行政区のレイヤを作る
VectorLayer baseLayer = new VectorLayer("行政区域");
baseLayer.DataSource = new ShapeFile(@"..\..\ShapeFiles\polbnda_jpn.shp");
//色を指定
baseLayer.Style.Fill = Brushes.GreenYellow;
baseLayer.Style.Outline = Pens.Green;
baseLayer.Style.EnableOutline = true;
//地図データに行政区レイヤを追加
_Map.Layers.Add(baseLayer);
//サイズを合わせてピクチャボックスに描画
_Map.ZoomToExtents();
picJapan.Image = _Map.GetMap();
}


実行してみる。


意外と簡単に地図が表示された。

今日はこれで満足。だけど、この後この記事の高度編の地図上に線を書くあたりからまったく上手くいかない。SharpMapのサイトのサンプルもまともに動かない。。。なんでだろう。まだまだ勉強が足りないなぁ。

ブログ投稿のために、ソースコードをHTMLに変換する

2014-10-26 21:01:49 | 日記
また間が空いてしまった。
単なるサボりといえばサボりだけど、正直作りたいもののネタを考えられない。。。まだまだ勉強することはいっぱいなのに。。。
周りの人に聞いてみたけど「プログラミングを勉強しても、ネタが無くて続かない。」みたいな人は多いみたい。
そこでどうやったらプログラミングのネタを探せるのかも、真剣に勉強しつつあります。
いずれはブログにも書いていけたらと思います。
ちなみに、その中で調べた「不便と思うことをプログラミングしてみる」というアドバイスを元に、今回は、このブログ向けの『ソースコードのHTML化ツール』を作ってみる。
参考にしたのは下記。(アイデアを考えるために参考にしている記事はまた他の回で紹介します。)

■参考資料
DOBON.NET > プログラミング道 > .NET Tips > 文字列、暗号化 文字列を置換する
http://dobon.net/vb/dotnet/string/replace.html

DOBON.NET > プログラミング道 > .NET Tips > 文字列、暗号化 文字列から一行ずつ読み込む
http://dobon.net/vb/dotnet/string/readline.html

僕がブログで自分の作ったソースコードを貼り付ける場合、下のような手順でやっている。

1) ソースの最初と最後に下記を追加
→ソースの内部が緑色に塗りつぶされる。
最初:<div style="background:#bfffdf; padding:10px; border:2px dotted #00bf00;">
最後</div>

2) tabの位置に合うように、下記を追記。
<div style="padding-left:30px">
戻すときは</div>を追加。

3) 不等号を『&lt;』『&gt;』に変更。

4) 『&lt;』『&gt;』の後は改行が入ってしまうので、改行を削除。

そのとおりに実装していく。
まず、画面を下図のように作成(①、②、③を配置)。textboxはマルチラインに。


文字列の変換にはstringクラスのReplace("変換前", "変換後")メソッドを使用。

簡単な内容なので、一気にコードを記載。

基本的な流れは
a) <div style="background:#bfffdf; padding:10px; border:2px dotted #00bf00;">を先頭に追加
b) SourceTextBoxからテキストを取出し、一行ずつ読み込んでいく。
c) 読み込んだ行のタブの数を数える
d) 読み込んだ行の不等号をエスケープ文字に変換、タブも削除する。
e) c)読み込んだ行のタブの数から下記を実行
   タブが増えていたら、タブ(<div style="padding-left:30px">)を追記
   タブが減っていたら、戻す(</div>を追記)
   前の行と同じなら、改行(Environment.NewLineを追記)
f) 整形した文字列(1行分)をstringbufferに追記
最後の行になるまでb)~f)でループ
g)最後に </div>を追記

ボタンをクリックしたときのイベントに上記のロジックを追記。
private void testButton_Click(object sender, EventArgs e)
{
StringBuilder sb = new StringBuilder();
string TmpString;
string SourceString;
int tabCnt = 0;
int curTab =0;
SourceString = SourceTextBox.Text;
System.IO.StringReader rs = new System.IO.StringReader(SourceString);sb.Clear();
sb.Append("<div style=\"background:#bfffdf; padding:10px; border:2px dotted #00bf00;\">");//(a)
while (rs.Peek() > -1) //(b)
{
TmpString = rs.ReadLine();
curTab =0;
foreach (char c in TmpString)
{
if (c == '\t')
{
curTab++; //(c)
}
}
TmpString = TmpString.Replace("\t", ""); //(d)
TmpString = TmpString.Replace(">", "&gt;");
TmpString = TmpString.Replace("<", "&lt;");
if (tabCnt < curTab) //(e)
{
sb.Append("<div style=\"padding-left:30px\">");
tabCnt++;
}
else if (tabCnt > curTab)
{
sb.Append("</div>");
tabCnt--;
}
else
{
sb.Append(Environment.NewLine);
}
sb.Append(TmpString); //(f)
}
sb.Append("</div>"); //(g)
ResultTextBox.Text =sb.ToString();
}


ソースによってはより特殊文字の変換が必要だけど、基本的なところはこれで抑えられているはず。

Webから情報(日経平均株価)を取ってきて表示する。

2014-08-24 20:46:31 | 日記
今日はWebから今日の日経平均を取ってきて表示するものを作ってみる。

一応、僕の仕事は金融ITなのだけど、株価にあまり興味がないので、上の人が株の話を振ってくるといつも困る。これで少しは株に親しみがわくかなぁ。。。

参考にしたのは下の二つのリンク。

DOBON.NET 正規表現を使って文字列を検索し、抽出する
http://dobon.net/vb/dotnet/string/regexmatch.html

@IT:.NET TIPS WebClientクラスでWebページを取得するには?
http://www.atmarkit.co.jp/fdotnet/dotnettips/302wcget/wcget.html

あと、肝心の株価は『Yahoo!ファイナンス』からとることにする。
http://stocks.finance.yahoo.co.jp/


処理の流れとしては、
1) WebClientというクラスのメソッド『OpenRead(URLを指定)』で、
 Yahooファイナンスのページを読み込んでHTML文全部をstringに格納。
2) 1の中から、Regex.Matchesというメソッドを使って日経平均の値を取り出し、
 MatchCollectionに格納し、stringに変換
3) 2)をFormのLabelに代入する。

という単純なもの。

まず、1)を実現する。string KabukaGet()というメソッドを作る。

WebClientクラスのOpenReadメソッドは、urlから読み込むための経路(Streamクラス)を返してくれる。これをStreamReaderクラスので読み込んで(ReadToEndで最後まで読む)htmlという名前のstringに入れる。
private string KabukaGet()
{
WebClient wc = new WebClient();
Stream st = wc.OpenRead("http://stocks.finance.yahoo.co.jp/");
Encoding enc = Encoding.GetEncoding("UTF-8");
StreamReader sr = new StreamReader(st, enc);
string html = sr.ReadToEnd();
sr.Close();
st.Close();
return html;
}


次に、2)の処理『取り出したhtmlファイルの内容から、日経平均部分を取り出す』を実装。

yahoo!ファイナンスの日経平均株価のところを見ると『<dt class="DataTitle">』で始まる箇所の、『日経平均株価』という文字列があった次の『<strong class="floatL">』タグの次からこのタグの最後『</strong>』までが日経平均株価だ。

正規表現に直すとこんな感じ。(<kValue>が日経平均株価の位置)
"<dtclass=\"DataTitle\">.*?日経平均株価.*?<strongclass=\"floatL\">(?<kValue>.*?)</strong>


なので、
private string GetValue()
{
string eValue;
string html = KabukaGet(); //1)で実装した、KabukaGetでurlのソースを取出す
html = html.Replace("\r", "").Replace("\n", "").Replace(" ", "");//改行、空白を削除
MatchCollection IndexData;

IndexData = Regex.Matches(
html, "<dtclass=\"DataTitle\">.*?日経平均株価.*?<strongclass=\"floatL\">(?<kValue>.*?)</strong>",
RegexOptions.IgnoreCase);
eValue = IndexData[0].Groups["kValue"].ToString();
return eValue;
}


Regex.Matcheで日経平均の値をkValueというインデックスで取り出し、それ(の1つ目)をeValueで取り出している。
すったもんだの末、間に改行、空白があると上手くいかないようなので、その前にこれらを削除。

FormにNikkeiLabelを配置(下図)し、フォームをダブルクリックしてForm1_Loadイベントを作成。
そこに、GetValue()を実行する一行を入れて完成。



private void Form1_Load(object sender, EventArgs e)
{
NikkeiLabel.Text = GetValue();
}


実行してみる。



こんな感じ。

折れ線グラフの作成の続き(データが抜けの対処)

2014-08-16 23:36:02 | 日記
また間が開いてしまったけど、体重のリストをグラフにするプログラムの続き。
体重のデータに空きができてしまった場合の処理を考える。
(ちょうど、僕みたいにすぐサボってしまう人間には必須機能。。。)

まず、前回はExcelのデータを全てグラフ化したけど、今回は直近1ヶ月のみ描画することを考える。

そこで、描画を開始するDateTime型のStartDateと、表示する日数nDayを作ってみる。
public partial class Form1 : Form
{
DateTime StartDate;
int nDays; // 行数


まず、描画開始の日は1ヶ月前なので、AddMonths関数を使う。
StartDate = DateTime.Today.AddMonths(-1);


ここでいきなり初心者の僕に悩ましいのが、最近の一ヶ月を何日(nDays)にするべきなのかという問題。。。
ググってもよくわからず、とりあえず、1月1日からの日数を計算するDayOfYearという関数があったので、1月の場合は定数にして、
こんな感じにしてみる(なんか格好悪い、上級者は絶対別の方法でやりそう。。。)
if (DateTime.Today.Month > 1) nDays = DateTime.Today.DayOfYear - StartDate.DayOfYear;
else nDays = 31;

(今が1月の場合は前月は12月なので31日にする)

あと、全部完成してから気づいたんだけど、1ヶ月分ってことはStartDateは1ヶ月前の日+1日になるんだね。

以上をまとめて、コンストラクタに下記を追記

StartDate = DateTime.Today.AddMonths(-1); //描画対象の日付を計算
if (DateTime.Today.Month > 1) nDays = DateTime.Today.DayOfYear - StartDate.DayOfYear;
else nDays = 31;
StartDate = StartDate.AddDays(1);



次は、描画(OnPaint(PaintEventArgs e))の方。
前回は、データの抜けがない前提だったので、データに書かれた日付をそのまま描画に使っていた。
今回は、該当の日付が無い可能性もあるので、単純にStartDateからnDays分目盛を描画する形式にする。
強いて言えば、ToShortDateString()を使ったのが特徴(?)なのかな。
for (int n = 0; n > nDays; n++)
{
double x = (n + 0.5) / nDays;
if (n % 7 == 0)
{
Rectangle rc = new Rectangle(nx(x) - 50, ny(0) + 7, 100, 14);
g.DrawLine(penGray, nx(x), ny(0), nx(x), ny(1));
TextRenderer.DrawText(g,StartDate.AddDays(n).ToShortDateString(), font, rc, Color.Black, flags);
}
g.DrawLine(penGray, nx(x), ny(0), nx(x), ny(0) - 7);
}



次はようやく、メインになるデータの描画。

データは図1のイメージ(8月10日が本日の日付)

まずは、1ヶ月以上前のデータは使わないので、そこまで読み飛ばす。
→StartDate(1ヶ月前)とdata配列内に入っている日付を比べて、ループさせる。

int k = 0;
while (StartDate.CompareTo(DateTime.Parse(data[1 + k][0])) > 0) k++;




次に、データが途中で抜けていた場合の対応。

図2のように、抜けている日は飛ばして線を引く実装を考える。

今回の場合、Excelにデータが無いとき、
・ループで『日付』と『x』は進める
・data[k+1][1]のkは進めない
となる。


なので、実装としては下記の感じか。。。
for(日付とxを一ずつ進める)
{
if(dataにその日のデータがあったら)
線を描く(x,y)
dataの読む位置をひとつ進める
x0,y0に現在のx,yを入れる
データを読み込む位置(k)をひとつ進める
}



実装の前にもうひとつ考慮点として、、、
1ヶ月前の日にデータが入っていなかった場合を考える。
いろいろやり方はあるのだろうけど、今回は、無い場合は何も表示しないことにする。

このため、図3のようにデータがあるまで描画しないロジックを入れる。


for(日付とxを一ずつ進める)
{
if(dataにその日のデータがあったら)
{
if(最初のデータがあったら→flagで判別) //★追加
{
線を描く(x,y)
}
最初のデータがあったことを認識(flagをtrueにする) //★追加
dataの読む位置をひとつ進める
x0,y0に現在のx,yを入れる
データを読み込む位置(k)をひとつ進める
}
}


これをコードに直すと、データの記述部分は下記のとおり。


for (int i=0 ; i < nDays; i++)
{
double x = (i + 0.5) / nDays;
double y = (double.Parse(data[k+1][1]) - Constants.YMIN) / (Constants.YMAX - Constants.YMIN);
if (StartDate.AddDays(i).Equals(DateTime.Parse(data[k + 1][0])))
{
if (isStart == true) //0の場合は書かない
{
g.DrawLine(pen, nx(x0), ny(y0), nx(x), ny(y));
}
x0 = x;
y0 = y;
isStart = true;
k++;
if (data.Count - 1 < k + 1) break;
}
}
pen.Dispose();


実行。

上手く動いている感じ。

でもコードはいまいち綺麗じゃないな。。。
いずれもっとうまいコードが書けるように祈りつつ、今週は終了。

あと、最後に完成したコードを載せたいけどうまい方法がみつからない。。。
これも次回の課題かなぁ。。。