2010年7月11日日曜日

GridViewも使ってます

MVCだけじゃないよ。WebFormも使ってるよ。最近GridViewで表示結果が1件だけだったら最初から選択状態にしたいな、なんて思ったわけですが、そんなときってSelectedIndexに行番号セットすれば済むよね。

  <asp:GridView runat="server" ID="gridView" 
    DataSourceID="dataSource" AutoGenerateColumns="false" 
SelectedRowStyle-CssClass="selectedRow" ShowHeader="false" DataKeyNames="Name"> <Columns> <asp:CommandField ShowSelectButton="true" /> <asp:BoundField DataField="Name" /> <asp:BoundField DataField="AkumaNoMi" /> </Columns> </asp:GridView> <asp:ObjectDataSource runat="server" ID="dataSource"
TypeName="WebApplication1.People" SelectMethod="Select">
</asp:ObjectDataSource> <asp:Button runat="server" ID="button" Text="ぼたーん" /> <h2 runat="server" id="subject"></h2>

こんなページでしょうか。

gridview1

コードビハインドはこうですよね。

  public class Person
  {
    public string Name { get; set; }
    public string AkumaNoMi { get; set; }
  }

  public class People
  {
    private static List<Person> _people = new List<Person>
               {
                 new Person {Name = "ルフィー", AkumaNoMi = "ゴムゴム"},
                 new Person {Name = "ニューゲート", AkumaNoMi = "グラグラ"},
               };

    public IEnumerable<Person> Select()
    {
      return _people;
    }
  }

  public partial class _Default : System.Web.UI.Page
  {
    protected void Page_Load(object sender, EventArgs e)
    {
      gridView.SelectedIndexChanged += (s, ev) => 
subject.InnerText = gridView.SelectedIndex + "行目"; } }

ObjectDataSourceを使ってるのはデータバインドの処理とか実際にはコード書かないっていう前提だからです。この状態で、選択ボタンを押すとちゃんと「n行目」って選択した行が表示されましょう。そうでしょう。

gridview2

これって普通なんだけど、GridViewのSelectedIndexにセットするようPage_Loadに書いてみても「n行目」とは表示されませぬ。されませぬ。

    protected void Page_Load(object sender, EventArgs e)
    {
      gridView.SelectedIndexChanged += (s, ev) => 
subject.InnerText = gridView.SelectedIndex + "行目"; if(!IsPostBack) { gridView.SelectedIndex = 1; } }

gridview3


↑ちゃんとニューゲート選択されてるけど「1行目」とは表示されない。なぜでしょうか。SelectedIndexプロパティに値をセットしても、GridViewがSelectedIndexChanging/SelectedIndexChangedイベントをRaiseしてくれないからですね。これをRaiseしてあげればいいんだけどそんなことしてくれないと思うので、そんな時にはGridViewにあるprotectedなOnSelectedIndexChanging/OnSelectedIndexChangedを呼べばいいでしょう。GetMethodで取り出せばprotectedだとしても知ったこっちゃないしね。

 

    private void GridViewSelectedIndex(GridView grid, int index)
    {
      grid.SelectedIndex = index;
      var selectedIndexChanged = typeof(GridView).
GetMethod("OnSelectedIndexChanged",
BindingFlags.NonPublic |
BindingFlags.Instance); if (selectedIndexChanged != null) { selectedIndexChanged.Invoke(grid,
new object[] { new EventArgs() }); } }

これをPage_Loadの”gridView.SelectedIndex = 1;”の部分で呼び出してあげるように書き換えちゃえば、ほら出来た!

gridview4

うぬ。悪くない。でも、これと同じことをクライアントサイドで選択ボタンを押したことにすることでも、実現できますね。例えばこう。

  <script type="text/javascript">
    <% if(!IsPostBack){ %>
    $(function () { 
      __doPostBack("<%=gridView.ClientID.Replace("_","$") %>","Select$1");
    });
    <%} %>
  </script>
  

固定でニューゲートを選択させてます。Select$1の部分。これはちょっと気持ち悪いかも。悪くないとは言いがたい。UpdatePanelに入れておけばそれなりな感じでいいかもしれないけど、普通に1回PostBackされるから悩ましい。

サーバーサイドとクライアントサイドのどちらをチョイスしてもいいかなと思うけど、どうでしょうね。いきなりGridViewの話をしているのはフリで、ここからが本題です。

AjaxControlToolkit(ACT)をがんばって使ってます。WebFormでの簡単Ajax導入に関してACTはとてもいい選択肢ではありますね。お金払わなくていいし、クライアントサイドの知識も要らないし。でも気に入らない部分も多くてウキャ~!ってなる。ACTとはいえ、レンダリングされてしまえばただのHTMLとJavaScriptなわけじゃないですか。気に入らなければ書き換えちゃえばいい。どこを書き換えるかがポイントだと思いますが、今回Accordionを書き換えてみました。ボス、ごめんね!

先程のGridViewのサンプルの続きに書き足していきます。

まずはASPXに以下を追加。これでAccordionが使えます(なんつーかツールボックスからのドラッグアンドドロップのやり方を知ってるともっとラクにかけるんだろうけど、そんなやり方はわすれたので普通に手で書いてます)。

  <asp:ToolkitScriptManager runat="server"></asp:ToolkitScriptManager>
  <asp:Accordion runat="server" ID="accordion"
       HeaderCssClass="acd_header" HeaderSelectedCssClass="acd_selected" 
ContentCssClass="acd_content"> <Panes> <asp:AccordionPane runat="server" ID="pane1"> <Header>pane 1</Header> <Content>パネルのコンテンツ その1 <p>The Ajax Control Toolkit contains a rich set of controls that you can use to build highly responsive and interactive Ajax-enabled Web applications. The Ajax Control Toolkit contains more than 40 controls, including the AutoComplete, CollapsiblePanel, ColorPicker, MaskedEdit, Calendar, Accordion, and Watermark controls. Using the Ajax Control Toolkit, you can build Ajax-enabled ASP.NET Web Forms applications by dragging-and-dropping Toolkit controls from the Visual Studio Toolbox onto a Web Forms page.</p> </Content> </asp:AccordionPane> <asp:AccordionPane runat="server" ID="pane2"> <Header>pane 2</Header> <Content>パネルのコンテンツ その2 <p>The Ajax Control Toolkit contains a rich set of controls that you can use to build highly responsive and interactive Ajax-enabled Web applications. The Ajax Control Toolkit contains more than 40 controls, including the AutoComplete, CollapsiblePanel, ColorPicker, MaskedEdit, Calendar, Accordion, and Watermark controls. Using the Ajax Control Toolkit, you can build Ajax-enabled ASP.NET Web Forms applications by dragging-and-dropping Toolkit controls from the Visual Studio Toolbox onto a Web Forms page.</p> </Content> </asp:AccordionPane> <asp:AccordionPane runat="server" ID="pane3"> <Header>pane 3</Header> <Content>パネルのコンテンツ その3 <p>The Ajax Control Toolkit contains a rich set of controls that you can use to build highly responsive and interactive Ajax-enabled Web applications. The Ajax Control Toolkit contains more than 40 controls, including the AutoComplete, CollapsiblePanel, ColorPicker, MaskedEdit, Calendar, Accordion, and Watermark controls. Using the Ajax Control Toolkit, you can build Ajax-enabled ASP.NET Web Forms applications by dragging-and-dropping Toolkit controls from the Visual Studio Toolbox onto a Web Forms page.</p> </Content> </asp:AccordionPane> <asp:AccordionPane runat="server" ID="pane4"> <Header>pane 4</Header> <Content>パネルのコンテンツ その4 <p>The Ajax Control Toolkit contains a rich set of controls that you can use to build highly responsive and interactive Ajax-enabled Web applications. The Ajax Control Toolkit contains more than 40 controls, including the AutoComplete, CollapsiblePanel, ColorPicker, MaskedEdit, Calendar, Accordion, and Watermark controls. Using the Ajax Control Toolkit, you can build Ajax-enabled ASP.NET Web Forms applications by dragging-and-dropping Toolkit controls from the Visual Studio Toolbox onto a Web Forms page.</p> </Content> </asp:AccordionPane> <asp:AccordionPane runat="server" ID="pane5"> <Header>pane 5</Header> <Content>パネルのコンテンツ その5 <p>The Ajax Control Toolkit contains a rich set of controls that you can use to build highly responsive and interactive Ajax-enabled Web applications. The Ajax Control Toolkit contains more than 40 controls, including the AutoComplete, CollapsiblePanel, ColorPicker, MaskedEdit, Calendar, Accordion, and Watermark controls. Using the Ajax Control Toolkit, you can build Ajax-enabled ASP.NET Web Forms applications by dragging-and-dropping Toolkit controls from the Visual Studio Toolbox onto a Web Forms page.</p> </Content> </asp:AccordionPane> </Panes> </asp:Accordion>

acd1

恐ろしいほど簡単。もちろん、サーバーサイドで動的にPaneを追加するような使い方も多いでしょう。そうすると、Paneが10個や20個にナッチャウことも。そうなるとインターフェースとしてちょっと問題あり。そんなに追加しなきゃAccordionっていいのにとつくづく思う。でも、かなり開発が進んでしまうと、おいそれとインターフェースを変更するのはしんどい作業になります。変更箇所多すぎるし。そんなこんなで、クライアントサイドで何とかしてしまいます。

acd2acd3

完成形は↑これです。何をしてるのかというと、AccordionのHeaderをSelect(DropDown)にして、選択したら該当するPaneの中身を表示するという動きです。ヒマがあったら動かしてみてね。

まずは、AccordionがレンダリングするHTMLを確認。

<div>
  <input type=”hidden” value=”選択してるPaneのIndex”/>
  <div class=”header class name”>header 1</div>
  <div class=”content class name”>content 1</div>
  <div class=”header class name”>header 2</div>
  <div class=”content class name”>content 2</div>
</div>

こんな構造ですね。ヘッダとコンテンツが、連続してるdiv要素をクラス名で判別というのがちょっと気に入らないですが、今回そこはどうでもいいです。最初にこういう構造でレンダリングされるというのがわかりさえすればいいです。

続いて、どういうスクリプトが動いてるのかを確認してみると、ページ内では↓これだけですね。

<script type="text/javascript"> 
//<![CDATA[
(function() {var fn = function() {
$get("ctl00_MainContent_ToolkitScriptManager1_HiddenField").value = '';
Sys.Application.remove_init(fn);};Sys.Application.add_init(fn);})();


Sys.Application.add_init(function() { $create(Sys.Extended.UI.AccordionBehavior,
{"ClientStateFieldID":"ctl00_MainContent_accordion_AccordionExtender_ClientState",
"HeaderCssClass":"acd_header",
"HeaderSelectedCssClass":
"acd_selected","id":"ctl00_MainContent_accordion_AccordionExtender"},
null, null, $get("ctl00_MainContent_accordion")); }); //]]> </script>

Sys.Application.add_initのタイミングでAccordionBehaviorオブジェクト(クラスのような作りかな?)を渡してますね。そもそもSystem.Extendedが何者なのかしらないですけど、ソースが公開されてるので確認してみたところ、各種Behaviorオブジェクトのprototype.initializeでいろいろ初期化処理を書いてました。

ということは、そこを壊せば、Accordionの初期化処理は走らなくなるということなので、add_initで追加したものが実行される前に、AccodionBehavior.prototype.initializeを壊してみました。runat="server"なform要素の閉じタグ直前に以下のコードを書き足します。

  <script type="text/javascript">
    if (Sys.Extended !== undefined && 
Sys.Extended.UI.AccordionBehavior !== undefined) { Sys.Extended.UI.AccordionBehavior.prototype.initialize = function () { }; } </script>

やりすぎな感じもしますが、まぁいいでしょう。

続いてドロップダウンを表示する枠と、Paneの中身を表示する枠を用意します。Accordionタグの上にでもおいておきます。

  <div id="dropdown"></div>
  <div id="viewer"></div>

最後に、Accordionそのものが表示されてしまわないように、Accordionを非表示するためのCSSを定義します。Visibleを指定しちゃうとホントになくなっちゃうからCSSだけで消しちゃうのがいいです。

  <asp:Accordion runat="server" ID="accordion"
       CssClass="accordion_hack" HeaderCssClass="acd_header" HeaderSelectedCssClass="acd_selected" 
       ContentCssClass="acd_content">

太字のところで、クラス名指定して、そのクラスはdisplay:none;だけを適用してます。

あとはJavaScriptでDropDownを生成するのと選択肢を変えたときに表示を切り替えるようなコードをゴリゴリ書けば完成。

<script type="text/javascript">
  $(function () {
    var hack = function () {
      var accordion = $(".accordion_hack:first");
      var viewer = $("#viewer");

      var dropdown = $("<select />");
      var currentId = accordion.find(".acd_selected")
.next()
.attr("id"); var prevId = currentId; accordion.find(".acd_content").each(function (idx, e) { $(dropdown).append( $("<option />").text($(e).prev().text()) .val($(e).attr("id"))); }); $(dropdown).appendTo("#dropdown"); $(dropdown).change(function (e) { prevId = currentId; currentId = $(this).val(); viewer.fadeOut("fast",function () { if (currentId != prevId) { $("#" + prevId).appendTo(accordion); } $("#" + currentId).show().appendTo(viewer); }).fadeIn("fast"); // accordion.find("input[type='hidden']:first")
.val($(this).find("[value=" + currentId + "]").index()); }); $(dropdown).val(currentId).trigger("change"); }; hack(); }); </script>

わざわざhack変数にいれてるのは、特に意味ないです。テスト目的なので気にしないでね!長々とMVCとは関係ないことを書きましたが、要はJavaScriptとHTML、DOM操作、CSSを理解してると、ASP.NETもいろいろカスタムなレンダリングを気軽に実装できるよっていう話なのと、ACTをハックすることでちょっと勝てた気になれたので、調子にのってエントリしてみました。

今回の一式もダウンロード出来るようにしておきます。興味があればどうぞ。

だた、注意点があります。VS2010でASP.NET 4をターゲットにして作ってるのに、ACTは4.0じゃなくて3.5向けのものを使ってます。理由はACT4のAccordionPaneがMasterPageを使ってる時に、id属性を正しく吐き出してくれないからです。3.5だと問題ないので、4.0でなんかおかしくなったんじゃないかな。気をつけてね。

2010年7月6日火曜日

DeserializingModelBinder

モダンチョキチョキズを知ってるのはどのくらい少数派なのかが気になる今日この頃。

ASP.NET MVC 2 Futuresに含まれているHtml.SerializeとDeserializeAttributeをクラスのプロパティに対して適用するのにお悩みな方へ。DeserializeModelBinder自体がなぜかDeserializeAttributeクラスのprivate sealedクラスと定義されてしまっているので、全く同じものを以下のように定義することで、ModelBinderAttributeを指定して比較的簡単に出来るようです。

  public class DeserializingModelBinder : IModelBinder
  {

    private readonly SerializationMode _mode;

    public DeserializingModelBinder() : this(SerializationMode.Plaintext) { }
    public DeserializingModelBinder(SerializationMode mode)
    {
      _mode = mode;
    }

    public object BindModel(ControllerContext controllerContext, 
ModelBindingContext bindingContext) { if (bindingContext == null) { throw new ArgumentNullException("bindingContext"); } var vpResult = bindingContext.ValueProvider
.GetValue(bindingContext.ModelName); if (vpResult == null) { // nothing found return null; } MvcSerializer serializer = new MvcSerializer(); string serializedValue = (string)vpResult.ConvertTo(typeof(string)); return serializer.Deserialize(serializedValue, _mode); } }

↑これはソースからコピペです。MvcSerializerが使えなかったらどうしようかと思いましたが、こちらは平気ですね。

  public class Division
  {
    public string Name { get; set; }
    public Person Boss { get; set; }
    public People People { get; set; }
  }

  [Serializable]
  [ModelBinder(typeof(DeserializingModelBinder))]
  public class People : List<Person>
  {}

  [Serializable]
  public class Person
  {
    public string Name { get; set; }
    public DateTime MemorialDay { get; set; }
  }

↑このようなモデルクラスたちを定義してみました。あえてPeopleクラスを定義しているのはModelBinderAttributeがプロパティに指定できないからです。DeserializeAttributeにいたってはParameterにしか指定できないし、ModelMetadataProviderらへンに手を入れる必要がある気がしなくもなく(たぶんメタデータを見てModelBinderを切り替えるような仕組みでしょうか)、難しそうだったので使っていません。

このようにクラスを定義した上で、ModelBinderAttributeでModelBinderを指定する方法が比較的簡単でスマート(?)じゃないかと思います。こうしておくとアクションでは何も意識する必要なく以下のようにアクションパラメータを生成してくれるようになります。

  public ActionResult Division()
  {
    var model = new Division
    {
      Name = "ブチャラティチーム",
      Boss = new Person { Name = "ブローノ・ブチャラティ", 
MemorialDay = new DateTime(2010, 1, 1) }, People = new People { new Person {Name = "ジョルノ・ジョバァーナ",
MemorialDay = new DateTime(2010, 2, 1)}, new Person {Name = "レオーネ・アバッキオ",
MemorialDay = new DateTime(2010, 3, 1)}, new Person {Name = "グイード・ミスタ",
MemorialDay = new DateTime(2010, 4, 1)}, new Person {Name = "ナランチャ・ギルガ",
MemorialDay = new DateTime(2010, 5, 1)}, new Person {Name = "パンナコッタ・フーゴ",
MemorialDay = new DateTime(2010, 6, 1)} } }; return View(model); } [HttpPost] public ActionResult Division(Division model) { return View(model); }

Viewは以下のようにシンプルです。

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MvcApplication1.Models.Division>" %>
<%@ Import Namespace="Microsoft.Web.Mvc" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
  Division
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <h2>Division</h2>

  <% using(Html.BeginForm("Division","Home")) { %>
    <%= Html.EditorFor(m=>m.Name) %>
    <h3>Boss</h3>
    <%= Html.EditorFor(m=>m.Boss) %>

    <h3>People</h3>
    <% foreach (var person in Model.People) { %>
      <%= Html.DisplayFor(m=>person) %>
    <% } %>

    <%= Html.Serialize("People",Model.People) %>
    <input type="submit" value="送信" />
  <% } %>
</asp:Content>

これを実行するとこのようにきちんと復元してくれます。

serialize1 serialize2 serialize3

@jsakamotoさん、いかがでしょうか?

2010年7月4日日曜日

Razorのポテンシャル

Introducing “Razor” – a new view engine for ASP.NET - ScottGu's Blog

↑これ読みました?

Razor View Syntax

↑これも。

これね、よくよく考えたらかなり凄いことに取り組んでるんだと思います。そもそもASP.NET MVCはASP.NETのフレームワークにのっかったものですよね。それはどういう事かというと

  • HttpApplicationのパイプラインで処理
  • HttpContextで現在のリクエストコンテキストに関する全ての情報にアクセス可能
  • System.Web.UI.PageがIHttpHandlerの実装としてPageパイプラインを処理しつつレンダリング

ですね。ASP.NET MVCのViewでは、このフレームワーク(Pageに関する部分)の使い方で変わった部分はほとんどないんですが、構造上使われなくなったものといえばコントロールツリーの構築とポストバックです。コントロールツリーに関しては「runat="server"なコントロールを使ってる場合には作られるじゃないか」と言われればそうなんですが、そこはPageクラスがそうだからで、Viewの性質として必須な機能じゃないのでWebFormとの対比という意味では使ってないと言うことにしましょう。ポストバックですが、これは特にMVC2になってから大きく変化のある部分で例えば以下の単純なViewでもIsPostBackはtrueになりません。

<%@ Page Language="C#" 
MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage" %> <script runat="server"> void Page_Load() { if(IsPostBack) { // PostBack eventはいずこ?
// ここには決して入ってこない } } </script> <asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server"> </asp:Content> <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> <form runat="server"> <asp:Login ID="Login1" runat="server"> </asp:Login> </form> </asp:Content>

Viewのレンダリングの仕様が変更になってるのでWebFormViewにはイベントがイベントとして認識できない形になったから(TextWriter渡しのRender呼び出し)。ようはControllerに対するPOST/PUT/DELETE/GETのHttp MethodがViewに影響を及ぼすことを是としない、という意志の表れなんじゃないか~、なんてな。

話がずれましたがRazorはこのPageクラスに依存しないViewEngineとして造られてるので、Page Pipeline(イベント)は処理されない、コントロールツリーも構築されないという事になって、ASPX構文解析→C#生成の部分と、Razorの準備コスト(テンプレートエンジンとしてのコスト)が同程度だとしても、処理コストは低くなるのでよりスケールするフレームワークになるんじゃないかと思います。

Pageクラス(WebForms)におけるPostBackという発明はPage Controllerの素晴らしい実装だと思いますが、MVCとなればControllerは役割の違うものとして分離されてるので、PostBackは不要だし、それを実装してるPageクラスはリッチすぎるでしょう。DataSetからRepositoryへのスタイルの変化も同じような考え方からきてるんじゃないでしょうかね(メモリバウンドよりCPUバウンド的な)。Pageクラス、Page Pipelineからの脱却。凄くないですか?

と、なんの根拠もないですが、妄想したりしてます。

2010年7月3日土曜日

ふぉー!フォー!デレシシシ。

なんかね~、そっけない箱がね~、あったんですよ。

IMG_0005


でも、開けるとピカピカな箱なんですよ。

IMG_0006


長かった。オンラインショップで予約した負け組で始まった今回の機種変。サイトダウンにめげずF5アタックを繰り返すも当日予約は途中で断念。翌朝7:30にはすんなり予約できたから、これは当日入手期待大だと妄想。それまでiPhone 3GにiOS4を入れて気分だけでも味わってやろうとしたら信じられないくらいパフォーマンス悪くてなんどもたたき壊してやろうかと思う辛い日々。だけど発売日を過ぎてもなんの連絡も無く。ちなみに受付番号下6桁は818XXX。一体全体この放置プレーはなんだ。そんなこんなで6/30にメールが来た。遅すぎるし放置しすぎ。遅れております、のメール1つで全然違うのになぜ放置。まぁ、いいか。

で、箱。長かった。

さて、アクティベーションしようかとしたところ、9:00~19:00が受付時間らしく何も出来ず。敗北感。仕事変えようかと本気で思った。

IMG_0009

もろもろ設定完了して使ってみると3Gとは比べものにならないくらいのパフォーマンスで操作が快適。さすがですぜ!

2010年6月22日火曜日

千の夜をこえて

いやはや、以前100を超えた時に次は500を目標にする!と言ってからほぼ1年?

stackoverflow

長かった。やっと500を超えた(いつマイナス評価がついて500を切るかヒヤヒヤするけど)デス!

嬉し~よ~。

んで、仕事で面接したりしてるんだけど、興味がある方は採用情報 | 株式会社クロスワープここから応募してみてね!意地悪なこと聞かないよ~。コーディングのテストはあるよ~。また社員募集やってますと同じ募集要項ですよ~。テスト問題はたけはらが考えましたよ~。なので「リンクリストの循環参照検出」なんていう難しい問題ではないですよ~。ぜひどうぞ~。

2010年6月18日金曜日

yield break

最近HaackさんとこでChecking For Empty EnumerationsNull Or Empty CoalescingでEmptyなIEnumerableに対する拡張メソッドで盛り上がりを見せてるますね。面白いですよね。

でね、ふと思ったんですけどね、IEnumerable<T>の空イテレータってどう表現するのがオシャレなのかな、と。

  public IEnumerable<T> Empty<T>()
  {
    return Enumerable.Empty<T>();
  }

  public IEnumerable<T> EmptyZero<T>()
  {
    return new T[0];
  }

  public IEnumerable<T> EmptyYield<T>()
  {
    yield break;
  }

んん~。個人的にはyield breakなんだけど。

2010年5月29日土曜日

ラテアート

いったいいつまでブログ放置をし続ければ気が済むのかね。ズボラにも程がありますよ、まったく。かといってコレといって面白いことも無く。あ、最近Code Contractsが面白そうか。

と、いいつつも最近テニス始めたんですよ。

IMG_2552

最近と言っても、今年に入ってからなんでもう半年位たちますね。いやはや。でも月1くらいしか出来てないんだけど、やり始めるとテレビとかでもテニスみちゃうもんですね。フランスのレザイが要注目やで!

雨の日にランチを食べに行くカフェでたまに素敵なカフェアートをしてくれるんですけど、これがちょっとカワイイ。

IMG_2568

いきなり3Dですよ!アリス イン ワンダーランドも真っ青ですよ。

IMG_2580

4匹のクマちゃん?たぶんクマちゃん。

IMG_2586

で、iPad。あ、間違えちゃった。ラテアートと間違えちゃった。てへ。

なんせ本を読むのとビデオを見るのをメインの用途に考えてるんだけど、本ってどうやったら見れるんだろ。Kindle入れとけばいいのけ?それともCloudReader?

ってことで、↑の写真はCloudReader入れてるところ。KindleってAmazon.comのアカウントじゃないとダメなんすかね。登録してとりあえず無料の本を買ってみたけど...。

もっと気軽に普通にPDFをみるのもいいかと思うも、そもそもPDFをどうやってiPadにコピーしたらいいのかよくわかんないじゃないか。Dropboxが入れてあるからそっちからPDF開いてみたら普通に見れたけど、なんかそんな面倒なハズない!と調べてみたらなんてことなかった。iTunesからD&Dで転送出来た。まずはPDF(いろんな公開ドキュメント)が見れるようになるだけでも超便利。iPhoneで見るとか小さすぎてありえない。見れるのと、ちゃんと読めるっていうのとこんなにも違うのかと電子書籍の衝撃ですよ(この本とても面白いのでおすすめ。あとアーキテクトの審美眼も面白かった)。

来週からの通勤が楽しみです。

そうそうそうそう。社員募集してるみたいデス。興味があればどうぞ。

dotnetConf2015 Japan

https://github.com/takepara/MvcVpl ↑こちらにいろいろ置いときました。 参加してくださった方々の温かい対応に感謝感謝です。