ゲームエフェクトデザイナーのブログ | A Real-Time VFX Artist's Blog

About Making Materials on UE, Making Tools with C#, etc

C# TreeViewのノードに対応した文字列をTextBoxに表示する

TreeViewとTextBoxを連動させてツリー構造のテキストエディタを作成中ですが、TreeViewのノードごとにTextBox.Textの内容を記憶させておかないといけません。

その方法についてメモっておきます。ちなみに対応後はこんな感じ。

f:id:moko_03_25:20190608162656g:plain

まず、テキストの内容などを保持しておくためのデータ保持クラスを用意します。

そしてフィールドなりプロパティで下記のような情報を保持する箱を用意します。

・ノードのラベル名 string型
・TextBoxの内容 string型
・キャレットの位置 int型

クラスの内容はこれだけでOKです。

次にMainFormの方に、新しくノードを追加する関数を用意します。

その際にTreeViewのノードのTagに対して、先ほど作成したデータ保持クラスのインスタンスを代入すれば、追加したノードごとにインスタンスを保持してくれます。

ここにテキストの内容を保存したり逆に表示したりすれば良い訳です。

こちらの記事が参考になるかと思います。

問題はノードの選択を変えた時にどのイベントを利用するかですが「BeforSelect」と「AfterSelect」を利用すれば良いようです。

・TreeView1_BeforeSelect イベント
 選択中のノードのTagからデータ保持クラスのインスタンスを取得
 TextBox.Textをインスタンスのフィールドやプロパティに代入

・TreeView1_AfterSelect イベント
 選択中のノードのTagからデータ保持クラスのインスタンスを取得
 インスタンスのフィールドやプロパティからTextBox.Textに代入

これでOKです。

ただし注意点がいくつかあります。

・新規ノード追加時に「BeforSelect」と「AfterSelect」が邪魔
 こちらは新規ノード追加時のフラグを用意しておき‥
 それぞれのイベントは新規ノード追加時には実行しないようにすればOKです。

・ルートノードを削除したような際に例外が発生する
 削除時は削除した直後の内容をデータ保持クラスに保存しようとしたりするので‥
 選択中のノードが null なら保存処理を実行しないようにします。

以上のような対処でひとまずうまく動作しました。

C# TreeViewの主なプロパティ

TreeViewはエクスプローラのナビゲーションウインドウのようにフォルダ階層をツリー表示するようなコントロールです。

ツリーに並ぶアイテムを「ノード」という呼称になります。

 

動作

●FullRowSelect
ノード選択時にTreeViewの幅全体をハイライトします。
ノード名の長さにハイライトの長さが左右されないようTrue推奨です。

●HideSelection
フォーカスが移ったら選択中のノードのハイライトを消します。
今何を選択しているのか分からなくなるのでFalse推奨です。

●Indent
ノードのインデントの設定。ただしデフォルトの19が丁度良いです。

●LabelEdit
ユーザーがノードのラベル名を編集できるようにします。
選択中のノードをさらにクリックすると編集可能状態になります。
ラベル名変更ダイアログなど自作しなくて済むのでTrue推奨です。
F2キーで編集可能にするにはそのように実装する必要があります(後述)。

●ShowLines
ノードの階層をラインで表示します。
シンプルな見た目にしたい場合はFalseで。

 

表示

●BackColor
背景色

●Font
ノードのラベルで表示される文字のスタイル

●BorderStyle
コントロールの枠線のスタイル。

●ForeColor
ノードのラベルの文字色。

 

よく使うコード例


ノードを追加する場合

 // ノードの生成
TreeNode インスタンス名 = new TreeNode(ラベルの文字列);

// 生成したノードの追加
treeView1.Nodes.Add(インスタンス名);

// 追加したノードを選択
treeView1.SelectedNode = インスタンス名;


ノードが選択されているか調べる場合

// ノードを選択しているかどうかを判定
if (treeView1.SelectedNode != null)

// 親ノードが存在するか判定
if (treeView1.SelectedNode.Parent == null)


選択中のノードに子ノードを追加

// 空のノードを生成
TreeNode 子のインスタンス名 = new TreeNode(子のラベルの文字列);

// 選択中の親ノードをインスタンスに代入
TreeNode 親のインスタンス名 = treeView1.SelectedNode;

// 親ノードに子ノードを追加
親のインスタンス名.Nodes.Add(childNode);

// 親のツリーを開く
親のインスタンス名.Expand();


親ノードを選択

// 空のノードを生成
TreeNode インスタンス名 = new TreeNode();

// 選択中のノードの親をインスタンスに代入
インスタンス名 = treeView1.SelectedNode.Parent;

// ラベル名を取得
string s = インスタンス名.Text;


選択中のノードを削除

// 選択中のノードを削除
treeView1.SelectedNode.Remove();


ノードをindexで指定する

// 0 番目のノード
treeView1.Nodes[0];


ルートノードの数をカウントする

// ルートノードの数をカウント
int i = treeView1.Nodes.Count;


選択中のノードと同じ親のノードをカウントする

// 同じ親に属するノードの数をカウント
TreeNode インスタンス名 = treeView1.SelectedNode;
int i = インスタンス名.Nodes.Count;


ノードにオブジェクトを渡す

// 空のノードを生成
var インスタンス名 = new クラス();

// 空のノードを生成
rootNode.Tag = fData;


ラベル名編集関連はこちらの記事が参考になります。

TreeViewのノードのテキストをユーザーが編集できるようにする - .NET Tips (VB.NET,C#...)

ツリーをドラッグ&ドロップで操作したい場合について。

TreeViewのノードをDrag&Dropにより移動、コピーできるようにする - .NET Tips (VB.NET,C#...)

 

※随時更新

C# Formの主なプロパティ

よく忘れるのでメモ。

 

ウインドウスタイル

●Icon
フォームタイトルとタスクバーに表示するアイコンを指定。

●MaximizeBox
最大化するか。

●MinimizeBox
最小化するか。

●ShowIcon

フォームのタイトルに表示されるアイコンの表示。 

●ShowInTaskbar
タスクバーに表示するか。

●TopMost
フォームを最前面に表示するか。 

 

動作

●AllowDrop
フォームへのファイルのドロップを受け付けるか。

 

配置

●MaximumSize
見た目的に許せる範囲で最大サイズを設定したい場合に設定。

●MinimumSize
人によってはコンパクトにして使いたい人もいるので、フォームの見た目的に許せる範囲で最小サイズを設定しておくと良い。

●StartPosition
起動時の初期位置を指定。CenterScreenだと中央に表示される。

 

表示

●BackColor
背景色。

●Font
フォームのタイトルの文字のスタイル。

●FormBorderStyle
フォーム外観の設定。一番重要。
Noneの場合は最小化・最大化・サイズ変更などを別途記述する必要がある。

●Text
タイトルとタスクバーに表示される文字列。
なのでフォームの外観を枠無しにした場合もちゃんと設定しておく。

 

フォーム外観をNoneにした際に、マウスドラッグでフォームのサイズを変更できるようにするには、こちらの記事を参考にさせていただいています。

C#でWPFを始める

C#Windowsフォームアプリを作っていると、特にグラフィックデザイナを生業としている身の場合にコントロールの標準のプロパティでは大して外観を変えることができずに「かゆいところに手が届かない!」感がストレスになってきます。

そこで「それならWFPかなあ」と手を出していきたいと思った場合に、ある程度まとまったチュートリアル記事が用意されているこちらの入門記事が良さそうです!

XAMLの書き方についてはUdemyのこちらのレッスンがおすすめ。

WPFは一般にはあまり普及していないイメージがありつつも、まとまった情報は探すとたまに出てきますね。。

という訳でとりあえずのメモでした。

更新していくかも知れません。

Cドライブの圧迫の原因を調べてお掃除した際のメモ

Cドライブの容量を削減したい場合にはいつも「DiskInfo」を利用しています。

こちらで各階層のフォルダごとに容量を圧迫しているものが一目で特定できます。

私が3年以上使用していたデスクトップマシンでは‥

●C:\Windows.old が 38GB ほど圧迫

こちらの記事を参考にディスククリーンアップで削除しました。


●C:\Autodesk\WI が 7.18GB ほど圧迫

削除して良さそうだったので削除しました。


MayaLT2018 が 1.55GB ほど圧迫

たまに1ヵ月だけMayaLTを契約して使うことがありますが、MayaLT2018 が 1.55GBほど圧迫していたので当面不要ならアンインストールするのも良いかと思います。

Visual Studio の各コンポーネント

インストール時にあらゆる開発環境をインストールすると全部で46GBに及んだりします。下記の記事に何がどれくらい容量を圧迫するか書いています。


ウェブアプリの開発環境

それから「プログラムの追加と削除」でサイズ順に並べると、ウェブアプリの開発環境関連のアプリケーションが思いのほか容量を占めています。

Ruby 2.6.3 ‥ 905MB
Vagrant ‥ 862MB
Java SE Development Kit 8 Update 121 ‥ 308MB
Java 8 Update 161 ‥ 100MB
Oracle VM VirtualBox 6.0.6 ‥ 235MB
Microsoft SQL Server 2016 LocalDB ‥ 233MB
MySQL Installer ‥ 429MB
MySQL Server 5.7 ‥ 319MB
MySQL Workbench 8.0 CE ‥ 126MB
MySQL Connector C++ 8.0 ‥ 115MB
MySQL Shell 8.0.16 ‥ 106MB
MySQL Router 8.0 ‥ 89MB
MySQL Documents 5.7 ‥ 79MB
MySQL Connector/ODBC 8.0 ‥ 54MB


また、Pythonの開発環境である Anaconda3 (C:\Users\<ユーザ名>\Anaconda3) がなんと 5.6GB ほど圧迫しています(!)。

VirtualBox などの仮想環境でちゃんと構築&管理しておけば、後になって「当面は開発環境いらないな~」となったら楽にアンインストールできるんでしょうか。

●C:\Program Files

Unity・Trend Microdotnet・Side Effects Software・NVIDIA Corporation・Autodeskあたりが1~3GBほど圧迫。

●C:\Program Files (x86)

Microsoft Office・Common Files・Steamなどのフォルダが1~3GBほど圧迫。

ちなみにUnreal EngineAdobe製品はDドライブにインストールしています。
UE_4.22フォルダは25.1GBありました。。

●C:\Windows\Installer が 8.5GB ほど圧迫

どうも手動では消せないようでフリーソフトが必要とのこと。
いくつかのサイトで「Patch Cleaner」が紹介されていました。
インストーラをDLしてのインストールが必要なようなので保留。。

●C:\Windows\WinSxS が 7.7GB ほど圧迫

5万を超えるファイル数でした。
ディスククリーンアップはすでに行っていてこの状態なので、下記の記事を参考にコマンドプロンプトを管理者権限で実行して「C:\Windows」に移動して、コマンド「DISM /OnLine /Cleanup-Image /StartComponentCleanup /ResetBase」を試してみました。

6.47GBになりました。1.3GBほど減った感じでしょうか。。
手動で消すと危険なフォルダみたいなので一旦これで放置。

●c:\Windows\System32\DriverStore が 1.2GB ほど圧迫

検索してみると人によっては10GBとか圧迫しているようですね。
私の場合は 1.2GB程度なのと、手動では消せずフリーソフトが必要なようなので放置。


●C:\Users\<ユーザ名>\Documents\My Kindle Content が 1.6GB ほど圧迫

Kindle for PC でDLしたものだと思うのでやむなし。

●C:\Users\<ユーザ名>\AppData が 9.7GB ほど圧迫

「Local」と「Roaming」がよく圧迫の原因になっています。
たまに整理しているため10GBいかない程度でした。

今確認してみたところではChromeFirefoxがキャッシュで圧迫している他、Atomが1GB使ってたり(パッケージのインストールとかが原因?)、あとはUnreal Engineが過去バージョンの何かしらの情報を保持しているようだったのでもう使わないバージョンのフォルダを削除。

という感じで今回は合計 50GB ほどお掃除ができました!

自宅のデスクトップマシンのCドライブは現状 222GB しか無いので、これでまたしばらくの間は作業に耐えられそうです。

ノートPCでメモを取ることについて

うちのヨメさんは「世界一受けたい授業」が好きなのですが、つい先日「めちゃ面白いから観て!」と前田裕二さんが取り上げられた回の録画を半ば強制的に見させられ、この方にとても興味が湧きました。一日に書くメモの量が尋常じゃないですね。

触発されたヨメはすでに前田さんの著書『メモの魔力』を買っており、自分はそれを借りて今はまだ読み始めた段階なのですが、文章から溢れる熱量もすごいですが読者に対する誠実さみたいなものも強く感じられます。

特に付録の自己分析1000問インパクトも強く、読み終えたら回答していきたいと思いました。

メモの魔力 The Magic of Memos (NewsPicks Book)

メモの魔力 The Magic of Memos (NewsPicks Book)

 


さて、そんな自分は普段あまりメモを取りませんし、前田さんが言われている知的生産のためのメモという行為は尚更できていません。なので本書は全く耳の痛い内容で、アンテナ張ってキャッチしたらそれを自分なりにちゃんと分析していく姿勢にシフトしていきたいと思わせられます。自分に足りないのは物事の本質的なところを考える部分だと痛感することが多く、身近で尊敬している人はみな本質的なところを話すんですよね。。

で、そんな知的生産のためのメモについては今回は抜きにして「ただ事実を記録に残す」という行為でのメモについて普段から思うところがあるので、この機に記事にしておきたいと思いました。前田さんも「知的生産に結び付けないとあまり意味が無い」「単に記録するだけなら機械に任せたら良い」と言われている部分、それもただ書き写すという行為そのものについてです。

自分の場合、普段あまりメモを取らないのですが、唯一、セミナーと会議に関してのみは「喋った内容とスクリーンに映し出された内容を全てメモしてやる!」という気持ちでノートPCでメモしています。

おかげで完全に独自の運指ではありますがブラインドタッチができるようになってきており(数字と記号の列はまだ全然ダメですが)、徐々にタイピング速度も上がってきています。

ただそれでもかなりメモを取りこぼしていて、しかもタイプすることに集中していると今しゃべっている内容に追いつかない時にまるまる聞き逃して、話を断片的にしか聞けていない状態になり話を理解できなくなるという本末転倒なことが多々あります。重要な部分をうまく要約して書いていけたら良いのでしょうけれど。。

ちなみに、実は以前はメモ帳に手書きでメモしていました。

例えばCEDECで受講したセッションはスライドの絵も気になるものは書き写したりしつつ書いていたのですが、自分の場合は人に共有する前提なので手書きのメモを後からデジタルに起こす作業が苦痛でノートPCに変えました。
ポメラとか携帯性に優れたデバイスも検討したのですが結局ノートPC

前田さんは手書きでのメモを勧められているため「むむむ‥」と葛藤な感じでもあります。

ただ、ノートPCでメモしていると日本語は圧倒的に不利だと日々痛感させられます。というのも、日本語をキーボードでタイプする際に「変換」という行為が挟まるからです。

具体的には次の3つの変換です。

 ① 正しい漢字への変換

 ② カタカナへの変換

 ③ 英語への変換

まず ① ですが、全てひらがなでタイプすれば写すことのみに集中できますが、非常に読み辛いメモになる上に、後から漢字に修正するのは苦痛です(機械学習等で、ひらがなの文章を適切な漢字&カタカナに一括変換させるとかできるサービスとか、探せばありますかね?あったら全てひらがなで書くということが超絶楽な速記法となり得るかも‥)。

しかし単語ごとに正しい漢字に変換していく作業はメモを取る行動を大きく阻害します。

② では、変換キーでは一発でカタカナにならない場合も多いため、1つの単語でタイプを止めてF7キーでカタカナに一括変換し、そして続きを打つ‥という面倒臭い行為が発生します。ノートPCはキーボードが小さいためF7は押す位置がズレやすいですし、ファンクションキーはホームポジションから少し離れるのでロスもあります。

③ は ② と同様にF10で変換可能ですが、空白スペースを入れて続けて英文を打っていきたい時には不便です。なので半角/全角ボタンを押して英語入力モードとかな入力モードを頻繁に切り替えてタイプすることになります。これがまた著しくメモ効率を下げているように思います。

あと手書きと比べて不利な点に、書く位置を変える際にカーソルの移動が足枷というのもあります。Home・End・PageUP・PageDownやらCtrl + 矢印キーを駆使したりマウスに持ち替えたりしてもロスなので、カーソルの移動という行為がとても煩わしく感じることが多いです。

これらの煩わしい行為が挟まれる度に、聞きとりとメモへの集中を削がれます。削がれながらもそれに抗いながらメモしていく感じになります。この状況からすごく解放されたいと思ったりします。少なくとも「ぜんぶ半角英数字でメモを取れたら、どんなに楽だろう」と。

手書きだとはこのあたりクリアされていますね。
でもパっとシェアできない。できればノートPCでメモしていきたい。

前田さんが言われているようにただ記録してシェアするだけなら、写真や音声録音などの方法があり、文字起こしもORCの精度が上がればもはや機械で全く良いのでしょうけれど‥そして自分はもっと深く掘り下げて考えることに時間を使っていかないといけないのでしょうけれど‥

とりあえず、本書の続きを読み進めてこのあたりまた考えてみたいと思いました。

ちなみに、セミナー等のメモをブログに載せる際に「なるべく自分の考えを添えるようにしなければ」とは思っていて、そういう意味ではモンハンワールドのエフェクトエディタの講演レポートは、これまで単なるメモを掲載し続けてきた中でようやくまともなレポートになったかと思います。。

ついでに前田さんが使っているというスマホアプリをペタリ。

smatu.net

CSSとjQueryでヘッダーをスライドアニメさせる

CSSjQueryでヘッダーをスライドアニメさせてみました。

こんな風に。

f:id:moko_03_25:20190530015526g:plain


まず、htmlはこちらのような感じです。
※赤字がヘッダー部分&アニメーションの記述です

<html>
<head>
    <title>URL Library | moko's web application</title>
    <%= csrf_meta_tags %>
    <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>

    <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
    <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.2/css/all.css" integrity="sha384-oS3vJWv+0UjzBfQzYUhtDYW+Pj2yciDJxpsK1OYPAYjqT085Qq/1cq5FLXAZQ7Ay" crossorigin="anonymous">
    <script>
        $(function() {
            $(".click").click(function() {
            $(this).toggleClass("clicked");
            });
        });
    </script>
</head>

<body>
<header>
    <hr size="2" color="#97BE38">
    <div class="box click">
        <hr size="25" color="#5E5C61">
    </div>
</header>

<div class="container">
    <%= yield %>
</div>
</body>
</html>


そしてこちらがCSSの記述です。
クリックした際にプロパティの一部を変化させています。

header {
    text-align: center;
    background: #5E5C61;
    margin: 0;
}

header hr {
    margin: 0;
}

.box {
    height: 200px;
    background-color: #5E5C61;
    padding: 0;
    margin: 0;
    transition: 1s;
    background-image: url(title_logo.png);
    background-size:377px 180px;
    background-repeat: no-repeat;
    background-position: center center;
}

.box.click.clicked {
    height: 25px;
    opacity: 0;
}


こんなシンプルな記述でいけるんですね。

ただ、ページ遷移するとクリックが効かなくなったりなど不具合があるのでさらに手を入れていく必要がある感じでした。

Font AwesomeのアイコンでEditとDestroyを行う方法

Font Awesome のアイコンをクリックしたら link_to ヘルパーで投稿を編集・削除する方法がうまくいったのでメモっておきます。

下記のように書けばOKです。

<%= link_to(
    content_tag(:i, nil, class: 'fas fa-edit'),
    edit_website_path(website)) %>

<%= link_to(
    content_tag(:i, nil, class: 'fas fa-times'),
    website_path(website),
    method: :delete,
    data: { confirm: '本当に削除しますか?' }) %>


リスト一番左のアイコンをクリックすると編集・削除できます。

f:id:moko_03_25:20190528015150p:plain

フォームからURLを受け取ってスクレイピングした値をテーブルに追加する

Ruby on RailsでURLを登録してリンク集を作成できるウェブアプリを制作中なのですが、データの新規登録ページ(new.html)の form_for ヘルパーでフォームから入力されたURLを受け取って、そのURL先のサイトのページタイトルをデータベースのテーブルに登録して index.html にリダイレクト時にページタイトルを一覧することができたので、メモしておきます。

こんな感じです。

f:id:moko_03_25:20190528010007g:plain

素人丸出しの滅茶苦茶強引な方法ですが、とりあえず実現だけはうまくいきました。

まず「new.html.erb」は下記のような感じです。
form_for でフォームに入力した値を受け取ります。こちらは普通の記述かと思います。

<h2>Add New URL</h2>
<%= form_for :website, url: websites_path do |f| %>
  <p>
    <%= f.text_field :address, placeholder: 'URLを入力してください' %>
    <% if @website.errors.messages[:address].any? %>
      <span class="error">
        <%= @website.errors.messages[:address][0] %>
      </span>
    <% end %>
  </p>
  <p>
    <%= f.text_field :detail, placeholder: 'サイトの説明を入力してください' %>
  </p>
  <p>
    <%= f.submit "登録する" %>
  </p>
<% end %>


コントローラはこんな感じです。
こちらはひどいです。nokogiriでスクレイピングしてタイトルを取得するコードをコントローラのアクション内にまるまる記述しています。
ちゃんとスクレイピング用にrbファイルを作って呼び出すべきなんでしょうね。。

def create
  @website = Website.new(params.require(:website).permit(:address, :detail))
  if @website.save

    require 'nokogiri'
    require 'open-uri'

    charset = nil

    html = open(@website.address) do |f|
      charset = f.charset
      f.read
    end

    doc = Nokogiri::HTML.parse(html, nil, charset)
    @website.title = doc.title
    @website.save

  redirect_to websites_path
  else
    render 'new'
  end
end


参考にさせて頂いたのはこちらの記事です。

とりあえずメモでした!