午後わてんのブログ

ベランダ菜園とWindows用アプリ作成(WPFとC#)

WPFとVB.NET、アプリでの編集状態保存、名前を付けて保存、回転角度を指定する2つの方法

 
前回は2日前の

gogowaten.hatenablog.com

WPFVB.NETでDataContextやBinding、INotifyPropertyChangedの練習 ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
http://blogs.yahoo.co.jp/gogowaten/14088361.html
の続き
 
前回のアプリに今の状態をファイルに保存する機能とそのファイルを読み込む機能をつけた、名前を付けて保存とファイルを開く、ゲームで言えばセーブとロード
 
 
表示した二つの画像の縦横回転角度をスライダーとボタンで変更
イメージ 1
前回と違うのはBorderから実際の画像ファイルをImageに表示したのと
回転角度を変更できるようになったところ
 
セーブとロード
状態の保存(セーブ)
イメージ 2
Saveボタンクリックで
 

f:id:gogowaten:20191025132556p:plain

保存画面が出るので適当な名前を付けて保存
ここではsave1にした
イメージ 5
保存されたsave1.zipファイルの中身
 
 
保存したファイルを開く
アプリを再起動で初期状態に戻す
イメージ 4
Loadボタンクリックで
 

f:id:gogowaten:20191025132611p:plain

ファイルを開く画面が出るのでさっき保存したsave1を開くと
 
イメージ 7
保存した時の状態になる
 
 
デザイン画面とXAML

f:id:gogowaten:20191025132623p:plain

VBコード

f:id:gogowaten:20191025132635p:plain

またヤフーブログの文字数制限に引っかかったのでコード部分も画像だけ…
 
 
セーブファイルはZip形式で保存するので準備として
System.IO.Compressionを参照に追加しておく
方法は
WPFVB.NETでデータをシリアライズしてZipファイルに保存 ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
http://blogs.yahoo.co.jp/gogowaten/14077359.html
 
 
縦横、回転角度を入れておくクラスSaveData
イメージ 10
前回とほとんど同じだけどこのクラスをシリアライズしてファイルに保存するので
クラスの宣言の頭にSerializableを付けて(230行目)これで良しと実行してみたら
 
イメージ 11
SerializationException はハンドルされませんでした。
'System.ComponentModel.PropertyChangedEventManager' はシリアル化可能として設定されていません。
っていうエラーが出た
 
エラーの内容を見るとPropertyChangedってあるからそれっぽい
Public Event PropertyChangedにシリアライズしません!属性のNonSerializedをつけたら、うまく行った(234行目)けどよくわからん
 
 
MainWindow
イメージ 12
グローバル変数、選択中の画像の識別のためにImageを入れておくImaImageと
BitmapSource、SaveData、Imageを入れておくリストが3つ
リストはObservableCollectionも使っているけど、普通のListでもいいのかも
 
アプリ起動時に実行するinitial
イメージ 13
BitmapImageを作成してリストに追加
Imageを作成してリストに追加
SaveDataを作成してリストに追加
 
Imageの作成の時に回転角度を設定する必要があったので74行目で0度を指定していたけど
イメージ 24
.RenderTransform = New RotateTransform(0)じゃなくて
.RenderTransform = New RotateTransformでよかった
角度指定しなくてもRotateTransformを入れるだけでよかったみたい
今気づいた
 
 
Imageの縦横回転角度とSaveDataとSliderのバインディング
Binding、DataContextの設定
この処理はアプリの起動時とセーブファイル読み込んだ直後に実行

f:id:gogowaten:20191025132701p:plain

ここが今回の一番の改良点
バインディングのモードにTwoWay(双方向)を指定するようにした
これでImageのWidthやHeightを変更した時でもSaveDataのWidthやHeightも変更されるようになった
SaveDataとImageのWidthプロパティをバインディングする時
前回は
Image.SetBinding(WidthProperty, New Binding("Width"))
今回は
Dim binding as New Binding("Width")
binding.Mode = BindingMode.TwoWay
Image.SetBinding(WidthProperty, binding)
 
さらに回転角度のBindingの指定の方法は
BindingOperations.SetBinding(Image.RenderTransform, RotateTransform.AngleProperty, binding)
これがわからなくてググッて参照したのがこちら
WPFxamlと同等の内容をC#のコードで書いた例 | ザワプロ!
http://zawapro.com/?p=938
ありがとうございます
 
47行目から49行目までの3行は書く場所間違えている、問題なく動くけど本当は

f:id:gogowaten:20191025132713p:plain

Nextの外側のここだわ、今気づいた
 
 
名前を付けて保存(セーブ)

f:id:gogowaten:20191025132724p:plain

画像ファイルをTiffエンコード、SaveDataはシリアライズ、この2つを1つのZipファイルにするこの部分は
 
WPFVBで複数画像(BitmapSource)とシリアライズした文字列(String)や数値(Integerとか)を1つのZipファイルにする ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
http://blogs.yahoo.co.jp/gogowaten/14079902.html
この時とほぼ同じ、だけどムダなことしていたっぽいところを除いた
Stream関連はわかっていないからまだムダなことしてるかも
Zipの圧縮レベル指定にCompressionLevel.Fastestを指定するようにしたけど
指定しなくても同じみたい
 
保存したZipファイルを読み込み

f:id:gogowaten:20191025132736p:plain

こちらもムダを取り除いたのと
画像読み込んでリストに追加するとことを少し変えただけ
For i As Integer = 0 to TiffBitmapDecoder.Frames.Count -1
bitmapList.Add(TiffBitmapDecoder.Frames(i))
Next
これを
bitmapList.AddRange(TiffBitmapDecoder.Frames)
に変えた
 

f:id:gogowaten:20191025132755p:plain

この辺は各イベントの時にさっきまでのを呼び出しているだけ
 
画像をクリックした時の処理

f:id:gogowaten:20191025132806p:plain

これもBorderがImageに変わっただけで前回と同じ
 
Button1と2を押した時の処理

f:id:gogowaten:20191025132821p:plain

選択中の画像の横幅と回転角度を変えている
Button1での処理は選択中のImageのDataContextからSaveDataを取得して
その中のプロパティに値を指定している
Button2での処理はDataListの中からSaveDataを取得して
その中のプロパティに値を指定している
どちらもできるみたいなのでどちらを使おうかなあってところ
特別なことがなければButton1の方法がラクかな
 
Button3クリックした時の処理

f:id:gogowaten:20191025132831p:plain

横幅と回転角度を選択中のImageに直接指定している
前回はこれが期待通りに動かせなかったけど解消できた
1つはさっき(19行目から)のバインディングのモードを双方に指定することで
もう1つがさっき(26行目)でBindingOperationsを使ってバインディングする方法に関連しているけど、回転角度の指定の方法を変えたこと
回転角度を60度に指定する時
前回は
Image.RenderTransform = New RotateTransform(60)
こうしていたのを今回は
Image.RenderTransform.SetValue(RotateTransform.AngleProperty, 60.0R)
こう変えた
 
回転角度の指定の方法が良くないんだなあと思っていて、それでもRenderTransformはあっているだろうから、その中でなんか良さそうなのがないかなあって一覧表を眺めていたら

f:id:gogowaten:20191025132842p:plain

SetValue、これ良さそう、でも使い方がわからん
助かりました
 
前回の方法だとバインディングが外れてしまったように無効になっていたのは、RotateTransformを上書きする形になっていたのから?
今回の方法ではRotateTransformはそのままで、そのなかのAnglePropertyの値だけ変更するような形だからバインディングはそのまま保たれているってことかなあ
あとはAnglePropertyに指定する値はDouble型だからなのか普通に60って指定するとエラー
イメージ 23
これはなんか方法があったはずだなあってググッてこちら
ありがとうございます
 
60.0RってRを数値の後ろに付け加えればいいみたい
他には#を付けるか、小数点以下まで入れる
でもなんでRなんだろうねえ
 
そんなこんなでなんとかできた
今回のでテストが終わったので次は本番のPixtack紫陽花2ndに名前を付けて保存を付ける、時間かかりそうだなあ
 
Imageとかの回転角度を指定するときは最初だけ
Image.RenderTransform = New RotateTransform(30)
こんなふうにNewでRotateTransformを作ったのを指定して
それ以降は例えば60度に変更するときはSetValueを使って
Image.RenderTransform.SetValue(RotateTransform.AngleProperty, 60.0R)
こうするのがいい…のかな
 
コード
 
 
 
関連記事
WPFVB.NET、複数のBitmapSourceを1つにしてシリアライズしてファイルに保存 ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
http://blogs.yahoo.co.jp/gogowaten/14072509.html
WPFVB.NETでデータをシリアライズしてZipファイルに保存 ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
http://blogs.yahoo.co.jp/gogowaten/14077359.html
WPFVBで複数画像(BitmapSource)とシリアライズした文字列(String)や数値(Integerとか)を1つのZipファイルにする ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
http://blogs.yahoo.co.jp/gogowaten/14079902.html
WPFVB.NETでDataContextやBinding、INotifyPropertyChangedの練習 ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
http://blogs.yahoo.co.jp/gogowaten/14088361.html
 
2016年4月29日追記
続き、今回の記事では足りないところがあった
WPFVB.NET、Bindingしたままコントロールを直接変形、TransformGroupの中のRotateTransform ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
http://blogs.yahoo.co.jp/gogowaten/14098125.html