Feeds:
投稿
コメント

Archive for 2012年4月

久々のMetro Style Apps記事

Metro Style AppsにはIsolatedStorageSettingsなクラスが存在しません。ですがWP7から移行する場合はこの辺使えると便利かなーと思うところがあると思います

そこでちょっと調べるとApplicationDataContainerSettings(Windows.Storage Namespace)があることが分かります

で、これにアクセスする方法なんですけどシンボルの検索しても使ってるよぅなプロパティは見当たりません
これはApplicationDataContainerSettingsにIPropertySetが実装されてるところがポイントです

つまりApplicationDataContainerSettingsはIPropertySetを通じて提供されます

このIPropertySetはApplicationDataContainer.Values Property[Windows.Storage namespace]で取得できます

で、このApplicationDataContainerってどうやってとるんですって話ですがApplicationDataにShared(static)なプロパティのCurrentがあるのでこれを利用してコンストラクタ代わりにインスタンス取得します。次にその中のLocalSettingsかRoamingSettingsがApplicationDataContainerを返すのでこの辺使います

Settings取れたらあとは普通に扱うだけですたぶん
Saveがないのですけど自動で保存されるのかな?
試してないのでわかりません

適当なサンプルは次の通り

Public Sub SaveObjAdd(key As String,instance As Object)
    Dim settings = ApplicationData.Current.LocalSettings.Values
    settings.Insert(key,instance)
End Sub

とりあえずこんくらいで

広告

Read Full Post »

WinRTが盛り上がってますがWP7な記事です

今回はhttps://fantasticswallow.wordpress.com/2012/04/10/controls-listbox-scroll-when-itemupdate/のxaml編です

xamlで何するのか、それはとても簡単です。イベントをハンドルします
コードビハインド?いやバインドしてるしビヘイビア書きます! とかもだいたい同じような感じです
今回はPivot+Listbox複数で切り替えるとハンドルしてるのがすべて解除されるのがめんどくさいとかその辺の場合に対処します

では始めましょう

上の記事通りにコードビハインド書いたとしてVerticalScrollBar_LayoutUpdatedがあるものとして話を進めます。そんな名前じゃないって場合は脳内補完でお願いします

そもそもxamlでどうするかといえば最終的には次のコードが書ければいいわけです

<ScrollBar x:Name="VerticalScrollBar" LayoutUpdated="VerticalScrollBar_LayoutUpdated" />

プロパティがっつり削除してますが気にしなくて大丈夫です
とりあえずこうすればコントロール生成時にハンドルしてくれていちいちGetChildだのなんだのしてAddHandlerしなくて済みます

ですが今回はListBoxの中のScrollViewerの中のScrollBarが対象なわけです。普通には書けません
ここで何使おうとしてるかわかる人すでにいる気がしますがStyleを使います
Expression Blendを使いましょう。たぶんWP7開発できる人なら全員持ってるはずです
ListBoxを右クリック→テンプレートの編集→コピーして編集 でControlTemplateの中身がコピーされるかと思います

Expression BlendからStyleの変更をするときの画面


適当な操作画面はこんな感じです

すると次のようなxamlがコピーができると思います

<Style TargetType="ListBox">
	<Setter Property="Background" Value="Transparent"/>
	<Setter Property="Foreground" Value="{StaticResource PhoneForegroundBrush}"/>
	<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
	<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
	<Setter Property="BorderThickness" Value="0"/>
	<Setter Property="BorderBrush" Value="Transparent"/>
	<Setter Property="Padding" Value="0"/>
	<Setter Property="Template">
		<Setter.Value>
			<ControlTemplate TargetType="ListBox">
				<ScrollViewer x:Name="ScrollViewer" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Foreground="{TemplateBinding Foreground}" Padding="{TemplateBinding Padding}">
					<ItemsPresenter/>
				</ScrollViewer>
			</ControlTemplate>
		</Setter.Value>
	</Setter>
</Style>

結構Silverlightのxamlよりはすっきりしてますね

で、次に同様の作業をします。さっきのxamlのScrollViewerのStyleも定義しましょう
問題は非常に残念なことにListBoxのStyleからStyle定義できませんので一時的にScrollViewer貼り付けてStyle定義するとかいろいろやりましょう

とりあえずそんな感じにやってみると次のxamlになります

<Style x:Key="ScrollViewerStyle1" TargetType="ScrollViewer">
	<Setter Property="VerticalScrollBarVisibility" Value="Auto"/>
	<Setter Property="HorizontalScrollBarVisibility" Value="Disabled"/>
	<Setter Property="Background" Value="Transparent"/>
	<Setter Property="Padding" Value="0"/>
	<Setter Property="BorderThickness" Value="0"/>
	<Setter Property="BorderBrush" Value="Transparent"/>
	<Setter Property="Template">
		<Setter.Value>
			<ControlTemplate TargetType="ScrollViewer">
				<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}">
					<VisualStateManager.VisualStateGroups>
						<VisualStateGroup x:Name="ScrollStates">
							<VisualStateGroup.Transitions>
								<VisualTransition GeneratedDuration="00:00:00.5"/>
							</VisualStateGroup.Transitions>
							<VisualState x:Name="Scrolling">
								<Storyboard>
									<DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="VerticalScrollBar"/>
									<DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="HorizontalScrollBar"/>
								</Storyboard>
							</VisualState>
							<VisualState x:Name="NotScrolling"/>
						</VisualStateGroup>
					</VisualStateManager.VisualStateGroups>
					<Grid Margin="{TemplateBinding Padding}">
						<ScrollContentPresenter x:Name="ScrollContentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}"/>
						<ScrollBar x:Name="VerticalScrollBar" HorizontalAlignment="Right" Height="Auto" IsHitTestVisible="False" IsTabStop="False" Maximum="{TemplateBinding ScrollableHeight}" Minimum="0" Opacity="0" Orientation="Vertical" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" Value="{TemplateBinding VerticalOffset}" ViewportSize="{TemplateBinding ViewportHeight}" VerticalAlignment="Stretch" Width="5"/>
						<ScrollBar x:Name="HorizontalScrollBar" HorizontalAlignment="Stretch" Height="5" IsHitTestVisible="False" IsTabStop="False" Maximum="{TemplateBinding ScrollableWidth}" Minimum="0" Opacity="0" Orientation="Horizontal" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" Value="{TemplateBinding HorizontalOffset}" ViewportSize="{TemplateBinding ViewportWidth}" VerticalAlignment="Bottom" Width="Auto"/>
					</Grid>
				</Border>
			</ControlTemplate>
		</Setter.Value>
	</Setter>
</Style>

かなり長くて嫌になりますね
ちなみにVisualStateいじってる方はこんなん使わなくてもいいと思います

さっきのxamlの中にVerticalScrollBarがありますのでそこのLayoutUpdatedをごにょごにょしましょう
(コードコピペするの疲れた)

さて次にStyleを適用しましょう
上の上にありますthe ScrollViewerさんのxamlのStyleを上のStyleに合わせます
上のStyleはx:Key=”ScrollViewerStyle1″となってますのでそのまま使いましょう

<ScrollViewer x:Name="ScrollViewer" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Foreground="{TemplateBinding Foreground}" Padding="{TemplateBinding Padding}" Style="{StaticResource ScrollViewerStyle1}">

対象のListBoxにも同様にしてStyle適用すれば完璧です
勝手にスクロール制御されます

とりあえずこのくらいで

Read Full Post »

Twiangle v1.9

更新通知しか書いていない

というわけで9時ごろメールが着弾したのでご報告です

とりあえずVer1.9となります
変更点は次の通りです
(・表示しているリストを切り替えるとスクロール制御がおかしくなるのを修正 )
・Urlを開くポップアップでpic.twitter.comの画像をプレビュー可能に 
・お気に入り削除が表示上適用されていないのを修正 
・会話ツリーを検索可能に

この中でスクロール制御がおかしくなる問題直ってないです
Style変更したほうがいいかもしれないなぁ

pic.twitter.comの画像が今までt.coのまま展開されてなかったのに対応するついでにプレビューをURLのとこでできるようにしました
お気に入り削除回りは呼ぶメソッド間違ってたので修正しておきました

会話ツリーはツイート検索ページで検索することで可能になってます
独自クエリ式が使用可能なので適当にどうぞ

現在既知の不具合は次の通りです
・URLだけの時Popupが開かない
・選択中のリストを切り替えるとスクロール制御がおかしくなる

Popupが開かない方は原因特定済みなので何とかしたいところ
スクロール制御はうまくいかない

とりあえずこのくらいで

Read Full Post »

Twiangle v1.8

Twiangleのv1.8の申請が通った報告です

http://www.windowsphone.com/ja-JP/apps/7ef40ada-1a39-4507-8167-68991c357bb4から
…すでに公開されてた…だと…?

今回の変更は次の通りです
 ・リアルタイム取得が有効な時にイベントを通知できるように
 ・ツイートを取得したときに表示している位置が元の位置からずれていたのをなるべくずれないように
 ・通信をgzipで圧縮して通信できるように
 ・設定を更新するときに同時に保存するように
 ・リストの選択するときのフォントが小さかったのを大きめに
 ・その他バグ修正など

イベント通知はトーストで出します。お気に入り追加、フォロー通知とあとリプライ受信です

表示している位置なんちゃらはスクロール制御のあれです。前の記事のやつです

通信時にgzipをサポートするようにしたので通常の取得は少し早くできます。試用版では不可です
ちなみにリアルタイム取得では無効です

他はリスト選択してないときに更新したらリスト一覧を更新するようにしたりしました

ちなみに現在ver1.8での既知のバグは次の通りです
 ・お気に入り削除が実行されない
 ・タイムライン以外のPivotを選択するとタイムラインでのスクロール制御がおかしくなる
 ・お気に入りに追加すると(?)リアルタイム取得が切断される
 ・設定のコンストラクタで例外が発生する

わかるものに関してはv1.9までに対処します

こんくらいで

Read Full Post »

すでにある気がする系記事

ListBoxにアイテムをごっそり追加したりするとスクロールしまくってなんじゃこりゃー状態になった経験ある人も多いかもしれません
おもに上にアイテム追加しちゃう人

というのもこれはScrollBarの問題で常に下に再計算し続けるからです
? 日本語わからない…

まあ簡単に言えば上にアイテム追加しようが下に追加しようが真ん中に入れようがスクロールバーのサイズは常に下に追加されます
そのため現在値は一定のままサイズだけ変わりアイテムだけが不自然にスクロールしていきます

さてこれではTwitterクライアント作ってても使いにくいですので(とくにUserStream)
上に追加したら現在値を計算してやればまあマシになると思います

つってもやることは簡単で足された分をもとの位置に足すだけです
ScrollBar.Maximumプロパティの値を別に保存しておいて変わったら見るようにしましょう

はい、ところでさっきからListBoxの話なのにScrollBarの話をしていて何を言ってるんだ状態になるかもしれないので
まずはListBoxからScrollBarを取り出します
今回は縦のスクロールずれが問題なのでVerticalScrollBarが必要です

参考として高橋さんのWP7.5 でリストボックスのスクロールエンドで圧縮されるときのイベントを取るが分かりやすいかもしれません

では始めます
まず対象のListBox内にあるScrollViewerを取り出します。VisualTreeHelper.GetChild(ListBox,0)でいけるはずです
次にその拾ったScrollViewerにもう一度VisualTreeHelper.GetChild(ScrollViewer,0)してFrameworkElementを取得します
これのFindNameでVerticalScrollBarを取得します。ここまでくればScrollBarはとれたのでまったりやりましょう

ここまでは先ほどのブログの記事の中でFindNameするオブジェクトは違いますがほぼ同じことをやってます
一応VB側のソースは置いときます。C#のほうはあきらめます

Private Sub AddEventToListBoxScrollBar(target As ListBox)
    'ListBoxからScrollViewerを取得する
    Dim ListboxScrollViewer As ScrollViewer = DirectCast(VisualTreeHelper.GetChild(target, 0), ScrollViewer)

    'ScrollViewerのFrameworkElementを取得する
    Dim element As FrameworkElement = DirectCast(VisualTreeHelper.GetChild(ListboxScrollViewer, 0), FrameworkElement)
    'ScrollViewerからVerticalScrollBarとして宣言されているScrollBarを取得する
    Dim scrlBar As ScrollBar = DirectCast(element.FindName("VerticalScrollBar"), ScrollBar)
    
    'TODO scrlBarのイベントにメソッドを関連付ける
End Sub

はい、ScrollBarはとれたので続いてこのScrollBarの処理をやっていきます

ScrollBarのMaximumが変更されたイベントがほしいのですがあいにくそんないいイベントないのでとりあえずLayoutUpdatedを使いました
ScrollBar.ValueChangedでは今回の最大の問題である勝手にスクロールに対処できないので使いません

LayoutUpdatedを使うとコントロールに変更があると呼ばれるのでMaximum変更やスクロールで取れるのでとても重宝します
ただし引数がどっちもnullがなぜかくるのでScrollBarへの参照を別に持たなくてはなりません

さてさて、それではLayoutUpdatedに対応するメソッドを書きましょう
やることは非常に簡単ですがいくつか注意点があるので結構な量になってしまいます

まあまずMaximumを保持しておくプロパティを作ります。どうせほかにもいろいろ作るのでクラス作ってもいいかもです
次にさっき言った通りScrollBarへの参照を作りましょう
ついでに現在の位置を保存しとくプロパティも用意しましょう
そして保持してあるMaximumより参照のScrollBar.Maximumのほうが大きければ増加分だけ現在の位置に足してスクロール制御します
とりあえず枠組みをちょっと書くと次のような感じ

    Private Sub VerticalScrollBar_LayoutUpdated(sender As System.Object, e As System.EventArgs)
        Dim tagBar As ScrollBar = scrollBarRef 'scrollBarRefは参照
        If scrollCache.Maximum < tagBar.Maximum Then
            scrollCache.Maximum = tagBar.Maximum
            'Todo スクロール処理
        ElseIf scrollCache.Maximum > tagBar.Maximum Then
            scrollCache.Maximum = tagBar.Maximum
            'Todo スクロール処理
        Else
            scrollCache.CurrentValue = tagBar.Value
        End If
    End Sub
private void VerticalScrollBar_LayoutUpdated(System.Object sender, System.EventArgs e)
{
    var tagBar  = scrollBarRef; // scrollBarRefは参照
    if( scrollCache.Maximum < tagBar.Maximum)
    {
        scrollCache.Maximum = tagBar.Maximum;
        // Todo スクロール処理
    }
    else if(scrollCache.Maximum > tagBar.Maximum)
    {
        scrollCache.Maximum = tagBar.Maximum;
        // Todo スクロール処理    
    }
    else
    {
        scrollCache.CurrentValue = tagBar.Value;
    }
}

さて、スクロール処理なんですがこれはScrollBarだけでは明らかに難しいです。なのでScrollViewer.ScrollToVerticalOffset(Double) を使います
(というのもScrollViewerのVerticalOffsetのSetプロパティがInternalで宣言されているためプロパティがセットできません。またScrollBarからアクセスしようにもバインド方向が一方通行で意味がないです)

はい、ScrollViewerへのアクセス処理がいちいちめんどくさいのです。
一応Listboxへの参照をもってやってみます(ScrollViewerを持ってもいいかもですが)

ただこのままにしておくとスクロールがガタガタになってしまいます。というのもScrollBar.Maximumが結構常に変化してるらしくスクロールするとここで指定したスクロール処理かかって戻されてしまいます。なのでできればアイテム数が変化したらスクロール制御しましょう

書き直すのがめんどくさくなったのでscrollCacheをscrollCollectionってしときますごめんなさい
インデントおかしい…

    Private TimelineListBox As ListBox '参照のListBox
    Private Sub VerticalScrollBar_LayoutUpdated(sender As System.Object, e As System.EventArgs)
            If scrollCollection.CurrentCount <> TimelineListbox.Items.Count Then
            Dim tagBar As ScrollBar = scrollBarRef
            scrollCollection.CurrentCount = TimelineListbox.Items.Count
            If tagBar IsNot Nothing Then
                If scrollCollection.Maximum = -1 Then
                    scrollCollection.Maximum = tagBar.Maximum
                    scrollCollection.CurrentValue = tagBar.Value
                End If
                If scrollCollection.Maximum < tagBar.Maximum Then
                    Dim tagObj As ScrollViewer = DirectCast(VisualTreeHelper.GetChild(TimelineListbox, 0), ScrollViewer)
                    Dim cacheMaximum As Double = scrollCollection.Maximum
                    scrollCollection.Maximum = tagBar.Maximum
                    tagObj.ScrollToVerticalOffset(scrollCollection.CurrentValue + (tagBar.Maximum - cacheMaximum))
                ElseIf scrollCollection.Maximum > tagBar.Maximum Then
                    Dim tagObj As ScrollViewer = DirectCast(VisualTreeHelper.GetChild(TimelineListbox, 0), ScrollViewer)
                    Dim cacheMaximum As Double = scrollCollection.Maximum
                    scrollCollection.Maximum = tagBar.Maximum
                    tagObj.ScrollToVerticalOffset(scrollCollection.CurrentValue + (cacheMaximum - tagBar.Maximum))
                Else
                    scrollCollection.CurrentValue = tagBar.Value
                End If
            End If
        Else
            If scrollBarRef IsNot Nothing Then
                scrollCollection.CurrentValue = scrollBarRef.Value
            End If
        End If
    End Sub
private System.Windows.Controls.Primitives.ScrollBar scrollBarRef;
		private ListBox TimelineListBox; // 参照のListBox
		private void VerticalScrollBar_LayoutUpdated(System.Object sender, System.EventArgs e)
		{
		    if (scrollCache.CurrentCount != TimelineListbox.Items.Count )
		    {
			    var tagBar = scrollBarRef;
			    scrollCache.CurrentCount = TimelineListbox.Items.Count;
                if(tagBar != null)
                {
                    if( scrollCache.Maximum = -1 ) // 初回の操作
                    {
                        scrollCache.Maximum = tagBar.Maximum;
                        scrollCache.CurrentValue = tagBar.Value;
                    }
				    if (scrollCache.Maximum < tagBar.Maximum )
                    {
                        var tagObj  = (ScrollViewer)(VisualTreeHelper.GetChild(TimelineListbox, 0));
					    var cacheMaximum = scrollCache.Maximum;
					    scrollCache.Maximum = tagBar.Maximum;
                        tagObj.ScrollToVerticalOffset(scrollCache.CurrentValue + (tagBar.Maximum - cacheMaximum));
                    }
                    else if(scrollCache.Maximum > tagBar.Maximum )
                    {
                        var tagObj  = (ScrollViewer)(VisualTreeHelper.GetChild(TimelineListbox, 0));
					    var cacheMaximum = scrollCache.Maximum;
					    scrollCache.Maximum = tagBar.Maximum;
					    tagObj.ScrollToVerticalOffset(scrollCache.CurrentValue + (cacheMaximum - tagBar.Maximum));
                    }
					else
                    {
                        scrollCache.CurrentValue = tagBar.Value;
                    }
                }
		    }
		    else
		    {
			    if(scrollBarRef != null)
			    {    
				    scrollCache.CurrentValue = scrollBarRef.Value;
			    }
		    }

	    }

あっもしListboxの下にも追加する場合このままだとおかしくなるのでぜひともそこらへんは適当に直してください

とりあえずこの辺で

追記:最初のVerticalScrollBarを取り出す操作以外のC#のソースコードを一応書きました。VB版のソースコードのほうが正確なので間違ってたらそっち参照していただけると

Read Full Post »

なぜかバージョンアップを行いました
ここからどうぞ

変更点は
・define.xmlに保存するファイルの設定などを書き出したことにより設定拡張を可能に
・zip化してファイルを保存するように(暗号化可能に)
程度です
それに伴って一部語句直しました

define.xmlは昨日書いたDataContractSerializerでぱぱっと書き出したものです
一応配列なんでアイテム追加は余裕です
また保存するものも細かく指定できます
なんとなくdefine.xml書き出した時のコード同梱しといたので使いたければどうぞ
しーしゃーぷはできません

zip化はDotNetZipとして名高いIonic.zip.dllを利用しました
zipの書庫に先にファイル等を追加してから保存先にzipファイルを生成するのでDropBoxにファイルの残骸が行くとかはないです
たぶんないです

まあ相変わらずディレクトリバージョン管理状態なんですけどね(

あとzip化するアプリケーションをxml編集で個別に選択できるので頑張ってお使いください
私はたぶん一括でxml書かせると思いますはい

これを使えばスコアファイルだけじゃなくTwitterクライアントの設定バックアップも超簡単ですよ!
まあ直接ファイル起動する場合は何もできませんけど…

とりあえずこの辺で

Read Full Post »

どっかにすでにある気がする

DataContractしたクラスを作れば非常に手軽にシリアル化してくれるDataContractSerializer。XmlObjectSerializerを継承してるためXmlObjectSerializer.WriteObject(IO.Stream,Object)とかすれば簡単にシリアル化してくれてオブジェクトをxmlに変換できます。

ですがこれを保存するときにStreamReaderとか使うととても悲しい感じになってしまいます。というのもStreamに書き出す際に改行しないためです。これは別に設定保存して誰も外からいじらないなら問題ないですがいじるんだったら可読性が下がってしまいます

というわけで楽に保存するためにXDocument [System.Xml.Linq Namespace]を使います
DataContractSerializerでWriteObjectしたStreamからXDocument.Load(IO.Stream)を使ってXDocumentを生成します。次にXDocument.Save(String fileName)で指定したパスに保存するだけです
すると普通のよく見かけるXmlの形みたいに改行、インデントを入れて書き出してくれます
しかもほとんど遅くならないので?(私の試した感じ(5000文字くらいですけど)では遅くなったという感じはなかったです)
別に人間が見るわけじゃなくてもどんどん使って行きたいですね! とか

とりあえずこんくらいで

追記:これで書き出すと2つくらい厄介な点があります
まずXDocument.Load(String fileName)で読み込んでそれのToStringをReadObjectかなんかするとエンコードおかしくて例外出ました(たぶんこっちのコードの問題ですけど)
これ自体は読み込んだXDocument.SaveでMemoryStreamに書き出せば解決できます
次にシリアル化する際にNamespaceをスキーマとして書き込むためにNamespaceのずれが起きるとそこでキャストできなくなってクラッシュします
これはDataContractAttributeでNamespace指定しないとクラスの名前空間を利用するため既定の名前空間などでずれが起きる可能性があります
(DataContractJsonSerializerはNamespace書き出さないので基本キャストできれば問題ないです)
DataContractAttributeのNamespaceを指定してあげるとうまくいくのでずれた場合はNamespaceを指定しましょう
参考はhttp://msdn.microsoft.com/ja-jp/library/ms731045(v=vs.100).aspxです

Read Full Post »

Older Posts »