Silverlight + MVVM モデルで DataGrid をバインドの落とし穴(その2)

落とし穴シリーズ(にするつもりは無いのですが)の続きです。
本当は、動的バインドの話を書こうと思ったのですが、ItemsSourceプロパティにバインドするときの注意を忘れてました。

DataGrid コントロールのItemsSourceプロパティには、配列やListコレクションなどが渡せます。最近はジェネリックの流行りが功を奏して(かな?)、List<>を渡す場合が多くなっています。
実は IList と List<> の違いどころを間違えるとややこしいことになるのですが、それは別の機会には話します。型チェックのキャストでは List<> の方が不利なんです。型が2つあるからね。

さて、先日のDataGridのソースでは List<> を渡しました。
ページクラスのコンストラクタを改めてみてみると、次のようになっています。

public PageGrid()
{
    InitializeComponent();
    MyModel = new ModelGrid();
    this.DataContext = MyModel;

    // コレクションを作成
    List<Product> list = new List<Product>();
    list.Add( new Product(){ ID="001", Name="Silverlight 3" });
    list.Add( new Product(){ ID="002", Name="Visual Studio 2010" });
    list.Add( new Product(){ ID="003", Name="Expression Bend 3" });
    // グリッドに設定
    this.DGrid.ItemsSource = list;
}

ここでは、コントロールのグリッドに直接設定しているのですが、これは MyModel クラスの Items プロパティに設定してもよいはずです。Items プロパティは DataGrid の ItemsSource プロパティにバインドされています。

public PageGridEdit()
{
    InitializeComponent();
    MyModel = new ModelGridEdit();
    this.DataContext = MyModel;

    // コレクションを作成
    List<Product> list = new List<Product>();
    list.Add(new Product() { ID = "001", Name = "Silverlight 3" });
    list.Add(new Product() { ID = "002", Name = "Visual Studio 2010" });
    list.Add(new Product() { ID = "003", Name = "Expression Blend 3" });
    MyModel.Items = list ;	★
    
}

変わった部分は★だけです。これを画面で動作確認すると、正常に動きます。
そうなんです。ここまでは、検索の例と同じになります。

ここで、このグリッドを編集できるようにしましょう。
グリッド上で編集することもできますが、ここは定番のテキストボックスを使って編集する方法を考えましょう。

<Grid x:Name="LayoutRoot" Background="White">
<StackPanel>
    <TextBox Name="TextID" Width="100" Text="{Binding ProductID, Mode=TwoWay}" />
    <TextBox Name="TextName" Width="200" Text="{Binding ProductName, Mode=TwoWay}"/>
    <Button Name="BtnAdd" Width="100" Content="新規追加" Click="BtnAdd_Click"/>
    <Button Name="BtnDel" Width="100" Content="削除" Click="BtnDel_Click"/>
    <Button Name="BtnUpdate" Width="100" Content="更新" Click="BtnUpdate_Click"/>
    <dt:DataGrid Name="DGrid" Width="300" Height="200"
                 ItemsSource="{Binding Items, Mode=TwoWay}" 
                 SelectionChanged="DGrid_SelectionChagned"
                 />
</StackPanel>
</Grid>

TextID と TextName が編集のためのテキストボックスです。これも ProductID と ProductName という名前でバインドしておきます。バインド先は MyModel です。
# 本当は、編集用のProductクラスにバインドするのですが、説明のために MyModel にバインドします。

ボタンは「新規追加」「削除」「更新」ボタンの3つを用意します。実際は、編集している途中はグリッドを動かせないとか、キャンセルができるとか、色々やることがありますが、今は基本だけを押さえておきましょう。

もうひとつ DataGrid コントロールのイベントを追加します。SelectionChangedイベントです。これはグリッドのカーソルが移動したときに発生するイベントで、カーソルを移動したときに、TextID や TextName コントロールの内容を変更します。

3つのボタンのイベントとデータグリッドの選択イベントを記述したのが次のコードです。

/// <summary>
/// 新規追加ボタン
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void BtnAdd_Click(object sender, RoutedEventArgs e)
{
    Product item = new Product();
    item.ID = MyModel.ProductID;
    item.Name = MyModel.ProductName;
    MyModel.Items.Add(item);
}
/// <summary>
/// 削除ボタン
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void BtnDel_Click(object sender, RoutedEventArgs e)
{
    if (this.DGrid.SelectedItem != null)
    {
        Product item = (Product)DGrid.SelectedItem;
        MyModel.Items.Remove(item);
    }
}
/// <summary>
/// 更新ボタン
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void BtnUpdate_Click(object sender, RoutedEventArgs e)
{
    if (this.DGrid.SelectedItem != null)
    {
        Product item = (Product)DGrid.SelectedItem;
        item.ID = MyModel.ProductID;
        item.Name = MyModel.ProductName;
    }
}
/// <summary>
/// カーソル移動
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void DGrid_SelectionChagned(object sender, SelectionChangedEventArgs e)
{
    if (DGrid.SelectedItem != null)
    {
        Product item = (Product)DGrid.SelectedItem;
        MyModel.ProductID = item.ID;
        MyModel.ProductName = item.Name;
    }
}

データグリッドで選択行を取得する以外は、MyModelのプロパティを使います。ここの部分が MVVM の原則をはみ出しているような気がする場合は、MyModel に SelectionChanged イベントを作るか、OnSelectionChangedメソッドを作って、それを呼び出せばよいでしょう。

さて、モデルクラスのほうは ProductID と ProductName プロパティを追加すればおわりです。

/// <summary>
/// モデルクラス
/// </summary>
public class ModelGridEdit : INotifyPropertyChanged
{
/// <summary>
/// グリッドのデータ
/// </summary>
private IList _Items;
public IList Items
{
    get { return _Items; }
    set
    {
        if (_Items != value)
        {
            _Items = value;
            OnPropertyChanged("Items");
        }
    }
}

private string _ProductID ;
public string ProductID
{
    get { return _ProductID; }
    set
    {
        if (_ProductID != value)
        {
            _ProductID = value;
            OnPropertyChanged("ProductID");
        }
    }
}

private string _ProductName ;
public string ProductName
{
    get { return _ProductName; }
    set
    {
        if (_ProductName != value)
        {
            _ProductName = value;
            OnPropertyChanged("ProductName");
        }
    }
}

#region INotifyPropertyChanged メンバ
/// <summary>
/// プロパティ変更時のイベント
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
    if (PropertyChanged != null)
        PropertyChanged(this, e);
}
protected virtual void OnPropertyChanged(string name)
{
    if (PropertyChanged != null)
        PropertyChanged(this, new PropertyChangedEventArgs(name));
}
#endregion
}

これを画面で実行すると、初期画面にリストが表示されるのですが。。。
新規追加ボタンと削除ボタンがうまく動作しません。
テキストボックスにIDとNameを入力して、追加ボタンを押してもグリッドが更新されません。ブラウザを一旦、最小化させたり、再表示させてもダメです。
しかし、この状態で別のグリッドをクリックすると、行が追加されます。変な動きですね。バグみたいです。

ちなみに、行をたくさん追加すると、スクロールバーを動かしたときにグリッドの表示が更新されます。変な動きですね。

「これはDataGridの不具合だ!」

と思ったのは私だけではないでしょう!

が、違うのです。実はバグではありません。ItemsSource プロパティの設定の仕方が悪いのです。
さっき、ItemsSource プロパティに入れたのは List<> のコレクションでした。そして、List<> コレクションの中身のオブジェクトは当然ながらコレクションの子項目になります。
あれ?先のエントリを読んでいる方は気づいたかもしれません。
そうです。TextコントロールやDataGridコントロールを直接バインドした場合には、それぞれのプロパティの変更は追随しないのです。画面の表示時に読みだすため、一見「バインドが遅い」という感じがするのです。

というわけで、List<> コレクションで自動的に DataGrid を更新することはできません。
困った。
というわけで、MSDN をよく読んでみると、「ObservableCollection<> コレクションを使え」となっています。なんでしょう?この ObservableCollection コレクションってのは?

実は ObservableCollection はリストへの追加や削除のイベントが拾えるコレクションです。というわけで、ObservableCollectionコレクションを使ってリストを作ると、このリストに追加/削除すると、元々のDataGridにイベントが飛ぶんですね。

では早速仰せの通り変更してみましょう。

public PageGrid()
{
    InitializeComponent();
    MyModel = new ModelGrid();
    this.DataContext = MyModel;

    // コレクションを作成
    ObservableCollection<Product> list = new ObservableCollection<Product>(); ★
    list.Add( new Product(){ ID="001", Name="Silverlight 3" });
    list.Add( new Product(){ ID="002", Name="Visual Studio 2010" });
    list.Add( new Product(){ ID="003", Name="Expression Bend 3" });
    // グリッドに設定
    MyModel.Items = list ;
}

修正する箇所は★の一か所だけです。使い慣れない名前ですが、まあ、インテリセンスを利用して打ち込んでみてください。
これを動かすと、おお動くじゃないですか!追加も削除もOK.グリッドでカーソルをぽちぽち動かしても大丈夫。「これはDataGridの不具合だ!」なんて思ってごめんね、Silverlight君。

ってなわけで、これでめでたしめでたし。ってなことになるはずですが、そう、忘れていましたね。
更新ボタンの処理です。

グリッドを選択して、テキストで編集して、更新ボタンをクリック。
さて、グリッドの表示は、変わりません!

なにこれぇ!MVVMって全然使えないジャン。データグリッドを更新してくれなくちゃ意味ないよ。
「これこそDataGridの不具合だ!」
って思って、投げ出したくなります。実際、私も投げ出したくなりました。データグリッドを無理矢理更新しようと思って、Remove/Insertをしたり、一旦ItemsSourceにnullを入れて改めてリストを代入したり、と。
巷のブログでも似たようなことで困っている人がいらっしゃいます。

実はこれはObservableCollectionコレクションに渡すオブジェクトが悪いのです。
Productクラスは次のように単純なデータ型です。

public class Product
{
    public string ID { get; set; }
    public string Name { get; set; }
}

実は、データを更新したときにObservableCollectionコレクションが受け取れるように(ItemsSourceプロパティが受け取れるように)、ここに INotifyPropertyChanged インターフェースが必要なのです。
う~ん。これに気付けってってのが難しいのですが、落とし穴ですね。

public class Product: INotifyPropertyChanged
{
private string _ID;
public string ID
{
    get { return _ID; }
    set
    {
        if (_ID != value)
        {
            _ID = value;
            OnPropertyChanged("ID");
        }
    }
}

private string _Name;
public string Name
{
    get { return _Name; }
    set
    {
        if (_Name != value)
        {
            _Name = value;
            OnPropertyChanged("Name");
        }
    }
}

#region INotifyPropertyChanged メンバ
/// <summary>
/// プロパティ変更時のイベント
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
    if (PropertyChanged != null)
        PropertyChanged(this, e);
}
protected virtual void OnPropertyChanged(string name)
{
    if (PropertyChanged != null)
        PropertyChanged(this, new PropertyChangedEventArgs(name));
}
#endregion
}

こんな風にProductクラスを書き換えます。定番となったOnPropertyChangedはそのままコピー&ペーストしてください。
# プロパティ作成のスペニットがあればいんですが、作り方がよくわからないし。いつもコピー&ペーストして置換をしています。まあ、後でスペニットを作りましょう。
# つーか、この書き方じゃない別に方法を考えましょう。Cの#defineマクロみたいなやつを。

2つの落とし穴があるわけですが、これを修正するとDataGridは思ったように動きます。多分Detailコントロールなんかはこのあたりを自動化しているのでしょう。

落とし穴をまとめると、

・ItemsSourceプロパティにはObservableCollection<>を使え!
・ObservableCollectionコレクションの型にはINotifyPropertyChangedインターフェースを使え!

です。

やれやれ(空条承太郎風ではなくてカート・ヴォネガット風に)。

カテゴリー: 開発 | 2件のコメント

Silverlight + MVVM モデルで DataGrid をバインドの落とし穴

動的バインドの話を書こうと思ったのですが、DataGrid のバインドの落とし穴の件を書くことにします。
多分、検証結果を具体的にみるほうが「何故だめなのか?」がわかりやすいので。

先に注意しておきますが、ここに書くのは「やってはいけない」方法です。

さて、ItemSource プロパティにバインドする場合は、前回書いたように即時実行が行われます。
これを「楽だから」という理由で、DataContextを使ってコントロール自身にバインドしてみましょう。

xaml ファイルを次のように設定します。

<Grid x:Name="LayoutRoot" Background="White">
<StackPanel>
    <dt:DataGrid Name="DGrid" Width="300" Height="200"
                 DataContext="{Binding DGridData, Mode=TwoWay}" />
    <Button Name="BtnSearch" Width="100" Content="検索" Click="BtnSearch_Click"/>
</StackPanel>
</Grid>

DataContext=”{Binding DGridData, Mode=TwoWay}” しているところが、バインドです。

モデルクラスはこんな感じになります。

/// <summary>
/// モデルクラス
/// </summary>
public class ModelGrid2 : INotifyPropertyChanged
{
/// <summary>
/// グリッドのデータ
/// </summary>
private DataGrid _DGridData = new DataGrid();
public DataGrid DGridData
{
    get { return _DGridData; }
    set
    {
        if (_DGridData != value)
        {
            _DGridData = value;
            OnPropertyChanged("DGridData");
        }
    }
}

#region INotifyPropertyChanged メンバ
/// <summary>
/// プロパティ変更時のイベント
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
    if (PropertyChanged != null)
        PropertyChanged(this, e);
}
protected virtual void OnPropertyChanged(string name)
{
    if (PropertyChanged != null)
        PropertyChanged(this, new PropertyChangedEventArgs(name));
}
#endregion
}

データグリッド本体に対してバインドするので、

public DataGrid DGridData

のようにプロパティを書きます。バインド名は「DGridData」です。
検索ボタンを押下したときの処理は、次のようにモデルクラスのほうを使います。

private void BtnSearch_Click(object sender, RoutedEventArgs e)
{
    // コレクションを作成
    List<Product> list = new List<Product>();
    list.Add(new Product() { ID = "001", Name = "Silverlight 3" });
    list.Add(new Product() { ID = "002", Name = "Visual Studio 2010" });
    list.Add(new Product() { ID = "003", Name = "Expression Bend 3" });
    list.Add(new Product() { ID = "004", Name = "New Application 1" });
    list.Add(new Product() { ID = "005", Name = "New Application 2" });
    list.Add(new Product() { ID = "006", Name = "New Application 3" });
    // グリッドに設定(バインド版)
    MyModel.DGridData.ItemsSource = list;
}

先ほどバインドしたのがコントロール自体なので、

MyModel.DGridData.ItemsSource = list;

な形でアクセスをします。
一見、これで十分動くような気もしますが、動きません。
画面上で動かしても、データグリッドの内容が更新されません。
また次のようなUnitTestでもエラーになります。

[TestMethod]
public void TestGridData()
{
    ModelGrid2 model = _page.MyModel;
    Assert.AreEqual(3, ((IList)_page.DGrid.ItemsSource).Count);

    List<Product> list = new List<Product>();
    list.Add(new Product() { ID = "001", Name = "Silverlight 3" });
    list.Add(new Product() { ID = "002", Name = "Visual Studio 2010" });
    list.Add(new Product() { ID = "003", Name = "Expression Bend 3" });
    list.Add(new Product() { ID = "004", Name = "New Application 1" });
    list.Add(new Product() { ID = "005", Name = "New Application 2" });
    list.Add(new Product() { ID = "006", Name = "New Application 3" });
    model.DGridData.ItemsSource = list;

    Assert.AreEqual(6, ((IList)model.DGridData.ItemsSource).Count);
    // ↓はバインドされずにエラーになる。
    // Assert.AreEqual(6, ((IList)_page.DGrid.ItemsSource).Count);
}

最後の Assert.AreEqual のところで、((IList)_page.DGrid.ItemsSource).Count は 3 を返します。
これは既にデータグリッドに設定したデータ数で、バインドが正常に働いていないように見えます。

ですが、これはバインドが働いていないのではなくて、バインドの仕方が間違っているのです。
バインドはコントロール自身となっているので、コントロール自体の変更は通知しますが、コントロールが持つ各種プロパティの変更には応答しません。
通知しないものだから、データグリッドの画面の更新も行われません。ItemsSourceプロパティにバインドしている場合は、正常に画面が更新されます。
これを通知するためには、モデルクラスのItemsSourceプロパティのほうをoverrideして、ObservableCollectionを使って、その通知を受けたものを、バインド先のコントロールに更に通知する、というような処理をする必要があります。これは、無駄です。こんな変なことをしなくてはいけないのは、コントロール自体にバインドをしたためであって、やってはいけないことなのです。まぁ、やってもいいけど、痛い目に会うという余計にややこしいことになります。

というわけで、「DataContext」を使ってコントロールを直接バインドしていはいけません、という話です。

さて、余談ですがここの DataContext 属性は何に使うのでしょうか?

実は先の PageGrid クラスのコンストラクタで DataContext にモデルクラスを設定していました。つまり、DataContext はモデルの元ネタを設定して、それぞれのプロパティ(TextプロパティやItemsSourceプロパティなど)にモデルの各プロパティをバインドするのです。

例では元のページクラスで DataContext を設定しましたが、それぞれのコントロールにデータ/モデルを設定することが可能です。例えば、ページクラスは MyModel クラスを使い、データグリッドは MyGirdModel を使うという使い分けが可能です。
このような場合には、DataGrid の DataContext に直接記述します。

<Grid x:Name="LayoutRoot" Background="White">
<StackPanel>
    <dt:DataGrid Name="DGrid" Width="300" Height="200"
                 DataContext="{Binding DataMyModel, Mode=TwoWay}"
                 ItemsSource="{Binding Items, Mode=TwoWay}" />
    <Button Name="BtnSearch" Width="100" Content="検索" Click="BtnSearch_Click"/>
</StackPanel>
</Grid>

このように、DataContext プロパティと ItemsSource プロパティにバインドを行います。DataContext のほうは一度設定したモデルクラス自身を切り替えないのであれば、Mode を TwoWay にする必要はありません。

このあたり、ASP.NET のデータバインドと同じなので合わせて参考にしてください。

カテゴリー: 開発 | Silverlight + MVVM モデルで DataGrid をバインドの落とし穴 はコメントを受け付けていません

Silverlight + MVVM モデルで DataGrid をバインド

昨日の続きです。

本当は「View の切替/換装ができる」ってのを書きたいのですが、その前にデータバインドのところを押さえないと訳がわからなくなるので、書いておきます。まあ、覚書ということで。

Silverlight にもグリッドで表示するための DataGrid コントロールがあります。Windows で言う DataGridView であったり、ASP.NET だと名前が違ったりと色々ありますが、まぁ似たような動きをします。
この DataGrid には、データベースから検索した DataTable や List のコレクションなどを指定して、自動的にグリッドに表示できます。

忘れちゃいけないのは「System.Windows.Control.Data.dll」を参照設定することですね。DataGridのコントロールは System.Windows.Control namespace にあるのですが、DLL は System.Windows.Control.Data という。
準備として xaml の namespace を付けます。
下記では「dt」という名前空間を設定しています。

<UserControl x:Class="SampleTextBinding.PageGrid"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:dt="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
    Width="400" Height="300">
    <Grid x:Name="LayoutRoot" Background="White">
  
    </Grid>
</UserControl>

そして、データグリッドを表示させるために「dt:DataGrid」を配置します。

<UserControl x:Class="SampleTextBinding.PageGrid"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:dt="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
    Width="400" Height="300">
    <Grid x:Name="LayoutRoot" Background="White">
        <StackPanel>
            <dt:DataGrid Name="DGrid" Width="300" Height="200"/>
        </StackPanel>
    </Grid>
</UserControl>

ここでは名前を「DGrid」にしてあるので、データグリッドのコントロールを使って直接指定する場合は、

public PageGrid()
{
    InitializeComponent();
    // コレクションを作成
    List<Product> list = new List<Product>();
    list.Add( new Product(){ ID="001", Name="Silverlight 3" });
    list.Add( new Product(){ ID="002", Name="Visual Studio 2010" });
    list.Add( new Product(){ ID="003", Name="Expression Bend 3" });
    // グリッドに設定
    this.DGrid.ItemsSource = list;

}
public class Product {
    public string ID { get; set; }
    public string Name { get; set; }
}

を記述します。

ページを表示したときに既に表示されるようにする場合は、Page クラスのコンストラクタに。
通常は、ボタンなどの何らかのアクションを起こした後の検索結果を表示すると思います。
この場合はボタンのイベントに記述します。

private void BtnSearch_Click(object sender, RoutedEventArgs e)
{
    // コレクションを作成
    List<Product> list = new List<Product>();
    list.Add(new Product() { ID = "001", Name = "Silverlight 3" });
    list.Add(new Product() { ID = "002", Name = "Visual Studio 2010" });
    list.Add(new Product() { ID = "003", Name = "Expression Bend 3" });
    list.Add(new Product() { ID = "004", Name = "New Application 1" });
    list.Add(new Product() { ID = "005", Name = "New Application 2" });
    list.Add(new Product() { ID = "006", Name = "New Application 3" });
    // グリッドに設定
    this.DGrid.ItemsSource = list;
}

この検索ボタン(BtnSearch)をクリックすると、グリッドが再描画されます。

データグリッドの中身にバインドする場合は、ItemsSource プロパティにバインドします。
先の「落とし穴」と同様にコントロール自身にバインドしてはいけません。

<Grid x:Name="LayoutRoot" Background="White">
    <StackPanel>
        <dt:DataGrid Name="DGrid" Width="300" Height="200"
         ItemsSource="{Binding Items, Mode=TwoWay}" />
        <Button Name="BtnSearch" Width="100" Content="検索" Click="BtnSearch_Click" />
    </StackPanel>
</Grid>

バインドする時の記法はテキストボックスのときと同じです。違いはバインド先が「ItemsSource」となることです。

モデルクラスもテキストの場合と似たようなものですが、プロパティの型が違います。

/// <summary>
/// モデルクラス
/// </summary>
public class ModelGrid : INotifyPropertyChanged
{
    /// <summary>
    /// グリッドのデータ
    /// </summary>
    private IList _Items;
    public IList Items
    {
        get { return _Items; }
        set
        {
            if (_Items != value)
            {
                _Items = value;
                OnPropertyChanged("Items");
            }
        }
    }
   
    #region INotifyPropertyChanged メンバ
    /// <summary>
    /// プロパティ変更時のイベント
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, e);
    }
    protected virtual void OnPropertyChanged(string name)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(name));
    }
    #endregion
}

プロパティ名を ItemsSource としてもよいのですが、ここでは「Items」という名前を使います。
先の xaml を見るとわかるのですが、バインドするときに名前が違います。

<dt:DataGrid Name="DGrid" Width="300" Height="200"
 ItemsSource="{Binding Items, Mode=TwoWay}" />

つまり、コントロールで使う名前とモデルクラスで使う名前と異なってもいいんです。これは当然で、View には同じコントロールを複数貼り付けることもあるので、バインド先のモデルの名前を別にすることが可能です。
これにより Model と View との分離ができます。

さて、Model と View を結びつけるための DataContextプロパティを指定すればおしまいです。

public partial class PageGrid : UserControl
{
    public ModelGrid MyModel;

    public PageGrid()
    {
        InitializeComponent();
        MyModel = new ModelGrid();
        this.DataContext = MyModel;

これも便宜上、Model のオブジェクトを Page クラス内に作っていますが、本当は別の場所に作ります。

さて、バインドの状態をSilverlightのUnitTestを使って動かします。

[TestMethod]
public void TestBindItems()
{
    ModelGrid model = _page.MyModel;

    // コレクションを作成
    List<Product> list = new List<Product>();
    list.Add(new Product() { ID = "001", Name = "Silverlight 3" });
    list.Add(new Product() { ID = "002", Name = "Visual Studio 2010" });
    list.Add(new Product() { ID = "003", Name = "Expression Bend 3" });
    list.Add(new Product() { ID = "004", Name = "New Application 1" });
    list.Add(new Product() { ID = "005", Name = "New Application 2" });
    list.Add(new Product() { ID = "006", Name = "New Application 3" });
    // グリッドに設定
    Assert.AreEqual(6, list.Count);
    model.Items = list;
    Assert.AreEqual(6, model.Items.Count); 
    Assert.AreEqual(6, ((IList)_page.DGrid.ItemsSource).Count); ★
}

このUnitTestは通ります。
特に★の部分に注目してください。モデルクラスの Items プロパティに設定した時に即時、データグリッドの ItemsSource プロパティに値が入っていることがわかります。イコール、画面にも即時反映されるということです。

実は、これを DataContextに直接コントロールをバインドさせると、即時にはなりません。画面を描画するときにバインドが発生するので、UnitTestは常にエラーになってしまいます。
画面で動かす分には問題がない、ように見えますが、いろいろ問題が出るので ItemsSource に直接バインドするのがよいでしょう。

カテゴリー: 開発 | Silverlight + MVVM モデルで DataGrid をバインド はコメントを受け付けていません

Silverlight MVVM の落とし穴

少し仕事のペースが落ち着いてきたので情報を流しておきます。

現在使っているフレームワークが SLExtensions の DLL を使っているので、なんじゃろ?という形で MVVM モデルってのがあるのことを知りました。以前から MVC モデルを利用しているし、.NET ラボの9月の勉強会(後ほど資料をアップする予定です)でも話しましたが、MVVM であろうと、MVC であろうと、「MV○」ってモデルには違いないので、最新って訳でもありません。
実装上はあれこれあるんですが、まぁ、タイトル通り「落とし穴」があるんです。

SLExtensionsを使ったM-V-VMパターン実装
http://d.hatena.ne.jp/coma2n/20090217/1234881006

を見ていくつかページを探っていきました。この方、業務用のアプリ作成に精通しているらしく MVC モデルの根本的なところを理解していらっしゃいます。VB 6.0 なんかが出てくるところが好感が持てます!
要は、
・ソフトウェア工学なところ(理学部的なところ)
・現場での実装/作業場の問題(工学部的なところ)
の問題があります。文系の方には、理学部と工学部の違いがよくわからないかもしれませんが、

「理論」と「実践」という比較で、理論が「理学部」、実践が「工学部」ってことです。
ソフトウェア工学の場合「工学」を名乗っているところもあるのですが、純粋に数学的なアルゴリズムの問題もあって、この理学/工学が混在しています。

画像認識の論文集を買ったのですが、理論的なところだけでなく、実装上の問題やスピードの問題も扱っているところが工学的ですね。収束スピードとか。

さて、MVVM パターンを実装した SLExtensions の DLL ですが、これが無くても MVVM パターンは実装可能です。というか個人的には SLExtensions を使わない方がコードがすっきりします。なんとなくですが、このあたり「理論としてのパターン」→「パターンをそのまま実装」している「理論」領域に留まっており、実際に作業量など(人的スキルも込み)を考慮した本来の「工学」に至っていない気がします。

# 余談ですが、Microsoft の実装面は、多少(?)ごちゃごちゃな面も含めて私は好きです。
# Java の整理されちゃったコードパターンよりも、C# のごちゃごちゃさはこの手のパターンを実装するときに、「パターン」≠「実装パターン」とすることが可能なのです。このあたり、C++ の template や #define マクロに通じるものがあるんですが。残念ながら C# はプリプロセッサを実装しないときたもんだ。

ええと、最初に言っておきますが、私としては MVVM モデルをこのような実装の仕方にするのは反対です。
というのも、ASP.NET や Silverlight の場合、既にVisual Studioの自動生成機能によって、コントロール自身がバインドされているので、Textプロパティ等に独自にバインディングする意味がありません。ViewとModelを分けたいのであれば、Silverlight に既に実装してあるものを利用するが分りやすいと思います。

ただし、データベースとの自動バインディングを推し進めた場合(いわゆるノンコーディングの場合)には、この実装が有効です。この話は別な機会に。というか、本当は.NETラボの勉強会で話したかったネタです。

と、やっとここから本題です。

■Textプロパティへのバインド

TextBoxコントロールに表示するTextプロパティのバインドを行います。
まず、xaml にバインドするポイントを記述します。

Page.xaml

<Grid x:Name="LayoutRoot" Background="White">
    <StackPanel>
        <TextBox Name="TextName" Width="100" Text="{Binding LastName, Mode=TwoWay}" />
        <Button Name="BtnSave" Width="100" Content="保存" Click="BtnSave_Click" />
        <TextBox Name="TextOut" Width="100" Text="{Binding OutName, Mode=TwoWay}" />
    </StackPanel>
</Grid>

Text=”{Binding LastName, Mode=TwoWay}”

のところがそれですね。「LastName」という名前でバインドされています。

さて、これが何処にバインドされるかというと、モデルクラスのプロパティになります。
バインドが双方向「TwoWay」なので、INotifyPropertyChangedインターフェースを実装します。INotifyPropertyChangedインターフェースは、お約束のコードがあるので、そのまま貼り付けます。
SLExtensions を使う場合は、ここのコードが既に実装済みです。

便宜上 Page.xaml.cs に入れていますが、どこにおいても構いません。

/// <summary>
/// モデルクラス
/// </summary>
public class Model : INotifyPropertyChanged
{
    /// <summary>
    /// 入力値
    /// </summary>
    private string _LastName = "";
    public string LastName
    {
        get { return _LastName; }
        set
        {
            if (_LastName != value)
            {
                _LastName = value;
                OnPropertyChanged("LastName");
            }
        }
    }

    /// <summary>
    /// 保存ボタン押下時の出力値
    /// </summary>
    private string _OutName = "";
    public string OutName
    {
        get { return _OutName; }
        set
        {
            if (_OutName != value)
            {
                _OutName = value;
                OnPropertyChanged("OutName");
            }
        }
    }
    #region INotifyPropertyChanged メンバ
    /// <summary>
    /// プロパティ変更時のイベント
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, e);
    }
    protected virtual void OnPropertyChanged(string name)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(name));
    }
    #endregion
}

これで、ViewとModelの準備ができました。
ただ、このままではダメで、これを結び付ける処理を入れます。
Page.xaml.cs

public partial class Page : UserControl
{
    public Page()
    {
        InitializeComponent();
        this.DataContext = MyModel;
    }
    public Model MyModel = new Model();

データバインドは、InitializeComponent の前に入れるのか、後ろに入れるのか決まってはいませんが、一応、後ろに入れます。何かと InitializeComponent の中で色々やってくれるので。
MyModel フィールドが public になっているのは、UnitTest のためです。本来、Model は View や ViewModel と切り離すのが目的なので、普通ここに Model オブジェクトの実体は置きません。
実行を確認してはいませんが、SLExtensions を使う場合は、InitializeComponent の前に入れる必要があるようです。

さて、これで MVVM の実装が済みました。

動かすところは、保存ボタンを押したときで、


<Button Name="BtnSave" Width="100" Content="保存" Click="BtnSave_Click" />

の BtnSave_Click イベントが発生したときです。
実は、イベント自体もバインドが可能(SLExtensionsでは、独自にバインドします)なのですが、ここでは Page.xaml.cs のほうに記述します。

private void BtnSave_Click(object sender, RoutedEventArgs e)
{
    MyModel.OutName = MyModel.LastName;
}

これは MyModel の LastName の値(文字列)を OutName に代入しているところです。
普通は、

TextOut.Text = TextName.Text;

な感じでテキストボックスのTextプロパティを参照するところですが、ここではModelクラスのプロパティを利用します。
Textプロパティのほうが、分りやすいと言えば分りやすいのですが、ここはMVVMモデルの弊害ですね。Modelクラスを使うと直観的でないのが残念です。ただ、慣れると、

・Viewクラスの挙動を知らなくてよい。
・シリアライズ等のデータに関するメソッドを Model クラスに集約できる
・単体テストがしやすい

という利点があります。特に単体テストは便利です。
普通 Silverlight の画面のテストは、ひとが手でマウスをクリックしながらやると思うのですが、Model クラスに分離すると、通常の NUnit のようなテストツールが使えます。
もっとも、Silverlight 用のテストツールもあるので、それを利用するのもテです。

先のModelクラスを動かすと、画面のTextBoxを触っていないのに、ボタンをクリックするときちんと文字列が表示されます。これが「バインド」の効果で、先に xaml にした Binding と Model のプロパティが結びついているために、このように動きます。

■バインドの落とし穴

さて、ここからが本題です。
このバインドの機能を使った MVVM モデルですが、経験が浅い(?)と Model の作成に閉口します。
たぶん、Visual Studio で画面をいっぺんい作っていると画面とイベントの結びつけを意識しないので(意識しない造りになっているので)、このあたりを分離するのが困難なのだと思います。

そういう場合は、素直に MVVM モデルを諦めて、先の Page.xml.cs に直接コントロール経由で値を操作したほうが無難です。もともと Silverlight や ASP.NET が MVC モデルで出来ているので、あえて厳しい制限された形で MVVM モデルを導入する必要はありません。

が、現場の状況によりけりでして、どうしても Model を作らないと駄目になったときに、思わず「コントロールそのものをバインドしてしまおう」という発想になります。
これが落とし穴です。

では実際に、コントロールをバインドしてみましょう。

Page.xaml

<Grid x:Name="LayoutRoot" Background="White">
    <StackPanel>
        <TextBox Name="TextBoxIn" Width="100" DataContext="{Binding TBoxInName, Mode=TwoWay}" />
        <TextBox Name="TextBoxOut" Width="100" DataContext="{Binding TBoxOutName, Mode=TwoWay}" />
        <Button Name="BtnSave" Width="100" Content="保存" Click="BtnSave_Click" />
    </StackPanel>
</Grid>

上記のように DataContext にバインドしてしまいます。
Page.xaml.cs に記述するときは TextBoxIn コントロールを直接参照できるのですが、Model からは TextBoxIn が見れません。ですので、バインドしてしまうわけです。

/// <summary>
/// モデルクラス
/// </summary>
public class Model : INotifyPropertyChanged
{

    private TextBox _TBoxOutName = new TextBox();
    public TextBox TBoxOutName
    {
        get { return _TBoxOutName; }
        set
        {
            if (_TBoxOutName != value)
            {
                _TBoxOutName = value;
                OnPropertyChanged("TBoxOutName");
            }
        }
    }
    private TextBox _TBoxInName = new TextBox();
    public TextBox TBoxInName
    {
        get { return _TBoxInName; }
        set
        {
            if (_TBoxInName != value)
            {
                _TBoxInName = value;
                OnPropertyChanged("TBoxInName");
            }
        }
    }
   
}

Model クラスでは、TBoxOutName プロパティで受けます。
TextBox クラスになるので、「嬉しい」ことに

TBoxOutName.Text

なって形でTextプロパティにアクセスできます。
なので、保存ボタンをクリックしたときは以下のようになります。

private void BtnSave_Click(object sender, RoutedEventArgs e)
{
 MyModel.TBoxOutName.Text = MyModel.TBoxInName.Text;
}

一見すると、何も変わっていないように見えますが、テキストボックスのコントロールを直接参照しています。コントロールなので、Textプロパティを直接扱える、という利点があるのですが。
利点というか難点というか、MVVM モデル上のルール違反ですよね。これは。デザインに関わるところをごっそり持ってきて Model クラスで操作してしまう、ってのが間違いです。

で、これで動作させると、普通に動きます。先のTextプロパティにバインドしたときと同じように保存ボタンをクリックすると、画面のテキストボックスにバインドされます。
なので、どっちでもいいじゃん、めでたしめでたし(ルール違反は別として)に思えるのですが、実は違います。
このコントロールのバインド。一見、TBoxOutName.TextプロパティがテキストボックスのTextプロパティに直接バインドできているように思えますが「違います」。画面を表示しているときに、テキストボックスがTextプロパティに設定しているだけなのです。
これは、Silverlight の UnitTest をした時に判明します。

    model.TBoxInName.Text = "masuda";
    Assert.AreEqual("masuda", model.TBoxInName.Text);
    _page.TestBtnSaveClick();
    Assert.AreEqual("masuda", model.TBoxOutName.Text);
    // 即時バインドされない
    Assert.AreEqual("masuda", _page.TextBoxOut.Text); ★

実は★のところでエラーになります。
モデルのプロパティ(model.TBoxOutName.Text)は「masuda」が設定されていますが、直接テキストボックスのプロパティ(TextBoxOut.Text)を参照したときには空白が返ります。
しかし、画面で動かすと問題はありません。きちんと、TextBoxOutコントロールに「masuda」が表示されます。
なぜでしょうか?

■バインドは即時行われる

MVVM モデルの実装で使われる OnPropertyChanged メソッドですが、内部でキューなどを利用しているわけではなく(実装上、そういうバインドもありでしょうが)、Silverlight の場合は即時実行(関数コール型)になっています。

なので、シーケンスとしては、

1.Modelに値を設定する
2.ModelプロパティがOnPropertyChagnedを呼び出す。
3.ViewのProeprtyChangedイベントが実行される。
4.Viewのプロパティに値が設定される。

のようにシーケンシャルに動きます。

が、コントロールをバインドした場合には、

・コントロール自身の変更ではイベントが発生するのですが、
・コントロールのプロパティ(Textプロパティなど)ではイベントは発生しません。

当たり前といえば当たり前ですが、バインドしたオブジェクトに対してのみバインドが発生します。オブジェクトが持っているフィールドや子のオブジェクトを変更しても、イベントは発生しません。

ここが「落とし穴」です。

なので、コントロールに対してバインドを行った場合、一見、バインドがうまく動いているように見えますが、実は全然役に立っていません。

シーケンスとしては、

1.ModelのコントロールのTextプロパティに値を設定する。
2.この時点では、バインドが走らず、Viewのコントロールには古い値が残ったまま。
3.画面を再描画するイベントが走る(ボタンイベントなど)。
4.表示時にViewのコントロールがTextプロパティを参照する。
5.ModelのコントロールのTextプロパティの値を取得する。
6.ViewのコントロールのTextプロパティに値が設定される。
7.ViewのコントロールのTextプロパティが画面に表示される。

という順序になっており、UnitTest を動かしたときには、2で値を取得してNG。だが、画面は7の時点で表示されるからOKっていう具合なのかな、と。

まぁ、どっちにせよ、↓な形でコントロールを直接バインドするのはルール違反ですね。副作用も大きいし、避けておきたい落とし穴です。

 

        <TextBox Name="TextBoxIn" Width="100" DataContext="{Binding TBoxInName, Mode=TwoWay}" />
カテゴリー: 開発 | Silverlight MVVM の落とし穴 はコメントを受け付けていません

.NETラボ勉強会 8月

先に宣伝をしておけば良かったのですが「.NETラボ 勉強会」で2時間程話をしてきました。
http://dn-lab.net/tabid/107/Default.aspx

今執筆中の「至高の技VB2008」から、そのままネタを持ってきて、主に

・SQL Server/Oracle/MySQLの準備の話
・ストアドプロシージャの話
・.NETストアドプロシージャや全文検索など諸々

いきなり反省点ですが、さすがに2時間続けて、というのは聴衆もお疲れで。聞くほうも大変。
やっぱり、この手のは1時間区切りが限界ですね。あと、えムナウのセッションを見て改めて思ったのですが、ほどよく話が簡単でないとつたわら伝わらないような気がしました。その場で理解をしないといけないので、私のように詰め込みすぎ(まぁ、今回は実験的な意味も込めて「詰め込んで」みたかったわけですが)なのは、消化不良というか、話がこんがらってしまいます。
そのあたり「本」とは違いますね。読み返せないのが難点。

というわけで、せっかく資料を作ったのでUPしておきます。

http://moonmile.net/wiki/index.php?.NET%E3%83%A9%E3%83%9C%E8%B3%87%E6%96%99

ストアドプロシージャの話はごちゃごちゃしてしまったので、もう少しまとめてブログで書き綴ってみる予定。

カテゴリー: 開発 | 2件のコメント

赤羽喫茶の会

mixiに全国喫茶の会というものがあるのですが、それの「赤羽喫茶の会」に行ってきました。

赤羽駅の東口にある「友路有」という店 http://www.kissaten.jp/ です。

自前でサイフォンの珈琲を入れられる、というか、自前で入れないといけない東京のお好み焼きの感覚の店です(関西ではお好み焼きは焼いてくれる。広島では自分で焼くなぁ)。いえ、普通の珈琲もあります。

大学の頃は喫茶店に入り浸っていたので、そんな雰囲気の店。ジャズ喫茶よりは(当然)静か。ミスタードーナッツの上の2階というのが不思議な感覚ですが、スターバックスやベローチェ、ドトールとは違った「喫茶店」が味わえます。商店街の身内喫茶店よりも入りやすい感覚で行けます。

この喫茶の会ですが、いわゆる異種業種交流ということで普通の人(?)が来ます。IT業界の集まりは常々行くことが多いのですが、普通の話ができます。というか、他の業界の話が普通に聞けます。それぞれ専門があって仕事があって、何かしらこだわりがあってという感じですね。

私としては何を期待して行ったのかと言えば、第一にサイフォン珈琲だったので、一人で珈琲豆をごりごりやってました。4人(最終的には5人)の中でひとりごりごり豆をひいている姿は異様な感じもするのですが、前回の赤羽喫茶の会では4人ごりごりやっていた訳だそうで、かなり異様ですね。この異様さはネタ的には慣れているのでいいので、まあ抵抗はないのですが。

話としては雑談(?)が中心なんですが、なんだろう会社員の集まりとは違うのは、それぞれ独立しているからですかねぇ。異業種交流会ってがつがつした印象が強いのですが、喫茶店で座って茶を飲みながら話して、お酒が入らないので大声にもならず、ソファに座ってだべって、なんか外を見て会話を聞いて、本棚を眺めて会話を聞いて、という雰囲気は結構好きです。

そうそう、フードコーディネータの仕事を垣間見れて楽しかった。「Heaven?」と「おいしい関係」を思い出しました。

カテゴリー: 雑談 | 赤羽喫茶の会 はコメントを受け付けていません

ブログ検索(仮3)

至高×技の原稿書きに苦戦中…SQL ServerとOracleとMySQLとAccessと同時にSQL書くのは辛い…と愚痴をこぼす。

閑話休題

どうやら他に興味を発散/拡散させていないと煮詰まってしまうらしい(本当?)ので、少しブログ検索(仮)を考えてみる。

イーストプレスの「Google誕生」を図書館で借りて読んでいるのですが、会社のスタートアップの資金面で参考になるところあり、ビジネスのモデルとしての参考になるところもあり、そしてGoogle検索の弱点も見えてきたような気もし(本当?)。

Google誕生 —ガレージで生まれたサーチ・モンスター

Googleの「検索」をするときには

1.キーワードを入れる。
2.「検索」ボタンを押す。
3.検索結果が表示される。
3.1 検索結果にプレビューが含まれる。
4.検索結果のリンクからジャンプする。

ってのを繰り返す。
最近は先のSQL関係で調べものをすることが多いので、IEのタブ機能を使ってあらかじめぽちぽちと4,5個ぐらいクリックして開いておく。そしてざっとみて閉じる、を繰り返してたりします。

これを繰り返しているうちに、いくつか気づいたことを書きだすと、

1) 検索先は、IT技術系とIT技術系以外の2つに分けられる。
2) プログラミング系はリファレンスが最初なはずなのだが、サンプルを含めると探すのが大変。
3) サンプルコードは色々なレベルが混じっている。
4) (当たり前だが)検索結果はカテゴリ化されない。
5) 考えてみれば、まとめサイトを検索した後、他のブログを検索するほうがよい。
1)は乱暴な話だけど、仕事用の検索と、それ以外の検索では求めるものが違うということ。あるいは、IT技術系の話がインターネットに溢れかえっている(その他のページとの比率はどのくらいなのだろう)ということ。これは、技術的な発端がIT系であることだし、パソコンを使う頻度が一番高いのがIT系だろうし、ということで仕方がないところもある。あるいは、IT系のもののほうがインターネットに載せるのが楽ということもあり、逆に言えばIT以外の技術はインターネットに載りにくい、という傾向もある。

何かの商品を検索してみるとわかるのだが、大抵はamazonか楽天に引っ張られる。比較サイトだと価格.comとか、Yahoo!の口コミサイトとか。何かを「買う」ときはそれでいいのだけど、あるいは何かを「比較」するときはそれでいいのだけど、

a)その商品がどんなものなのか?
b)いつ頃発売されたものなのか?
c)代替するものはあるのか?
d)どの位の人が買っているのか?
e)使いごこちはどうなのか?

というような、「買う」いわゆるビジネスに直結しないものは検索しにくい。

が、何処で検索すればいいかは大体わかってきている。
インターネット検索の黎明期(インターネット黎明期ではない)の2000年頃、つまりはGoogleやYahoo!の検索が本格的に使われた頃には、ネットワークを縦断して検索したほうが当たりが多いのだが、今はあれこれサイトが多すぎて(広告だけブログとか、アダルト系とか)検索結果にゴミが混じる率が高くなってきている。おそらく今後も高くなると思う。

これをGoogle側の検索技術に頼ってもいいのだが(一方で頼るわけだけど)、

a)のような商品の写真、プレス記事、なんてのはプレス記事のサイトを先に検索したほうが早い。
おそらくITの日常化ができているからだと思うんだけど、日々私が見るサイトは限られている。余り新しいものには当たらない。これはGoogleで検索していても同じで、同じ技術サイトや同じブログに突き当たる。

ということは、人は何かを「検索」するときには何かを「目的」としているわけで、検索自体を楽しんでいる訳ではない(楽しむ場合もあるけど)。それが、自然言語で検索できる アスクジープスだったわけだけど、日本のask.jpは最近閉じられている。日本語の検索が難しかったのか、自然言語で検索するのが難しかったのか、はたまたgoogleと競合してしまったのか(協調しているはずなんだけど)。

幸いにしてask.comは生きているので検索するのだが、自然言語で検索するのは難しい。

というか、一発で検索するのは無理があるのでは?と思った次第。

例えば「Oracleの初心者が使うような本でいいものは無いか?あるいはネットでいいサイトはないだろうか?」という質問に対して。

「Oracle 入門書 評判 良い」
「Oracle サイト 初心者向き」

でGoogleを検索してもよいのだが、これを人に問い合わせるときは

A「Oracleを知りたいんだけど?」
B「Oracleのどんなことを知りたい?」
A「ああ、初心者なんでいまいちよくわからなくて」
B「インストールの方法?それとプログラミング?」
A「プログラミングの方かな?」
B「他のデータベースは触ったことはある?」
A「いや、まったくない。そもそもデータベースがなんだかよくわからない」
B「じゃあ、Oracleの簡単な解説の本と、簡単なプログラムの本がいいかな」
B「プログラムは何を使っている?」
A「一応、Javaを使っているけど」
B「なら、このJavaでアクセスできる本がいいんじゃない?」
A「本じゃなくてサイトもあると売れしいんだけど」
B「JavaでOracleにアクセスする解説をしているサイトがあるから、ここを読むといいよ」

な感じで会話が進んでいるハズ。
当然、Aは問いかけるのだが、一発で喋れるわけではない。そもそも何が分かっていて何が分かっていないかが分からない。無知の無知というやつだ。
これを専門家のBに聞くと、

 ・初心者なのか上級者なのか?
 ・システム屋なのかプログラマなのか?
 ・プログラマならばどんあ言語を使うのか?
 ・本が欲しいのか、ネットでサイトを知りたいのか?

ぐらいは質問を返してくるハズだ。
(いわゆる、Aがメーリングリストで言われていた「教えて君」状態だったり)

だから、Googleの一発検索あるいは検索した結果を絞り込み、という方法ではなくて、なんらかの示唆が途中で必要になる。実は図書館員の質問の受け答えが先の問い掛けになる。

これを「カテゴリ」→「検索」という先にカテゴリがが来ては駄目で(これは後日理由を書こう)、Aが何かを発したときに、Bが何かを示す、という順番でなくてはいけない。

という訳でブログ検索(仮)の話はまだ続く。

カテゴリー: 雑談 | ブログ検索(仮3) はコメントを受け付けていません

Ubuntu 9.04 インストール

しばらく避けて通っていた(謎)Ubuntuをインストールしました。

20090622_01

Ubuntu Japanese Team
http://www.ubuntulinux.jp/

から、デスクトップ版とサーバー版をダウンロードしました。
VMWare版とVirtualBox版ばありますが、今回はCD-ROM版をダウンロードして、VMWare Workstation にインストール。
# HDD容量が不明なので。
# あとインストール画面が見てみたかったので。

■デスクトップ版

デスクトップ版のインストールは特に問題なし。なんとなく入れても大丈夫そうで、Windows XPやVistaよりも早く終わります(10分弱ぐらいかな)。
ネットブック用にアレンジしたバージョン http://www.ubuntu.com/getubuntu/download-netbook もあるので、5万円パソコンに入れても大丈夫そう。

さて、デスクトップ版の場合、PCに直接入れて使う方が多いのでしょうが、私の場合 Vista 上のVMWareに入れて使います。かつ、VistaはノートブックのVaoi Type-T からリモート接続するので、ちょっとマウスの動きが面倒臭い。サーバー版のように telnet だけを使う場合はよいのですが(これがほとんどなんですけどね)、Windows から Linux へリモートデスクトップできれば便利。

なので、これも避けて通っていたのですが、VNCを使います。
# 昔~使おうとして、動作が遅かったのですが、Linux -> Windows の場合は X Windows 経由だからよいかな、と。

VNC の場合、サーバー(この場合はLinux)のほうにもインストールが必要のですが、Ubuntu の場合は既にサーバー(vino)が入っています。
なので、クライアント版をWindows Vista/XPに入れればOK。

VNC ダウンロード
http://remomani.com/download.html
VNCでUbuntuへ
http://nexus7.at.webry.info/200811/article_6.html
本家 RealVNC
http://www.realvnc.com/
# 気になる場合は本家で Free 版をダウンロードすればOK。

20090622_02

Windows -> Windows のリモートデスクトップよりも、多少もたつきまが、十分使えます。無線LANでつないでるのが原因かもしれませんが。
これで、リモートデスクトップ→VMWareの画面、という二重苦のマウス操作から抜け出せます(苦笑)

■サーバー版

さて(私にとって本命の)サーバー版ですが、これも数ステップでインストールできます。
サーバー版というのは、デスクトップが無いバージョン。昔のslackware 2.4.xx http://www.slackware.com/ の頃から使っていた者(間が相当空いていますが)としては、telnet経由の方がなじみが深かったりします。

が、slackware でインストールすると暇が掛かる(今はどうなのかな?)ので、Ubuntu のサーバー版をインストールします。
サーバー版ではおなじみのLAMP(Linux+Apache+MySQL+PHP)の組み合わせを一遍にインストールしてくれます。所謂、Windows Server の「役割」みたいなものです。

が、注意しないといけないのが、サーバー版でLAMPパッケージだけをインストールすると telnet/sshで繋げません。
サーバー版なのにコンソールからしか操作できない、ってのも変な構成のような気もするのですが、

「OpenSSH パッケージ」

も忘れずに入れておきます。

# 後から telnetd を入れることもできるらしいのですが、私の場合何度やってもうまくいかず、
# 結局再インストールして「OpenSSH」を入れ直しました。
# SSH なので、クライアント側でそのまま telnet を使うことができません。
# tera term pro http://www.forest.impress.co.jp/lib/inet/servernt/remote/utf8teraterm.html
# PuTTy http://www.chiark.greenend.org.uk/~sgtatham/putty/
# を使います。私は昔ながらの tera term 派

Ubuntu サーバーの日本語版はデフォルトで文字コードをUTF-8を使います。
なので(?)、通常のコンソールを使うと文字化けします。Tera Term(たぶんPuTTyも)はUTF-8を扱えますので、このまま接続すると日本語が通ります。

20090622_03

いちいちコマンドラインのエラーメッセージが日本語だと嫌な人は(私もそう)、

export LANG=c

で英語に変えてしまいましょう。そうすると、母国語じゃない言語で喋るので聞き流しが可能です(笑)。
さて、言語を日本語(UTF-8)以外にするほうが素直かと思ったのですが、

– MySQLで日本語を扱うときは、UTF-8がデフォルトになっている。
– PHPで日本語を扱うときは、UTF-8がデフォルトになっている。

ので apache 経由で WEB サイトを作るときには、UTF-8のままのほうがいいと思います。

・・・と思ったけど、MySQLのデフォルトキャラクタセットが「latin1」になっていますね。むう。

Variables (–variable-name=value)
and boolean options {FALSE|TRUE}  Value (after reading options)
——————————— —————————–
auto-rehash                       TRUE
character-sets-dir                (No default value)
default-character-set             latin1
comments                          FALSE

にしても、LAMPパッケージを入れただけで、apache がさっくりと動いてくれるのは便利です。

20090622_04

続きは、phpMySQLAdmin とか入れてから。

■おまけ

うぶんちゅ!
http://www.aerialline.com/comics/ubunchu

ネットトップ版PC
http://www.vspec-bto.com/bto/bto-light/1810/system_detail.html

カテゴリー: 開発 | Ubuntu 9.04 インストール はコメントを受け付けていません

第37回いたばし起業塾 森本尚樹氏

2年振りに行って来ました、いたばし起業塾。
http://www.itabashi-kigyou.jp/seminar_juku.html

「ヒット商品のつくりかた」
(変革マーケティングインストラクター 森本尚樹氏)

ということで、タイトル的には胡散臭い(申し訳ない)のですが、

マーケティングは他社の強みを捨てることから始まる―いつも失敗するのは「差別化したつもり」だった (アスカビジネス)

の著者の方です。この本、日経新聞に広告が載っていたのですが、そのとき思ったのが
– なぜ、「他社」の強みを捨てるのか?
– なぜ、他社の強みを「捨てる」のか?
ということでした。

# 今見ると発売日が2006年11月だから2年前に出版済みだったのですね。
# 新聞を見たときは新刊かと思った。
森本さんのブログ
http://www.morimotonaoki.com/blog/2009/06/post-4669.html
日本USP協会
http://www.juspa.jp/

このあたりの言葉遊びは、森本さんが心理学を学んだことと出発点が医療関係だからなのと関係が深そうです。ブログには「しゃべりすぎ」とありますが、手加減なしということだったので、丁度良かったのではないでしょうか?私としては頭を使うことができて楽しい時間でした。

マーケティング、新商品開発というと、市場リサーチや技術力、特許取得、ニッチな市場、ということで技法に走りがち(?)ですが、実のところはその現場の値を使ったシミュレーションが大切になります(というのを最近気が付きました)。シミュレーションというのは、

1.現場の値を正しく把握する。
2.その値に変更(商品やアイデア)を加える。
3.未来の予測値(売上とか皮算用とか)を計算する。

の手順のことです。起業塾で話されていた差別化のマトリクスの話に絡めると、

1.他社(ライバル)の動向/長所短所/強み弱みを把握する。
2.自分の位置はどこか?
3.お客は付いてくるか?お客のことを考えているか?買わないという選択肢に陥らないか?

ってところです。このあたりを、紙を使って何度も考えます。私の場合、こうやってブログに載せたり/載せなかったり、紙に書いたり、ホワイトボードに書いたりします。しばらく寝かせておいたり、マルチで動かしたり、忘れてしまったりします。
# 私も一点集中型ではないもので…

で、普通(?)の商品の場合、「製造→売る」あるいは「仕入る→売る」という仮定を重視しますが、なぜかIT業界の場合(あるいはプログラマの場合)「作る→お終い」の場合が多いです。これ、起業の場合もそうなのですが、最初のアイデアを考え抜くことと実現する(販売する)こととを同時に廻していかないと立ち行きません。まぁ、IT業界の場合、古くはフリーなところ(Linuxを始めとして)がスタート地点なので、ここを根差すことが多いのですが、製造業としてのITを考えたとき、ここの「顧客不在」「ユーザー不在」が大きな落とし穴になります。逆に言えば、ちょっと前のIT業界の胡散臭さはこのあたりに根差すものもあるのですが。

と、思ったとき駒林さん曰く「IT業界も他のノウハウうが適用できるジャン!」(ジャン、とは言いませんでしたが)なわけで、他の業界から流用できるところは多いのです。というか、流用しないと駄目です。

という訳でブログに書いたり/書かなかったりすることを再び検討するつもりです。
まぁ、売れる/売れないだけでプログラマをやっているわけではないのですが、年収100万円ちょいでアニメーターをやるのも大変だし、志と健康とちょっとした余裕が欲しい、というところでしょうか。

カテゴリー: 起業塾 | 4件のコメント

インストールマニアックス2009決勝

というわけで、インストールマニアックスの決勝戦を見学に行ってきました。
http://www.thinkit.co.jp/maniax/2009/finalgame.html

13時15分頃に会場に行くと「人が少なッ!」(座っている人が20名弱)の状態でしたが、決勝戦の3時間中ぼちぼち人が増え始め最後には50名ぐらいにはなっていました。関係者としては、インプレスの人とMSの人が15名ぐらいでしたから、入賞者を除くと観客は20名ぐらい?

決選は「Windows Server 2008 R2」からインストールするという、、、最初からインストールしておいたほうがいい気もしますが、OSインストールで約20分間はまったりとした時間が流れておりました。

3時間(OSインストール以外だと2時間半)の間、OSSのインストールを続けるわけですが、優勝者の三浦さんが26個(だったっけ?)、準優勝の工藤さんが25個という具合で僅差1本。約5分に1本のペースでインストールするわけですから、見ている方としてはまさに彼らの「仕事振り」を見ている感じでした。あれだけ集中力がよく続くなぁと感心します。小向さんの追い上げもすごったし、細田さんのマイペースぶりは日常的な感じがしました。草刈さんのあきらめ具合(MTのインストールへ挑戦とか)も。

25,6本インストールに成功するわけですが、失敗したものも3分の1ぐらいあります。なので、ペーストしては、2時間半で40本弱をインストールの試行錯誤(インターネットからのダウンロードも含めて)します。

優勝者の三浦さん(左)と前回優勝者の田中さん(右)

09-06-14_18-331

で、私的に感想をつらつらと。

プログラム系の見知った顔が全くいませんでした。SE系だから畑違いなのか、インプレス&マイクロソフトという組み合わせが「マニアック」過ぎたのか。当選確率としては500台に対して750名近くの応募があったので、それなりにあった模様。定員割れではなかったようです。

パンフレットには、主催が株式会社インプレスビジネスメディア、協力がマイクロソフト株式会社となっており、会場も新宿のマイクロソフトの会議室が提供されました(ここの会議室はよく勉強会などで利用されます)。というわけで、NECの名前はありません。NECの営業の方(と思われる)が1名いらっしゃっただけで、関係者はいないので、主にNECからサーバー機をマイクロソフトが買い込み、梱包&発送していたようです。なので(想像するに)、「OSが入っていない」、「サーバ機が届かない」のトラブルはマイクロソフト側に問題がありそうですね(インプレスの企画部門かもしれませんが)。

見たところ、インプレス以外のプレス関係の人は(たぶん)一人もいませんでした。他社に宣伝するにせよ/しないにせよ、マイクロソフトとしてはあまりいい手ではなかったのかなぁ、と。デブサミや他の勉強会に比べて、決勝戦の会場が同じくらいの雰囲気というのは、あまりにもかなぁと。実を取る気だったのか、宣伝を取る気だったのかは不明ですが、

なので、商品がしょぼい件は、先のNECの協賛が無いことから、サーバー代はマイクロソフトが用意したと考えられます。前回の2008がDELL 100台でしたが、今回 NEC 500台の価格差が諸に響いているみたいです。ただし、マイクロソフトは NEC のサーバー機と萌えサーバーでコラボレーションしているので、内部購入はしているんでしょうが。
そのあたり、景品に掛けるお金が減ってしまったのかなぁと。

Windows Server 2008 R2は、実は64bitバージョンしか出ません。Windows Server 2008 Core で ASP.NET アプリケーションを動かそうとすると、64bit 版が必須になります。逆に言えば、32bit のサーバー機を打ち止めにしたい。32/64の両方を確認するのはコストがかかる、という省力化でしょう。ただし、Windows 7 は 32bit もサポートするし、Windows Server 2008 R2 のコアは Windows 7 と共有しているそうなので、まぁ、ハードウェアメーカも含めた「戦略」のにおいがします。

アンケート的には Think IT http://thinkit.jp/ が中心だったので、ここのコミュニティメンバが中心だったのでしょう。
そういえば、チャンピオンベルトは継承するもので、第2回のチャンピオン用のベルトを新しく作るのはおかしいぞ!と突っ込みを入れる予定だったのですが、

WBCフライ級チャンピオンベルトの横の方に付いているバッチは、誰の写真ですか?
http://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q1313092323?fr=rcmd_chie_detail

なるほど、ボクシングの場合は新しく作るんですね~。確かに入場曲はボクシング風だった気が(ということに)。

追記 2009/06/22

工藤さんのブログ技術的な何か。
http://d.hatena.ne.jp/jkudo/20090614/1244988853
# 「何か。」は、「何か。」なのか?(謎)

カテゴリー: インストールマニアックス2009 | インストールマニアックス2009決勝 はコメントを受け付けていません