セルの編集可能なリストビューコントロール

Last Updated 2011/10/13


ListView コントロールはデータを表示する便利なコントロールですが、データの編集にも使えます。しかし、標準のコントロールにはデータの編集機能がありません。このページではセルに表示したテキストデータを編集する機能を追加する手順を説明します。


リストビューコントロール内でデータを編集したいケースは多いものです。.NET Framework SDK の解説の中に、"How to: Create a ListView with Editable Cells" というタイトルの項目があるので期待させますが、コードの全体は Microsoft のサイトからダウンロードしなければなりません。

ファイルをダウンロードするぐらいの手間はたいしたことはありませんが、その内容は問題ありです。一応、目的は達成できるのですが、どうもシックリきません。さらに WEB サイトをウロついてみると、いくつかのサンプルコードは得られましたが、いずれもこんなものかなあとの思いが消えません。私が納得できないのはもっと簡単にできるのではないかと考えるからです。

Note .NET Framework の標準の機能として実装していてくれればいいのですが、Microsoft がそうしなかったということは簡単に実装可能と考えたのではないかと想像します。そうでもないかなあ!

そこで、作ってみました。リスト項目を選択すると指定のカラムのデータが編集可能になります。編集したくない項目も編集可能になる欠点はありますが、すごく簡単なコードだけで実現できるメリットはあると思います。なお、ここで紹介した手順はテキストボックス以外のコントロール、たとえば、コンボボックスを配置する場合にも使えることを指摘しておきます。

EditableListView

以下のコードのポイントは、MainWindow.xaml の CellTemplate に TextBlock と TextBox とを配置し、リスト項目の選択状態に応じて TextBlock と TextBox との Visibility プロパティを切り替えるだけです。

(MainWindow.xaml)

<Window x:Class="EditableListViewTest.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:ctrl="clr-namespace:emanual.WPf.Controls"
    Title="編集可能な ListView コントロール" Height="300" Width="500">

  <Window.Resources>
    <ctrl:BooleanVisiblityConverter x:Key="booleanConverter"/>

    <Style x:Key="unselectedListViewStyle" TargetType="{x:Type TextBlock}">
      <Setter Property="Visibility"
              Value="{Binding Path=IsSelected, RelativeSource={RelativeSource FindAncestor,
                AncestorType={x:Type ListViewItem}}, Converter={StaticResource booleanConverter},
                  ConverterParameter=False}" />
    </Style>
    <Style x:Key="selectedListViewStyle" TargetType="{x:Type FrameworkElement}">
      <Setter Property="Visibility"
              Value="{Binding Path=IsSelected, RelativeSource={RelativeSource FindAncestor,
                AncestorType={x:Type ListViewItem}}, Converter={StaticResource booleanConverter},
                  ConverterParameter=True}" />
    </Style>
  </Window.Resources>

  <DockPanel>
    <TextBlock Name="txetBlock" DockPanel.Dock="Bottom" Height="40" Margin="20,0,20,10" TextWrapping="Wrap"
               Text="項目を選択すると「市区町村」が編集可能になります。編集後のデータは元のコレクションに反映されます。" />
    <ListView Name="listView" Margin="10">
    <ListView.View>
      <GridView>
        <GridViewColumn  DisplayMemberBinding="{Binding Path=Prefecture}" Header="都道府県" Width="100" />
        <GridViewColumn Header="市区町村" Width="100">
          <GridViewColumn.CellTemplate>
            <DataTemplate>
              <Grid>
                <TextBlock Text="{Binding Path=City}" Style="{StaticResource unselectedListViewStyle}"/>
                <TextBox Text="{Binding Path=City}" Style="{StaticResource selectedListViewStyle}" />
              </Grid>
            </DataTemplate>
          </GridViewColumn.CellTemplate>
        </GridViewColumn>
      </GridView>
    </ListView.View>
  </ListView>
</DockPanel>
</Window>

(BooleanVisibilityConverter.cs)

/* 
 *  リスト項目の IsSelected プロパティの設定値に応じて
 *  TextBlock または TextBox の Visibility プロパティを true/false に切り替えるための変換クラス
 */
using System;
using System.Collections.Generic;
using System.Windows.Data;
using System.Windows;

namespace emanual.WPf.Controls
{
  public class BooleanVisiblityConverter : IValueConverter
  {
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
      bool param = this.GetConverterParameter(parameter);
      bool selected = (bool)value;

      return param == selected ? Visibility.Visible : Visibility.Collapsed;
    }

    //---------------------------------------------------------------------------------------------
    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
      throw new NotSupportedException("Not Implemented");
    }

    //---------------------------------------------------------------------------------------------
    private bool GetConverterParameter(object parameter)
    {
      bool result = false;
    
      try
      {
        if (parameter != null)
          result = System.Convert.ToBoolean(parameter);
      }
      catch (Exception e)
      {
        MessageBox.Show(e.Message);
      }

      return result;
    }
  }
}

(MainWindow.xaml.cs)

using System.Windows;
using System.Windows.Controls;
using System.Collections.ObjectModel; // ObservableCollection
using System.Diagnostics;

namespace EditableListViewTest
{
  public partial class MainWindow : Window
  {
    private ObservableCollection<Town> FTowns;

    //-----------------------------------------------------------------------------------------------
    public MainWindow()
    {
      InitializeComponent();

      FTowns = new ObservableCollection<Town>();

      var town = new Town("北海道", "札幌市");
      FTowns.Add(town);

      town = new Town("宮城県", "仙台市");
      FTowns.Add(town);

      town = new Town("福島県", "福島市");
      FTowns.Add(town);

      town = new Town("岩手県", "盛岡市");
      FTowns.Add(town);

      listView.ItemsSource = FTowns;
    }
  } // end of MainWindow class

  //***********************************************************************************************
  public class Town
  {
    public string Prefecture { get; set; }
    public string City { get; set; }

    public Town(string prefecture, string city)
    {
      this.Prefecture = prefecture;
      this.City = city;
    }

    public override string ToString()
    {
      return this.Prefecture + "-" + this.City;
    }
  } // end of Town class
} // end of namespace

−以上−