プログラミングのブログ記事

プロジェクトをsymfony 1.1にアップデートするには? プロジェクトをsymfony 1.1にアップデートするには?

symfony 1.1にupgradeするとsymfony 1.0.xでつくったプロジェクトはそのままでは動かなくなるらしい。
http://blog.symfony.jp/2008/07/01/pear-upgrade-1-0-17/

じゃぁ、動くようにするにはどうすんの?

http://trac.symfony-project.org/browser/branches/1.1/UPGRADE

に書いてあった。

1. プロジェクトディレクトリに移動する

2. symfonyファイルを下記コマンドで更新する

cp /path/to/symfony/lib/task/generator/skeleton/project/symfony symfony

3. ProjectConfiguration.class.phpファイルを下記コマンドで作成する


cp /path/to/symfony/lib/task/generator/skeleton/project/config/ProjectConfiguration.class.php config/ProjectConfiguration.class.php

config\ProjectConfiguration.class.phpの##SYMFONY_LIB_DIR##を/path/to/symfony/で置き換える

4. upgradeコマンドを叩く

./symfony project:upgrade1.1

apps/frontend/config/logging.yml
apps/frontend/config/i18n.yml
apps/frontend/config/config.php
config/config.php
消せとか言われるので消した。

web/frontend_dev.php
web/index.php
はコメントを有効に。って言われるので修正。

symfony ccして、動かすとエラー。

エラー1. Call to undefined function form_tag()

<?php use_helper('Form'); ?> 
を入れないといけないらしい。

エラー2. An internal URI must contain a module and an action

http://www.symfony-project.org/forum/index.php/m/55475/#msg_55475

1.1から必ずdefault_actionで省略できず、必ずactionを指定しないといけないらしい

あとで気がついたが日本語で説明してくれてる記事があった
http://www.thinkit.co.jp/article/71/2/

エラー1,エラー2についてはやむを得ぬ事情があったに違いない。本来では少しでもコードを書きたくないはずなのに、書くコードが増えるほうに倒れた件に関してどのような葛藤があったのか、作者達に聞いてみたい。

※/path/to/symfony/については、アップデート前のプロジェクトディレクトリの/config/config.phpにある$sf_symfony_lib_dirになると思われる。

いろいろ間違えたので7/23修正。

めっちゃはまった。そしてゲイツぶっとばす。と3回はつぶやいた。

えっと、、、先日Silverlight 2 Beta 2がリリースされました。

昨日、「:【RIA開発セミナー】プロデューサー、ディレクター必見 Silverlightによる開発のおさえどころはココだ!」を聞いたのですが、冠の通りdeveloperが聞く内容ではなかったです。「開発のおさえどころ」ではなく、「プロジェクトのおさえどころ」が正しかったのではないかと・・・

それはさておきタイトルのSilverlightのcrossdomainアクセスの話です。

Silverlightは一応クライアント側で動作しますが、crossdomainアクセスに関してはAjaxやflashの用に制限がかかっています。

制限のかけ方については基本的にはflashに似ていて、アクセスされる側がpolicyファイルを置くと、Silverlight Runtimeがそのファイルを読んで対応します。

今回、実際にcrossdomainでアクセスしたいことがあり、以前なにかで、Adobe flashのpolicyファイル(crossdomain.xml)にも対応していると読んだので、crossdomain.xmlを置いて、Beta 2のドキュメントにあったサンプルコードを貼り付けて動かしてみました。

が、アクセスできません。いろいろ中身を変えてみてもダメ。あきらめてドキュメントをまじめに読みました。超適当な訳による簡易メモを下記に載せます。

Silverlightは二つのpolicyファイルに対応する

Flash policy file:
- crossdomain.xml
- System.NetのWebClientとSystem.Net.HTTPクラスで使われる

Silverlight policy file:
- clientaccesspolicy.xml
- System.Net.Socketsのソケットクラス達で使われる

アクセスの許可を調べるためにSilverlightランタイムがこのpolicyファイルをダウンロードする。いずれもターゲットdomainのルート直下を探す。

WebClient/HTTPのときはHTTPプロトコルでpolicyファイルのダウンロードを試みる。先にSilverlight policy file(clientaccesspolicy.xml)のダウンロード試みて、なければFlash policy file(crossdomain.xml)のダウンロードを試みる。

socketからのリクエストならTCPプロトコルでSilverlight policy file(clientaccesspolicy.xml)のダウンロードを試みる。

policyファイルがなかったりパースできないときは接続はできない。

いくつか注意するところがありました。

  1. Silverlightのpolicyファイルを使うときはcrossdomain.xmlではなくclientaccesspolicy.xmlで保存しないといけないということ。
  2. crossdomain.xmlとclientaccesspolicy.xmlが両方あった場合はclientaccesspolicy.xmlが優先されるということ。
  3. ソケットクラスによるアクセス時にはcrossdomain.xmlは使われないということ。

とりあえず最初の失敗はドキュメントのサンプル(silverlight policy fileの書式)をcrossdomain.xmlで保存してしまったことにありました。

というわけでcrossdomain.xmlをclientaccesspolicy.xmlにリネームして試しました。

が、やっぱりアクセスできません。何故だ。何故なんだ。

問題を切り分けたいのですが、困ったことにexceptionも発生しないし、エラーとか何も教えてくれません。WebClient.DownloadStringAsyncを使っていたのですが正常系のフローで空文字が返ってきます。これだとcrossdomainアクセスに失敗しているのか、成功しているんだけど404なのか、いや200だけど本当に空ページなのか、、、全然わかりません。ブラウザでそのurlを開いてみてもページは見えるし、clientaccesspolicy.xmlも見えるし。

そこでFlash policy fileを試してみることにして、とりあえずテストなので全アクセス許可のcrossdomain.xmlを拝借して置いて、clientaccesspolicy.xmlを退避させてみたところ見事に接続できるようになりました。clientaccesspolicy.xmlとcrossdomain.xmlの両方を置いたらやっぱり接続できないのでようやくclientaccesspolicy.xmlの内容に問題があるとわかりました。

しかし、中身を見てもやっぱりどこが悪いかわかりません。ひょっとしてBeta 2のバグなのか?と思いForumを覗いてみることに。しかし、ピンとくるスレッドがありません。一つBeta 2になってからallow-from要素のhttp-request-headers属性が必須になったという話があったのですが、それはちゃんと入れてるし、っていうかBeta 2のドキュメントのサンプルを使ってるわけだし。。。

まさか日本語環境だけで起きる問題?

とにかくわからないのでForumに投稿しようかと思っていたとき、あるスレッドで全アクセス許可のサンプルコードが貼り付けられていたので、間違いさがしのつもりでそれをペーストしてみました。すると間違いどころか行が増えました。

参考にしたドキュメントのサンプルコード

<?xml version="1.0" encoding="utf-8"?>
<access-policy>
<cross-domain-access>
<allow-from http-request-headers="*">
<domain uri="*"/>
</allow-from>
<grant-to>
<resource path="/api" include-subpaths="true"/>
</grant-to>
</cross-domain-access>
</access-policy>

こっちがフォーラムで見つけたサンプル

<?xml version="1.0" encoding="utf-8"?>
<access-policy>
<cross-domain-access>
<policy>
<allow-from http-request-headers="*">
<domain uri="*"/>
</allow-from>
<grant-to>
<resource path="/" include-subpaths="true"/>
</grant-to>
</policy>
</cross-domain-access>
</access-policy>

resoure pathは"/"に修正して使っていたので異なる点は、cross-domain-access要素の下にpolicy要素があるか、ないか。ドキュメントを見るとサンプルコード4つのうち2つがこのpolicy要素が抜けています。

policy要素を入れたところ接続できるようになったのは言うまでもありません。ゲイツぶっとばす。と3回つぶやいたあとにマイクロソフトのドキュメントチームにフィードバックを送りました。

2008/08/05 追記:iPod touch 1.1.4をJailbreakしてOpenSSHをインストールした状態で行ったものです。JailbreakしてないiPod/iPhoneでは勝手アプリはインストールできません。またiPhone 3Gのようなバージョン2.0ではまだ試していませんのでご注意を

いよいよ日本でのiPhone発売に向けてカウントダウンを切ったところですが、いまさらながら今日はWindows上でiPhone/iPod touchの開発環境を構築する手順のメモを載せてみようと思います。

さて、思えばiPod touchアプリの開発を夢見てiPhone SDKのリリースを首を流して待ったわけですが、iPhone SDKがLeopardでしか動かないと知り、Leopardを買ってきてインストールしてやっとのことでSDKをダウンロードしたらIntel Macじゃないとダメよと言われ愕然とし(当方G4)、会社でIntel Macを買ってもらいSDKをインストールできたものの、SDKだけでは実機にアプリは転送できず、それを実現するためのiPhone Developer Programは限られた人数しか参加できずAppleからハブられ続けてやる気をなくす。という長い流れがあったわけです。

でじゃぁjailbreakしてアプリをコピーするか、ということにしたのですが、そもそもSDKが公開される前からiPhone/iPod touchアプリの開発方法はいろいろ公開されてました。intel macやLeopardが必要なのはあくまでSDKの話であって(OSやプロセッサに制限があるのは技術的な問題ではなく単純に開発&検証コストの削減だと思われる)、エミュレーションは無理でもgccできれば別にOSやプロセッサに関係なくアプリは作れるはずと思っていました。

んで、探したところありました。cygwinで開発する方法
http://wiki.iphonegameover.com/Windows_Cygwin_Binary_Toolchain_Installation

なんでまたMacを持ってるのにWindowsで開発するのかという話もあります。本当はMacで全部開発したい気持ちもありますが、Nico2Mobileとか.NETアプリも開発してる関係上、今のところWindowsのモバイルノート一台で全ての開発ができるのが好ましいという個人的事情が理由です。(VMWareは重いし、OS切り替えるのとかメンドイ)iPod touchを買うような人はマカーな人が多いと思いますが、OSのシェアを考えるとWindows PC + iPod touchを使ってる人も結構いると予測でき、案外Windowsで開発できるってのも大きいのではないかと思います。

前置きが長くなりましたが、手順のメモです。基本的にはhttp://wiki.iphonegameover.com/Windows_Cygwin_Binary_Toolchain_Installation
の日本語訳みたいなものなので、キャプチャーとかは原文サイトを見てもらうのが良いかと。

Windows_Cygwin_Binary_Toolchain_Installation手順メモ

iPhone/iPod touch開発用のcygwinセットアップが公開されておりそれを使う。
脱獄している悪人とは言え、ルール上セットアップにはMacOSX10.4u.sdkとiPhoneのf/wから取り出すライブラリは含まれていない。
これらは別途用意する必要がある。

  • /Developer/SDKs/MacOSX10.4u.sdk
    1. Xcode 2.5 Developer Tools (Disk Image) (902.9 MB as of 30 Oct 2007)をダウンロードする
    2. ISOイメージから\Packages\Packages\MacOSX10.4.Universal.pkg\Contents\Archive.pax.gzを取り出す
      • iphonegameorver.comではPowerISOというツールを使う手順になっているが、他にツールを持ってるならそれでも良いし、なんならいったんDVDに焼いてからDVDから取り出すのもありだろう
      • PowserISOを使う場合http://www.poweriso.com/download.htmからダウンロードしセットアップする、起動したら、ファイル->開くよりダウンロードしたDiskImageを開き\Packages\Packages\MacOSX10.4.Universal.pkg\Contents\Archive.pax.gzをデスクトップなどにD&Dすればコピーできる
  • iPhone/iTouch root filesystem
    • いろいろな取り出し方がWebにあふれているがiPhone/iPod touch開発用のcygwinの起動時に実行されるスクリプトに乗っかるのが良いと思われる、よってこの後の「cygwinの起動と設定」を参照されたい
  • cygwinのインストール
    1. cygwin setupをダウンロードし、実行する
    2. パッケージのダウンロード先として下記のURLを入力して追加する
      • http://www.iphonegameover.com/cygwin
    3. ダウンロード先として追加され選択状態になっていたら次へ進む
    4. パッケージの選択は特にデフォルトのままでよい
  • cygwinの起動と設定
    1. Developer/SDKs/MacOSX10.4u.sdkの準備
      • cygwinの起動時のスクリプトで要求される
      • ISOイメージから取り出した\Packages\Packages\MacOSX10.4.Universal.pkg\Contents\Archive.pax.gzを適当なディレクトリに置く
      • 適当な場所がわからない場合は、c:\cygwin\tmp\Archive.pax.gzとかに置く
      • ちなみにVistaでやったときはhomeディレクトリはWindowsの環境設定が使われた(C:\Users\{アカウント名})、起動時のcurrentディレクトリはhomeディレクトリなのでここにArchive.pax.gzを置いておくと自動的に見つけられる。ただXPでやったときはhomeディレクトリはc:\cygwin\home\{アカウント名}だった。よくわからない。
    2. iPhone/iPod touchの準備
      • cygwinの起動時のスクリプトでiPhone/iTouch root filesystemも要求される、ssh経由でアクセスするのでiPhone/iPod touchをWiFiにつないでおきIPアドレスを調べておく
    3. cygwinを起動する
    4. iPhone/iTouch root filesystemが無い、機器からダウンロードするか('Do you wish to connect to your device and download the required files?')と聞かれるのでyを押す
    5. 機器のIPアドレスを入れろ('Please enter the remote IP address of your device:')と言われるので先ほど調べたiPhone/iPod touchのIPアドレスを入力する
    6. たぶん最初のアクセスなので接続を続けるか('Are you sure you want to continue connecting (yes/no)?')と聞かれるのでyesを入力する
    7. rootのパスワードを聞かれるのでalpineと入力する
      • alpine
    8. うまくいけばファイルシステムがインストールされるので待つ
    9. Xcodeのヘッダがないので入れるか('Do you wish to install the XCode header files?' )と聞かれるのでyを押す
    10. currentディレクトリにArchive.pax.gzがあれば読み込まれる
      • currentディレクトリにない場合はファイルが置いてあるディレクトリを入力する
      • c:\cygwin\tmp\Archive.pax.gzに置いたなら/tmp/Archive.pax.gzと入力する
    11. Your toolchain installation is now complete!が表示されたら成功。ファイルが見つからない場合などで失敗した場合、このスクリプトを削除するか?と聞かれるのでnoを入力し、パスなどを確認して再度cygwinを起動する

4798115827

Joelのソフトウェア開発者採用ガイドはなかなか面白かったです。どちらかというと採用される側の立場で呼んでみたのですが、そうだよそうだよ。とさすがにプログラマのことをよく理解されていると感服しました。

採用に限らず、部下にプログラマを持つプログラミング経験の少ないプロジェクトマネージャーさんなんかにもおすすめできる本かと思います。

YouTube Data APIで取得したデータをWPFのリストで表示する の続きです。

宣言通り、ItemsControlとListViewについて。

前回、ListBoxを使って動的に項目を並べることをやりましたが、ListBoxはあくまでListBoxなわけで並んだ項目はListBoxItemの中にあり、ListBoxItemが選択できます。別に選択する必要もなく、ただ単に画像を並べたいということもあると思います。そのやり方に少し悩みました。StackPanelのChildrenにコードで追加すればできるのですが、ListBoxに配列をBindしたようにスマートにやりたいものです。

ItemsControlでそれができると知りました。ListBoxやListViewの継承関係は下記のようになっています。

System.Object 
   System.Windows.Threading.DispatcherObject 
     System.Windows.DependencyObject 
       System.Windows.Media.Visual 
         System.Windows.UIElement 
           System.Windows.FrameworkElement 
             System.Windows.Controls.Control 
               System.Windows.Controls.ItemsControl 
                 System.Windows.Controls.Primitives.Selector 
                  System.Windows.Controls.ListBox
                     System.Windows.Controls.ListView 

ItemsControlには項目の配列を並べることが選択機能なしにできるので単に項目を並べるときに使えました。ListBoxのXAMLを書いたときに値を変更したItemsSource、ItemTemplate, ItemsControl.ItemTemplateなどはまさにItemsControlのものなのでListBoxのXAMLをそのまま流用できます。(その逆もしかり)

        <ItemsControl Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" ItemsSource="{Binding Source={StaticResource VideoItemCollection}}" ItemTemplate="{StaticResource VideoOnItemsControl}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel Orientation="Horizontal" HorizontalAlignment="Center" ItemHeight="100" ItemWidth="100"/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>

またListViewでも同じようにXAMLが書けます。ListViewのReport形式の表示をGridViewつかって表示してみます。

        <ListView Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" ItemsSource="{Binding Source={StaticResource VideoItemCollection}}" ItemTemplate="{StaticResource VideoOnItemsControl}">
            <ListView.View>
                <GridView ScrollViewer.VerticalScrollBarVisibility="Visible">
                    <GridViewColumn Header="Thumbnail">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <Image Source="{Binding Path=Thumbnail}"/>       
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                    <GridViewColumn Header="Title">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <TextBlock Text="{Binding Path=Title}"/>
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                    <GridViewColumn Header="Author">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <TextBlock Text="{Binding Path=Author}"/>
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                </GridView>
            </ListView.View>
        </ListView>

こんな感じで、GridViewでもカラムの中でどういうコントロールで表示するかをGridViewColumn.CellTemplateを使って表現できます。

同じCollectoinデータをListBox, ItemsControl, ListViewに設定して並べて表示してみました。

WPFYouTube200805240.jpg


最終的なXAMLは以下の通りです。このサンプルプログラム(VS2008)を実際に動かしてみたい人はWPFYouTube.zipをダウンロードしてください。

<Window x:Class="WPFYouTube.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:me="clr-namespace:WPFYouTube"
    Title="Window1" Height="300" Width="300">
    <Window.Resources>
        <CollectionViewSource Source="{DynamicResource VideoItems}" x:Key="VideoItemCollection"/>
        <DataTemplate x:Key="VideoOnItemsControl">
            <StackPanel Orientation="Vertical">
                <TextBlock Text="{Binding Path=Title}"/>
                <Image Source="{Binding Path=Thumbnail}"/>
                <TextBlock Text="{Binding Path=Author}"/>
            </StackPanel>
        </DataTemplate>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="20"/>
            <RowDefinition Height="200"/>
            <RowDefinition Height="200"/>
            <RowDefinition Height="200"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition Width="60"/>
        </Grid.ColumnDefinitions>
        <TextBox Grid.Row="0" Grid.Column="0" x:Name="tbxKeyword" Text="ドラゴンボール"/>
        <Button Grid.Row="0" Grid.Column="1" Content="Search" Click="Button_Click"/>
        <ListBox Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" ItemsSource="{Binding Source={StaticResource VideoItemCollection}}" ItemTemplate="{StaticResource VideoOnItemsControl}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <StackPanel Orientation="Vertical" HorizontalAlignment="Center" ScrollViewer.VerticalScrollBarVisibility="Visible"/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ListBox>
        <ItemsControl Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" ItemsSource="{Binding Source={StaticResource VideoItemCollection}}" ItemTemplate="{StaticResource VideoOnItemsControl}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel Orientation="Horizontal" HorizontalAlignment="Center" ItemHeight="100" ItemWidth="100"/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
        <ListView Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" ItemsSource="{Binding Source={StaticResource VideoItemCollection}}" ItemTemplate="{StaticResource VideoOnItemsControl}">
            <ListView.View>
                <GridView ScrollViewer.VerticalScrollBarVisibility="Visible">
                    <GridViewColumn Header="Thumbnail">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <Image Source="{Binding Path=Thumbnail}"/>       
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                    <GridViewColumn Header="Title">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <TextBlock Text="{Binding Path=Title}"/>
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                    <GridViewColumn Header="Author">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <TextBlock Text="{Binding Path=Author}"/>
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>
</Window>

自宅サーバでSubversionを立ち上げてるので、会社でもこっそり趣味のプログラミングをしてコミットしたりできる。

eclipseを使うときはそのまま使えるのだが、Visual Studioを使うときはVisual StudioにはVSSに関連づけておきたいのでRapidSVNを使っていたりする。

あるマシンにRapidSVNを入れたのだが既存のリポジトリを追加する方法がよくわからなかった。
メニューに「リポジトリ(R)」というのがあるのだが、「作成」を選ぼうとするとsvnadminがないとなんとやら、「インポート」も「エクスポート」もちょっとニュアンスが違う。

あきらめてヘルプを見た。

既存のリポジトリをブラウズする方法
1.「ブックマーク」メニューの「既存のリポジトリに追加」を選択
2. URLをタイプしOKを押す

なんと、「リポジトリ」メニューじゃなくて「ブックマーク」メニューからだった。毎回困惑しそうなので備忘録としてブログに残すことにした。

ようやくWPFもわかってきたのですが、なかなか情報が少ない状況です。.NET 3.0がリリースしてしばらく経ったと思うのですがあまり流行っていないような気もします。さんざん苦労したので調べてわかったことなどを書いてみようと思いました。

別にWPFに限ったことではないのですが、新しいプラットフォームでアプリをつくるときに必ずといって悩むことがあります。

特にUI系のサンプルプログラムはUIコントロールの説明を重視するために静的なデータを表示することがほとんどです。コードの中に表示する文字列や画像のパスが入っていることも珍しくありません。

ところが実際にアプリを作るときは、実行時には表示するデータはまだわからず、何からのアクションに伴いデータを取得して表示するということがよくあります。しかし、表示する項目が動的に変わるようなサンプルはまだ少ないように思います。

WPFを使ったときに複数のアイテムを表示する方法にかなり悩んだので、今回は、YouTube Data APIで検索した結果をいくつかのコントロールで表示するサンプルプログラムを書いてみることにします。

1. データのカプセル化(モデルの作成)

何かしらのデータを表示する際に単にその名前だけを表示することは少なく、複数のメタデータを並べて表示することが多いと思います。たとえばRSS Readerの記事の項目で言えば、タイトルだけじゃなく、作者や、公開日時も表示しますし、YouTubeなどの動画系の項目なら名前だけじゃなく、サムネイルやタグなんかを表示すると思います。

ますはこういったデータの固まりをモデルとしてクラスを用意します。今回はGoogle Data API SDKを使うので、Google.GData.Client.AtomEntryをモデルとして使います。(名前のとおりAtomEntryは基本的なモデルクラスでYouTube用のVideoデータのプロパティ、たとえば動画のサムネイルなどはとれないようです)

2. UI用Adapterクラスの作成

モデルとUI用のクラスを別に分ける必要がないのですが、クラスの再利用を考えるとデータの管理する箇所がUIのフレームワークに依存するのを避けるために別ける方が良いと思います。(そもそも今回はモデル用のGoogle.GData.Client.AtomEntryは変更できないのだが)

具体的にはSystem.ComponentModelにあるINotifyPropertyChangedを継承したUI用のクラスを作ります。名前の通りプロパティの変更をUIコントロールに通知することができ、あとはXAMLでプロパティをBindするだけになるので非常に楽ちんです。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using Google.GData.Client;
using Google.GData.Extensions;

namespace WPFYouTube
{
public class UIVideoItem : INotifyPropertyChanged
{
public UIVideoItem(AtomEntry entry)
{
Title = entry.Title.Text;
}
public string Title { get; set; }

public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string info)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}

ますは名前だけ返すクラスにしておきます。

3. Collectionの用意

リストにはUI用のクラスの配列を渡します。ここのやり方を知るのに時間がかかりました。配列の内容の変更を通知してくれるObservableCollectionを使ってXAMLでBindをします。

ちなみにこれから説明する例が素直なやり方なのかはよくわかっていません。

Windowのクラス側にてObservableCollectionのテンプレートを使いUIVideoItemの配列を定義します。

private ObservableCollection<UIVideoItem> videoItems = new ObservableCollection<UIVideoItem>();

XAMLの方でCollectionViewSourceを定義して、SourceにWindowの"VideoItems"というリソースを動的リソースとして指定します。(ObservableCollection<UIVideoItem>のXAMLでの書き方がわからなかったのでこうしてます)

    <Window.Resources>
        <CollectionViewSource Source="{DynamicResource VideoItems}" x:Key="VideoItemCollection"/>
    </Window.Resources>

動的リソースの"VideoItems"は、Windowクラスのプロパティではありません。従ってpublicのメンバでVideoItemsを定義しても意味がありません。Resourcesメンバに指定する必要があります。

        public Window1()
        {
            this.Resources["VideoItems"] = this.videoItems;
            InitializeComponent();
        }

上記によって、動的リソースの"VideoItems"にObservableCollection<UIVideoItem>を指定します。

最後にXAMLの方に戻って、CollectionViewSourceをListBoxのItemsSourceにBindします。これでC#側でmediaItemsを変更するだけでリストの内容が変えることができます。

<ListBox ItemsSource="{Binding Source={StaticResource VideoItemCollection}}"/>

実行してみましょう。(XPクラシックモードです)

WPFYouTube200805230.jpg


なんじゃこれは?ですが、正しい挙動です。ListBoxは項目のToStringの結果を表示するのでこうなります。

4. ItemTemplateの用意

UIVideoItemに下記のようなToStringを実装すればリストボックスにタイトルが並ぶようになります。

        public override string ToString()
        {
            return Title;
        }

しかし、これだけならCollectionViewSource にstringの配列を渡すだけでもできてしまうので、モデルやUIクラスを作った意味があまりありません。そこでリストの項目に複数のプロパティをそれぞれ別のコントロールを使って表示するようにします。

とりあえずAuthorプロパティをUIVideoItemに用意します。

public UIVideoItem(AtomEntry entry)
        {
            Title = entry.Title.Text;
            Author = (entry.Authors != null && entry.Authors.Count > 0) ? entry.Authors[0].Name : "";
        }
        public string Title { get; set; }
        public string Author { get; set; }

ListBoxはデフォルトでは項目のToStringを単にテキストラベルで表示するだけですが、表示方法をItemTemplateで指定できます。

<ListBox ItemsSource="{Binding Source={StaticResource VideoItemCollection}}">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal">
                            <TextBlock Text="{Binding Path=Title}"/>
                            <TextBlock Text="{Binding Path=Author}"/>
                        </StackPanel>
                    </DataTemplate>                    
                </ItemsControl.ItemTemplate>
            </ListBox>

これを実行すると、TitleとAuthorがStackPanelで水平に並べられて表示されます。

WPFYouTube200805231.jpg


水平に並べたためあまり効果は見えませんが、TItleとAutorが別々のTextBlockになっているのでTitleだけ太字にしたり、Authorだけ色を変えるなどスタイルの変更が柔軟に対応できます。

なお項目の表示方法を共通化したい場合などは、リソースとしてあらかじめ宣言しておくことができます。

    <Window.Resources>
        <CollectionViewSource Source="{DynamicResource VideoItems}" x:Key="VideoItemCollection"/>
        <DataTemplate x:Key="VideoOnItemsControl">
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Path=Title}"/>
                <TextBlock Text="{Binding Path=Author}"/>
            </StackPanel>
        </DataTemplate>
    </Window.Resources>

上記のように用意しておくと、ListBoxのところは

<ListBox ItemsSource="{Binding Source={StaticResource VideoItemCollection}}" ItemTemplate="{StaticResource VideoOnItemsControl}"/>

で済むようになります。

5. コンテナのTemplateの変更

表示方法を変更できるのは項目だけではありません。項目を入れている箱、コンテナも変更することができます。通常リストボックスは垂直方向に項目が並びますがこれを水平方向に変えることがWPFでは簡単にできます。

            <ListBox ItemsSource="{Binding Source={StaticResource VideoItemCollection}}" ItemTemplate="{StaticResource VideoOnItemsControl}">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <StackPanel Orientation="Horizontal"/>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
            </ListBox>

と書くと、、、

WPFYouTube200805232.jpg


のようになります。

だいぶ長くなってしまいました。今日はここまでにします。次回はItemsControlとListViewについて書いてみたいと思います。

Adobe AIRアプリケーションを配布する場合に、.airという拡張子でパッケージングして配布します。.airファイルは中身はzipファイルで拡張子を.airにしたものです。

Adobe AIRをインストールすると .airファイルにAIR Application Installerが関連付き、.airファイルを実行すると、アプリがインストールされてなければインストールし、既にインストールされていれば置き換えるかどうか、古いバージョンが入っていればバージョンアップするかどうか、など良きにはからってくれ、起動してくれるようになります。

これによりwebには.airファイルを公開しとけばそれが自動解凍式インストーラーになるわけです。

しかし、IEでairファイルをダウンロードすると中身を勝手に判定して.zip拡張子でローカルに保存しようとするので AIR Application Installerに関連付かなくなりばつが悪いです。

Adobeによるとwebサーバに下記を追加せよと書いてあります。

AddType application/vnd.adobe.air-application-installer-package+zip .air

ですが、レンタルサーバなどで設定がいじれないケースは自分でHTTP headerをmimeを返したりしたくなります。

phpの場合は下記のようなスクリプトを.airファイルと同じところにおいて、ダウンロードのリンクはこのphpファイルにすればよいです。

<?
$filename = "foo.air";
$mime = "application/vnd.adobe.air-application-installer-package+zip";
header("Content-Type: {$mime}");
header('Content-Disposition: attachment; filename="'.$filename.'"');
readfile($filename);
?>

最近lightbox.jsというライブラリを使って遊んでいます。
http://www.huddletogether.com/projects/lightbox/
http://www.huddletogether.com/projects/lightbox2/

lightbox.jsは画像のポップアップ表示を可能にするjavascriptライブラリですが、javascriptを一行も書かずとも

<script type="text/javascript" src="js/lightbox.js"></script>

と入れるだけで

あとは

<a href="images/image-1.jpg" rel="lightbox" title="my caption">image #1</a>

とaタグのrel要素のlightboxと書くと利用できてしまうすぐれものです。

コードを見ればわかりますが利用者にjavascriptを書かなくて済むようにするためにonloadでinitLightboxを呼び出してごにょごにょしています。

Ajaxのページではこのonloadでごにょごにょしているというところに注意点があります。たとえばprototype.jsのAjax.Updaterなどで動的にページの一部を更新する場合に、

<a href="images/image-1.jpg" rel="lightbox" title="my caption">image #1</a>

と入れてもonloadが呼び出されたあとだと意味がありません。通常のaタグのまま動作してしまいます。

さて、どうすればいいか。initLightboxでごにょごにょしているところを追ってみます。
ごにょごにょしていますが、重要なところを抜き出すと
lightboxの場合

if (anchor.getAttribute("href") && (anchor.getAttribute("rel") == "lightbox")){
anchor.onclick = function () {showLightbox(this); return false;}
}

lightbox 2の場合(2008/12/06追記: 2.04ではmyLightboxというグローバルな変数は使わなくなっていました)

// use the string.match() method to catch 'lightbox' references in the rel attribute
if (anchor.getAttribute('href') && (relAttribute.toLowerCase().match('lightbox'))){
anchor.onclick = function () {myLightbox.start(this); return false;}
}

と。どちらもやってることはaタグを探して、ref="lightbox"であればonclickにshowLightboxを表示しretturn falseし、ページ遷移を抑止する。ということだけです。

つまりは、onload以降にAjaxなどで動的に要素を追加する場合は、それぞれ

<!-- lightbox -->
<a href="images/image-1.jpg" rel="lightbox" onclick="showLightbox(this); return false;" title="my caption">image #1</a>
<!-- lightbox2 -->
<a href="images/image-1.jpg" rel="lightbox" onclick="myLightbox.start(this); return false;" title="my caption">image #1</a>

と明示的にonclickに処理を書いてあげれば良いのです。

これで後から追加されたaタグでもlightboxを表示できます。(2008/12/06追記: 2.04ではmyLightbox変数を使わなくなっておりこの技が使えません)

さて、lightbox 2では複数の画像でスライドショーもどきな表示を行うことができます。
コードを見てみると、表示を開始するところで・・・・

// if image is NOT part of a set..
if((imageLink.getAttribute('rel') == 'lightbox')){
  // add single image to imageArray
  imageArray.push(new Array(imageLink.getAttribute('href'), imageLink.getAttribute('title')));			
} else {
  // if image is part of a set..
  // loop through anchors, find other images in set, and add them to imageArray
  for (var i=0; i<anchors.length; i++){
    var anchor = anchors[i];
    if (anchor.getAttribute('href') && (anchor.getAttribute('rel') == imageLink.getAttribute('rel'))){
      imageArray.push(new Array(anchor.getAttribute('href'), anchor.getAttribute('title')));
    }
  }
  imageArray.removeDuplicates();
  while(imageArray[imageNum][0] != imageLink.getAttribute('href')) { imageNum++;}
}

とrel要素でスライドショーする画像を集めていることがわかります。

本家のサイトではスライドショー表示するときには

<a href="images/image-1.jpg" rel="lightbox[roadtrip]">image #1</a> <a href="images/image-2.jpg" rel="lightbox[roadtrip]">image #2</a> <a href="images/image-3.jpg" rel="lightbox[roadtrip]">image #3</a>

というhtmlを例に出しています。

ここまで見たコードをもとにlightbox2では


  • rel要素に'lightbox'という文字が含まれているとonloadでonclickに処理が追加される

  • 同じrel要素をもつものでスライドショーが行える


ということが理解できます。

ちなみに先ほどのAjaxの例で明示的にonclickにlightboxを表示する処理を記述した場合は、
rel要素に'lightbox'という文字列を入れる必要はなくなります。(2008/12/06追記: 2.04ではrel='lightbox'は必須ぽいです)
ですが、rel要素に何も入れない場合には、すべてのaタグでスライドショー表示を行おうとするので注意が必要です。'lightbox'という文字列を入れる必要はありませんが、なんらかの文字列を入れておくとよいでしょう。

aTunesを少しばかり更新しました。

すっかり音楽番組も見なくなってアーティストの新曲が発見できない状態だったので、
Amazonから新譜を取得するようにしてみました。新譜といってもAmazonのWebサービスで発売日の新しい順でItemSearchしているだけです。アーティストによってはNew Releaseと言いながらずいぶん古いアルバムが並んだりしますが、まぁ・・・それはスルーしてください。

さて、備忘録的にaTunesの内部実装を少し書きます。

言語はPHP5、フレームワークとしてsymfonyを使っています。今回の更新ついでにsymfonyのバージョンも最新の1.0.11に更新しました。Ruby on Railsを使うことも検討したのですが、Rubyを勉強してる時間を惜しんで、とりあえずリリースしたかったのでPHPのRailsといえるsymfonyで実現しました。

Mashupとしては、
・動画コンテンツの再生、検索にYouTube APIを使用、実装にはPEARのServices/YouTubeを利用
・関連アーティストの取得には、Last.fmのAPIを使用、実装は自前
・アーテイスト情報は、SimpleAPI経由でWikepdiaから取得、実装は自前
・ページフッタにあるアーティストのタグクラウドはMoraのシングルランキング100のアーティストを取得、実装は自前
・アーティストのNewReleaseはAmazonから取得、実装にはPEARのService/Amazon(AmazonECS4)を利用
というようになっています。

割とさくっと作ったけどこうやって箇条書きにしてみるといろいろ使ったりしていました。

現状、ユーザ管理などが面倒なので会員登録とかログインしてユーザごと何かするとか、SNS的な機能などはありません。個人的にふらっと立ち寄って、使える気軽さが欲しいと思っているため、そういった機能も考えてもいません。個人設定はCookie&Sessionでできる範囲のことをやろうと思います。そういわけで、dbを使う操作も今のところありません。

残念ながらまだNow Loading...消えない問題が起きている。。。

1   2   3     

Twitter

WALKMAN.LOG

このアーカイブについて

このページには、過去に書かれたブログ記事のうちプログラミングカテゴリに属しているものが含まれています。

前のカテゴリはサーバ構築です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。