ブリリアントなWP7 RSSフィード

2011-12-26

Windows Live SkyDrive でエクスプローラっぽい操作を行う方法 -テキストファイル操作編-

f:id:bs-wp7:20111226154736p:image
前回はSkyDriveをエクスプローラっぽい操作でフォルダ移動する所までやりました。今回はSkyDrive上のテキストファイル作成更新削除します。

実装

エクスプローラっぽい操作で、ファイルを新規作成したり、ファイルを選択して内容更新やファイル削除を行います。具体的には以下のような操作になります。

  • ディレクトリ表示時にApplicationBarIconButtonの"New"ボタンを押下して、新規ファイル作成。
  • ファイル選択時にファイルタイトルとファイル内容をダイアログに表示させて、内容を編集して更新もしくはファイル削除。

事前準備

前回の"Windows Live SkyDrive でエクスプローラっぽい操作を行う方法 -フォルダ移動編-"の続きになりますので、こちらの実装は済ませておいてください。

利用権限の付与

SkyDriveのファイル/フォルダ情報を取得する事になります。SingInButtonに対して以下の権限付与を行なってください。

  • wl.skydrive_update(ファイル操作を行う権限)

前回のと合わせると、以下の一行をSingInButtonコントロールのScopesプロパティに設定すればOKです。
wl.basic wl.signin wl.signin wl.skydrive wl.photos wl.skydrive_update

画面レイアウト(MainPage.xaml)

前回と比べて、ファイル内容を表示するダイアログと、ApplicationBarIconButtonに"New"ボタンが追加しました。
以下のコードは、前回と被っている部分を省略しています。

<phone:PhoneApplicationPage
    …省略…
    >
    …省略…
    <phone:PhoneApplicationPage.FontFamily>
        <StaticResource ResourceKey="PhoneFontFamilyNormal"/>
    </phone:PhoneApplicationPage.FontFamily>
    <phone:PhoneApplicationPage.FontSize>
        <StaticResource ResourceKey="PhoneFontSizeNormal"/>
    </phone:PhoneApplicationPage.FontSize>
    <phone:PhoneApplicationPage.Foreground>
        <StaticResource ResourceKey="PhoneForegroundBrush"/>
    </phone:PhoneApplicationPage.Foreground>
    <Grid Height="Auto" Width="Auto" d:DataContext="{d:DesignData /SampleData/FileViewModelSampleData.xaml}">
    …省略…
        <!-- ダイアログ -->
        <Grid x:Name="dialog" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.RowSpan="3" Width="380" Height="350" Background="{StaticResource PhoneAccentBrush}" Visibility="Collapsed">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="0.334*"/>
                <ColumnDefinition Width="0.333*"/>
                <ColumnDefinition Width="0.333*"/>
            </Grid.ColumnDefinitions>
            <i:Interaction.Behaviors>
                <ec:DataStateBehavior Binding="{Binding DialogTitle}" TrueState="VisualState0" FalseState="VisualState1"/>
            </i:Interaction.Behaviors>
            <Grid.RowDefinitions>
                <RowDefinition Height="0.18*"/>
                <RowDefinition Height="0.593*"/>
                <RowDefinition Height="0.227*"/>
            </Grid.RowDefinitions>
            <TextBox x:Name="txtDialogTitle" Margin="0,0,5,0" TextWrapping="Wrap" d:LayoutOverrides="Height" HorizontalAlignment="Center" Grid.ColumnSpan="3" BorderThickness="0" Width="300" Text="{Binding DialogTitle, Mode=TwoWay}"/>
            <TextBox x:Name="txtDialogDetail" TextWrapping="Wrap" d:LayoutOverrides="Height" Grid.Row="1" Grid.ColumnSpan="3" Text="{Binding DialogDetail, Mode=TwoWay}"/>
            <Button x:Name="btnDialogDel" Content="削除" d:LayoutOverrides="Width, Height" Grid.Row="2" Click="btnDialogDel_Click" />
            <Button x:Name="btnDialogSave" Content="保存" Grid.Column="1" Grid.Row="2" d:LayoutOverrides="Width, Height" Click="btnDialogSave_Click" />
            <Button x:Name="btnDialogClose" Content="閉じる" Grid.Row="2" Click="btnDialogClose_Click" Grid.Column="2" FontSize="25.333" />
        </Grid>
    </Grid>

    <!-- ApplicationBar -->
    <phone:PhoneApplicationPage.ApplicationBar>
        <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
            <shell:ApplicationBarIconButton IconUri="/icons/appbar.new.rest.png" Text="New" x:Name="appBarIconBtnNew" Click="appBarIconBtnNew_Click" />
        </shell:ApplicationBar>
    </phone:PhoneApplicationPage.ApplicationBar>
</phone:PhoneApplicationPage>
メイン処理(MainPage.xaml.cs)

今回追加したApplicationBarIconButtonとダイアログのボタンをクリックした時のイベントを追加しています。メインの処理は後述するFileViewModel.csで行なっています。
以下のコードは、前回と被っている部分を省略しています。

…省略…
namespace PhoneApp1 {
    public partial class MainPage : PhoneApplicationPage {
        // ダイアログの削除ボタンが押下された時のイベント
        // SkyDrive上から現在開いているファイルを削除してダイアログを閉じます
        private void btnDialogDel_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            model.deleteDialog();
        }

        // ダイアログの保存ボタンが押下された時のイベント
        // 現在開いているテキストをSkyDrive上に保存してダイアログを閉じます
        private void btnDialogSave_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            // ファイル名に拡張子を付けないとアップロード時にエラーになるので、".txt"と追加しておきます
            model.saveDialog(txtDialogTitle.Text + ".txt", txtDialogDetail.Text);
        }

        // ダイアログの閉じるボタンが押下された時のイベント
        // ダイアログを閉じます
        private void btnDialogClose_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            model.closeDialog();
        }

        // Newボタンが押下された時のイベント
        // ファイルの新規作成ダイアログを開きます
        private void appBarIconBtnNew_Click(object sender, System.EventArgs e)
        {
            model.createFile();
        }
    }
}
データバインド用のListBoxアイテム作成(FileItemViewModel.cs)

テキストファイルの新規作成/更新/削除を行なっています。
前回はエクスプローラ上でフォルダを選択するとディレクトリ移動して、それ以外を選択しても何も処理させませんでしたが、今回は "folder" が選択された時にテキストファイルの中身を読み込む処理を行っています。
以下のコードは、前回と被っている部分を省略しています。

…省略…
namespace PhoneApp1 {
    public class FileViewModel : INotifyPropertyChanged {
        private int currentIndex;

        // アイテムが選択された時のイベント
        public void selectItem(int index)
        {
            // 選択されたアイテムがフォルダの場合はディレクトリ移動します
            if ("folder".Equals(items[index].Type))
            {
                // 選択されたディレクトリのIDと名前を履歴に保存しておきます
                …省略…
            }
            else if ("file".Equals(items[index].Type))
            {
                // 選択されたファイルの中身をダイアログに表示します
                getFileDetail(index);
            }
        }

        …省略…

        // ファイル中身を取得します
        private void getFileDetail(int index)
        {
           currentIndex = index;    // 後のファイル操作で使用します

           // テキストファイルの中身をダウンロードします
           WebClient wc = new WebClient();
           wc.DownloadStringCompleted += new DownloadStringCompletedEventHandler(wc_DownloadStringCompleted);
           wc.DownloadStringAsync(new Uri(items[index].Source));
        }

        // ファイル中身のテキストがダウンロードされた時のイベント
        void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                DialogDetail = e.Result.ToString();
                DialogTitle = items[currentIndex].Name;
            }
        }

        // 開いているファイルを削除してダイアログを閉じます
        public void deleteDialog()
        {
            LiveConnectClient fileDeleteClient = new LiveConnectClient(session);
            fileDeleteClient.DeleteCompleted += new EventHandler<LiveOperationCompletedEventArgs>(fileDeleteClient_DeleteCompleted);
            fileDeleteClient.DeleteAsync(items[currentIndex].Id);

            clearDialogInfo();
        }

        // テキストファイルの削除処理が完了した時のイベント
        void fileDeleteClient_DeleteCompleted(object sender, LiveOperationCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                MessageBox.Show("テキストファイルの削除に失敗しました");
            }
        }

        // 開いているファイルを保存してダイアログを閉じます
        public void saveDialog(string title, string detail) {
            // アップロード対象はStream型にしておきます
            var stream = new MemoryStream(Encoding.GetEncoding("UTF-8").GetBytes(detail));

            // アップロードを行います。UploadAsyncの第一引数のPashは、保存ディレクトリのIDを設定する事に気をつけてください。
            LiveConnectClient fileUpdateClient = new LiveConnectClient(session);
            fileUpdateClient.UploadCompleted += new EventHandler<LiveOperationCompletedEventArgs>(filesClient_UploadCompleted);
            fileUpdateClient.UploadAsync("/" + histryDirectoryId[histryDirectoryId.Count - 1], title, true, stream, null);

            clearDialogInfo();
        }

        // ダイアログを閉じます
        public void closeDialog()
        {
            clearDialogInfo();
        }

        // テキストファイルのアップロード処理が完了した時のイベント
        void filesClient_UploadCompleted(object sender, LiveOperationCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                MessageBox.Show("テキストファイルの更新に失敗しました");
            }
        }

        // ダイアログ表示内容をクリア
        // Titleがクリアされる事によって、DataStateBehaviorがダイアログを非表示にします
        private void clearDialogInfo()
        {
            DialogTitle = null;
            DialogDetail = null;
        }

        // ダイアログを空の状態で表示します
        // データに空が設定される事でダイアログがビヘイビアによって表示されます
        public void createFile()
        {
            DialogDetail  "";
            DialogTitle = "";
        }

        …省略…
    }
}

テキストファイルの作成や読み込みではエンコードに注意してください。また、テキストファイルを作成する際に拡張子を付け忘れるとエラーになるので注意してください。

画面キャプチャ

テキストファイルの新規作成画面が左画像、テキストファイルの編集画面が右画像になります。
f:id:bs-wp7:20111226153352p:image

利用API

LiveConnectClient
メソッド名意味
UploadAsyncファイルのアップロードを行います
第一引数はアップロード先のフォルダIDか、パスを設定します
第二引数はファイル名
第三引数は上書きするかの設定
第四引数はよく分からないですがnullで問題なさそうです
DeleteAsyncファイルの削除を行います
第一引数はファイルIDを設定します
イベント名意味
UploadCompletedファイルのアップロード処理が完了したら呼ばれるイベントです
DeleteCompletedファイルの削除処理が完了したら呼ばれるイベントです

参考

REST API -MSDN


技術部 かわかみひろき