[win8] SkyDrive の活用法をいくつか紹介

最近は、大きなファイルを転送するのに、宅ファイル便や、その他のストレージサービスを必要としなくなりました、で、もっぱら SkyDrive でやりとりです、という話を。

image

SkyDrive を使う前は、Dropbox を使っていたのですが、iPhone, iPad で SkyDrive が使えるよ、ってことが分かってから、mac にも SkyDrive がはいっています。

■Mac から画面キャプチャ, プログラムのソースを win 8 に。

Mac で撮った画面キャプチャをがんがんと SkyDrive に入れます。多少、タイムラグがあるのですが、Windows 8 のフォルダーで受け取ります。

image

メインで使っているのが Windows 8 マシンなので、Mac のほうは執筆のため、iPhone プログラミング用ってことで Xcode ぐらいしか入っていません(ためしに、Excel だけ入れた覚えが)。
なので、画像の編集やら、ファイル名の変更、ソースコードの修正なんかは Windows のほうでやるわけです。

image

 

■Excel の予定表を iPhone で確認する

これも iPhone 絡みですが、SkyDrive のアプリを iPhone に入れておくと、Excel の閲覧ができます。編集はできないのですが、閲覧で十分です。そもそも、あの小さな画面で、全体の更新は無理なので、そのあたりは「閲覧」に特化したほうがよいかと。

image

なので、特に Excel 互換アプリをいれずに、SkyDrive から直接閲覧だけをさせています。外でみられるし、いろいろ確認ができるので便利です。更新自体は、パソコンのほうで Excel で編集すれば OK ってことで。

■メールの添付がわりに使う。他人と共有する。

ブラウザのほうからですが、フォルダの共有をしておくと、大きなファイルをメールに添付せずにすみます。以前は、宅ふぁいる便とかストレージサービスを利用しているたのですが(SkyDrive 自体もストレージサービスなんですが)、ここで、共有のフォルダの URL を取得して、短縮した後にメールに URL を貼ります。

image

相手に見せるだけであれば、閲覧だけで。何か返してもらう場合には、編集可能の URL を送ります。

■写真フォルダとして使う

一番、お手軽なのが写真フォルダの公開ですね。身内だけに公開したい場合には、URL だけメールで知らせれば OK です。パソコンで見ていれば、元の大きさでダウンロードができるので、プリンターに印刷するのも自由というわけで。まあ、セキュリティ的にどうとか…という話もあるので、期間限定にしておくと無難かと。下記は、うちの娘の保育園のときの集まりです。

image

デスクトップ版の IE で見ると、こんな風にブラウジングできます。

image

ちなみに、Windows 8 の SkyDrive では、全画面でカーソルを使って閲覧できます。一応、スナップにも対応しています。

image

こんな感じで使っているよ、という紹介でした。

カテゴリー: 雑談 | 1件のコメント

[win8] OpenStreamForWriteAsync は上書きモードで開くので注意せよ

StorageFile の Open は、通常の Open/Close とちょっと違う | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/3880

の続きです。

Windows ストア アプリでは、fopen/fclose のようにファイル名を指定して、ファイルの読み書きができません。サンドボックス的なセキュリティを維持するために常に StorageFile クラスを使います。このクラスは、アプリ ローカルのフォルダから直接作るか、ピッカー(FileSavePicker など)を使って作ります。

ってのが前回の話で、FileSavePicker から戻ってきたときには、ファイルが既に作られています、ってのがちょっとした「落とし穴」なのです。

■ピッカーで保存先のファイルを選択

ピッカーを使って、デスクトップに保存するファイルを選んでね、ってのが次のコードです。

private StorageFile _file;
/// <summary>
/// ファイル選択のみ
/// </summary>
/// <param name=&quot;sender&quot;></param>
/// <param name=&quot;e&quot;></param>
private async void Button_Click_0(object sender, RoutedEventArgs e)
{
	// FileSavePicker を用意する
	var picker = new FileSavePicker();
	picker.CommitButtonText = &quot;データを保存する&quot;;
	picker.DefaultFileExtension = &quot;.txt&quot;;
	picker.FileTypeChoices.Add(&quot;テキスト形式&quot;, new string[] { &quot;.txt&quot; });
	picker.SuggestedStartLocation = PickerLocationId.Desktop;
	picker.SuggestedFileName = &quot;Sample.txt&quot;;
	// FileSavePicker を出して、ファイルを指定する。
	StorageFile file = await picker.PickSaveFileAsync();
	if (file == null)
	{
		// キャンセルされたときは何もしない
		return;
	}
	_file = file;
}

ピッカーの PickSaveFileAsync メソッドの戻り値は、StorageFile オブジェクトなので、キープしておくと、あとから利用できます。いわゆる2度目に「保存」ボタンを押したときには、ユーザーに問い合わせをしない、ことが可能です。上書き保存ってやつです。

ここで、保存先のファイルってのは新規ファイルとは限らなくて、存在するファイル(中身があるファイル)でも ok なわけです。まあ、そうですよね。なので PickSaveFileAsync メソッドからの戻りは、

1.PickSaveFileAsync が作った空ファイル(サイズ 0 のファイル)
2.元からある、いくらかのサイズのファイル。
3.元からある、サイズ 0 のファイル

ここで、1 と 3 の区別(新規ファイルなのか、既存ファイルなのか)がつかないのが問題…になるかもしれんッ!!! と思ったわけですが、これから上書きしようとするわけだし、どっちでも構わないんじゃない?というのが設計思想のようです。

■保存するモデル

アプリのデータを DataModel にしておきます。

public class DataModel : BindableBase
{
	private int _id;
	public int ID
	{
		get { return _id; }
		set { _id = value; this.SetProperty(ref this._id, value); }
	}
	private string _name;
	public string Name
	{
		get { return _name; }
		set { _name = value; this.SetProperty(ref this._name, value); }
	}
}

画面のほうには、適宜 Binding を書く、というパターンですね。
クラスにしておくとシリアライズが楽なので。Windows ストア アプリの場合は、DataContractSerializer を使うと便利です。

■DataContractSerializer を使う

モデルデータを、そのままシリアライズするのが一番楽です。既に用意されている DataContractSerializer を使って、WriteObject メソッドを使って XML 形式で書き込めば okですね。

private async void Button_Click_1(object sender, RoutedEventArgs e)
{
	StorageFile file = this._file;	// 選択済み
	var data = new DataModel()
	{
		ID = 10,
		Name = "masuda tomoaki"
	};
	// これは正常に動く
	using (var stream = await file.OpenStreamForWriteAsync())
	{
		var serializer = new DataContractSerializer(typeof(DataModel));
		serializer.WriteObject(stream, data);
	}
}

ただ、DataContractSerializer クラスで書き出す XML って、改行とかインデントがついていなくて、デバッグに不都合があるのです。
なので、XmlWriter を使えば整形できるのでは?と思ったのが、ハマリどころです。

■XmlWriter を使う

XmlWriter を使って整形(改行やインデント)を使いながら、書き出します。

private async void Button_Click_2(object sender, RoutedEventArgs e)
{
	StorageFile file = this._file;	// 選択済み

	var data = new DataModel()
	{
		ID = 10,
		Name = "masuda tomoaki"
	};
	// これは正常に動く
	using (var stream = await file.OpenStreamForWriteAsync())
	{
		var serializer = new DataContractSerializer(typeof(DataModel));
		// ストリームを切り詰める
		stream.SetLength(0);		// これが必須
		// インデント等を設定する
		var st = new XmlWriterSettings();
		st.Indent = true;
		st.IndentChars = "\t";
		st.Async = false;        // 書き込み時に async を使わない
		using (var xw = XmlWriter.Create(stream, st))
		{
			serializer.WriteObject(xw, data);
		}
	}
}

この中で「stream.SetLength(0)」ってのがあります。これ、ファイルは「新規だ」と思っていると、実は違っていて「既存のファイル」かもしれないのです。で、既存のファイルを OpenStreamForWriteAsync メソッドで開くと、中身はそのままオープンって具合なわけです。

なので、もし既存のファイルよりも短い XML を書き込んだ場合、最後の閉じタグの後ろにゴミが残るんですよね…これはかなりはまりました。
fopen 関数だと、新規オープンなのか、追加オープンなのかの選択があるのですが、実は OpenStreamForWriteAsync には、その選択肢がないのです。さらに、PickSaveFileAsync では、新規のファイルでも 0 バイトのファイルができるし。
OpenStreamForWriteAsync の場合は、ファイルをオープンすると、先頭にカーソル位置が置かれるのですが、これは既存ファイルをオープンしたときも先頭にカーソルがあります。ってのが落とし穴ですね。

なので、この変な状態を解決するために「新規ファイル」として扱うためには「stream.SetLength(0)」のように、ストリームの長さを 0 に初期化する必要があるのです。

■テキストファイルに保存

そんな訳で、メモ帳のようなテキストファイルを扱うアプリを作ると更にはまります。

private async void Button_Click_3(object sender, RoutedEventArgs e)
{
	StorageFile file = this._file;	// 選択済み
	// 実は上書きモードでオープンしている
	using (var stream = await file.OpenStreamForWriteAsync())
	{
		// 新規扱いの場合は、サイズを 0 にする必要あり
		stream.SetLength(0);

		using (var writer = new StreamWriter(stream))
		{
			// テキストをそのまま書き込み
			writer.Write(text1.Text);
		}
	}
}

毎回、全体を保存するから、StreamWriter クラスの Write メソッドで書き出せば ok … と思いきや、この「stream.SetLength(0)」を忘れると、もともとあったファイル長さよりも文章(text1.Text)の方が短い場合には、後ろにごみが残るのです。

■Stream にも StreamWriter にも Close がない

で、Stream も StreamWriter も Close がありません。Open したら Close したくなるものなのですが、まあ、開きっぱなしでいいみたいです。というか、using で範囲を決めないとダメかもしれませんね。
テキストの書き込みだと、StreamWriter クラスの using で抜けるときに、同時に Stream のほうも閉じてくれます…と云いますか、閉じてしまいます。これは、以前からの仕様だったような気がします。

なので、ファイルの後ろを切り詰めようとして次のように書くとエラーになります。

private async void Button_Click_3(object sender, RoutedEventArgs e)
{
	StorageFile file = this._file;	// 選択済み
	// 実は上書きモードでオープンしている
	using (var stream = await file.OpenStreamForWriteAsync())
	{
		using (var writer = new StreamWriter(stream))
		{
			// テキストをそのまま書き込み
			writer.Write(text1.Text);
		}
		// 現在位置で切り詰める
		stream.SetLength( stream.Position );	// ★
	}
}

★の位置では、stream が閉じられちゃっているので、SetLength できません。
下記のように、StreamWriter のブロックで SetLength するとうまく動きます。

private async void Button_Click_3(object sender, RoutedEventArgs e)
{
	StorageFile file = this._file;	// 選択済み
	// 実は上書きモードでオープンしている
	using (var stream = await file.OpenStreamForWriteAsync())
	{
		using (var writer = new StreamWriter(stream))
		{
			// テキストをそのまま書き込み
			writer.Write(text1.Text);
			// 現在位置で切り詰める
			stream.SetLength( stream.Position );	// ★
		}
	}
}

これもなんか変な仕様ですよね…と思いつつ、まぁ、いいかと。

OpenStreamForWriteAsync メソッドに、新規バージョンと追加バージョンがあればわかりやすいんですがね。

・OpenStreamForNewWriteAsync()
・OpenStreamForAppendWriteAsync()

のような名前にすればよかったのに。。。まあ、拡張メソッドを2,3行書けば、済むはなしなんですが。

カテゴリー: C#, windows 8 | 1件のコメント

[win8] コントロールパネルの表示方法

もともと、Windows 7 でもコントロールパネルを「クラシック表示」にしているので、あれ?どこに行ったっけ?というよりも「コントロールパネル」自身の構造を知っているので、メインのコントロールパネルを開けばなんとかなるのですが…、じゃあ、Windows 8 ではコントロールパネル自身はどうやって開くのか?っていうのを、忘れるのでメモがてらに。

Win + I キーを押すと、左に設定画面がでるので、ここに「コントロールパネル」が…あるのです。

image

あるけど…忘れますよね。ショートカットはショートカットなので、あまり覚えてられないので…

デスクトップの画面の左下で右クリックすると、コントロールパネルもろもろのメニューが出ます。

image

まあ、わかりやすいと言えば、わかりやすいですね。スタートボタンの代わりのような気もするし、よくわからないコンピュータの設定関係は、ここにある、と覚えておけばいいわけです。

が、もうひとつ、いいことがあって、Win キーを押して、スタート画面で「コントロール」で検索させます。ほら、コントロールパネルがありますね。

image

まあ、もっといいほうほうは、この「コントロール パネル」を右クリックして、アプリ バーを出して、「スタート画面にピン留め」するのがいいですね。

image

こんな風に、スタート画面においておけば忘れないでしょう。たぶん(置いたこと自体を忘れる可能性もありますが)。

image

まあ、そんなときは、

[win8] Windows 8 でアプリの検索 | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/3897

で書いたように、「設定」から検索するってのがおすすめです。

カテゴリー: windows 8 | [win8] コントロールパネルの表示方法 はコメントを受け付けていません

[win8] Windows 8 でアプリの検索

Windows 8 に「スタート」ボタンは要らない、という構造 | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/3711

で、「Windows 8 はひょっとしてキーボードが主流?」ってところを書きましたが、実際のところどうやって、つかうのか?ということをいくつか。最近、Windows 8 に移行させて、Visual Studio + QX + まめ5File  + Excel などなど、と併用しているときの感想です。

Windows 7 でおなじみだった「スタートボタン」がなくなって「スタート画面」になったわけですが、このスタート画面を使って「アプリを探し出す」ってことをやってません。強いて言えば、Win キーを押したときに、左にあるボタンのいくつか、を目標にしてクリックします。

image

この画面でいえば、左上の「デスクトップ」だけですね w 。画面が真っ黒なのは、背景画像を「黒」にしているからです。

さて、他はどうしているかとうと、デスクトップのアイコンから…って訳ではなくて、スタート画面の検索機能を直接使います。Win キーを押して、直接「vi」と打てば、

image

こんな風に、Visual Studio 2012 が見つかります。

image

Excel の場合は、「Ex」と打ったぐらいで、見つかります。

image

これは「設定」の場合も同じで、左のアイコンを「設定」にして切り替えれば、たとえばマウス関係を調べたいときには、「mouse」にすれば、

image

こんな感じで絞り込めます。という話です。アプリ名とだいたいの設定で絞り込んでいくと、わかりやすいし、おおまかな検索から絞り込めるので、重宝しています。

カテゴリー: windows 8 | 2件のコメント

[win8] StorageFile の Open は、通常の Open/Close とちょっと違う

win8 本の執筆がてら、実験プログラムのメモ書き

古き良き時代から使われた C 言語の open/close の思考は、Windows ストア アプリの StorageFile に適用されるのかな?と思っていたの出すが、「違います」という話を少し。

■なんで違うのか?

端的に言うと、StorageFile クラスには Close メソッドがありません。ええ、C 言語の場合は open/close がワンセットになっているし、C# の場合は、StreamWriter/StreamReader でも Close しないといけないよ、というのがあるのですが、Windows ストア アプリの StorageFile クラスにはありません。Stream クラスにも Close がないし、StreamWriter クラスにも Close がありません。

なんじゃ、Flush というメソッドがあるから、それでいいじゃん、とも思っていたのですが、Flush は単にバッファのフラッシュをするだけ(ディスクに書き込みする)だけで、クローズとは違います。このあたりの落とし穴はまた後日書きますが…

■Close しないとどうなるのか?

Close メソッドがない StorageFile クラスですから、そもそもクローズを呼び出せません。
なので、ファイルが閉じられないのでは?と心配になるのですが、そのあたりは OS と StorageFile の間(実際は、Stream のところ?)でうまく調節をしているようです。

■実験コード

デザイナで、こんな画面をつくっておきます。

<Grid Background=&quot;{StaticResource ApplicationPageBackgroundThemeBrush}&quot;>
    <Button 
        FontSize=&quot;24&quot;
        Click=&quot;Button_Click_1&quot;
        Content=&quot;ファイルに保存&quot; HorizontalAlignment=&quot;Left&quot; Margin=&quot;40,55,0,0&quot; VerticalAlignment=&quot;Top&quot; Height=&quot;99&quot; Width=&quot;197&quot;/>
    <Button 
        FontSize=&quot;24&quot;
        Click=&quot;Button_Click_2&quot;
        Content=&quot;ファイルから読込&quot; HorizontalAlignment=&quot;Left&quot; Margin=&quot;40,159,0,0&quot; VerticalAlignment=&quot;Top&quot; Height=&quot;99&quot; Width=&quot;197&quot;/>
    <TextBox 
        Name=&quot;text1&quot;
        FontSize=&quot;24&quot;            
        HorizontalAlignment=&quot;Left&quot; Margin=&quot;268,55,0,0&quot; TextWrapping=&quot;Wrap&quot; 
        Text=&quot;ここにサンプルの文書を表示します。&quot; 
        VerticalAlignment=&quot;Top&quot; Height=&quot;429&quot; Width=&quot;611&quot;/>
</Grid>

ボタンのイベントはこちら


private StorageFile _file;
/// <summary>
/// ファイルピッカーで保存する
/// </summary>
/// <param name=&quot;sender&quot;></param>
/// <param name=&quot;e&quot;></param>
private async void Button_Click_1(object sender, RoutedEventArgs e)
{
	// FileSavePicker を用意する
	var picker = new FileSavePicker();
	picker.CommitButtonText = &quot;データの保存先を指定する&quot;;
	picker.DefaultFileExtension = &quot;.xml&quot;;
	picker.FileTypeChoices.Add(&quot;設定&quot;, new string[] { &quot;.txt&quot; });
	picker.SuggestedStartLocation = PickerLocationId.Desktop;
	picker.SuggestedFileName = &quot;Sample.txt&quot;;
	// FileSavePicker を出して、ファイルを指定する。
	StorageFile file = await picker.PickSaveFileAsync();
	if (file == null)
	{
		// キャンセルされたときは何もしない
		return;
	}
	// ファイル名をデバッグ出力する
	Debug.WriteLine(&quot;保存先 {0}&quot;, file.Path);
	// 編集中の文章を保存する
	using (var stream = await file.OpenStreamForWriteAsync())
	{
		// サイズを0にしておく
		stream.SetLength(0);
		using (var writer = new StreamWriter(stream))
		{
			writer.Write(text1.Text);
		}
	}
	// 保存先のファイルをキープしておく
	_file = file;

}

/// <summary>
/// キープしている StorageFile を再利用する
/// </summary>
/// <param name=&quot;sender&quot;></param>
/// <param name=&quot;e&quot;></param>
private async void Button_Click_2(object sender, RoutedEventArgs e)
{
	// 保存先がないときは何もしない
	if (_file == null)
		return;

	StorageFile file = _file;
	using (var stream = await file.OpenStreamForReadAsync())
	{
		using (var reader = new StreamReader(stream))
		{
			text1.Text = reader.ReadToEnd();
		}
	}
}

文章をピッカーで保存しておいて、再び読み込むだけのプログラムです。メモ帳でありがちな「保存」ボタンの場合は、保存のほうで StorageFile を再利用するのですが、ここでは実験のために、読み込みのところで再利用をしています。

どうせクローズがないので using を使う必要もないのですが(Stream を破棄するために Dispose が必要なんですが)、まあつけておきます。Windows ストア アプリの場合、アプリ単位のサンドボックスになっている(はず)なので、多少リソースの解放を忘れても問題ないかと…乱暴ですが。

■実験1 PickSaveFileAsync 直後にファイルが作られる

if (file == null) でブレークポイントさせるとどうなるのか?ってのを試してみてください。
これ、別の実験のときに気づいたのですが、どうやら PickSaveFileAsync から戻ってきたときに「ファイルが作られます」。
このピッカーは、デスクトップに保存するようにしているので、デスクトップに「この時点」でファイルができます。
ちなみに、従来の Windows アプリ(デスクトップアプリ)の場合には、SaveFileDialog クラスを使いますが、ファイルは作られません…といいますか、SaveFileDialog は FileName プロパティで、ファイル名を取得するので、ファイル自体は作られないのです。

通常、Windows ストア アプリの場合、アプリのローカル フォルダー(LocalState フォルダー)にファイルを保存するので、さしても問題はないのですが、デスクトップとかスカイドライブとかにファイルを作成する場合には、この違いはちょっと注意が必要ですね。

■実験2 「保存」と「読み込み」の間にメモ帳で、Sample.txt を書き換えてみる

「保存」をしたときに StorageFile をキープして、「読み込み」のときに再利用しているわけですが…というか、アプリが続いている間はこういう再利用ができます。
ただし、アプリが中断したときは…と書こうと思って試してみると、中断→再開 をしても StorageFile オブジェクトはキープされていますね。これは後で調べておきましょう。

さて、
1.「保存」ボタンでテキストファイルに保存
2.メモ帳で、sample.txt を開いて内容を書き換える。
3.「読み込み」ボタンでテキストを読み込む

って手順をやると、途中の2で編集した結果が読み込めます。

なんとなく自然な感じがしますが(それはそれで「自然な感じがする」のは重要なのです)、よく考えるとちょっと不思議です。StorageFile クラスって、なにをキープしているのでしょうか?、そうなるとファイル名だけでもアクセスができるのでは?ってことですね。

■StorageFile クラスにはコンストラクタがない

実は StorageFile クラスにはコンストラクタがありません。

StorageFile class (Windows)
http://msdn.microsoft.com/en-us/library/windows/apps/windows.storage.storagefile(v=win.10).aspx

コンストラクタがないということは、なんらかの形でどこかのメソッドで作成してもらうわけけで、なんらかの「制限」がかけられている訳です。なんらかの「制限」ってのは、Windows ストア アプリが開けるファイルを OS 側(あるいはライブラリ側)が制限しているってことです。従来の SaveFileDialog クラスのようにファイル名を受け渡ししてから、StreamWriter クラスを使ってしまうと、どんなファイルでも開けるのですが、Windows ストア アプリでは、StreamWriter に渡すファイルオブジェクトは StorageFile に変更されているのです。
なるほど、そういうセキュリティなのね、という具合です。

■StreamWriter や TextWriter に Close がない理由

ここは想像ですが、ファイルに対しては非同期アクセスをするので(書き出し、読み出し自体は、同期メソッドが用意されているのですが、オープン自体が非同期なので)、クローズのタイミングを取るのはプログラム側では難しい、ってことなのかなと。
ただし、ここに落とし穴があって、C 言語タイプの open/close を想定していると、既存のファイルをオープンして書き出したときに不都合が生じるんですよね。いや、実際は不都合ではないのですが、OpenStreamForWriteAsync メソッドの仕様というところでしょうか。

	// 編集中の文章を保存する
	using (var stream = await file.OpenStreamForWriteAsync())
	{
		// サイズを0にしておく
		stream.SetLength(0);
		using (var writer = new StreamWriter(stream))
		{
			writer.Write(text1.Text);
		}
	}

なところで、ストリームのサイズを 0 に設定しておかないと、ファイルの書き出しが正常に行われません…というか、思ったように書き出せません。試しに stream.SetLength(0) のコメントアウトして、「文章の長さが縮まるように」してから保存してください。
保存したファイルを開くと、後ろに「ごみ」が残っているように見えます。
実は、OpenStreamForWriteAsync メソッドは、ランダムモードでオープンしている(たぶん)ので、ファイルが新規作成ではなくて、上書きになっているからなのです。

このあたり長くなるので、次の記事に続きます。

…と、ここまで書いて、いやいや「CreationCollisionOption」を付ければよいのでは?と気が付いた。後で修正。

カテゴリー: C#, windows 8 | [win8] StorageFile の Open は、通常の Open/Close とちょっと違う はコメントを受け付けていません

[win8] パンダ福笑いを作るために ManipulationDelta を使う

acer w500 のタブレットPC に windows 8 を入れて(これには元 winodws 7 が入っています)、子供に遊ばせようッ!!! っての第1弾

パンダ福笑い無料ダウンロード版 どうぶつ雑貨「パンダスプン」♪/ウェブリブログ
http://panda-spoon.at.webry.info/200803/article_5.html

からパンダのパーツを貰ってきて、下記なのを作ります。

目と耳、口のパーツは指を使って動かせるので、うちの2歳児でもできます。製作時間は、プログラミングに1時間弱(いろいろ調べたし orz)、パーツ切り取りに数時間です。どちらかというとパーツの切り取りが面倒だった、ってぐらいで、慣れるとこの手のドラッグ操作のものは簡単にできそうですね。

■画面を XAML で作る

<Grid Background=&quot;White&quot;>
    <Canvas x:Name=&quot;canvas&quot; ManipulationDelta=&quot;el_ManipulationDelta&quot;>
        <Image Source=&quot;ms-appx:///panda/face.png&quot; Canvas.Left=&quot;310&quot; Canvas.Top=&quot;216&quot; ></Image>
        <Image 
            ManipulationMode=&quot;All&quot;
            Source=&quot;ms-appx:///panda/mouth.png&quot;
            Canvas.Left=&quot;549&quot; Canvas.Top=&quot;567&quot; 
            />
        <Image 
            ManipulationMode=&quot;All&quot;
            Source=&quot;panda/left_ear.png&quot; Canvas.Left=&quot;310&quot; Canvas.Top=&quot;239&quot; >
        </Image>
        <Image 
            ManipulationMode=&quot;All&quot;
            Source=&quot;panda/right_ear.png&quot; Canvas.Left=&quot;663&quot; Canvas.Top=&quot;172&quot; ></Image>
        <Image 
            ManipulationMode=&quot;All&quot;
            Source=&quot;panda/left_eye.png&quot; Canvas.Left=&quot;455&quot; Canvas.Top=&quot;429&quot; ></Image>
        <Image 
            ManipulationMode=&quot;All&quot;
            Source=&quot;panda/right_eye.png&quot; Canvas.Left=&quot;646&quot; Canvas.Top=&quot;366&quot; ></Image>
    </Canvas>
</Grid>

ManipulationDelta のパターン ≪ 宇宙仮面の C# 研究室
http://uchukamen.wordpress.com/2012/08/16/manipulationdelta-%E3%81%AE%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3/
マルチタッチができるWindows 7アプリ作成の基礎 – @IT
http://www.atmarkit.co.jp/fwcr/design/ux/win7_01/01.html
ManipulationDelta を知ろう – 高橋 忍のブログ – Site Home – MSDN Blogs
http://blogs.msdn.com/b/shintak/archive/2012/05/01/10299351.aspx

マウスやタッチの移動をどうやって取得するか?なのですが、MouseMove は大変(つーか、タッチの場合は Mouse イベントは発生しません)、Thumb コントロールを使ったパターンも作ったのですが、そこそこ大変。で、ManipulationDelta イベントを取得するのが簡単ってことです。

# これ、WPF、Windows Phone にもあるので見落としていた模様 orz

画面は、Grid に直接配置するパターンがあるのですが、今回は Canvas を使っています。こっちのほうは xy 座標を直接指定できるので楽かなと。あとレイアウト変更に関係ないから描画が早いかも、っていう想像です。

ManipulationDelta イベント自体は、それぞれの Image コントロールに設定してもよいのですが、いくつか調べると、

  • コンテナ(Grid とか Canvas とか) ManipulationDelta イベントを設定する。
  • 動かしたい Image コントロールに ManipulationMode=”All” を設定する。

ってことで動きました。この例では、顔(face.png”)は動かさないようにするので、ManipulationMode を設定しません。

■ドラッグさせるコード

移動したとき ManipulationDelta イベントで、e.Delta を使うと、前回の移動からの差分が取得できます。絶対値がほしい場合は e.Position ですが、今回は使っていません。

/// <summary>
/// 移動中のイベント
/// </summary>
/// <param name=&quot;sender&quot;></param>
/// <param name=&quot;e&quot;></param>
private void el_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
    var el = e.OriginalSource as UIElement;
    var img = el as Image;

    double x = Canvas.GetLeft(el) + e.Delta.Translation.X;
    double y = Canvas.GetTop(el) + e.Delta.Translation.Y;

    // 画面からはみ出ないようにする
    if (-img.ActualWidth / 2 <= x && x <= canvas.ActualWidth - img.ActualWidth / 2 &&
            -img.ActualHeight / 2 <= y && y <= canvas.ActualHeight - img.ActualHeight / 2)
    {
        Canvas.SetLeft(el, x);
        Canvas.SetTop(el, y);
    }
}

Grid を使う場合には img.Margin = new Thickness(…) とするのですが、Canvas の場合は Canvas.SetLeft と Canvas.SetTop を使います。
実際に指やマウスを使って動かすとわかるのですが、すーっと「慣性」が働いてどこかに行ってしまう(笑)ので、画面の端(実際には Canvas の端)で止まるようにしています。

■で、拡大と回転とかできないの?

早速、遊ばしてみるとうちの奥さんから「で、拡大とかはできないの?」っていう要求が…ええ、以前作っていた Thumb コントロールの場合その手のイベントを拾うのが大変そうだったのでやめていたのですが、ManipulationDelta であれば大丈夫そうですね。これは、後ほど実装をしましょう。

ってなわけで続く。

カテゴリー: C#, windows 8 | 1件のコメント

[win8] DefaultViewModel と DataContext は同時に使えない

昨日の続きで、気になるところをチェック。

もともと、WPF とか Silverlight では DataContext を使っていた…ような気がするので、じゃあ、Windows ストア アプリで登場した「DefaultViewModel」との違いは? とか、同時に使えるのか? ってところを試してみます。

ええ、結論から言えば「同時には使えません」。と言うか、おそらく DefaultViewModel のマップオブジェクトは内部動作として DataContext を使っているだろうから、同時には使えんのだろう、ってのが結論ですね。

■画面の準備

実験的に下記のような表を作っておきます。

<Grid HorizontalAlignment=&quot;Left&quot; Height=&quot;243&quot; Margin=&quot;32,38,0,0&quot; Grid.Row=&quot;1&quot; VerticalAlignment=&quot;Top&quot; Width=&quot;727&quot;>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width=&quot;200&quot;/>
        <ColumnDefinition Width=&quot;261*&quot;/>
        <ColumnDefinition Width=&quot;266*&quot;/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height=&quot;1*&quot;/>
        <RowDefinition Height=&quot;1*&quot;/>
        <RowDefinition Height=&quot;1*&quot;/>
        <RowDefinition Height=&quot;*&quot;/>
    </Grid.RowDefinitions>
    <TextBlock Grid.Row=&quot;0&quot; FontSize=&quot;24&quot; Margin=&quot;10&quot; TextWrapping=&quot;Wrap&quot; Text=&quot;ID&quot;/>
    <TextBlock Grid.Row=&quot;1&quot; FontSize=&quot;24&quot; Margin=&quot;10&quot; TextWrapping=&quot;Wrap&quot; Text=&quot;Name&quot;/>
    <TextBlock Grid.Row=&quot;2&quot; FontSize=&quot;24&quot; Margin=&quot;10&quot; TextWrapping=&quot;Wrap&quot; Text=&quot;Address&quot;/>
    <TextBlock Grid.Row=&quot;3&quot; FontSize=&quot;24&quot; Margin=&quot;10&quot; TextWrapping=&quot;Wrap&quot; Text=&quot;Telephone&quot;/>
    <!-- Buiding by DataContext -->
    <TextBlock 
        Text=&quot;{Binding ID}&quot;
        Grid.Row=&quot;0&quot; Grid.Column=&quot;1&quot; FontSize=&quot;24&quot; Margin=&quot;10&quot; TextWrapping=&quot;Wrap&quot;/>
    <TextBlock 
        Text=&quot;{Binding Name}&quot;
        Grid.Row=&quot;1&quot; Grid.Column=&quot;1&quot; FontSize=&quot;24&quot; Margin=&quot;10&quot; TextWrapping=&quot;Wrap&quot; />
    <TextBlock 
        Text=&quot;{Binding Address}&quot;
        Grid.Row=&quot;2&quot; Grid.Column=&quot;1&quot; FontSize=&quot;24&quot; Margin=&quot;10&quot; TextWrapping=&quot;Wrap&quot; />
    <TextBlock 
        Text=&quot;{Binding Telephone}&quot;
        Grid.Row=&quot;3&quot; Grid.Column=&quot;1&quot; FontSize=&quot;24&quot; Margin=&quot;10&quot; TextWrapping=&quot;Wrap&quot;/>

    <!-- Buiding by DefaultViewModel -->
    <TextBlock 
        Text=&quot;{Binding Item.ID}&quot;
        Grid.Row=&quot;0&quot; Grid.Column=&quot;2&quot; FontSize=&quot;24&quot; Margin=&quot;10&quot; TextWrapping=&quot;Wrap&quot;/>
    <TextBlock 
        Text=&quot;{Binding Item.Name}&quot;
        Grid.Row=&quot;1&quot; Grid.Column=&quot;2&quot; FontSize=&quot;24&quot; Margin=&quot;10&quot; TextWrapping=&quot;Wrap&quot; />
    <TextBlock 
        Text=&quot;{Binding Item.Address}&quot;
        Grid.Row=&quot;2&quot; Grid.Column=&quot;2&quot; FontSize=&quot;24&quot; Margin=&quot;10&quot; TextWrapping=&quot;Wrap&quot; />
    <TextBlock 
        Text=&quot;{Binding Item.Telephone}&quot;
        Grid.Row=&quot;3&quot; Grid.Column=&quot;2&quot; FontSize=&quot;24&quot; Margin=&quot;10&quot; TextWrapping=&quot;Wrap&quot;/>
</Grid>

何をやっているかというと、左に DataContext でのバインディング、右に DefaultViewModel でのバインディングの表を作っているだけです。

■実験コード

  • DataContext だけで設定するパターン
  • DefaultViewModel をだけで使うパターン
  • 両方を使うパターン

の3つを用意しておきます。

/// <summary>
/// DataContext に設定する
/// </summary>
/// <param name=&quot;sender&quot;></param>
/// <param name=&quot;e&quot;></param>
private void clickDataContext(object sender, RoutedEventArgs e)
{
    var item = new DataModel()
    {
        ID = 10,
        Name = &quot;masuda&quot;,
        Address = &quot;Tokyo&quot;,
        Telephone = &quot;03-0000-0000&quot;
    };
    this.DataContext = item;

}

/// <summary>
/// DefaultViewModel に設定する
/// </summary>
/// <param name=&quot;sender&quot;></param>
/// <param name=&quot;e&quot;></param>
private void clickDefaultViewModel(object sender, RoutedEventArgs e)
{
    var item = new DataModel()
    {
        ID = 10,
        Name = &quot;tomoaki&quot;,
        Address = &quot;Osaka&quot;,
        Telephone = &quot;06-1111-0000&quot;
    };
    this.DefaultViewModel[&quot;Item&quot;] = item;
}

/// <summary>
/// 両方で設定する
/// </summary>
/// <param name=&quot;sender&quot;></param>
/// <param name=&quot;e&quot;></param>
private void clickDual(object sender, RoutedEventArgs e)
{
    var item1 = new DataModel()
    {
        ID = 10,
        Name = &quot;masuda&quot;,
        Address = &quot;Tokyo&quot;,
        Telephone = &quot;03-0000-0000&quot;
    };
    var item2 = new DataModel()
    {
        ID = 10,
        Name = &quot;tomoaki&quot;,
        Address = &quot;Osaka&quot;,
        Telephone = &quot;06-1111-0000&quot;
    };
    this.DataContext = item1;
    this.DefaultViewModel[&quot;Item&quot;] = item2;

}

さて、これで Dual ボタンをクリックするとどうなるのか?両方にうまく表示されるのか?というと、実は違って、DataContext のほうだけが表示されます。

なんか不思議ですが「DataContext」と「DefaultViewModel」のボタンを交互に押すと、「DataContext」のほうしか残りません。つーか、おそらく DefaultViewModel のほうは上書きされちゃったのでしょう。
なので、うっかり DataContext を使ったコードを流用すると、新しい DefaultViewModel を使ったものが動かなくなる、っていう「落とし穴」がありますね。まあ、どちらか片方の方式で統一するということで。

ちなみに、DataContext のほうは、オブジェクトを渡して内部のプロパティ名を Binding するので、複数のオブジェクトをバインディングすることができないという罠があります。コンテキストをひとつの DataModel としてまとめられればいいのですが、画面によっては複数のオブジェクトを割り付けたい場合は多々あります。
なので、DefaultViewModel のように、複数のオブジェクト(Item オブジェクトと SubItem オブジェクトとか)を渡せるのは、改善なんでしょう、やっぱり。

カテゴリー: C#, windows 8 | [win8] DefaultViewModel と DataContext は同時に使えない はコメントを受け付けていません

[Win8] ひとつだけのデータバインドを metro アプリではどう実装するのか?

ちょっとメモ書き的に。

metro アプリでは、XAML を使ってデータバインディング、ってのが主流?なのですが、プロジェクトテンプレートを見ると、GridView とか FlipView とかの「コレクション」に対するバインディングは多いのですが、ひとつのアイテムだけをバインディング、っていう簡単な手法が示されていない…んですよね。たぶん。見つけ方が悪いだけなのかな?

ここのところ、MVVM パターンのほうはさておき、実装的にどうすれば「教えやすいのか?」ってのを考えると、
コードを使ってちまちまとコントロールの Text プロパティやらに設定していく、ってのが一番安全な方法だと思うわけです。で、まあ、それでも良いのですが、せっかく MVVM パターンのテンプレートを使っているわけだし、それなりのバインディング用のクラス「BindableBase」があるわけで、これを活用しようかな、と悩んでいたわけですが。

■モデルクラスを作る

namespace SampleDataBinding.DataModel
{
    class MainItemSource : BindableBase
    {
        private int _id;
        public int ID {
            get { return this._id ; }
            set { this.SetProperty(ref this._id, value); }
        }

        private string _name ;
        public string Name {
            get { return this._name; }
            set { this.SetProperty(ref this._name, value); }
        }

        private string _address ;
        public string Address {
            get { return this._address; }
            set { this.SetProperty(ref this._address, value); }
        }

        public override string ToString()
        {
            return string.Format("{0}:{1}", this._id, _name);
        }
    }
}

ひとつの Model がひとつの View に対応するという一番単純な例です。この単純さだと、Text プロパティにちまちまでもいいのです
「基本ページ」などでインポートされる、BindableBase クラスを継承してモデルクラスを作ります。コレクションに利用するのではなくて、が、バインディングの練習がてらに。

■リソース経由で参照させるパターン

以前から使われているリソースから直接渡すパターンです…と思います。
「xmlns:model=”using:SampleDataBinding.DataModel”」という名前空間をあらかじめ指定しておいて、スタティックリソースに「DataSource」という名前をつけます。MainItemSource クラスを直接参照できるので、便利といえば便利なのですが。

    <Page.Resources>
        <!-- TODO: Delete this line if the key AppName is declared in App.xaml -->
        <x:String x:Key=&quot;AppName&quot;>My Application</x:String>
        <model:MainItemSource x:Key=&quot;DataSource&quot; />
    </Page.Resources>

下のように、Source=”{StaticResource DataSource}” が必要になります。デフォルトのデータソースを指定できたような気もするのですが、ちょっと忘れてしまいました

<TextBlock x:Name=&quot;labelID&quot; 
     Text=&quot;{Binding ID, Source={StaticResource DataSource}}&quot;
     HorizontalAlignment=&quot;Left&quot; Margin=&quot;272,73,0,0&quot; Grid.Row=&quot;1&quot; 
	TextWrapping=&quot;Wrap&quot; VerticalAlignment=&quot;Top&quot; FontSize=&quot;24&quot; Height=&quot;29&quot;>
 </TextBlock>
 <TextBlock x:Name=&quot;labelName&quot;
     Text=&quot;{Binding Name, Source={StaticResource DataSource}}&quot;
     HorizontalAlignment=&quot;Left&quot; Margin=&quot;272,123,0,0&quot; Grid.Row=&quot;1&quot; 
	TextWrapping=&quot;Wrap&quot; VerticalAlignment=&quot;Top&quot; FontSize=&quot;24&quot;/>

ここで問題なのは、MainItemSource のオブジェクトってどこでで作成されているんだろうか?というのと、いったい、ID とか Name とかはどこで設定すればよいのか? ってことなのです。業務的には、よくわからないから、いきおい MainItemSource クラスを変更して static であらかじめ初期値を入れておいたり、ってことになるわけですが、それはちょっとあんまりなコードですよね。

…と、ここまで書いて思い出しました。View に DataContext というプロパティがありました。
XAML のほうを、次のようにバインディングする ID や Name プロパティ名を指定しておきます。

<TextBlock x:Name=&quot;labelID&quot; 
     Text=&quot;{Binding ID}&quot;
     HorizontalAlignment=&quot;Left&quot; Margin=&quot;272,73,0,0&quot; Grid.Row=&quot;1&quot; 
	TextWrapping=&quot;Wrap&quot; VerticalAlignment=&quot;Top&quot; FontSize=&quot;24&quot; Height=&quot;29&quot;>
 </TextBlock>
 <TextBlock x:Name=&quot;labelName&quot;
     Text=&quot;{Binding Name}&quot;
     HorizontalAlignment=&quot;Left&quot; Margin=&quot;272,123,0,0&quot; Grid.Row=&quot;1&quot; 
	TextWrapping=&quot;Wrap&quot; VerticalAlignment=&quot;Top&quot; FontSize=&quot;24&quot;/>

でもって、コードのほうで

protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
{
    var item = new MainItemSource()
    {
        ID = 10,
        Name = &quot;masuda&quot;,
        Address = &quot;Tokyo&quot;
    };
    this.DataContext = item;
}

という形で MainItemSource オブジェクトを設定しておけばよいわけです。なるほど、これで十分動きます。

■DefaultViewModel を使う

そんなわけで、もうひとつの方法を。
XAML をもういちど見ていくと、

<common:LayoutAwarePage
    x:Name=&quot;pageRoot&quot;
    x:Class=&quot;SampleDataBinding.BasicPage1&quot;
    DataContext=&quot;{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}&quot;
    xmlns=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;

DataContext なところがあります。いくつか解説もあるのですが、要するに PHP 風や、ASP.NET の ViewState 風に画面のほうにデータを引き渡せる手段です。

Windowsストアアプリにおける グリッドアプリケーションについて(1) – 荒井省三のBlog – Site Home – MSDN Blogs
http://blogs.msdn.com/b/shozoa/archive/2012/10/18/about-grid-application-1-on-windows-store-application.aspx

下記のように、DefaultViewModel[“Item”] を使って「Item」という名前をつけておくと、

protected override void LoadState(Object navigationParameter, Dictionary pageState)
{
    var item = new MainItemSource()
    {
        ID = 10,
        Name = "masuda",
        Address = "Tokyo"
    };
    this.DefaultViewModel["Item"] = item;
}

こんな風に「Item.ID」とか「Item.Name」とかで MainItemSource オブジェクトの中身が参照できます。

<TextBlock x:Name=&quot;labelID&quot; 
    Text=&quot;{Binding Item.ID}&quot;
    HorizontalAlignment=&quot;Left&quot; Margin=&quot;272,73,0,0&quot; Grid.Row=&quot;1&quot; 
    TextWrapping=&quot;Wrap&quot; VerticalAlignment=&quot;Top&quot; FontSize=&quot;24&quot; Height=&quot;29&quot;>
</TextBlock>
<TextBlock x:Name=&quot;labelName&quot;
    Text=&quot;{Binding Item.Name}&quot;
    HorizontalAlignment=&quot;Left&quot; Margin=&quot;272,123,0,0&quot; 
    Grid.Row=&quot;1&quot; TextWrapping=&quot;Wrap&quot; VerticalAlignment=&quot;Top&quot; FontSize=&quot;24&quot;/>

なるほど、こっちのほうがわかりやすい気がします。

ちなみに、ボタンを配置して、

private void Button_Click_1(object sender, RoutedEventArgs e)
{
    _item.Name = "masuda tomoaki";
}

のように、Name プロパティの値を変更すると、画面の表示も変わります。MainItemSource クラスの SetProperty が効いているということですね。
そんなわけで、DefaultViewModel を使うほうが、直感的でわかりやすいかなと。まあ、バインディング自体を XAML に記述するところがアレですが、ひとまずこれで。

カテゴリー: C#, windows 8 | 1件のコメント

フューチャーワークス カンファレンスを開催します

来る 12/1(土) 13時半から「フューチャーワークス カンファレンス」開催します。
場所は「日本工学院八王子専門学校」です。

image

主催は増田(moonmile)個人ですが(笑)、手弁当でプレゼントなぞ用意します。

Professional Programmer’s Club
http://pp-club.net/

PP-Club の活動(?)の一環として、啓蒙…でもないのですが、やる気のある学生さんプログラマや新人プログラマの「やる気を削がない」と、その先に「何をしたらいいのか?」ってことをお話します。話自体は、一般的な技術論とかマネージメントとかではなくて、「自分が現実に今やっていることがら」が中心になりますので、それぞれの「現場」の雰囲気がわかると思います。IT 業界って、ゲーム業界とか組み込み系やら情報系やらサーバー管理やら、なにやらといっぱいあって実は「選択肢は広いッ!!!」ってことです。

スピーカーは、

・Lonnie&Associates株式会社 高橋さん
・株式会社イーオス21 CEO 鈴木さん
・片柳学園 サーバ管理者 水庭さん
・ワタクシ 増田

です。

当日は、

・PP-Club ロゴ入りのクリアファイルを参加者全員にッ!!!
・抽選で、増田著作の「逆引き本」「ひと目本」を数名様にッ!!!
・抽選で、iPad mini 1名様にッ!!!

今回は、はじめての開催ということでプレゼントは多少「豪華め」に用意いたしました。ええ、手弁当なもので、そのあたりは察していただければよいかと(苦笑)。

参加申し込みは <a href=”http://pp-club.net/”>こちら</a> 「参加受付中」あたりをクリックして進んでください。

カンファレンス終了後には、八王子駅あたりで「懇親会」(単なる打ち上げとも言う)を予定していますので、ぜひ参加してください。学生の方は、いわゆる「後輩価格」にする予定です(まあ、人数と私の懐具合にもよりので、そのあたりは大人の判断で)。

ぼちぼち、無料プレスを発射しますので、そのあたりも参考にしていただければよいかと思います。

カテゴリー: PP-Club | フューチャーワークス カンファレンスを開催します はコメントを受け付けていません

学生向けカンファレンスをやります

1週間ほど死ぬ気で仕事をしておりましたが、やっと持ち直してきたので告知します。いや、単に死ぬ気でやるよりも、「楽しんで仕事をやろうッ!!!」ってのがワタクシの趣旨だったことを思い出したわけで。

とある八王子な場所で、学生向けカンファレンス(とはいえ、社会人もOKです)をやります。

とある「87 Clockers」監修な方、およびワタクシ、かつ諸々で手弁当で講演などをさせていただきます。手弁当なもので、なんらかの形で「手弁当」をしようかなと思っているのですが、予定としては、

  • (いらないかもしれないけど)資料を配布するためのクリアファイルを、参加者全員に
  • 「87 CLOCKERS」を(可能ならば)サイン本で、数名に
  • ワタクシの著書を(希望があればサインして)、数名に
  • iPad mini を抽選で1名様にッ!!!

ってのを考えているのですが、どうでせうか?

本格的な告知は、数日後に  http://pp-club.net/ で行いますので、ご期待ください。

カテゴリー: 雑談, PP-Club, 勉強会 | 学生向けカンファレンスをやります はコメントを受け付けていません