System.out.println("またJavaの季節がやってきた!" + 2);
■ このスレッドは過去ログ倉庫に格納されています
去年の今頃Java学習開始するも挫折
しかし不屈の闘志をめらめらと燃やしながら
そびえ立つ岩壁にいどむため、再びこの地にやってきたのだ!
プログラミング歴は独学Cチョビッツ+独学VBA少々
きょうかしょ
https://www.amazon.co.jp/dp/484433638X
スッキリわかるJava入門 第2版 (スッキリシリーズ) Javaでは e と打ったあと、自動補完システムが e について使えるフィールドやメソッドを
示してくれた
C#でもインテリセンスが示してくれるんだけど、
Objectクラスのメンバのようなものしか表示されていないようだ
そこで、この一行を@の代りに使ってみる
MessageBox.Show(sender.ToString() + "\n" + ((MouseEventArgs)e).Clicks);
これは、((MouseEventArgs)e). まで書いたときに、インテリセンスが羅列した候補からClicksを選んだ場合だ
このようにMouseEventArgsでキャストすることで、Javaと同じように e の実際のメンバが表示できた
要は、基底クラスのEventArgsのままだとメンバが表示されないので
派生クラスのMouseEventArgsにキャストし直したというわけだ
e がどのクラスなのかは、>>394でやったようにToString()を使って知ることができる
ちなみに、結果は
System.Windows.Forms.Label, Text: ダブルクリックされました
2
とでる
e から、クリックの回数である 2 という情報を引き出せたことになる
一応満足した ((MouseEventArgs)e).X
((MouseEventArgs)e).Y
相対座標もこれで取得できる
ラベルの左上が原点かな?たぶん
C#はこの辺の理解全くなしでGUIアプリを組めるという点では楽だな
その反面理解なしに進んでしまうから、そのうち壁にぶつかりそうだけど
これまでコンソールアプリだとVisualStudioの画面に、
windowsアプリだとMessageBox.Show()でメッセージボックスに
文字情報を出力してきたけど
これからはフォーム上に作ったテキストボックスに出力する方がよさそうだ
こぴぺもできるしね
ここにコード貼るときにタブというかスペースを加工しないと
うまくインデントを表現できなかったので、正規表現使えるエディタで置換してた
ただ、ちょっと使いづらい
そこで、この加工をする専用のwindowsアプリを自分で作ってみようかなとか思ってる
簡単そうだし役に立ちそう でけた〜
初のC#アプリ
やっぱりC#は簡単に作れるなぁ
正規表現でちょっと迷ったところがあったけど
サブマッチパターンに名前を付けて処理することで解決した private void button1_Click(object sender, EventArgs e)
{
string textBox1Value = textBox1.Text;
var rgx = new Regex(@"(?<wideSpaces>^( )*)(( ){4})", RegexOptions.Multiline);
while (rgx.IsMatch(textBox1Value))
{
textBox1Value = rgx.Replace(textBox1Value, "${wideSpaces} ");
}
textBox2.Text = textBox1Value;
}
目的は、
マルチラインのtextBox1に表示されているコードの全てのインデント(半角スペース4個)を
全角スペース1個に置換し、結果をtextBox2に表示させること
方法はいろいろあると思うけど、こういう流れでやった
マルチラインモードの正規表現で文字列先頭にある半角スペース4個セットを検索して、
これを全角スペース1個に置換。
半角スペース4個セットは複数並んでいる場合もあるため、一度では処理できない
そこで、whileで半角スペース4個セットがなくなるまで回す方向で考える。
正規表現に少し手を加える
文字列先頭にある「0個以上の全角スペースと、これに続く半角スペース4個セット」を検索し
これを置換する。0個以上の全角スペースとは、置換済のインデントを意味する
どのように置換するかだが、
0個以上の全角スペースはそのままで、半角スペース4個だけを全角スペース1個へ置換する
0個以上の全角スペースはそのまま残すため、
この部分を丸括弧でくくってサブマッチパターンとし、wideSpacesと名前を付ける
(?<wideSpaces>^( )*)
そして、置換後文字列にwideSpacesを利用する
最初はsplitして一行ごとに処理していたけど、せっかくだからマルチラインモードとかの
復習も兼ねて、てんこ盛りでやってみた
文字列の途中に半角スペース4個がないと仮定するならもっと簡単なのだが
そこはこだわってちゃんと作ってみた
貼ってあるコードは、もちろん作ったばかりのアプリで置換作業をしたもの。 windowsアプリの入門本使っていくつか作ってるんだけど簡単に作れるわ
今の知識だけでもいろんなもの作れそう
Javaでも外部ライブラリやら導入すればもっと簡単にできたのかもしれないけど
VisualStudioだと追加導入の手間いらずでこれだけ作れるのはすごい
今ちょうど>>119-121に相当するコードを書いていたのだけど、ずっと簡単にかける
前回閉じた場所で開く機能も試しにやってみたいがどうすっか 閉じたときの場所をlocation.logに記録して
開いた時にlocation.logから座標を読み取って再現するという
Javaでもやった例のパターンなんだけど
開いた時にロードする時、当然コンストラクタに記述するんだろと思いきや
windowsアプリだとイベントとして記述するみたいだ
これも斬新だな private void Form1_Load(object sender, EventArgs e)
{
string str = null;
int x = 0;
int y = 0;
if (File.Exists(Path))
{
using (sr = new StreamReader(Path))
{
str = sr.ReadToEnd();
}
var rgxX = new Regex(@"(^x=)([0-9]+)", RegexOptions.Multiline);
var strX = rgxX.Match(str).Groups[2].Value;
var rgxY = new Regex(@"(^y=)([0-9]+)", RegexOptions.Multiline);
var strY = rgxY.Match(str).Groups[2].Value;
if (int.TryParse(strX, out x) && int.TryParse(strY, out y))
{
this.Left = x;
this.Top = y;
}
else { SetCenter(); }
}
else { SetCenter(); }
void SetCenter()
{
this.Left = (Screen.PrimaryScreen.Bounds.Width - this.Width) / 2;
this.Top = (Screen.PrimaryScreen.Bounds.Height - this.Height) / 2;
}
}
座標をロードするハンドラ
Pathはクラス定数としてlocation.txtが定義されてる
location.txtにはフォームを閉じる際、
x=100
y=150
のような形式で座標が登録されるようになっているので、
ロードハンドラではこれを読み込むことになる
正規表現はお気に入りのサブマッチパターンを使った
TryParseでx,y座標ともにintへの変換が成功した場合のみフォーム表示の座標として利用
失敗したときは内部メソッドのSetCenterを呼び出してモニタ中央に表示させる フォームを開く時にモニタ中央に表示させるコードとしてこういうものがある
this.StartPosition = FormStartPosition.CenterScreen;
これを使うのであれば、コンストラクタに書く必要がある
上のロード用のイベントハンドラはどうやらフォームが表示されたあとに動いているようだ
ハンドラにStartPositionを書いてしまっても今更ということになってしまう
そのため、SetCenterメソッドでは、
すでに表示されているフォームの位置を変更するという形をとっている 例えばカレンダーをつくるとして、日付を表示するパネルが42枚並んでるとする
このとき各日付パネルをクリックすることで何らかのアクションを起こしたい時
Form1クラスに42個のイベントハンドラを並べるのはどうなんだと思い調べてみた
https://dobon.net/vb/dotnet/control/buttonarray.html
配列を利用することになるんだけど
いくつかの方法があるようだ uint x;
Console.WriteLine(uint.TryParse("-10", out x));
結果はfalse
これを利用することで、モニタの表示画面からはみ出した座標が記録されていた場合
次に開く時はモニタ中央に開けるかやってみた
string str = null;
uint x = 0;
uint y = 0;
if (File.Exists(Path))
{
using (sr = new StreamReader(Path))
{
str = sr.ReadToEnd();
}
var rgxX = new Regex(@"(^x=)([0-9]+)", RegexOptions.Multiline);
var strX = rgxX.Match(str).Groups[2].Value;
var rgxY = new Regex(@"(^y=)([0-9]+)", RegexOptions.Multiline);
var strY = rgxY.Match(str).Groups[2].Value;
if (uint.TryParse(strX, out x) && uint.TryParse(strY, out y))
{
this.Left = (int)x;
this.Top = (int)y;
}
else { SetCenter(); }
}
else { SetCenter(); }
void SetCenter()
{
// this.StartPosition = FormStartPosition.CenterScreen;はForm_Loadイベントハンドラでは機能しない
this.Left = (Screen.PrimaryScreen.Bounds.Width - this.Width) / 2;
this.Top = (Screen.PrimaryScreen.Bounds.Height - this.Height) / 2;
}
一応いけるんだけどサブモニタ上ではうまく機能しないようだ
マイナスのy座標が記録されている場合でも、そのままの座標で開いてしまう
メインモニタ上では機能するので、とりあえずいいか あーちょいまてよ
正規表現にマイナスが含まれてないな
この辺でおかしくなってそうだ
今いそがしいからあとから触ってみよう >>404の正規表現部分だけを修正
var rgxX = new Regex(@"(^x=)(-?[0-9]+)", RegexOptions.Multiline);
var strX = rgxX.Match(str).Groups[2].Value;
var rgxY = new Regex(@"(^y=)(-?[0-9]+)", RegexOptions.Multiline);
var strY = rgxY.Match(str).Groups[2].Value;
これでサブモニタでマイナス側に飛び出しても大丈夫
可能性として、下とか左に飛び出す場合もあるわけで
そういう場合には対応できていない
そう難しいわけではなさそうだけど >>404をさらにアプデ
private void Form1_Load(object sender, EventArgs e)
{
string str = null;
int x = 0;
int y = 0;
if (File.Exists(Path))
{
using (sr = new StreamReader(Path))
{
str = sr.ReadToEnd();
}
var rgxX = new Regex(@"(^x=)(-?[0-9]+)", RegexOptions.Multiline);
var strX = rgxX.Match(str).Groups[2].Value;
var rgxY = new Regex(@"(^y=)(-?[0-9]+)", RegexOptions.Multiline);
var strY = rgxY.Match(str).Groups[2].Value;
if (int.TryParse(strX, out x) && int.TryParse(strY, out y))
{
this.Left = x;
this.Top = y;
}
else { SetCenter(); }
}
else { SetCenter(); }
var scr = Screen.GetBounds(this);
const double RemainLimit = 0.6;
bool result = true;
result &= (scr.X + scr.Width - this.Left > this.Width * RemainLimit);
result &= (this.Left + this.Width - scr.X > this.Width * RemainLimit);
result &= (scr.Y + scr.Height - this.Top > this.Height * RemainLimit);
result &= (this.Top + this.Height - scr.Y > this.Height * RemainLimit);
if (!result) { SetCenter(); }
void SetCenter()
{
this.Left = (Screen.PrimaryScreen.Bounds.Width - this.Width) / 2;
this.Top = (Screen.PrimaryScreen.Bounds.Height - this.Height) / 2;
}
} >>408
uintは座標がマイナスの場合しか使えないロジックなのでやめやめ
こんな風にした
保存してある座標をそのまま使って一旦フォームを表示させる
もちろんlocation.txtがないとか、数値が想定通りに記録されてない場合はメインモニタ中央へ表示
その上で、フォームがモニタ画面から飛び出し「すぎてる」場合はメインモニタ中央へ移動
RemainLimit = 0.6はフォームの幅・高さの6割がモニタ画面に映っているならOK
それを下回ったらメインモニタ中央へ表示、という定数
これで上下左右・メインモニタ/サブモニタ関係なしにオールマイティにはみ出し補正可
resultのロジックだけど
result = result && (〜〜〜);
でやってたんだけど、もしかしてとおもって
result &= (〜〜〜);
にしたら行けたんでこれにした
要は if を並べて階層深くしたり、条件式が長くなるのが嫌だっただけなんだけど
これはクラスにまとめてこれから作っていくアプリにも使っていこうかなぁ〜 フォームを一旦表示させてから問題あるなら移動させるという2段階の方法を採った理由は
一度に所定の位置へ置こうとすると、scr.X とかがうまく取得できなかったから どうでもいいような部品なんだけど
うまくいったんで満足である
独習はまだしっかり頭にはいってないところがたくさんあるけど
辞書的に使うのにも適してる
今のGUIの入門本しばらくやってから、独習の残り1割ぐらいだけど
まだ読んでない部分読むか
復習も何度かしないといけないな >>403で書いた複数コントロールを束ねる処理についてなんだけど、
今やってる入門本でも紹介されていた
複数のボタンをひとつのハンドラでコントロールする方法を使う
デザイン画面で複数ボタンを選択してから
右下プロパティウィンドウ→イベントタブ→Clickの項目にハンドラ名を入力してEnter
で、Form1に選択した複数ボタンに一括対応するハンドラが自動生成
ここでひとつ問題がある
Form1のこのハンドラを見ただけでは
このハンドラがどのコントロールに対応してるかがわからない
覚えてりゃいいんだけど、またはコメント書いておけばいいとかいう話ではあるんだけど
やはりちゃんとコード上で確認できるべき
そこで、確認方法
左上ソリューションエクスプローラウィンドウ→Form1をクリックして展開
→Form1.Disigner.csをダブルクリックして開く
コード上に
「Windows フォーム デザイナーで生成されたコード」
というregionがあるので左端+をクリックして展開
この中にbutton2に対応する部分がある
//
// button2
//
this.button2.Location = new System.Drawing.Point(13, 13);
this.button2.Name = "button2";
this.button2.Size = new System.Drawing.Size(125, 100);
this.button2.TabIndex = 0;
this.button2.UseVisualStyleBackColor = true;
this.button2.Click += new System.EventHandler(this.buttons_Click);//←ここ
複数ボタン対応ハンドラの名前は「buttons_Click」としたのだが、
button2.Clickにbuttons_Clickハンドラが登録されている
ここからbutton2.Clickがbuttons_Clickメソッドによりコントロールされることがわかる
同じようにbutton3, button4, button5 ・・・・といったbuttons_Clickハンドラにより
コントロールされるボタンのコードにも、buttns_Clickが登録されているのがわかる 複数コントロールを一括してひとつのハンドラにまとめるには
対象となるコントロールをぽちぽちとctrl+左クリすることで選択していくわけだけど
こういう方法によると、対象外のオブジェクトを間違えて選択してしまうことがある
実際に自分がやった間違いとしてこれ↓
//
// splitContainer1.Panel2
//
this.splitContainer1.Panel2.Controls.Add(this.button10);
this.splitContainer1.Panel2.Controls.Add(this.button7);
this.splitContainer1.Panel2.Controls.Add(this.button4);
this.splitContainer1.Panel2.Controls.Add(this.button9);
this.splitContainer1.Panel2.Controls.Add(this.button6);
this.splitContainer1.Panel2.Controls.Add(this.button3);
this.splitContainer1.Panel2.Controls.Add(this.button8);
this.splitContainer1.Panel2.Controls.Add(this.button5);
this.splitContainer1.Panel2.Controls.Add(this.button2);
this.splitContainer1.Panel2.Click += new System.EventHandler(this.buttons_Click);//←ここ
this.splitContainer1.Size = new System.Drawing.Size(412, 396);
this.splitContainer1.SplitterDistance = 54;
this.splitContainer1.TabIndex = 0;
ボタンの背景にあるパネルを間違えてクリックしてしまい
パネルがbuttons_Clickハンドラの支配下に置かれてしまっている
このままだとボタンではなく背景のパネルをクリックしただけで
buttons_Clickハンドラに記述されている処理が実行されてしまう
パネルが選択されたかどうかは、デザイン画面上は非常にわかりにくく間違いを招きやすい
選択されたオブジェクトが羅列されているウィンドウは見当たらない
注意深くやっていくしかないというところか ところで、>>413のようにミスしてしまった場合の対応なのだが、
実際、どのオブジェクトがどのハンドラの支配下にあるのかは>>412でわかるように
「隠された」コードを見るしかない
しかも、この部分にはこんなコメントが記述されている
/// <summary>
/// デザイナー サポートに必要なメソッドです。このメソッドの内容を
/// コード エディターで変更しないでください。
/// </summary>
>>413の「//←ここ」の行を削除すればいいんだろうなということは
今まで貯めた知識で分かってはいるんだけど、本当にいいんだろうかと。
別の方法により、「自動的に」削除させる方法があるんだろうか
まぁよくわからないので、マイクロソフトさんに逆らって勝手にコードを削ることにする
この辺の知識なしでVisualStudioをさわる人はどう処理をしているのか
もう一度対象オブジェクトを全て削除してから自動生成し直しているのかな
なんだかんだこだわって理解しようとしてると、そのうち役に立つもんだ >>412のこれだけど
this.button2.Click += new System.EventHandler(this.buttons_Click);
this.button2.Click += new EventHandler(this.buttons_Click);
だとエラーがでる
コードが記述されているのはForm1のパーシャルクラスなんだけど
Form1.csにはusing System;が記述されているから省略してもいいんじゃないかと思ったのだが
パーシャルクラスであってもファイルが違うなら、冒頭にusing System;入れないと
省略できないようだ
実際にusing記述したら省略してもいけた
ここは基本的にユーザーがいじらない前提でコードが自動生成される場所なので
usingでの省略は逆に足手まといになるのかな イベントドリブンモデルについて再考
>>391なんだけど、こう書き直してもいい
public delegate void MyEventHandler(object sender, EventArgs e);
class Program
{
public event MyEventHandler myEvent = (sender, e) => { }; //←ここ注目
public void handler(object o, EventArgs e)
{
Console.WriteLine("handler called");
}
public static void Main(string[] args)
{
Program target = new Program();
target.myEvent += target.handler;//+= new MyEventHandler(target.handler)も可
target.myEvent(target, EventArgs.Empty);
}
}
独習567ページのコードに含まれているこれ、
public event KeyCommandEventHandler KeyCommand = v => { };
の理解のために比較対象としてあげてみた
独習の方の
v => { }
は空のメソッドを意味する。
= v => { }
により空のメソッドを初期値としてKeyCommandに代入していることになる
そもそもデリゲート型変数であるKeyCommandにはメソッドが入る
int型変数の初期値として 0 を入れることがあるように
デリゲート型変数にラムダ式を使って空メソッドを初期値として代入しているわけだ。
もちろん初期値を代入することは必須ではないので
public event KeyCommandEventHandler KeyCommand;
と書いてもエラーはでない(はずだ!)
同様に、初期値を設定しない>>391のようなコードも問題なく動く(これは確認済)
上で書きなおしたコードは
(sender, e) => { }
を初期値としてデリゲート型変数myEventに代入している
ラムダ式で表したメソッドだ
デリゲートは引数2つと戻り値voidなので、これに対応した形になっている >>417修正
イベントドリブンモデルにおいては、もちろん通常のデリゲートでも同じような事が
言えるのかもしれんが、
いや、もっというと初期値というもの全般について言えることなんだけど
変数に中身が入ってない状態で呼び出されてしまった場合に備えて
やはり空のメソッドでも入れておくべきだ
初期値についてはいろいろルールがあったはずだが忘れたw
調べるのが面倒なのでおいおいということにしよう
クラス変数とローカル変数でも変ってきたはず
とりあえずこれだけは確認した。下のコードはエラーがでる
初期値代入なしで、中身となるメソッドも入れずに実行した場合
public delegate void MyEventHandler(object sender, EventArgs e);
class Program
{
public event MyEventHandler myEvent;
public void handler(object o, EventArgs e)
{
Console.WriteLine("handler called");
}
public static void Main(string[] args)
{
Program target = new Program();
target.myEvent(target, EventArgs.Empty);
}
} VisualStudioでは、ボタンクリックなどで使われるデリゲート型変数の初期値は
どうなっているのか調べてみた
>>412に従って表示されたbutton2.ClickのClickにカーソルを合わせて右クリ
→定義へ移動
public event EventHandler Click;
初期値となる空メソッドは代入されていないように見える
public event EventHandler Click = (sender, e) => { };
こう書き加えてやろうと思ったがもちろん勝手には書き込めないようだ
中身となるメソッド(ハンドラ)が自動生成されて、自動で代入されていくので
初期値がないことによって生じる問題は基本的には起きないと想定した結果か 忘れてた
>>418で、
初期値設定した上で、中身のメソッドを入れなかった場合はエラーなし
何も表示されずに終了
public delegate void MyEventHandler(object sender, EventArgs e);
class Program
{
public event MyEventHandler myEvent = (sender, e) => { }; //初期値代入
public void handler(object o, EventArgs e)
{
Console.WriteLine("handler called");
}
public static void Main(string[] args)
{
Program target = new Program();
//中身メソッド代入しない
target.myEvent(target, EventArgs.Empty);
}
} private void buttons_Click(object sender, EventArgs e)
{
if (((Button)sender).Text == "あたり")
{
MessageBox.Show("あたり!");
}
}
イベントハンドラの第一引数の具体的な使い方
>>395-396では第二引数 e をやったけど
今回は第一引数のsenderについて
使い方は e と同じで、object型になっているので派生クラスにキャストすることで
senderに格納されてるデータを引き出せる
派生クラスの正確な名称は
MessageBox.Show(sender.GetType().ToString());
を一旦置いてみることで取得できる
この場合
System.Windows.Forms.Button
と表示されるので「Button」が派生クラスだと分かる
Javaだとこの辺のデータも e に格納されていたと思う
>>109の e.getComponent() がそれか 派生クラスの名前は
Form1.Designer.csを見ても分かる
this.button2 = new System.Windows.Forms.Button();
ボタンには Buttonクラスのインスタンスが代入されている フォームの大きさを変えたときにボタンとかテキストボックスが自分の思う通りに
追随してサイズを変えたり、または固定されたり・・・といったことをやってた
SplitContainer→FixedPanel, IsSplitterFixed
TableLayoutPanel
などなど・・・
なれないと思った通りにするのに時間かかる イベントハンドラの中で、Form1の座標を取得したいとき、
イベントハンドラに渡されるsenderの実体がForm1の時は問題ない
((Form1)sender).Left
((Form1)sender).Top
senderの実体がButtonの時は同じようにやってもエラーが出る
ButtonなのにForm1型にキャストしようとするからだ
この場合はこうする
((Button)sender).FindForm().Left
((Button)sender).FindForm().Top
Buttonが位置するFormを取得するメソッドFindFormを使えばいい 自分で気に入ったアイコンをexeファイルやフォームに表示できるようになった! TableLayoutPanel内にコントロールを並べてるのを忘れてると
これらコントロールのサイズを変更しようとしたときに動かなくて
あれれれれ
と思う事があるので注意
TableLayoutPanelで1行2列の2セルにそれぞれテキストボックスを置いていたんだけど
2つのテキストボックスの間にSplitterを挟むことでテキストボックスの幅を
ユーザーが自由に変更出来る仕組みにしようとした
上の記述はこの時遭遇したトラブル
TableLayoutPanelのセル内でもコントロール配置場所は、該当するコントロールの
Anchor,Dockプロパティによって影響を受ける
そもそもの話になるが、Splitterを使う場合はTableLayoutPanelは使わないので排除する Splitterを使う時はこれと接するコントロールのDockプロパティの使い方に注意
>>427のように、2つのtextboxを横に2つ並べて、その間にSplitterを置く場合、
1つ目の左側に置くtextboxのDockプロパティはLeft
Splitterは自動的にLeftに設定され
2つ目の右側に置くtextboxのDockプロパティはFill ←ここ大事
右側をFillにしないと実行時にSplitterにカーソルを合わせてもカーソル表示が変らないし
幅を変えることもできなかった 入門本にgmailチェッカーが載ってたので作ってみたんだけど
テスト用にあらためてgmailアカウント新規でとろうと思ったら電話番号要求してくんのな
でもってSMSとかで確認するとか
あほらしくなって昔取得したアカウント引っ張り出してきて使うことにした
でも結局gmail側のセキュリティ機能が働いてちゃちなアプリではアクセスできないことがわかったので終了 ツイッター投稿アプリも載ってたので作ろうと思ったのだが
ツイッター側からアプリ作成に必要なAPIキーをもらうために
開発者用アカウントをつくらねばならない
これまた電話番号登録しろと出る
これもいやなのでパスでーす >>415について
プロパティウィンドウでイベントを表示させると、
登録済のイベントがあれば右側にハンドラの名前が表示される
これを右クリ→リセットすればForm1.Designer.csからも該当コードが自動的に削除されるようだ 1.
VisualStudio自体を複数開くことができる
ソリューションはslnファイルから直接ひらくことができるが
複数のslnファイルを順々にクリックしていけばどんどん新しいVisualStudioのウィンドウが開いていく
デザイン画面で、複数の設定がめんどくさそうなコントロールを選択してctrl+c
別のVisualStudioのデザイン画面でctrl+vをすればこぴぺもできる
2.
全然別の話だが、clickイベントとmouseClickイベントの違い
mouseClickはそのまんまだけど、clickイベントは、例えば
ボタンにフォーカスがある時にキーボードでEnterを押すことでもイベント発生可能 グラフィックについて
ざっとみたところ理解しないといけないクラスとして
1.Graphics
2.Image
3.Bitmap
入門本では説明が簡略化しすぎてるので、自分で調べないとダメだ グラフィック関係については一体どこから手を付ければいいのか難しいところだ
そもそも外部から持ってきた画像を表示する方法を明確にしていかないといけなさそうだ
つまりリソースの管理をVisualStudioでどうやっているのかについてだ
画像をフォームに表示する方法として、入門本で初めてやったのはこれだ
PictureBoxというコントロールをフォーム上に置く
フォーカスを当てると右上に表示される三角形から「イメージの選択」
次にローカルリソース→インポートにより外部の画像ファイルを選択して取り込むという流れになる
ここで疑問が出てくる
このインポートされた画像は一体どこに保存されているのだろうか
もちろん作成されたexeの中に入っているのは分かっているのだが
その前段階として該当するソリューションフォルダの中のどこかに
画像ファイルが取り込まれていると考えるのが妥当だろう
しかし探しても、画像ファイル自体は見つからないし、
コードをみても画像ファイルの拡張子からして見当たらない
ここでいろいろ検索してみたところ、どうやら取り込まれた画像ファイルは
拡張子「resx」の「XMLリソースファイル」とやらに格納されているようだ
実際にウィンドウ右上のソリューションエクスプローラーのForm1.csを展開するとForm1.resxがある
これをダブルクリックすると、この中に先ほどインポートした画像が入っているのを確認できる
resxには画像だけではなく、音声や文字列を格納することもできるそうだ
resxを利用しない方法もあるようだが、とりあえず利用する流れでやっていくことにしよう
インポートした画像をForm1.resxの中で選択すると、
pictureBox1.Imageという名前が付けられていることが分かる
画像ファイルとしての拡張子が消えている点に注意しなければならない
pictureBox1.Imageをコードの中から探すと、Form1.Designer.csの中に見つかる
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
this.pictureBox1 = new System.Windows.Forms.PictureBox();
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
this.SuspendLayout();
//
// pictureBox1
//
this.pictureBox1.Dock = System.Windows.Forms.DockStyle.Fill;
this.pictureBox1.Image = ((System.Drawing.Image)(resources.GetObject("pictureBox1.Image")));
this.pictureBox1.Location = new System.Drawing.Point(0, 0);
this.pictureBox1.Name = "pictureBox1";
this.pictureBox1.Size = new System.Drawing.Size(412, 450);
this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
this.pictureBox1.TabIndex = 0;
this.pictureBox1.TabStop = false;
usingを使っていないと長ったらしく非常にわかりにくい と、ここまで書いて気づいたのだが、
入門本で初めてフォームに画像を表示した方法はこの方法ではなかったようだ
具体的にはこうやった
ソリューションエクスプローラのプロジェクト名を右クリック→プロパティ
→左側ペインからリソース選択
中央に表示される白いスペースに外部の画像ファイルをDDして放り込む
すると、>>434でもやったが、pictureBoxの右上三角マーク→イメージの選択
(ここでローカルリソースから外部画像ファイルを選ぶのが>>434の方法だ)
→プロジェクトリソースファイルを選択することで
この中から表示する画像ファイルを選択することができる
この方法によればソリューションフォルダの中に画像ファイルがそのままの形で保存されている
プロジェクト名のフォルダの直下にResourcesフォルダが作られており、この中に保存されている
この方法によるリソースの管理は単純なので>>434の方法より理解しやすいが
せっかくなので>>434の方法についても調べてみることにする (結局分からないかもしれないが >>435の方法についていくつか気づいた点
ソリューションエクスプローラのプロジェクト名を右クリック→プロパティ
→左側ペインからリソース選択
ここで開いているウィンドウだが、>>435で、「白いスペース」と書いたが、実際には
デフォルトでは文字列をリソースとして格納するウィンドウが開いており、白いスペースはない
ここに画像ファイルを放り込めば自動的にイメージ対応のウィンドウに切り替わる
対応するリソースの種類に合わせて手動でウィンドウを切り替えるには、ウィンドウのすぐ上の
項目の一番左側、デフォルトだと「文字列」となっているが、右の逆三角形のマークから
イメージ、アイコン、オーディオなどの選択ができる
白いスペースが表示されるのはイメージの対応ウィンドウとなったときだ
もう一点。
>>435の方法を採った場合に、画像ファイルをpictureBoxに置きたい場合
つまり、一旦リソースとして取り込んだ画像ファイルを実際にコードの中で呼び出して使いたい場合だが
デザイン画面のpictureBoxにフォーカスしたときの右上三角マークからイメージ選択する場合は
対応コードはForm1.Designer.csに自動生成されることになる
そうではなく、自分でハンドラなどから呼び出したい場合はこうする
pictureBox1.ImageLocation = @"C:\Image\aaa.jpg";
ImageLocationプロパティに、画像ファイルをフルパスで指定すればよい
Imageプロパティを使っても指定は可能なようだ
前述のForm1.Designer.csに自動生成されるコードもImageプロパティを使用しているようだ↓
this.pictureBox1.Image = global::TestProject.Properties.Resources.aaa;
//(TestProjectはプロジェクト名)
「global::」はグローバル名前空間のエイリアス、独習393ページで一応学習済
この場合なくても動いた
上の一行をusingを使うことで
using TestProject.Properties;
this.pictureBox1.Image = Resources.aaa;
こう書いてもいけた >>434のコードを分析
再度書き直すと、この方法は画像ファイルをForm1.resxというXMLリソースファイルに格納し
ここから画像ファイルを呼び出してpictureBoxに表示させるというものだ
>>434の最後に貼ってあるコードは、アプリを開いた時に該当する画像が表示されるよう
Form1.Designer.csに記述されたものだ。
このコードはコンストラクタを通じてアプリ起動時に読み込まれる
このコードのうち、大事なのは↓の2行
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
this.pictureBox1.Image = ((System.Drawing.Image)(resources.GetObject("pictureBox1.Image")));
これらをusingを使ったとして、部分的に省略すると
ComponentResourceManager resources = new ComponentResourceManager(typeof(Form1));//@
this.pictureBox1.Image = ((Image)(resources.GetObject("pictureBox1.Image")));//A
となる。
この2行は、そのままボタンクリックのハンドラに移しても機能する
(↓では変数resourcesはrsに変更)
private void button1_Click(object sender, EventArgs e)
{
var rs = new ComponentResourceManager(typeof(Form1));//@
this.pictureBox1.Image = ((Image)(rs.GetObject("pictureBox1.Image")));//A
}
@についてだが、わからん、なにやらよくわからん
コンストラクタの引数typeof(Form1)はForm1という型に対するTypeオブジェクトだ
「指定した ComponentResourceManager の情報に基づいて、
サテライト アセンブリでリソースを検索する Type を作成します。」
https://docs.microsoft.com/ja-jp/dotnet/api/system.componentmodel.componentresourcemanager?view=netframework-4.7.2
ということらしいんだけど、わからん
ま、とりあえずForm1.resxを探し出すためのものと理解しておこう
Aについてだが、GetObjectは例えば画像以外にも音声などもresxから取得することになるため
戻り値の型はオールマイティーなobjectとなっている
ただし、ここで取得しているのは画像なのでImage型にキャストしている ちょいとまとめると
外部にある画像をリソースとして使うには大きくわけて2つの方法を学んだ
@resxファイルに格納する方法>>434
AResourcesフォルダに格納する方法>>435
そして、どちらにも言える事だが、
アプリ起動時に表示させる場合はForm1.Designer.csに
そのためのコードが自動生成されていることになる
起動後にボタンをクリックするなどによって画像を表示させたいときは
ハンドラ内に自分で記述することになる
ただし、コードの内容は自動生成であろうが自分で記述したものであろうが、
基本的に差異はない https://ja.wikipedia.org/wiki/%E3%83%93%E3%83%83%E3%83%88%E3%83%9E%E3%83%83%E3%83%97%E7%94%BB%E5%83%8F
●ビットマップ画像
ビットマップ画像(ビットマップがぞう、bitmap image / bitmap graphics)とは、
コンピュータグラフィックスにおける画像の表現形式で、ピクセル(画素)を用いたもの。
画像をドットマトリクス状のピクセル群として捉え、
RGB等の表色系に基づいたピクセルの色・濃度の値の配列情報として取り扱う。
●ベクタ画像
これに対し、幾何図形を作成するための情報を数値や式として表現したものをベクタ画像と呼ぶ。 @
継承 Object→MarshalByRefObject→Image→Bitmap
A
http://koshinran.hateblo.jp/entry/2017/06/30/223423
●System.Drawing.Image
ベクタ画像、ビットマップ画像問わず、画像を扱う為のクラス。
png、jpg、gif 等、さまざまな画像形式ファイルを読み書きできる。
●System.Drawing.Bitmap
ビットマップ 画像を扱う為のクラス。
System.Windows.Forms はビットマップベースの GUI 環境なので、
System.Windows.Forms を使った GUI プログラミングでは
Bitmap クラスをよく使用する。
だが、ビットマップに対する図形の描画などは
Graphics クラスを介して行う。
わかりやすいっす
ここでGraphicsが出てくるのですね >>441の@から分かるようにBitmapはImageの派生クラスでビットマップ画像に特化したクラスということになる
じゃ>>437のこれ↓を
private void button1_Click(object sender, EventArgs e)
{
var rs = new ComponentResourceManager(typeof(Form1));
this.pictureBox1.Image = ((Image)(rs.GetObject("pictureBox1.Image")));
}
こう↓書き換えたら・・・(キャスト先の型をImageからBitmapに変更)
private void button1_Click(object sender, EventArgs e)
{
var rs = new ComponentResourceManager(typeof(Form1));
this.pictureBox1.Image = ((Bitmap)(rs.GetObject("pictureBox1.Image")));
}
いけた! 今気づいたんだけど、pictureBoxに画像を設定したりごちょごちょやってると
いつの間にかForm1.resxにあった画像が消えてるというかResourcesフォルダに移ってる・・・?
何らかの連動性があるようだ
この辺については後回しにしていこうとは思うが
こういう現象から導き出される答えは、できるだけResourcesの方を使った方が今のところは安全だということなのかもしれない すでにある画像を読み込んだり、キャンバスを用意したりするのがImageやBitmapクラスで
四角や円や線などを書き込む筆のようなクラスがGraphicsクラスかな
https://so-zou.jp/software/tech/programming/c-sharp/graphics/
Graphicsオブジェクトの取得
3通りの方法が紹介されているが入門本では3つ目の方法を使っている >>444の3通りについてはちゃんとやろうと思う
特に入門本は3つ目の方法でGraphicsを取得しているんだけど
入門本ではImageではなくBitmapを使っている
この辺の違いも明確にしていきたい
ちなみにImageは抽象クラスなのでnewできないが
Bitmapは抽象クラスではないのでnewできる 入門本で玉が跳ね返りながら動くコードを学んだので
高速で跳ね返って動く玉をクリックして捕まえるゲームを作った
クリックのアタリ判定はクリック座標と玉の中心座標の距離を求めてこれが玉の半径以内ならアタリ
イライラするだけのゲームになった JavaでグローバルIPアドレスを毎日記録するアプリを作って
これをタスクスケジューラに登録してあるんだけど、何日か経つと勝手に無効化してしまう
これは一体何が原因なのか
いろいろ調べてみたんだがイマイチわからない
とりあえず正常に動いているタスクの設定を真似して設定を見直してみた
これで動くといいのだが
自分で作ったJavaのプログラムが常時起動しているせいで
タスク実行時にすでに実行中だと判断されてしまっているとか・・・? JavaでグローバルIPアドレスを記録するアプリをつくって
これを毎日起動するようにタスクスケジューラに登録してあるんだけど* 使っているwindowsアプリの入門本は
「作って覚えるVisualC#2017」
なんだけど、VisualStudioの基本操作が紹介されてるという点
どういう流れでアプリをつくっていくのかが分かるという点
この辺がいい
学習の通過点としてこういうテキストは必要だと思う
ただ、このテキスト通りに作っていくと次第に分かっていくんだけど
背後で自動生成されている膨大な量のコードがあるという事
結局これが理解できてないと応用が利かなくなるという点に気づかされる
もちろん俺はまだ一部しか理解できない、というか一部しか見てない
「プログラミング経験ゼロでもしっかりわかる」と銘を打っているんだけど
さすがにこれは言い過ぎ感が否定できない
アマゾンのレビューをみると、誤植を指摘している人がいる
正確にどのページのどの部分かと指摘してるわけではないのだが、
大文字と小文字を間違えてるとかうんぬん
おれはまだ全て読み終えたわけではないが、
ここのことを言っているのではないかと思い当たる部分に出くわした
結論から言うと、誤植ではないと思う
あるフォーム@のボタンを押すことで別のフォームAを一時的に立ち上げるという機能を考える
Aは利用後すぐに閉じられるが、必要な時に何度も立ち上げることになる
このときにAで使うデータを@で作って置いて、
Aを立ち上げるたびに引数を使って渡していきたい
ここで、Aを立ち上げるとは、コード上ではインスタンスを生成することになる
Aクラスのコンストラクタに引数を渡してAインスタンスを生成するのだ
ここで引数がintやstringなら簡単なのだが、
引数自体も自作クラス(CategoryDataSet)のインスタンスなのだ
@の側で引数となるインスタンス内にデータを入れてから、インスタンスごとAに渡す流れなのだ
こうなると
1.@の側で生成する実引数となるインスタンス(categoryDataSet1)
2.Aのコンストラクタでの仮引数(dsCategory)
という2つの変数が登場する
これだけでも初心者は混乱する
さらにだ、Aの側ではdsCategoryとは別に、同じ型の空の変数を用意して、これと
dsCategoryをMergeするという手法を使っている(401ページList2)
この変数が
3.Aの側で用意してある変数(categoryDataSet)
つまりだ、
クラス名CategoryDataSet
@の変数categoryDataSet1
Aの変数categoryDataSet
この3つがごっちゃごちゃになって、別々のものだと理解できず、その結果、
誤植だろこれ!となったのではないかと思う
独習なみにしっかりとした説明があればいいんだけど、背後に生成されるコードについては
ほとんど説明はなく、変数categoryDataSetに至っては、俺の見落としがなければなんだけど、
テキスト内に唐突に出現しているのだ
この変数は、背後で自動生成されているFormItem.Designer.cs内の
InitializeComponentメソッドでnewされているのだが、そんなことに触れようものなら
パンドラの箱をあけるようなもので収拾が付かなくなる
仕方なかったのだろう・・・・・ DataGridViewとDataSet、DataTableの関連についていろいろ調べてた
一体どれがどういう目的で使われているのかとか
なんとなく分かった 引き続き入門本(作って覚えるVisual C#)を読んでるんだけど
DataTableに格納されたデータを取得してこれをcsvファイルに書き込むという場面で
こういうコードがテキスト内に書かれている
foreach (MoneyDataSet.moneyDataTableRow drMoney in moneyDataSet.moneyDataTable)
唐突になにやら面妖なコードが出現
foreachに続く丸括弧内のコードなのだが
普通ここに書くコードの内容を考えると、こう推測できる
@「moneyDataSet.moneyDataTable」 というコレクション(or配列)があって、
A「MoneyDataSet.moneyDataTableRow」 という型の要素が入っている
DataSetはエクセルでいうブック
DataTableはエクセルでいうワークシート
ということらしいので、ワークシートの中の行をコレクションの要素をして扱っているようなイメージか
これぐらいで納得できればいいのだが、じゃ実際にこういう機能、
つまりDataTable内に格納されたデータを取り出す場合に
具体的にどういうコードを書けばいいのかを考えると、この程度の理解では書けないのではないかと思う
そこで、ひとまず@、Aは一体何なのかを実際のコードから読み取ってみよう、と思ったわけだ が、見事に打ち砕かれた
@「moneyDataSet.moneyDataTable」
moneyDataTableがコレクションまたは配列ということであれば、どこかでそういった宣言が
なされているはずだと考えた。もちろん自分でそういったコードを書いた覚えはないので
自動生成されているコードがあるのだろう
そこで、moneyDataTableにカーソルを合わせ右クリ→定義へ移動
するとMoneyDataSetDesigner.cs(これをデザイナと言うそうだ)内に移動する
MoneyDataSetクラスのプロパティとしてmoneyDataTableが定義されているようだ
public moneyDataTableDataTable moneyDataTable {
get {
return this.tablemoneyDataTable;
}
}
これによるとmoneyDataTableを取得すると同じクラス内の変数tablemoneyDataTableが
かえされると・・・もうここでギブアップしたくなるが先に進む
つまり、moneyDataTableはtablemoneyDataTableのゲッターのようなもんで
moneyDataTableの正体はtablemoneyDataTableという認識でいいのか
いや、ゲッターならスペルは同じにして頭文字の大文字小文字変えるだけにしてくれとか
そういうのはなしなんだろうな・・・
取り合えずそういうことみたいなんで、tablemoneyDataTableを同様に右クリ→定義へ移動
すると同じクラス内のここに移動する
private moneyDataTableDataTable tablemoneyDataTable;
「moneyDataTableDataTable」というクラスの変数として宣言されている
この辺のクラス名や変数名をみてると頭が痛くなってくる
moneyDataTableDataTableを右クリ→定義へ移動
public partial class moneyDataTableDataTable : global::System.Data.TypedTableBase<moneyDataTableRow>
ふぅ・・・もうやめたほうがいいのか・・・
これはよくみると同じMoneyDataSetクラスの内部に設けられたインナークラスのようだ
TypedTableBaseクラスを継承している
わからん!
が、このクラスの中をずっと見ていくと、これか・・・と思い当たるメソッドを発見
public moneyDataTableRow this[int index] {
get {
return ((moneyDataTableRow)(this.Rows[index]));
}
}
独習を復習し直さないとこれは理解できない
インデクサーを使っているんだよねこれ
独習441ページのコードと類似点が多い
この辺からコレクション的な何かを「感じる」
これぐらいにしておこうか・・・
俺の実力だとこれ以上やっても理解するまでにメリット以上の時間がかかりそうだ
それより独習をちゃんと復習してもう少しコードを読解する能力を身につけた方がよさそう https://ohke.hateblo.jp/entry/2016/12/03/231909
>・DataSet、DataTable、DataColumn、DataRowを継承したクラスを使う
>・プロパティを介してDataTable、DataColumn、DataRowの値へアクセスできる
デザイナを使った、つまり自動生成されたコードの特徴だそうだ
継承とプロパティを介したアクセス
知識ある人には便利なんだろうが、おれにとってはこの2つが理解を拒む壁となってそびえ立つ
DataSet関連のコードはデザイナを使わなくても組めるので
分からなかったら自分で一から書いていった方が良いのかもしれない
そう思って基礎的なコードをいくつか書いてDataGridViewで表示させてみた
この辺はこれぐらいでいいかと思う
続けていけばそのうち分かることもあるだろう >>452の最後のコードはメソッドではなく、まさにインデクサーそのものか
(再掲)
public moneyDataTableRow this[int index] {
get {
return ((moneyDataTableRow)(this.Rows[index]));
}
}
今、独習のインデクサーのところを見直しているんだけど、
インデクサーはぶっちゃけて言えばインスタンスの配列化をするための技法
つまり、moneyDataTableDataTableクラスのインスタンスを配列化しているといえる
そして、moneyDataTableDataTableクラスのインスタンスがtablemoneyDataTable
で、こいつをそのまま戻り値として返しているのがmoneyDataTableプロパティ
>>451 @「moneyDataSet.moneyDataTable」は
MoneyDataSetクラスのインスタンスmoneyDataSetのプロパティmoneyDataTableであり
これにより上に書いたような流れで、最終的には
moneyDataTableDataTableクラスの配列化されたインスタンスを意味することになる
この配列の要素となっているのが
((moneyDataTableRow)(this.Rows[index]))
であり
簡単にいえば
Rows[ ] の要素となるのだろう
Rowsにカーソルを合わせると
DataTable.Rows このテーブルに属している行のコレクションを取得します
と出てくる。継承元となっているDataTableがここででてくるわけだ
そしてついに、コレクションという言葉にたどり着いた
道が険しく長すぎる
これでいいだろと言いつつ、調べた結果なんとなく分かった気がしてきた
(配列配列といっているが実際にはインデクサーによる配列的ななにかで実際に配列と行って良いのかは知らない) 独習読んでると、いや、こんなの使わないだろ〜
少なくとも自分では使わないよ、とか思うところが結構あるんだけど
デザイナ使った場合、デザイナで自動生成されたコードから
何かを取得して自分のコードで使いたいと思った時、
つまり上に書いたような場合なんだけど、
こういうときに自動生成されたコードが理解できてないと全くコードを書けない
とするとやはり独習レベルの知識は頭に入れておかないとダメなんだろう
道は長いぞ! テキストボックスをデザイナでForm1上に置くことにより自動生成されるコード
テキストボックスのNameプロパティをmyTextBoxとした
特に大事なのはmyTextBoxがTextBoxクラスのインスタンス変数名として「も」利用されているという点。
private System.Windows.Forms.TextBox myTextBox;
//↑Nameへの代入値がそのままインスタンス変数名としても使われる
//
// myTextBox
//
this.myTextBox.Location = new System.Drawing.Point(13, 13);
this.myTextBox.Name = "myTextBox";
this.myTextBox.Size = new System.Drawing.Size(100, 19);
this.myTextBox.TabIndex = 0;
//
// Form1
//
this.Controls.Add(this.myTextBox); StreamReaderを使う時、一行ずつ読み込むとして
whileの繰り返し条件として使う部分
StreamReader sr = new StreamReader(path);
@while (sr.EndOfStream) { }
Awhile (sr.Peek() >= 0) { }
Bwhile (sr.ReadeLine != null) { }
@、Aは正解
Aは少し古い方法で次に読み込む文字のint型整数値(Char型の中身)を返す
読み込む文字がないと「-1」を返すので、0 以上で有る限り、という条件にする
@のEndOfStreamがプロパティであるのに対してPeekはメソッドなので丸括弧忘れないこと
Bはダメ
読み込む行がないとReadLineがnullを返す事を利用しようとしたものだが、これはダメ。
理由は、ReadLineは単純にチェックのために使いたかったのだが、
実際にここで使ってしまうと、読み込み済として一行進めてしまい、
whileブロックの中で読もうと思っていた行を先取りしてしまう
エラーが出るわけではないが使えない
別途変数を用意して使う方法も考えられるが、そこまでしなくても@、Aがあるので・・・ >>451
@「moneyDataSet.moneyDataTable」関連のコードの位置
【moneyDataSet】はForm1.Designer.csで宣言されるインスタンス
private MoneyDataSet moneyDataSet;
↓
【moneyDataTable】はMoneyDataSet.Designer.cs(以下同じ)、MoneyDataSetクラスのプロパティ
public moneyDataTableDataTable moneyDataTable {
get {
return this.tablemoneyDataTable;
}
}
↓
【tablemoneyDataTable】MoneyDataSetクラスのフィールド
private moneyDataTableDataTable tablemoneyDataTable;
↓
【moneyDataTableDataTable】MoneyDataSetクラスのインナークラス
public partial class moneyDataTableDataTable : global::System.Data.TypedTableBase<moneyDataTableRow> { }
↑のインデクサー↓
public moneyDataTableRow this[int index] {
get {
return ((moneyDataTableRow)(this.Rows[index]));
}
}
A「MoneyDataSet.moneyDataTableRow」関連のコードの位置
【MoneyDataSet】MoneyDataSet.Designer.csの中のクラス
public partial class MoneyDataSet : global::System.Data.DataSet { }
↓
【moneyDataTableRow】↑のインナークラス
public partial class moneyDataTableRow : global::System.Data.DataRow { } 頼まれたんでVBAやってるんだけど
VBAでも普通に正規表現使えたのか
知らなかったわ
IEがらみのライブラリかなんか使うみたいだけど
C#で正規表現結構やったんで役に立った VBAに没頭
自分が作ったものじゃないんだけど、あるwebアプリがある
現在ブラウザに表示してる特定のデータをCSVで吐き出す機能を持っている
このCSVからデータをエクセルに取り込んで加工するということをやってる
ただ、
大したデータ量ではないのに、CSVを作らせるのに1分ぐらいかかったりして結構とろい
ローカルで作成するんじゃなくて、鯖に作らせているようだ
そこで、IEによってローカルに取込み済のHTMLを横取りして加工できないかと。
つまり、HTMLスクレイピングをVBAでできないかと思って
ボトルネックになりそうな部分をちょっと触ってみたんだけど、
意外といけそうなんだよなぁ
とりあえず、CSVを使うバージョンを作ってから
暇な時にHTMLスクレイピングやってみようかな メソッドたくさん作ってからどれ使うかあとから決めると頭が整理できていいな
以前はひとつのメソッドを作り込んでたんっだけど
これをやってしまうと、別のメソッドとして切り出した方が
いい機能まで突っ込んでしまって融通が利かなくなる
ちょっとした機能でもどんどん独立させて作る
同じ機能でも少し違うやつが考えられるなら、全部作ってみる
作ってからどれ使うか選びながら組み立てていく
あと、Functionを作ったら、すぐ下にテストのためのメソッドを作っておくと何かと便利 あと、複雑なロジックは紙にシャーペンで書く
絵や図を書き加えながら、コードを書いていく
消しゴムも必須
やっぱ深く考えるにはアナログが一番いい HTMLスクレイピングというかウェブスクレイピングをVBAでやってるんだけど
Dictionaryないかなと思って調べたら、一応使えるようだ
しかし、使ってみたら、ソートの機能すらない
仕方ないので自分で書いてたんだけど、どうもうまく動かない
この原因探しが大変だった
結論から言えば、forのカウンタの部分で最大値を指定するわけだけど
ここに同じforブロックのなかで変化してしまう変数を使ってしまっていた
繰り返し処理の途中で予期せず最大値が変化してしまい、エラーに繋がっていたのだ
VBAのDictionaryで使うソートの機能をネットで調べると、いろいろと洗練されたコードが出てくる
こぴぺの誘惑にかられたのだが、無骨なコードになってしまうとは言えども
この程度自分で書けないのも悔しくて、、あー疲れた 列挙体初めて実戦で使ってみた
これ便利だな
要はbool系は2つしか選択肢ないけど
これをいくつも増やした感じか
switchというかvbaだとselectなんだけど
これにも列挙体使えるし 正規表現をまともに使えるようになるとグンと幅が広がるわぁ 戻り値のないFunctionはSubとどう違うのだろうか ふぅ大体終わったぜ・・・・
今回のVBAプログラムは処理が重くて表示が固まるのが特徴のひとつだった
これをどうやって軽くしていくかが課題
もっと速くなると思うけど、一応固まらないようにはなった C#に戻るとするか・・・とか言いながらVBAに足引っ張られそうだ 昔書いたコードみると書き直したくなるわ
あまりに酷すぎる プログラミングばかりやってたらアジアカップ見るの忘れてたわ
次の試合から録画する
このタイミングで決勝が日韓戦だと妙な盛り上がりを見せそうだ! 凄くがんばってるなぁ・・・頭の下がる思いでみてる。 独習最初から読み直してる
コードかきかきしながら
おれぐらいすぐ忘れる人間はこうでもしないとな
>>473
ぐへへへへへ これからはクラスを作ったらドキュメンテーションコメントを使っていくことにする
///
これ 時間がなくてちょびっとずつしか独習読めないが
これでいいのだ いままでちゃんと覚えてこなかった基本的な事を読み返しながら覚えるようにしてる
例えば値型のサフィックスとか表になって紹介されてるようなやつ vbaでのIE操作に脱線してた
これ面白そうなの作れそうなんだよなぁ null条件演算子
null許容型
三項演算子
null合体演算子
? を使うこいつら if とか switch とかすごく基本的なもの復習した
基本とかいいつつ独習は細かい論点も触れるので忘れてるところもあった ん〜
いろいろ試しているがvbaからhtmlが取得できない いろんなもの作れるようになってきた
IEコントロールおもろいな
VBAだけど 今日もずっとVBAいじってた
リンククリックしてウィンドウを閉じたり開いたりしてると
いろいろエラーが出てくるんだよね
再現性の高いエラーならいんだけど、ネットと関わるエラーはそうじゃないものが多くてやっかい
とりあえずはエラーがでないようなのを作ってみたんだけど
時間を置いて何度も繰り返さないと確信もてない
なぜか時間を置いてからもう一度やるとエラーがおきてたんでその辺気をつけないといけない 新しいウィンドウをIEで開いた時なんかに
開ききる前にIEを捉えようとするとエラーが出る
見た目開いたように見えても読み込みが不十分だったりすることがあるから
IEを掌握する処理をループで回すのがキモとなるようだ CStr と Str の違い
Str()は、符号の1桁分が必ず確保されていて、
数値が正の時の戻り値の頭にはスペース(空白)が入ります。
Cstr()は、プラスのときにスペースは入りません。
これな・・・・これなんだよ
やっと原因見つけたわ windows10だとIE使えないと思ってたんだけど
機能追加でInternet Explorer11が使えるのか
知らなかったわ
ウェブアプリでIE使ってる場合はそのまま移行できるってことかな
その辺勉強してないからわからんが ふ〜む
EdgeをVBAから操作することもできるようだ
でもWebDriverとやらをダウンロード・インストールしないといけない
これができない環境だとやはり厳しいのかもしれない
https://www.ka-net.org/blog/?p=6018
WebDriverを使わない方法もある
https://www.ka-net.org/blog/?p=6033
しかし公式サポートないそうだ 最近VBAでプログラミングするときは
public宣言をできるだけ避けるようにしてる
Subプロシージャはメインとなるひとつだけにして
あとはFunctionを使う
戻り値が欲しいときFunctionの戻り値を使えば良いんだけど
使い勝手が悪いときは、Subプロシージャ内で変数宣言してから
これを参照渡しすることにしてる
昔は大量のPublic宣言された変数があって見通しが悪かったんだけど
この方法に変えてからマシになった感じがする
こういうやり方が正しいかどうかは知らないんだけど windowsのタイムスケジューラってなにが原因かよくわからんけど
いつのまにか無効になってて一日一回設定してあるプログラム起動が
実行しないようになっちゃうのな
設定変えていろいろやってるんだけど、もういっそのことC#で常時起動の何か作って
そこから実行するようにしようかな
常時起動のJavaプログラムもあるからそこからやってもいいんだけどせっかくだし 忙しくてなかなか再開できないわい
また秋ぐらいから再開しようかなぁ
その頃になれば時間ができるだろうから ■ このスレッドは過去ログ倉庫に格納されています