- 投稿日:2020-05-25T23:45:05+09:00
C# 勉強(3) ファイル入力2
テキストファイルを読み込んで、特定店舗の行のみ出力する。
Program.csusing System; using System.IO; using System.Text; namespace List094 { class Program { static void Main(string[] args) { // var filePath = @"C:\Example\Greeting"; string filePath = "Sales.txt"; string[] lines = File.ReadAllLines(filePath,Encoding.UTF8); foreach(var line in lines){ string[] items = line.Split(','); Sale sale =new Sale{ ShopName = items[0], Product =items[1], Amount =int.Parse(items[2]) }; if(sale.ShopName=="浅草店") Console.WriteLine("{0} {1} {2}",sale.ShopName,sale.Product,sale.Amount); } } } }クラス
Sales.csusing System; namespace List094 { public class Sale{ public String ShopName { get; set; } public String Product { get; set; } public int Amount { get; set; } } }
- 投稿日:2020-05-25T23:45:05+09:00
C# 勉強(4) ファイル入力2
テキストファイルを読み込んで、特定店舗の行のみ出力する。
:Program.cs using System; using System.IO; using System.Text; namespace List094 { class Program { static void Main(string[] args) { // var filePath = @"C:\Example\Greeting"; string filePath = "Sales.txt"; string[] lines = File.ReadAllLines(filePath,Encoding.UTF8); foreach(var line in lines){ string[] items = line.Split(','); Sale sale =new Sale{ ShopName = items[0], Product =items[1], Amount =int.Parse(items[2]) }; if(sale.ShopName=="浅草店") Console.WriteLine("{0} {1} {2}",sale.ShopName,sale.Product,sale.Amount); } } } }クラス
:Sales.cs using System; namespace List094 { public class Sale{ public String ShopName { get; set; } public String Product { get; set; } public int Amount { get; set; } } }
- 投稿日:2020-05-25T22:40:34+09:00
スマートデバイスプログラミング④(上からランダムに降ってくるボールを拾ってスコアを競うゲーム)
UnityとGameCanvasのダウンロード
1:UnityHubをDLしてインストール。(一部のwindows環境では拡張子を.exeに変更する必要があります)
2:UnityHubを起動して、Unity2019.3.11f1をインストール。
3:GameCanvasのダウンロード
https://github.com/sfc-sdp/GameCanvas-Unity/
でGameCanvasをダウンロード4:追加画像をダウンロードしてAssetsに追加
http://web.sfc.keio.ac.jp/~wadari/sdp/k04_res.zipGame.csの編集
Assets内のGame.csを別エディタで編集
using Sequence = System.Collections.IEnumerator; /// <summary> /// ゲームクラス。 /// 学生が編集すべきソースコードです。 /// </summary> public sealed class Game : GameBase { const int BALL_NUM = 30; int[] ball_x = new int [BALL_NUM]; int[] ball_y = new int [BALL_NUM]; int[] ball_col = new int [BALL_NUM]; int[] ball_speed = new int [BALL_NUM]; int ball_w = 24; int ball_h = 24; int player_x = 304; int player_y = 448; int player_speed = 3; int player_w = 32; int player_h = 32; int player_img = 4; int score = 0; int time = 1800; int player_col = 4; int combo = 0; /// <summary> /// 初期化処理 /// </summary> public override void InitGame() { gc.SetResolution(640, 480); for(int i =0 ; i < BALL_NUM ; i ++ ) { resetBall(i); } } /// <summary> /// 動きなどの更新処理 /// </summary> public override void UpdateGame() { time = time - 1; for(int i =0 ; i < BALL_NUM ; i ++ ) { ball_y[i] = ball_y[i] + ball_speed[i]; if(ball_y[i]> 480){ resetBall(i); } if(gc.CheckHitRect(ball_x[i],ball_y[i],ball_w,ball_h,player_x,player_y,player_w,player_h)){ if(time>=0){ score=score+ball_col[i];//ballごとに違った点数を加える if(player_col == ball_col[i]) { combo++; score+= combo; } else { combo = 0; } player_col = ball_col[i]; } resetBall(i); } } if(gc.GetPointerFrameCount(0) > 0 ){ if(gc.GetPointerX(0) > 320) { player_x += player_speed; player_img = 4; } else { player_x -= player_speed; player_img = 5; } } if(player_x<=0){ player_x=0; } if(player_x>=616){ player_x=616; } } /// <summary> /// 描画の処理 /// </summary> public override void DrawGame() { gc.ClearScreen(); for(int i =0 ; i < BALL_NUM ; i ++ ){ gc.DrawImage(ball_col[i],ball_x[i],ball_y[i]); } gc.SetColor(0,0,0); if(time>=0){ gc.DrawString("time:"+time,0,0); } else { gc.DrawString("finished!!",0,0); } gc.DrawString("score:"+score,0,24); gc.DrawString("combo:"+combo,0,48); //gc.DrawClipImage(player_img,player_x,player_y,0,64,32,32); if(time>=0){ int u = 32+ ((time%60)/30)*32;//0.5秒ごとにユーザー画像を切り替えx int v = (player_col - 1) *32;//最初4 色に応じてユーザーの色の画像を指定y gc.DrawClipImage(player_img,player_x,player_y, u,v,32,32); } else { gc.DrawClipImage(player_img,player_x,player_y, 96,(player_col - 1) *32,32,32); } } void resetBall(int id){ ball_x[id] = gc.Random(0,616); ball_y[id] = -gc.Random(24,480); ball_speed[id] = gc.Random(3,6); ball_col[id] = gc.Random(1,3); } }
- 投稿日:2020-05-25T22:37:44+09:00
スマートデバイスプログラミング③(制限時間内にボールを反射させてブロックを壊すゲーム)
UnityとGameCanvasのダウンロード
1:UnityHubをDLしてインストール。(一部のwindows環境では拡張子を.exeに変更する必要があります)
2:UnityHubを起動して、Unity2019.3.11f1をインストール。
3:GameCanvasのダウンロード
https://github.com/sfc-sdp/GameCanvas-Unity/
でGameCanvasをダウンロードGame.csの編集
Assets内のGame.csを別エディタで編集
using Sequence = System.Collections.IEnumerator; /// <summary> /// ゲームクラス。 /// 学生が編集すべきソースコードです。 /// </summary> public sealed class Game : GameBase { int ball_x; int ball_y; int ball_speed_x; int ball_speed_y; int player_x; int player_y; int player_w; int player_h; const int BLOCK_NUM = 50; int[] block_x = new int [BLOCK_NUM]; int[] block_y = new int [BLOCK_NUM]; bool[] block_alive_flag = new bool [BLOCK_NUM]; int block_w = 64; int block_h = 20; int time ; /// <summary> /// 初期化処理 /// </summary> public override void InitGame() { gc.SetResolution(640, 480); ball_x = 0; ball_y = 0; ball_speed_x = 3; ball_speed_y = 3; player_x = 270; player_y = 460; player_w = 100; player_h = 20; for(int i =0 ; i < BLOCK_NUM ; i ++ ) { block_x[i] = (i % 10 ) * block_w; block_y[i] = (i / 10 ) * block_h; block_alive_flag[i] = true; } time = 0; } /// <summary> /// 動きなどの更新処理 /// </summary> public override void UpdateGame() { if(countBlock()!=0){ time++; } player_y = gc.GetPointerY(0) - player_h/2; ball_x = ball_x + ball_speed_x; ball_y = ball_y + ball_speed_y; if( ball_x < 0 ) { ball_x = 0; ball_speed_x = -ball_speed_x; } if( ball_y < 0 ) { ball_y = 0; ball_speed_y = -ball_speed_y; } if( ball_x > 616 ) { ball_x = 616; ball_speed_x = -ball_speed_x; } if( ball_y > 456 ) { ball_y = 456; ball_speed_y = -ball_speed_y; } if(gc.GetPointerFrameCount(0) > 0 ){ player_x = gc.GetPointerX(0) - player_w/2; } if(gc.CheckHitRect(ball_x,ball_y,24,24,player_x,player_y,player_w,player_h)){ if(ball_speed_y>0){ ball_speed_y = -ball_speed_y; } } for(int i = 0; i<BLOCK_NUM; i++){ if(gc.CheckHitRect(ball_x,ball_y,24,24,block_x[i],block_y[i],block_w,block_h)){ block_alive_flag[i]=false; } } } /// <summary> /// 描画の処理 /// </summary> public override void DrawGame() { // 画面を白で塗りつぶします gc.ClearScreen(); // 0番の画像を描画します gc.DrawImage(0, 0, 0); gc.DrawImage(1,ball_x,ball_y); gc.SetColor(0, 0, 255); gc.FillRect(player_x,player_y,player_w,player_h); for(int i = 0; i<BLOCK_NUM; i++){ if(block_alive_flag[i]){ gc.FillRect(block_x[i],block_y[i],block_w,block_h); } } gc.DrawString("time:"+time,60,0); if(countBlock()==0){ gc.DrawString("clear",60,30); } } int countBlock(){ int num = 0; for(int i =0 ; i < BLOCK_NUM ; i ++ ){ if(block_alive_flag[i]){ num++; } } return num; } }
- 投稿日:2020-05-25T22:34:30+09:00
スマートデバイスプログラミング②(ランダムにカードを出して得点を競うゲーム)
UnityとGameCanvasのダウンロード
1:UnityHubをDLしてインストール。(一部のwindows環境では拡張子を.exeに変更する必要があります)
2:UnityHubを起動して、Unity2019.3.11f1をインストール。
3:GameCanvasのダウンロード
https://github.com/sfc-sdp/GameCanvas-Unity/
でGameCanvasをダウンロードGame.csの編集
Assets内のGame.csを別エディタで編集
using Sequence = System.Collections.IEnumerator; /// <summary> /// ゲームクラス。 /// 学生が編集すべきソースコードです。 /// </summary> public sealed class Game : GameBase { // 変数の宣言 int money; const int CARD_TYPE = 10; int[] card_count = new int [CARD_TYPE]; string[] card_name = {"A","B","C","D","E","F","G","H","I","J"}; bool isComplete; int new_card ; /// <summary> /// 初期化処理 /// </summary> ///起動時に一回だけ呼ばれる・・初期化設定用 public override void InitGame() { resetValue(); } /// <summary> /// 動きなどの更新処理 /// </summary> //1フレームごとに呼ばれる・動きの処理を入れる public override void UpdateGame() { //タップした時の処理 if (gc.GetPointerFrameCount(0)==1 && ! isComplete) { money -= 100; if (gc.Random(0,3)==0){ new_card = gc.Random(0,4); }else{ new_card = gc.Random(5,9); } card_count[new_card]++; for (int i = 0; i < 5; i++) { if (card_count[i] > 4){ isComplete = true; } } } //長押しした時の処理 if(gc.GetPointerFrameCount(0) >= 120){ resetValue(); } } /// <summary> /// 描画の処理 /// </summary> //1フレームごとに呼ばれる・描画の処理 public override void DrawGame() { gc.ClearScreen(); gc.SetColor(255,0,0); gc.SetFontSize(36); gc.DrawString("money:"+money,60, 40); if(new_card >= 0){ gc.DrawString("new:"+card_name[new_card],60, 80); } for(int i=0 ; i< CARD_TYPE ; i++){ gc.DrawString(card_name[i] + ":" + card_count[i],60, 120+i*80); } if(isComplete ){ gc.DrawString("complete!!",60, 920); } } void resetValue(){ money = 10000; for (int i = 0; i < CARD_TYPE; i++) { card_count[i] = 0; } isComplete = false; new_card = -1; } }
- 投稿日:2020-05-25T22:32:09+09:00
スマートデバイスプログラミング①(制限時間内にクリックした回数を競争するゲーム)
UnityとGameCanvasのダウンロード
1:UnityHubをDLしてインストール。(一部のwindows環境では拡張子を.exeに変更する必要があります)
2:UnityHubを起動して、Unity2019.3.11f1をインストール。
3:GameCanvasのダウンロード
https://github.com/sfc-sdp/GameCanvas-Unity/
でGameCanvasをダウンロードGame.csの編集
Assets内のGame.csを別エディタで編集
using Sequence = System.Collections.IEnumerator; /// <summary> /// ゲームクラス。 /// </summary> public sealed class Game : GameBase { // 変数の宣言 int sec = 0; int time = 600; int score = 0; /// <summary> /// 初期化処理 /// </summary> public override void InitGame() { // キャンバスの大きさを設定します gc.SetResolution(720, 1280); } /// <summary> /// 動きなどの更新処理 /// </summary> public override void UpdateGame() { // 起動からの経過時間を取得します sec = (int)gc.TimeSinceStartup; time = time - 1; if(gc.GetPointerFrameCount(0)==1){ if(time >= 0){ score = score + 1; } } if(gc.GetPointerDuration(0) >= 2.0f){ time =600; score =0; } } /// <summary> /// 描画の処理 /// </summary> public override void DrawGame() { // 画面を白で塗りつぶします gc.ClearScreen(); // 0番の画像を描画します gc.DrawImage(0, 0, 0); // 黒の文字を描画します gc.SetColor(0, 0, 0); gc.SetFontSize(48); gc.DrawString("この文字と青空の画像が", 40, 160); gc.DrawString("見えていれば成功です", 40, 270); gc.DrawRightString($"{sec}s", 630, 10); if(time >= 0 ){ gc.DrawString("time:"+time,60,0); } else { gc.DrawString("finished!!",60,0); } gc.DrawString("score:"+score,60,60); } }
- 投稿日:2020-05-25T20:54:27+09:00
自分が XAML 系プラットフォームのクラスを覚えるときにしてること
既に大体覚えてる人は対象外です。都度都度コントロールを調べて覚えてるんだけど、覚えることが多すぎて大変な人向け。
XAML 系プラットフォームのコントロールの特徴
継承を使って色々なコントロールが定義されています。
端的にいうと継承ツリーの中で重要なコントロールについて覚えておくと、他でも使いまわしが効いていいです。UWP & WPF
UWP と WPF はコントロールの構造が似ています。なので覚え方も大体同じです。大まかに全体に共通することと大きく 4 つのカテゴリーに分けて覚えます。
全体に大体共通
- Style でプロパティの値をまとめて設定できる
- 依存関係プロパティはバインド出来る
- Margin の設定とか幅と高さを持ってる
ContentControl
Content プロパティに設定したものをいい感じに表示してくれる。
見た目を整えるなら Content プロパティに何でもいいからオブジェクト突っ込んで ContentTemplate に DataTemplate を設定して見た目整える。以下のように色々なコントロールがこれに該当。
- ContentControl
- Window
- ToolTip
- Button
- CheckBox
- RadioButton
- ListBoxItem
- Label
ItemsControl
複数個のデータを表示するコントロール。ItemsSource プロパティにコレクションを設定(もしくはバインド)して、ItemTemplate で 1 項目ごとの見た目を DataTemplate で定義する。
因みにデータの 1 行単位でみると ContentControl です。
- ItemsControl
- ListBox
- TreeView
- Menu
厳密にはちょっと違うけど似た雰囲気として使えるものとして DataGrid がある。
これは 1 セル 1 セルが ContentControl みたいな感じで、編集モードとか表示モードとかあってちょっと複雑。こいつは真面目に覚えるしかない。Panel
Children プロパティに子要素を置いてレイアウトをいい感じにしてくれる。
- Grid: 行と列を定義して配置する。一番使う。何かレイアウトパネル覚えるならまずこれ。
- StackPanel: 縦並び、横並びが出来る。Grid 覚えたら次はこれでいいと思う
- Canvas: 絶対座標指定。そんなに使わないけど、便利なこともある。
- RelativePanel: UWP のみだけど便利。相対座標での配置もなれれば楽。レスポンシブデザインに対応するのが他の Panel より楽。
- DockPanel: WPF のみだったかな?(UWP の Toolkit に入ってたかも?) 聖杯レイアウトが出来る。アプリの大枠切るのに便利。
- WrapPanel: WPF のみ。UWP は Toolkit にある。横並び縦並びで端まで行くと折り返すレイアウト
その他
諦めて個別に覚えよう。
総括
ということで以下のクラスを重点的にリファレンスを見て、どんな機能が定義されるのかをまずみましょう。
- FrameworkElement
- Control
↑のクラスは、ほぼすべてのコントロールの先祖にあたるため、ここにあるものは他のコントロールで使えます。覚えて損なし。
次に以下の 2 つのコントロールの使い方を見てみよう。
- ContentControl
- ItemsControl
1 項目を表示するコントロールと、複数のデータを表示するコントロールのベースとなるクラス。
大体のコントロールは上の 2 つのどちらから継承してたりすることが多いので、この 2 つを覚えてると、どんなコントロールでも見た目を思い通りに定義しやすくなります。そして Grid コントロールでのレイアウトの仕方を覚えておけば思った通りの見た目は大体出来ると思います。
Xamarin.Forms
Xamarin.Forms は XAML だけど、UI コントロールが最終的に各プラットフォームのネイティブコントロールに直接マッピングされたりするに関係上かわかりませんが、ContentControl とかがなかったりして勝手が少し違います。
似たコントロールとして ContentView がありますが、そんなに継承しているクラスは多くない感じなので覚えても費用対効果がそんなに高くなかったりします。でも、ItemsControl と同じノリで使える感じのものは多いです。これは覚える価値あり。
- ItemsControl
- CollectionView
- TaggedPage: ItemsSource とは継承関係にないですが ItemsSource プロパティと ItemTemplate プロパティを持ってる
レイアウトパネルは UWP や WPF と大体同じですが StackPanel じゃなくて StackLayout だったり微妙に名前違うものがあってちょっと戸惑います。
あとは FlexLayout という Web の CSS Flexbox と同じノリでレイアウトを組めるやつがあるので、そこらへんもおさえておくといいかもです。まとめ
ということで、UI 系の部品の多くはオブジェクト指向の継承を使って作られてるものが XAML 系では多いので、継承のツリーを見て、肝となる機能を実装している親クラスを見つけて、そいつの機能を抑えることで、そのクラスを継承しているクラスの使い方のほとんどを押さえることが出来るので、費用対効果が高くてオススメです。
- 投稿日:2020-05-25T18:08:37+09:00
[C#] awaitを含むコードの排他制御
この投稿の前提情報
await
を含むということはほとんどの場合時間的に長い処理であり、この間ずっと他のスレッドをブロックしっぱなしというものは決して褒められたコードではない。
できるのであれば、その非同期メソッド内の同期処理部分の必要な部分だけをlock
するのが最善である。
ここに記載されている情報は、嬉々として採用するべきものではなく、
- 非同期部分を同時に呼び出してしまうと困ったことがおきる
- 非同期メソッド呼び出し前後でデータの整合性を保つ必要がある
といった状況化で仕方なく、しぶしぶ採用するべきものになる。
lock
ブロック内ではawait
できない通常、排他制御をおこなう場合、
lock
ステートメントを使用する。readonly object LockHandler = new object(); void Hoge() { lock (LockHandler) { DoSomething(); } }しかし、この
lock
ブロック内にawait
が含まれるコードはコンパイルエラーが発生する。
というのも、ロックを解放するのはそれを獲得したスレッドでなければならず、await
演算子の前と後ろは違うスレッドで実行される可能性があるためだ。// Compile Error lock (LockHandler) { await DoSomethingAsync(); }
SempahoreSlim
を使うそこで、ロックの獲得と解放を異なるスレッドで行うことのできる仕掛けが必要になる。
これに適したクラスがSemaphoreSlim
であり、これでawait
を含むコードの排他処理ができるようになる。readonly SemaphoreSlim Semaphore = new SemaphoreSlim(1, 1); public async Task AsynchronousMethodAsync() { await Semaphore.WaitAsync(); try { await DoSomethingAsync(); } finally { Semaphore.Release(); } }もし、同じクラスの同期処理部分でも排他制御をおこないたければ、同じ
SemaphoreSlim
オブジェクトのWait
メソッドを使用すればよい。public void SynchronousMethod() { Semaphore.Wait(); try { DoSomething(); } finally { Semaphore.Release(); } }
SemaphoreSlim
の注意点
SemaphoreSlim
を使用している場合、lock
ステートメントと異なり、すでに獲得しているロックを再取得できない。
以下のコードは1を出力して、そのまま停止する。2には到達しない。readonly SemaphoreSlim Semaphore = new SemaphoreSlim(1, 1); public async Task AsynchronousMethodAsync() { await Semaphore.WaitAsync(); try { Console.WriteLine("1"); // 1 await Semaphore.WaitAsync(); Console.WriteLine("2"); // 2 } finally { Semaphore.Release(); } }これが意味するのは、ある
SemaphoreSlim
オブジェクトでガードされているメソッドから、同じオブジェクトでガードされているメソッドを呼び出せないということだ。
以下のAsynchronousMethodAsync()
は、SynchronousMethod()
のSemaphore.Wait()
で処理が停止し、それ以上処理が継続されない。public async Task AsynchronousMethodAsync() { await Semaphore.WaitAsync(); try { SynchronousMethod(); await ... } finally { Semaphore.Release(); } } public void SynchronousMethod() { Semaphore.Wait(); ... }
IDisposable
を実装するべきか
SemaphoreSlim
はIDisposable
を実装している。そして、この型をインスタンスフィールドに含むクラスは一般論で言えばIDisposable
を実装し、そこでSepamhoreSlim
オブジェクトをDispose
するべきだということになる。
つまり、排他制御しているオブジェクトを使いおわったらDispose()
する必要がある。lock
ステートメントで済んでいたクラスと比べて、とても面倒だ。安心して欲しい。
SemaphoreSlim
のソースを追えばわかることだが、AvailableWaitHandle
プロパティを使用しない場合、そのDispose()
は内部の変数にnullを代入する以外の仕事はおこなわれない。
そして、今回の要件ではこのプロパティは不要なので、SemaphoreSlim
オブジェクトをDispose
しなくても実質的な問題は生じない。もちろん未来永劫そうであると保証があるわけではない。
どうするべきかという問いには、「IDisposable
を実装するべきだ」と回答するしかない。
だから、コードレビューで原理主義者に追求を受けそうだというのなら、IDisposable
を実装することになるだろう。現実的なところでは、
SemaphoreSlim
のラッパークラスを作り、AvailableWaitHandle
を呼び出せなくしてしまうという手がある。
うるさいコード分析ツールを黙らせるのも楽になるだろう。コンストラクタの引数(1, 1)
を隠蔽できるようになるのもいい。
AvailableWaitHandle
のないSemaphoreSlim
を再実装してしまう方法もある。
何かまちがっている気もするが、こわいコードレビュアーをかかえたチームでは歓迎されるのではないだろうか。
AsyncLock
とその注意点https://www.atmarkit.co.jp/ait/articles/1411/18/news135.html にあるように、
SemaphoreSlim
そのものを使うのではなく、それを使いやすくしたクラスを使用するという方法がある。readonly AsyncLock LockHandler = new AsyncLock(); public async Task AsynchronousMethodAsync() { using (await LockHandler.LockAsync()) { await DoSomethingAsync(); } }簡潔で美しい方法だが、
await
を忘れusing (_LockHandler.LockAsync())
としてしまうと、ロックされている場合に実行中のTask
をDispose
しようとして例外を投げるコードができあがってしまう。
困ったことに、非同期メソッドの呼び出しがawait
されてないという警告も出力されない。若干一名の尊い犠牲のもとになりたっている情報なので、もし採用するのなら注意してほしい。
犠牲担当の意見としては、二度と利用する気はない。参考にした情報
非同期:awaitを含むコードをロックするには?(SemaphoreSlim編)[C#、VB]
https://www.atmarkit.co.jp/ait/articles/1411/11/news117.html非同期:awaitを含むコードをロックするには?(AsyncLock編)[C#、VB]
https://www.atmarkit.co.jp/ait/articles/1411/18/news135.html
SemaphoreSlim
のソース
https://source.dot.net/#System.Private.CoreLib/SemaphoreSlim.cs
- 投稿日:2020-05-25T16:29:27+09:00
【.NetCore】ViewComponentとAjaxでSPA化する【jQuery】
ViewComponentとAjaxでSPAっぽい挙動を作る
ちょっとしたアクションで画面遷移すると、画面がチラチラして鬱陶しいという要望があった。
とりあえず、あるモノでサクッと直してみる。Core2.1のサンプルプロジェクトをSPAっぽくしてみる
View直下にComponentsというフォルダを作成し、その中にViewファイルを移動させる。
※Home.cshtmlはindex.cshtmlをコピーして名前を変えただけ
index.htmlの最初と最後に書き換え用のdivタグ追加(updateView)
Index.html<div id="updateView"> <div class="row"> <div class="col-md-3"> --省略-- </div> </div> </div>コントローラーにViewComponentとGetアクションを追加していく
HomeController.cspublic class HomeController : Controller { public IActionResult Index() { return View(); } [HttpGet] public IActionResult About() { return ViewComponent("AboutView"); } [HttpGet] public IActionResult Contact() { return ViewComponent("ContactView"); } [HttpGet] public IActionResult Privacy() { return ViewComponent("PrivacyView"); } [HttpGet] public IActionResult Home() { return ViewComponent("HomeView"); } } [ViewComponent(Name = "AboutView")] public class AboutView : Microsoft.AspNetCore.Mvc.ViewComponent { public IViewComponentResult Invoke() { return View("Views/Home/Components/About.cshtml"); } } [ViewComponent(Name = "ContactView")] public class ContactView : Microsoft.AspNetCore.Mvc.ViewComponent { public IViewComponentResult Invoke() { return View("Views/Home/Components/Contact.cshtml"); } } [ViewComponent(Name = "PrivacyView")] public class PrivacyView : Microsoft.AspNetCore.Mvc.ViewComponent { public IViewComponentResult Invoke() { return View("Views/Home/Components/Privacy.cshtml"); } } [ViewComponent(Name = "HomeView")] public class HomeView : Microsoft.AspNetCore.Mvc.ViewComponent { public IViewComponentResult Invoke() { return View("Views/Home/Components/Home.cshtml"); } }Layoutファイルのlistの部分を少し改造する。
移動しないのでhref消して、代わりに振り分けでつかうIDを付けます。_Layout.cshtml<ul class="nav navbar-nav"> <li><a id="Home">Home</a></li> <li><a id="About">About</a></li> <li><a id="Contact">Contact</a></li> <li><a id="Privacy">Privacy</a></li> </ul>html取得用のjsを追加
_Layout.cshtml$('a').click(function () { var action = $(this).attr("id"); $.get('/Home/' + action) .done(function (data) { $("#updateView").fadeOut(500, function fadeOut() { $("#updateView").html(data); $("#updateView").fadeIn(500); }); }); });fadeさせてるので、いい感じに画面がヌルっと切り替わるはずです。
- 投稿日:2020-05-25T11:38:23+09:00
Unityど素人の奮闘記
Unityでドミノ倒し作ってみた.
今回はUnityの物理エンジンを使ってドミノ倒しを作ってみました.
ただ同じ大きさのドミノを倒しても面白くないと思いn番煎じながら1.5倍の大きさのドミノを倒していくものにしました.
参考:https://estorypost.com/social-network/youtube/domino-can-topple-1-5-times-its-size/
もはやリアルで示されているものをわざわざやる必要もないのですが笑Step1 地面をつくる
Createから[3D Object]の[Plane]もしくは[Terrain]を選択します.
今回は自分自身の復習の意味もあってTerrainを選択しました.
Terrainを使う際の注意はTerrainの中央がシーンビューの中央とずれていることです.
一辺が500の正方形なのでtransformでPositionをx=-250,y=0,z=-250にしておきましょう.Step2 自然をつくる
AssetStoreからStandardAssetsをインストールしてください.
驚くほど簡単に山や木,湖がつくれてびっくりします.
今回は一応全てインポートしておきましょう.
山や木を作ることは非常に簡単でTerrainから[Raise or Lower]を選択して左クリックで山ができます.
山をへこませたい時はシフトキーを押しながら左クリックをすればいいです.
木も同様に木のマークをクリックしインポートしたStandardAssetsの素材をEditから加えてタッチすれば出来ます(適当)
山の表面,地面の表面の素材も同様にもはやドラッグするだけです.Step3 ドミノ作成
ドミノの作成で注意することは一つだけです.
それは各辺の長さをr倍した時,体積はr^3倍となることです.そのことにだけ注意すれば問題ないと思います.
あとはAdd ComponentでRigidBodyを追加してあげましょう.
Cubeからドミノを作成した場合,Box Colliderは最初から追加されています.
あとは一番小さなドミノを傾けて...
と言いたいところですがまだ何もコードを書いていないので書きましょう.
すべてのドミノが倒れた時に歓声が起こるコードを書きます.Step4 歓声作成
上のようなソースコードでpublic変数としてAudioを追加できるようにしました.
Audioは効果音ラボさんからダウンロードさせていただきました.
効果音ラボ(https://soundeffect-lab.info/)
あとはCreateから[CreateEmpty]を選択し,[Timekeep]と名前を変更しました.
オブジェクトに先ほどのTimekeepスクリプトを追加し,音声も追加しました.
完成!
これからも勉強していきます...
- 投稿日:2020-05-25T10:19:37+09:00
【Unity】非同期処理を理解する〜async/await編〜
async/await とは
- C# の機能として用意されている
- C# 5.0の新機能
- Unity 2018 から C# 6.0 に対応した(それまでは C# 4.0 でした。)
- Task クラスに対して使うものという軽い認識(本当は
INotifyCompletion
インターフェースのIsCompleted
とGetResult
)- Unity で
.NET 4.x
を選択すれば使用可能- async = asynchronous = 非同期
- await を使うメソッドにつけなければならないキーワード
- async をつけただけでは普通のメソッドと挙動は変わらない
- await
- 非同期メソッドを呼び出し、完了するまで実行を中断するキーワード
- 戻り値を取得できる(コルーチンは無理)
- 並列処理だけど、普通のメソッドの呼び出しと同じようにかける = 可読性が上がる
基本的な構文は、
async 戻り値の型 メソッド名(引数) { await ~~~~ }戻り値は、基本的に
Task / Task<T>
型と考えて問題ないかと思います。
(といいながら、サンプルはvoid
で書いたりしています…)機能
Task.Delay(Int32 millisecondsDelay)
引数で指定されたミリ秒待機します。
using System.Threading.Tasks; using UnityEngine; public class Test : MonoBehaviour { void Start() { AsyncSample1(); } async void AsyncSample1() { Debug.Log("AsyncSample1 Start."); await Task.Delay(1000); Debug.Log("AsyncSample1 End."); } }今回のサンプルでは、1000ミリ秒 = 1秒 待機するようにしたので、実行結果は以下のようになります。
ContinueWith
直列処理できます。
非同期処理のメソッドも、ContinueWith
でつなぐことで、直列化することができます。ちなみに
ContinueWith
を使わずに、普通に並列的に処理にしたければ、下のようになイメージになります。using System.Threading.Tasks; using UnityEngine; public class Test : MonoBehaviour { void Start() { AsyncSample1(); AsyncSample2(); } async void AsyncSample1() { Debug.Log("AsyncSample1 Start."); await Task.Delay(1000); Debug.Log("AsyncSample1 End."); } async void AsyncSample2() { Debug.Log("AsyncSample2 Start."); await Task.Delay(1000); Debug.Log("AsyncSample2 End."); } }並列なので、
1.AsyncSample1 Start.
2.AsyncSample2 Start.
3.AsyncSample1 End.
4.AsyncSample2 End.
という結果になります。
ContinueWith
を使って直列処理(AsyncSample1 を実行完了後、 AsyncSample2 を実行)するサンプルは、using System.Threading.Tasks; using UnityEngine; public class Test : MonoBehaviour { void Start() { AsyncSample1().ContinueWith(_ => AsyncSample2()); } async Task AsyncSample1() { Debug.Log("AsyncSample1 Start."); await Task.Delay(1000); Debug.Log("AsyncSample1 End."); } async Task AsyncSample2() { Debug.Log("AsyncSample2 Start."); await Task.Delay(1000); Debug.Log("AsyncSample2 End."); } }注意点は、メソッドの返り値の型が Task になっていることくらいです。
AsyncSample1 Start.
AsyncSample1 End.
AsyncSample2 Start.
AsyncSample2 End.
という結果になります。Task.Run()
引数として与えられた処理を別スレッドで実行します。
用途として、重い処理を非同期にしたいときに使います。ちょっと良い例が思いつかないので、超簡単なサンプルだけ書きます。
Task.Run(() => Debug.Log("重い処理..."));Task.WhenAll()
指定された Task が全て完了してから Task を実行することができます。
using System.Threading.Tasks; using UnityEngine; public class Test : MonoBehaviour { async void Start() { await Task.WhenAll(AsyncSample1(), AsyncSample2()); Debug.Log("All Completed."); } async Task AsyncSample1() { Debug.Log("AsyncSample1 Start."); await Task.Delay(1000); Debug.Log("AsyncSample1 End."); } async Task AsyncSample2() { Debug.Log("AsyncSample2 Start."); await Task.Delay(1000); Debug.Log("AsyncSample2 End."); } }
AsyncSample1()
,AsyncSample2()
の実行が完了したら、All Completed.
を出力するようなサンプルです。
実行結果は、
Task<T>
戻り値が欲しい場合は
Task<T>
を使います。using System.Threading.Tasks; using UnityEngine; public class Test : MonoBehaviour { async void Start() { var str = await AsyncSample1(); Debug.Log(str); } async Task<string> AsyncSample1() { Debug.Log("AsyncSample1 Start."); await Task.Delay(1000); return "AsyncSample1 End."; } }
AsyncSample1()
の結果が返ってくるまで、Debug.Log
されると困るので、await
する必要があります。
(待ち受けするイメージです。)
結果は、
終わりに
Unity 初心者なので、間違いがあったら教えてくれるとありがたいです。
Unity で Task 使うなら、 UniTask 使おう。(理由はいまいち知らない…)
理由は UniTask の記事を書くときに、調べたいと思います。参考文献
https://docs.microsoft.com/ja-jp/dotnet/api/system.threading.tasks.task?view=netcore-3.1
https://torikasyu.com/?p=1554
https://docs.microsoft.com/ja-jp/dotnet/api/system.threading.tasks.task.whenall?view=netcore-3.1
https://www.slideshare.net/UnityTechnologiesJapan/unite-tokyo-2018asyncawait
- 投稿日:2020-05-25T08:21:10+09:00
C#でMarkdownを表示するライブラリMarkDigの紹介
概要
MarkDigはC#でMarkdownを表示できるライブラリです。
同様の機能を持つ、Marked.NET、MarkdownSharpよりも高速で動くらしいです。Webアプリで使う場合はHTMLに、WPFで使う場合は専用ControlかRichTextBoxに変換して表示できます。
HTMLに変換する場合
Markdown文字列をHTMLに変換するのは
Markdig.Markdown.ToHtml(元Markdown文字列)
とするだけです。以下のコードでは加えて、入力文字列をリソースファイルから取得して、生成したHTMLをファイルへ出力、ソースコードを見やすくするMarkdig.SyntaxHighlighting拡張を導入しました。
private void CreateHtml() { var pipeline = new MarkdownPipelineBuilder() .UseAdvancedExtensions() .UseSyntaxHighlighting() .Build(); string sourceText = Properties.Resources.MarkDownText; string markdownText = Markdig.Markdown.ToHtml(sourceText, pipeline); const string ouputPath = "result.html"; File.WriteAllText(ouputPath, markdownText); }出力結果は以下を参照してください。
出力結果HTMLファイルWPFで表示する場合
WPFで表示する場合は
FlowDocument
に変換してRichTextBox
に入力してもよいですが、Markdig-WPF拡張を導入してMarkDownViewer
を使用するほうが手軽です。
<markdig:MarkdownViewer Markdown="元Markdown文字列" />
で表示できます。以下のコードでは加えて、初期入力文字列をリソースファイルから取得して、TextBoxに指定。TextBoxのTextを変更するとMarkdownViewerに反映されるようにしました。またHyperLinkをクリック時にブラウザで開く処理も追加しました。
MainWindow.xaml<Window x:Class="MarkDigTest.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:markdig="clr-namespace:Markdig.Wpf;assembly=Markdig.Wpf" xmlns:properties="clr-namespace:MarkDigTest.Properties" Title="MainWindow" Width="800" Height="650"> <FrameworkElement.CommandBindings> <CommandBinding Command="{x:Static markdig:Commands.Hyperlink}" Executed="OpenHyperlink" /> </FrameworkElement.CommandBindings> <UniformGrid Columns="2"> <TextBox x:Name="sourceText" AcceptsReturn="True" Text="{x:Static properties:Resources.MarkDownText}" /> <markdig:MarkdownViewer x:Name="Viewer" Markdown="{Binding Text, ElementName=sourceText}" /> </UniformGrid> </Window>MainWindow.cspublic partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void OpenHyperlink(object sender, ExecutedRoutedEventArgs e) { Process.Start(new ProcessStartInfo("cmd", $"/c start {e.Parameter}") { CreateNoWindow = true }); } }動作時のイメージは以下です。
左のTextBoxに入力すると、右のMarkdownViewerに反映されます。全体コード
WPF版の全体コードを以下に置いておきます。
https://github.com/soi013/MarkDigTest/tree/master環境
VisualStudio 2019
C# 8c
.NET Core 3.1MarkDigTest.csproj<ItemGroup> <PackageReference Include="Markdig" Version="0.18.0" /> <PackageReference Include="Markdig.Wpf" Version="0.3.1" /> </ItemGroup>参考
https://github.com/lunet-io/markdig
Markdownのサンプルは一部以下のリンクを元にしています。
https://qiita.com/tbpgr/items/989c6badefff69377da7HTMLファイルを表示するためにGitHub.Pagesを使いました。
https://qiita.com/Yuki_Yamashina/items/5d8208c450195b65344c
- 投稿日:2020-05-25T07:34:41+09:00
C# 勉強(3) ファイル入力
ファイルを入力し店舗別売上を集計する。
1、プログラム
:Program.cs using System; using System.Collections.Generic; using System.IO; namespace Sec0225 { class Program { static void Main(string[] args) { SalesCounter sales = new SalesCounter(ReadSales("sales.csv")); Dictionary<string,int> amountPerStore = sales.GetPerStoreSales(); foreach(KeyValuePair<string,int>obj in amountPerStore){ Console.WriteLine("{0}{1}",obj.Key,obj.Value); } } static List<Sale>ReadSales(string filePath){ List<Sale>sales = new List<Sale>(); string[] lines = File.ReadAllLines(filePath); //テキストファイル読む foreach(string line in lines){ //1行づつ処理 string[]items = line.Split(','); //行を分解 Sale sale = new Sale{ //分解したデータからオブジェクト作成 ShopName = items[0], ProductCategory = items[1], Amount = int.Parse(items[2]) }; sales.Add(sale); //オブジェクトをリストに追加 } return sales; //結果を返す } } }2.クラス
:Sale.cs using System; namespace Sec0225{ public class Sale{ public string ShopName { get; set; } public string ProductCategory { get; set; } public int Amount { get; set; } } }:SalesCounter.cs using System; using System.Collections.Generic; namespace Sec0225{ public class SalesCounter{ private List<Sale> _sales; public SalesCounter(List<Sale>sales){ _sales = sales; } public Dictionary<string,int> GetPerStoreSales(){ Dictionary<string,int> dict = new Dictionary<string, int>(); foreach(Sale sale in _sales){ if(dict.ContainsKey(sale.ShopName)) dict[sale.ShopName]+=sale.Amount; else dict[sale.ShopName]=sale.Amount; } return dict; } } }
- 投稿日:2020-05-25T07:30:46+09:00
Excel, Word, PowerPointで開いているファイルを取得して一覧表示する
Reloadボタンで再読み込みします。
ListViewのアイテムをダブルクリックすると、そのフォルダを開きます。コンパイル方法
compile.batcsc ^ /r:C:\Windows\assembly\GAC_MSIL\Microsoft.Office.Interop.Excel\15.0.0.0__71e9bce111e9429c\Microsoft.Office.Interop.Excel.dll ^ /r:C:\Windows\assembly\GAC_MSIL\Microsoft.Office.Interop.Word\15.0.0.0__71e9bce111e9429c\Microsoft.Office.Interop.Word.dll ^ /r:C:\Windows\assembly\GAC_MSIL\Microsoft.Office.Interop.PowerPoint\15.0.0.0__71e9bce111e9429c\Microsoft.Office.Interop.PowerPoint.dll ^ /r:C:\Windows\assembly\GAC_MSIL\office\15.0.0.0__71e9bce111e9429c\Office.dll ^ %*
compile.bat GetDirOfOfficeOpening.cs
を実行するとコンパイルされます。(dllの場所は環境により異なる可能性があります。)ソース
機能拡張しようとして作成中の部分がコメントアウトで残っているのはご容赦ください。
GetDirOfOfficeOpening.csusing System; using System.Drawing; using System.IO; //using System.Collections.Generic; //using System.Reflection; using System.Runtime.CompilerServices; // to use [MethodImpl(MethodImplOptions.NoInlining)] using System.Runtime.InteropServices; using System.Text.RegularExpressions; using System.Windows.Forms; using Excel = Microsoft.Office.Interop.Excel; using Word = Microsoft.Office.Interop.Word; using PowerPoint = Microsoft.Office.Interop.PowerPoint; //using Microsoft.Office.Core; class GetDirOfOfficeOpening : Form { // column index of lsvFiles //readonly int CI_FileName = 1; //readonly int CI_Path = 2; ListView lsvFiles; Button btnReload; //Button btnExportAllLinks; void ReloadList() { lsvFiles.BeginUpdate(); lsvFiles.Items.Clear(); try { AppendExcelFileList(); AppendWordFileList(); AppendPowerPointFileList(); } finally { lsvFiles.EndUpdate(); } // GC.Collect(); GC.WaitForPendingFinalizers(); } [MethodImpl(MethodImplOptions.NoInlining)] void AppendExcelFileList() { Excel.Application oExcelApp; try { oExcelApp = (Excel.Application)Marshal.GetActiveObject("Excel.Application"); } catch(COMException) { return; // Excelが起動していない、もしくは不明なエラー } Excel.Workbooks oWorkBooks = (Excel.Workbooks)oExcelApp.Workbooks; foreach ( Excel.Workbook book in oWorkBooks ) { string t = book.FullName; if ( t.IndexOf("/") >= 0 || t.IndexOf("\\") >= 0 ) { // pathあり lsvFiles.Items.Add(MakeLsvItem("Excel", Path.GetFileName(t), Path.GetDirectoryName(t))); } else { lsvFiles.Items.Add(MakeLsvItem("Excel", t, "")); } } } [MethodImpl(MethodImplOptions.NoInlining)] void AppendWordFileList() { Word.Application oWordApp; try { oWordApp = (Word.Application)Marshal.GetActiveObject("Word.Application"); } catch(COMException) { return; // Wordが起動していない、もしくは不明なエラー } Word.Documents oWordDocs = (Word.Documents)oWordApp.Documents; foreach ( Word.Document doc in oWordDocs ) { string t = doc.FullName; if ( t.IndexOf("/") >= 0 || t.IndexOf("\\") >= 0 ) { // pathあり lsvFiles.Items.Add(MakeLsvItem("Word", Path.GetFileName(t), Path.GetDirectoryName(t))); } else { lsvFiles.Items.Add(MakeLsvItem("Word", t, "")); } } } [MethodImpl(MethodImplOptions.NoInlining)] void AppendPowerPointFileList() { PowerPoint.Application oPptApp; try { oPptApp = (PowerPoint.Application)Marshal.GetActiveObject("PowerPoint.Application"); } catch(COMException) { return; // PowerPointが起動していない、もしくは不明なエラー } PowerPoint.Presentations oPptPresentations = (PowerPoint.Presentations)oPptApp.Presentations; foreach ( PowerPoint.Presentation presen in oPptPresentations ) { string t = presen.FullName; if ( t.IndexOf("/") >= 0 || t.IndexOf("\\") >= 0 ) { // pathあり lsvFiles.Items.Add(MakeLsvItem("PowerPoint", Path.GetFileName(t), Path.GetDirectoryName(t))); } else { lsvFiles.Items.Add(MakeLsvItem("PowerPoint", t, "")); } } } /* [MethodImpl(MethodImplOptions.NoInlining)] void CreateShortcut(string lnkDest, string saveDest, ref dynamic shell) { // WshShellを作成 if (shell == null) { var type = Type.GetTypeFromProgID("WScript.Shell"); shell = Activator.CreateInstance(type); } // IWshRuntimeLibrary.WshShell shell = new IWshRuntimeLibrary.WshShell(); dynamic shortcut = shell.CreateShortcut(saveDest); // リンク先 shortcut.TargetPath = lnkDest; // shortcut.Arguments = "/a /b /c"; // shortcut.WorkingDirectory = Application.StartupPath; // 実行時の大きさ 1が通常、3が最大化、7が最小化 shortcut.WindowStyle = 1; // shortcut.Description = "xxx"; // shortcut.IconLocation = Application.ExecutablePath + ",0"; // ショートカットを作成 shortcut.Save(); } [MethodImpl(MethodImplOptions.NoInlining)] void ExportAllLinks(string saveDestPath) { dynamic shell = null; foreach (ListViewItem item in lsvFiles.Items) { string basePath = item.SubItem[CI_Path].Text; if (basePath != "") { string fileName = item.SubItem[CI_FileName].Text; string lnkDest = Path.Combine(basePath, fileName); string saveDest = Path.Combine(saveDestPath, fileName+".lnk"); CreateShortcut(lnkDest, saveDest, ref shell); } } } void ExportAllLinksWithDialog() { //FolderBrowserDialogクラスのインスタンスを作成 FolderBrowserDialog fbd = new FolderBrowserDialog(); //上部に表示する説明テキストを指定する fbd.Description = "フォルダを指定してください。"; //ルートフォルダを指定する //デフォルトでDesktop fbd.RootFolder = Environment.SpecialFolder.Desktop; //最初に選択するフォルダを指定する //RootFolder以下にあるフォルダである必要がある fbd.SelectedPath = Environment.SpecialFolder.Desktop; // @"C:\Windows"; //ユーザーが新しいフォルダを作成できるようにする //デフォルトでTrue fbd.ShowNewFolderButton = true; //ダイアログを表示する if (fbd.ShowDialog(this) == DialogResult.OK) { ExportAllLinks(fbd.SelectedPath); } } */ ListViewItem MakeLsvItem(string appName, string fileName, string path) { return new ListViewItem(new string[]{appName, fileName, path}); } GetDirOfOfficeOpening() { Controls.Add(btnReload = new Button(){ Text = "Reload", Location = new Point(0, 0), Width = 100 }); btnReload.Click += (s,e)=>{ReloadList();}; /* Controls.Add(btnExportAllLinks = new Button(){ Text = "Reload", Location = new Point(0, 0), Width = 100 }); btnExportAllLinks.Click += (s,e)=>{ExportAllLinksWithDialog();}; */ Controls.Add(lsvFiles = new ListView(){ View = View.Details, FullRowSelect = true, HideSelection = false, MultiSelect = false, GridLines = true, Location = new Point(0,30), Size = new Size(100,100) }); lsvFiles.Columns.Add("Application", 80, HorizontalAlignment.Left); lsvFiles.Columns.Add("FileName", 100, HorizontalAlignment.Left); lsvFiles.Columns.Add("Path", 400, HorizontalAlignment.Left); lsvFiles.MouseDoubleClick += lsvFiles_MouseDoubleClick; ClientSize = new Size(600 ,300); Load += (s,e)=>{MyResize();ReloadList();}; Resize += (s,e)=>{MyResize();}; ResizeEnd += (s,e)=>{MyResize();}; } void MyResize() { int h = ClientSize.Height - lsvFiles.Top; lsvFiles.Size = new Size(ClientSize.Width, (h<50)?50:h); } void lsvFiles_MouseDoubleClick(object sender, MouseEventArgs e) { ListViewHitTestInfo info = lsvFiles.HitTest(e.Location); if ( info.SubItem != null && info.SubItem.Text != "" ) { System.Diagnostics.Process.Start(info.SubItem.Text); } } [STAThread] static void Main(string[] args) { Application.Run(new GetDirOfOfficeOpening()); } }
- 投稿日:2020-05-25T07:30:46+09:00
Excel, Word, PowerPointで開いているファイルのフルパスを取得して一覧表示・ジャンプできるようにする
Reloadボタンで再読み込みします。
ListViewのアイテムをダブルクリックすると、そのフォルダを開きます。コンパイル方法
compile.batcsc ^ /r:C:\Windows\assembly\GAC_MSIL\Microsoft.Office.Interop.Excel\15.0.0.0__71e9bce111e9429c\Microsoft.Office.Interop.Excel.dll ^ /r:C:\Windows\assembly\GAC_MSIL\Microsoft.Office.Interop.Word\15.0.0.0__71e9bce111e9429c\Microsoft.Office.Interop.Word.dll ^ /r:C:\Windows\assembly\GAC_MSIL\Microsoft.Office.Interop.PowerPoint\15.0.0.0__71e9bce111e9429c\Microsoft.Office.Interop.PowerPoint.dll ^ /r:C:\Windows\assembly\GAC_MSIL\office\15.0.0.0__71e9bce111e9429c\Office.dll ^ %*
compile.bat GetDirOfOfficeOpening.cs
を実行するとコンパイルされます。(dllの場所は環境により異なる可能性があります。)ソース
機能拡張しようとして作成中の部分がコメントアウトで残っているのはご容赦ください。
GetDirOfOfficeOpening.csusing System; using System.Drawing; using System.IO; //using System.Collections.Generic; //using System.Reflection; using System.Runtime.CompilerServices; // to use [MethodImpl(MethodImplOptions.NoInlining)] using System.Runtime.InteropServices; using System.Text.RegularExpressions; using System.Windows.Forms; using Excel = Microsoft.Office.Interop.Excel; using Word = Microsoft.Office.Interop.Word; using PowerPoint = Microsoft.Office.Interop.PowerPoint; //using Microsoft.Office.Core; class GetDirOfOfficeOpening : Form { // column index of lsvFiles //readonly int CI_FileName = 1; //readonly int CI_Path = 2; ListView lsvFiles; Button btnReload; //Button btnExportAllLinks; void ReloadList() { lsvFiles.BeginUpdate(); lsvFiles.Items.Clear(); try { AppendExcelFileList(); AppendWordFileList(); AppendPowerPointFileList(); } finally { lsvFiles.EndUpdate(); } // GC.Collect(); GC.WaitForPendingFinalizers(); } [MethodImpl(MethodImplOptions.NoInlining)] void AppendExcelFileList() { Excel.Application oExcelApp; try { oExcelApp = (Excel.Application)Marshal.GetActiveObject("Excel.Application"); } catch(COMException) { return; // Excelが起動していない、もしくは不明なエラー } Excel.Workbooks oWorkBooks = (Excel.Workbooks)oExcelApp.Workbooks; foreach ( Excel.Workbook book in oWorkBooks ) { string t = book.FullName; if ( t.IndexOf("/") >= 0 || t.IndexOf("\\") >= 0 ) { // pathあり lsvFiles.Items.Add(MakeLsvItem("Excel", Path.GetFileName(t), Path.GetDirectoryName(t))); } else { lsvFiles.Items.Add(MakeLsvItem("Excel", t, "")); } } } [MethodImpl(MethodImplOptions.NoInlining)] void AppendWordFileList() { Word.Application oWordApp; try { oWordApp = (Word.Application)Marshal.GetActiveObject("Word.Application"); } catch(COMException) { return; // Wordが起動していない、もしくは不明なエラー } Word.Documents oWordDocs = (Word.Documents)oWordApp.Documents; foreach ( Word.Document doc in oWordDocs ) { string t = doc.FullName; if ( t.IndexOf("/") >= 0 || t.IndexOf("\\") >= 0 ) { // pathあり lsvFiles.Items.Add(MakeLsvItem("Word", Path.GetFileName(t), Path.GetDirectoryName(t))); } else { lsvFiles.Items.Add(MakeLsvItem("Word", t, "")); } } } [MethodImpl(MethodImplOptions.NoInlining)] void AppendPowerPointFileList() { PowerPoint.Application oPptApp; try { oPptApp = (PowerPoint.Application)Marshal.GetActiveObject("PowerPoint.Application"); } catch(COMException) { return; // PowerPointが起動していない、もしくは不明なエラー } PowerPoint.Presentations oPptPresentations = (PowerPoint.Presentations)oPptApp.Presentations; foreach ( PowerPoint.Presentation presen in oPptPresentations ) { string t = presen.FullName; if ( t.IndexOf("/") >= 0 || t.IndexOf("\\") >= 0 ) { // pathあり lsvFiles.Items.Add(MakeLsvItem("PowerPoint", Path.GetFileName(t), Path.GetDirectoryName(t))); } else { lsvFiles.Items.Add(MakeLsvItem("PowerPoint", t, "")); } } } /* [MethodImpl(MethodImplOptions.NoInlining)] void CreateShortcut(string lnkDest, string saveDest, ref dynamic shell) { // WshShellを作成 if (shell == null) { var type = Type.GetTypeFromProgID("WScript.Shell"); shell = Activator.CreateInstance(type); } // IWshRuntimeLibrary.WshShell shell = new IWshRuntimeLibrary.WshShell(); dynamic shortcut = shell.CreateShortcut(saveDest); // リンク先 shortcut.TargetPath = lnkDest; // shortcut.Arguments = "/a /b /c"; // shortcut.WorkingDirectory = Application.StartupPath; // 実行時の大きさ 1が通常、3が最大化、7が最小化 shortcut.WindowStyle = 1; // shortcut.Description = "xxx"; // shortcut.IconLocation = Application.ExecutablePath + ",0"; // ショートカットを作成 shortcut.Save(); } [MethodImpl(MethodImplOptions.NoInlining)] void ExportAllLinks(string saveDestPath) { dynamic shell = null; foreach (ListViewItem item in lsvFiles.Items) { string basePath = item.SubItem[CI_Path].Text; if (basePath != "") { string fileName = item.SubItem[CI_FileName].Text; string lnkDest = Path.Combine(basePath, fileName); string saveDest = Path.Combine(saveDestPath, fileName+".lnk"); CreateShortcut(lnkDest, saveDest, ref shell); } } } void ExportAllLinksWithDialog() { //FolderBrowserDialogクラスのインスタンスを作成 FolderBrowserDialog fbd = new FolderBrowserDialog(); //上部に表示する説明テキストを指定する fbd.Description = "フォルダを指定してください。"; //ルートフォルダを指定する //デフォルトでDesktop fbd.RootFolder = Environment.SpecialFolder.Desktop; //最初に選択するフォルダを指定する //RootFolder以下にあるフォルダである必要がある fbd.SelectedPath = Environment.SpecialFolder.Desktop; // @"C:\Windows"; //ユーザーが新しいフォルダを作成できるようにする //デフォルトでTrue fbd.ShowNewFolderButton = true; //ダイアログを表示する if (fbd.ShowDialog(this) == DialogResult.OK) { ExportAllLinks(fbd.SelectedPath); } } */ ListViewItem MakeLsvItem(string appName, string fileName, string path) { return new ListViewItem(new string[]{appName, fileName, path}); } GetDirOfOfficeOpening() { Controls.Add(btnReload = new Button(){ Text = "Reload", Location = new Point(0, 0), Width = 100 }); btnReload.Click += (s,e)=>{ReloadList();}; /* Controls.Add(btnExportAllLinks = new Button(){ Text = "Reload", Location = new Point(0, 0), Width = 100 }); btnExportAllLinks.Click += (s,e)=>{ExportAllLinksWithDialog();}; */ Controls.Add(lsvFiles = new ListView(){ View = View.Details, FullRowSelect = true, HideSelection = false, MultiSelect = false, GridLines = true, Location = new Point(0,30), Size = new Size(100,100) }); lsvFiles.Columns.Add("Application", 80, HorizontalAlignment.Left); lsvFiles.Columns.Add("FileName", 100, HorizontalAlignment.Left); lsvFiles.Columns.Add("Path", 400, HorizontalAlignment.Left); lsvFiles.MouseDoubleClick += lsvFiles_MouseDoubleClick; ClientSize = new Size(600 ,300); Load += (s,e)=>{MyResize();ReloadList();}; Resize += (s,e)=>{MyResize();}; ResizeEnd += (s,e)=>{MyResize();}; } void MyResize() { int h = ClientSize.Height - lsvFiles.Top; lsvFiles.Size = new Size(ClientSize.Width, (h<50)?50:h); } void lsvFiles_MouseDoubleClick(object sender, MouseEventArgs e) { ListViewHitTestInfo info = lsvFiles.HitTest(e.Location); if ( info.SubItem != null && info.SubItem.Text != "" ) { System.Diagnostics.Process.Start(info.SubItem.Text); } } [STAThread] static void Main(string[] args) { Application.Run(new GetDirOfOfficeOpening()); } }