locked
動的列を持つDataGridにおける編集内容チェック方法 RRS feed

  • 質問

  • お世話になります。
    VS2010ProのC#でSilverlight4のアプリケーションを開発している者です。

    列を動的に生成したDataGridにおいて、DataGridの編集内容をC#のコードで確認したいのですが、うまくいかず困っています。
    ご存じの方、あるいはこのような方法で実現できるのではないか?等のアイデアをお持ちの方でも構いませんので、ご教示頂けないでしょうか。

    【現在のソースコード】

    以下がコードになります。
    <MainPage.xamlの内容>

    <UserControl x:Class="DynamicDataGridCS.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      mc:Ignorable="d"
      d:DesignHeight="335" d:DesignWidth="589" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">
      <Grid x:Name="LayoutRoot" Background="White" Height="349" Width="569">
        <sdk:DataGrid AutoGenerateColumns="False" Height="224" HorizontalAlignment="Left" Margin="29,41,0,0" Name="dataGrid1" VerticalAlignment="Top" Width="465" SelectionMode="Single">
          <sdk:DataGrid.Columns>
            <sdk:DataGridTemplateColumn CanUserReorder="True" CanUserResize="True" CanUserSort="False" Width="Auto" DisplayIndex="0">
              <sdk:DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                  <Button Name="staticGridButton" Content="静的ボタン" Click="DataGridBtnClick"/>
                </DataTemplate>
              </sdk:DataGridTemplateColumn.CellTemplate>
            </sdk:DataGridTemplateColumn>
          </sdk:DataGrid.Columns>
        </sdk:DataGrid>
        <Button Content="データ生成" Height="23" HorizontalAlignment="Left" Margin="27,12,0,0" Name="BtnGenerateData" VerticalAlignment="Top" Width="115" Click="BtnGenerateData_Click" />
        <Button Content="行選択チェック..." Height="23" HorizontalAlignment="Left" Margin="148,12,0,0" Name="BtnCheckRowSelectData" VerticalAlignment="Top" Width="113" IsEnabled="False" Click="BtnCheckRowSelectData_Click" />
        <Button Content="全選択" Height="23" HorizontalAlignment="Left" Margin="267,12,0,0" Name="BtnSelectAll" VerticalAlignment="Top" Width="110" IsEnabled="False" Click="BtnSelectAll_Click" />
        <Button Content="全選択解除" Height="23" HorizontalAlignment="Left" Margin="383,12,0,0" Name="BtnDisSelectAll" VerticalAlignment="Top" Width="111" IsEnabled="False" Click="BtnDisSelectAll_Click" />
      </Grid>
    </UserControl>

    <MainPage.xaml.csの内容>

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Shapes;
    using System.Collections;
    using System.IO;
    using System.Text;
    using System.Windows.Browser;
    using System.Collections.ObjectModel;
    using System.Windows.Data;
    using System.Windows.Markup;
    using com.bodurov;
    namespace DynamicDataGridCS
    {
      public partial class MainPage : UserControl
      {
        public MainPage()
        {
          InitializeComponent();
        }
        private const int CST_MAX_DATA = 100;
        private void BtnGenerateData_Click(object sender, RoutedEventArgs e)
        {
          try
          {
            //データ生成ボタンの無効化
            BtnGenerateData.IsEnabled=false;
            //GRID定義
            //動的にカラム定義(1列目・小数点なし数値データ)
            var HeadCol1 = new DataGridTextColumn();
            HeadCol1.DisplayIndex = 1;
            HeadCol1.Header = "1列目";
            HeadCol1.Binding = new Binding("COL1");
            var CellTemp = new StringBuilder();
            CellTemp.Append("<Style TargetType=\"TextBlock\" ");
            CellTemp.Append("xmlns=\"http://schemas.microsoft.com/client/2007\" ");
            CellTemp.Append("xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">");
            CellTemp.Append("<Setter Property=\"HorizontalAlignment\" Value=\"Right\"/>");
            CellTemp.Append("</Style>");
            HeadCol1.ElementStyle = (System.Windows.Style)XamlReader.Load(CellTemp.ToString());
            dataGrid1.Columns.Add(HeadCol1);
            //動的にカラム定義(2列目・文字列)
            var HeadCol2 = new DataGridTextColumn();
            HeadCol2.DisplayIndex = 2;
            HeadCol2.Header = "2列目";
            HeadCol2.Binding = new Binding("COL2");
            dataGrid1.Columns.Add(HeadCol2);
            //動的にカラム定義(3列目・小数点あり数値データ)
            var HeadCol3 = new DataGridTextColumn();
            HeadCol3.DisplayIndex = 3;
            HeadCol3.Header = "3列目";
            HeadCol3.Binding = new Binding("COL3");
            CellTemp.Clear();
            CellTemp.Append("<Style TargetType=\"TextBlock\" ");
            CellTemp.Append("xmlns=\"http://schemas.microsoft.com/client/2007\" ");
            CellTemp.Append("xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">");
            CellTemp.Append("<Setter Property=\"HorizontalAlignment\" Value=\"Right\"/>");
            CellTemp.Append("</Style>");
            HeadCol3.ElementStyle = (System.Windows.Style)XamlReader.Load(CellTemp.ToString());
            dataGrid1.Columns.Add(HeadCol3);
            //動的にカラム定義(4列目・書式変換した数値データ)
            var HeadCol4 = new DataGridTextColumn();
            HeadCol4.DisplayIndex = 4;
            HeadCol4.Header = "4列目";
            HeadCol4.Binding = new Binding("COL4");
            CellTemp.Clear();
            CellTemp.Append("<Style TargetType=\"TextBlock\" ");
            CellTemp.Append("xmlns=\"http://schemas.microsoft.com/client/2007\" ");
            CellTemp.Append("xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">");
            CellTemp.Append("<Setter Property=\"HorizontalAlignment\" Value=\"Right\"/>");
            CellTemp.Append("</Style>");
            HeadCol4.ElementStyle = (System.Windows.Style)XamlReader.Load(CellTemp.ToString());
            dataGrid1.Columns.Add(HeadCol4);
            //動的にカラム定義(5列目・チェックボックス)
            var HeadCol5 = new DataGridCheckBoxColumn();
            HeadCol5.DisplayIndex = 5;
            HeadCol5.Header = "5列目";
            HeadCol5.Binding = new Binding("COL5");
            dataGrid1.Columns.Add(HeadCol5);
            //動的にカラム定義(6列目・ボタン)
            var HeadCol6 = new DataGridTemplateColumn();
            HeadCol6.DisplayIndex = 6;
            HeadCol6.Header = "6列目";
            CellTemp.Clear();
            CellTemp.Append("<DataTemplate ");
            CellTemp.Append("xmlns=\"http://schemas.microsoft.com/client/2007\" ");
            CellTemp.Append("xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">");
            //動的XAMLではClick属性を指定すると実行時エラーとなる
            //CellTemp.Append("<Button x:Name=\"gridButtonName\" Content=\"確認...\" Click=\"DataGridBtnClick\"/>");
            CellTemp.Append("<Button x:Name=\"dynamicGridButtonName\" Content=\"動的生成ボタン\"/>");
            CellTemp.Append("</DataTemplate>");
            HeadCol6.CellEditingTemplate = (DataTemplate)XamlReader.Load(CellTemp.ToString());
            dataGrid1.Columns.Add(HeadCol6);
            //テストデータ生成
            dataGrid1.ItemsSource = GenerateData().ToDataSource();
            //ボタン制御
            BtnCheckRowSelectData.IsEnabled = true;
            BtnSelectAll.IsEnabled = true;
            BtnDisSelectAll.IsEnabled = true;
            //画面初期状態は未選択
            dataGrid1.SelectedIndex = -1;
          }
          catch (Exception)
          {
          }
        }
        public IEnumerable<IDictionary> GenerateData()
        {
          decimal d_col3;
          String s_col4;
          int i_col4_len;
          for (var i = 0; i < CST_MAX_DATA; i++)
          {
            var dict = new Dictionary<string, object>();
            //COL0は非表示のキー情報
            dict["COL0"] = i;
            //1列目データ(小数点なし数値データ)
            dict["COL1"] = (i + 1);
            //2列目データ(文字列データ)
            dict["COL2"] = "2列目データ" + (i + 1);
            //3列目データ(小数点あり数値データ)
            d_col3 = new decimal((i + 1) * 10000);
            switch (i % 3)
            {
              case 0:
                d_col3 = d_col3 + decimal.Parse("0.1");
                break;
              case 1:
                d_col3 = d_col3 + decimal.Parse("0.25");
                break;
            }
            dict["COL3"] = d_col3;
            //4列目データ(3列目の小数点あり数値データを「3桁区切り・小数点以下を2桁固定」した文字列データ)
            s_col4 = d_col3.ToString("N2");
            //4列目でソートしても「論理的な数値データ」としてソートされるよう先頭桁をスペースで埋める
            i_col4_len = s_col4.Length;
            while (i_col4_len < 14)
            {
              s_col4 = " " + s_col4;
              i_col4_len++;
            }
            dict["COL4"] = s_col4;
            //5列目データ(チェックボックス)
            dict["COL5"] = false;
            yield return dict;
          }
        }
        private void DataGridBtnClick(Object sender, EventArgs e)
        {
          string s = "";
          System.Windows.Controls.TextBlock t;
          t = (System.Windows.Controls.TextBlock)(dataGrid1.Columns[1].GetCellContent(dataGrid1.SelectedItem));
          s = s + " 1列目=" + t.Text;
          t = (System.Windows.Controls.TextBlock)(dataGrid1.Columns[2].GetCellContent(dataGrid1.SelectedItem));
          s = s + " 2列目=" + t.Text;
          t = (System.Windows.Controls.TextBlock)(dataGrid1.Columns[3].GetCellContent(dataGrid1.SelectedItem));
          s = s + " 3列目=" + t.Text;
          t = (System.Windows.Controls.TextBlock)(dataGrid1.Columns[4].GetCellContent(dataGrid1.SelectedItem));
          s = s + " 4列目=" + t.Text;
          System.Windows.Controls.CheckBox c;
          c = (System.Windows.Controls.CheckBox)(dataGrid1.Columns[5].GetCellContent(dataGrid1.SelectedItem));
          if( c.IsChecked == true ){
            s = s + " 5列目=選択";
          }
          else{
            s = s + " 5列目=未選択";
          }
          s = s + " ### 5列目を反転させます";
          MessageBox.Show(s, "この情報はDataGridを参照して出力", MessageBoxButton.OK);
          if (c.IsChecked == true)
          {
            c.IsChecked = false;
          }
          else
          {
            c.IsChecked = true;
          }
        }
        private void BtnCheckRowSelectData_Click(object sender, RoutedEventArgs e)
        {
          //dataGrid1.ItemsSourceから5列目がチェックされたデータを検索
          foreach (Object rowdata in dataGrid1.ItemsSource)
          {
            Type row_type = rowdata.GetType();
            bool b = false;
            //このようなキャストを行いたい
            //b = ((row_type)(rowdata)).COL5;
            if (b == true)
            {
              //選択された行データに対する処理
            }
          }
          //DataGridを直接参照すると非表示の列はチェックできない
          string s = "";
          int org_index = dataGrid1.SelectedIndex;
          System.Windows.Controls.TextBlock t;
          for (int i = 0; i < CST_MAX_DATA; i++)
          {
            dataGrid1.SelectedIndex = i;
            System.Windows.Controls.CheckBox c;
            c = (System.Windows.Controls.CheckBox)(dataGrid1.Columns[5].GetCellContent(dataGrid1.SelectedItem));
            if (c == null) break;
            if (c.IsChecked == true)
            {
              if (s.Length == 0)
              {
                s = "5列目で選択されたNo.(1列目)";
              }
              else
              {
                s = s + " と ";
              }
              t = (System.Windows.Controls.TextBlock)(dataGrid1.Columns[1].GetCellContent(dataGrid1.SelectedItem));
              s = s + t.Text;
            }
          }
          dataGrid1.SelectedIndex = org_index;
          if (s.Length == 0)
          {
            s = "5列目は未選択です";
          }
          MessageBox.Show(s);
        }
        private void BtnSelectAll_Click(object sender, RoutedEventArgs e)
        {
          MessageBox.Show("開発中です");
        }
        private void BtnDisSelectAll_Click(object sender, RoutedEventArgs e)
        {
          MessageBox.Show("開発中です");
        }
      }
    }

    <DataSourceCreator.csの内容>

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Reflection;
    using System.Reflection.Emit;
    using System.Text;
    using System.Text.RegularExpressions;
    
    /*
    Blog entry:
    http://blog.bodurov.com/How-to-Bind-Silverlight-DataGrid-From-IEnumerable-of-IDictionary
    */
    namespace com.bodurov
    {
      public static class DataSourceCreator
      {
        private static readonly Regex PropertNameRegex =
            new Regex(@"^[A-Za-z]+[A-Za-z0-9_]*$", RegexOptions.Singleline);
        private static readonly Dictionary<string, Type> _typeBySigniture =
            new Dictionary<string, Type>();
    
        public static IEnumerable ToDataSource(this IEnumerable<IDictionary> list)
        {
          IDictionary firstDict = null;
          var hasData = false;
          foreach (var currentDict in list)
          {
            hasData = true;
            firstDict = currentDict;
            break;
          }
          if (!hasData)
          {
            return new object[] { };
          }
          if (firstDict == null)
          {
            throw new ArgumentException("IDictionary entry cannot be null");
          }
          var typeSigniture = GetTypeSigniture(firstDict);
          var objectType = GetTypeByTypeSigniture(typeSigniture);
          if (objectType == null)
          {
            var tb = GetTypeBuilder(typeSigniture);
            var constructor = tb.DefineDefaultConstructor(MethodAttributes.Public |
                        MethodAttributes.HideBySig |
                        MethodAttributes.SpecialName |
                        MethodAttributes.RTSpecialName);
            var onExecMethod = AddPropertyChangedEvent(tb);
            foreach (DictionaryEntry pair in firstDict)
            {
              if (PropertNameRegex.IsMatch(Convert.ToString(pair.Key), 0))
              {
                CreateProperty(tb, Convert.ToString(pair.Key), GetValueType(pair.Value), onExecMethod);
              }
              else
              {
                throw new ArgumentException(
                      @"Each key of IDictionary must be 
                    alphanumeric and start with character.");
              }
            }
            objectType = tb.CreateType();
            _typeBySigniture.Add(typeSigniture, objectType);
          }
          return GenerateEnumerable(objectType, list, firstDict);
        }
        private static MethodBuilder AddPropertyChangedEvent(TypeBuilder tb)
        {
          var eventHandlerType = typeof(PropertyChangedEventHandler);
          var eventArgsConstrInfo = typeof(PropertyChangedEventArgs).GetConstructor(new[] { typeof(string) });
          var invokeDelegate = typeof(PropertyChangedEventHandler).GetMethod("Invoke");
          const string eventName = "PropertyChanged";
          const MethodAttributes eventMethodAttr = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.Final | MethodAttributes.SpecialName;
          const MethodImplAttributes eventMethodImpAtr = MethodImplAttributes.Managed | MethodImplAttributes.Synchronized;
          var fieldBuilder = tb.DefineField(eventName, eventHandlerType, FieldAttributes.Private);
          var eventBuilder = tb.DefineEvent(eventName, EventAttributes.None, eventHandlerType);
          var addMethodBuilder = tb.DefineMethod("add_" + eventName, eventMethodAttr, null, new[] { eventHandlerType });
          addMethodBuilder.SetImplementationFlags(eventMethodImpAtr);
          addMethodBuilder.DefineParameter(1, ParameterAttributes.None, "value");
          var combineInfo = typeof(Delegate).GetMethod("Combine", new[] { typeof(Delegate), typeof(Delegate) });
          var addIL = addMethodBuilder.GetILGenerator();
          addIL.Emit(OpCodes.Ldarg_0);
          addIL.Emit(OpCodes.Ldarg_0);
          addIL.Emit(OpCodes.Ldfld, fieldBuilder);
          addIL.Emit(OpCodes.Ldarg_1);
          addIL.Emit(OpCodes.Call, combineInfo);
          addIL.Emit(OpCodes.Castclass, eventHandlerType);
          addIL.Emit(OpCodes.Stfld, fieldBuilder);
          addIL.Emit(OpCodes.Ret);
          var removeMethodBuilder = tb.DefineMethod("remove_" + eventName,
            eventMethodAttr, null, new[] { eventHandlerType });
          removeMethodBuilder.SetImplementationFlags(eventMethodImpAtr);
          removeMethodBuilder.DefineParameter(1, ParameterAttributes.None, "value");
          var removeInfo = typeof(Delegate).GetMethod("Remove", new[] { typeof(Delegate), typeof(Delegate) });
          var remIL = removeMethodBuilder.GetILGenerator();
          remIL.Emit(OpCodes.Ldarg_0);
          remIL.Emit(OpCodes.Ldarg_0);
          remIL.Emit(OpCodes.Ldfld, fieldBuilder);
          remIL.Emit(OpCodes.Ldarg_1);
          remIL.Emit(OpCodes.Call, removeInfo);
          remIL.Emit(OpCodes.Castclass, eventHandlerType);
          remIL.Emit(OpCodes.Stfld, fieldBuilder);
          remIL.Emit(OpCodes.Ret);
          eventBuilder.SetAddOnMethod(addMethodBuilder);
          eventBuilder.SetRemoveOnMethod(removeMethodBuilder);
          var onExecute = tb.DefineMethod("On" + eventName,
                        MethodAttributes.Private | MethodAttributes.HideBySig,
                        null, new[] { typeof(string) });
          onExecute.DefineParameter(1, ParameterAttributes.None, "propName");
          var exeIL = onExecute.GetILGenerator();
          var lblDelegateOk = exeIL.DefineLabel();
          exeIL.DeclareLocal(typeof(PropertyChangedEventHandler));
          exeIL.Emit(OpCodes.Nop);
          exeIL.Emit(OpCodes.Ldarg_0);
          exeIL.Emit(OpCodes.Ldfld, fieldBuilder);
          exeIL.Emit(OpCodes.Stloc_0);
          exeIL.Emit(OpCodes.Ldloc_0);
          exeIL.Emit(OpCodes.Ldnull);
          exeIL.Emit(OpCodes.Ceq);
          exeIL.Emit(OpCodes.Brtrue, lblDelegateOk);
          exeIL.Emit(OpCodes.Ldloc_0);
          exeIL.Emit(OpCodes.Ldarg_0);
          exeIL.Emit(OpCodes.Ldarg_1);
          exeIL.Emit(OpCodes.Newobj, eventArgsConstrInfo);
          exeIL.Emit(OpCodes.Callvirt, invokeDelegate);
          exeIL.MarkLabel(lblDelegateOk);
          exeIL.Emit(OpCodes.Ret);
          return onExecute;
        }
        private static Type GetTypeByTypeSigniture(string typeSigniture)
        {
          Type type;
          return _typeBySigniture.TryGetValue(typeSigniture, out type) ? type : null;
        }
        private static Type GetValueType(object value)
        {
          return value == null ? typeof(object) : value.GetType();
        }
        private static string GetTypeSigniture(IDictionary firstDict)
        {
          var sb = new StringBuilder();
          foreach (DictionaryEntry pair in firstDict)
          {
            sb.AppendFormat("_{0}_{1}", pair.Key, GetValueType(pair.Value));
          }
          return sb.ToString().GetHashCode().ToString().Replace("-", "Minus");
        }
        private static IEnumerable GenerateEnumerable(
             Type objectType, IEnumerable<IDictionary> list, IDictionary firstDict)
        {
          var listType = typeof(List<>).MakeGenericType(new[] { objectType });
          var listOfCustom = Activator.CreateInstance(listType);
          foreach (var currentDict in list)
          {
            if (currentDict == null)
            {
              throw new ArgumentException("IDictionary entry cannot be null");
            }
            var row = Activator.CreateInstance(objectType);
            foreach (DictionaryEntry pair in firstDict)
            {
              if (currentDict.Contains(pair.Key))
              {
                var property = objectType.GetProperty(Convert.ToString(pair.Key));
                property.SetValue(
                  row,
                  Convert.ChangeType(
                      currentDict[pair.Key],
                      property.PropertyType,
                      null),
                  null);
              }
            }
            listType.GetMethod("Add").Invoke(listOfCustom, new[] { row });
          }
          return listOfCustom as IEnumerable;
        }
        private static TypeBuilder GetTypeBuilder(string typeSigniture)
        {
          var an = new AssemblyName
          {
            Name = "TempAssembly" + typeSigniture,
            Version = new Version(1, 0, 0, 0)
          };
          var assemblyBuilder =
            AppDomain.CurrentDomain.DefineDynamicAssembly(
              an, AssemblyBuilderAccess.Run);
          var name = "TempAssembly" + typeSigniture + ".dll";
          var moduleBuilder = assemblyBuilder.DefineDynamicModule(name);
          var tb = moduleBuilder.DefineType("com.bodurov.TempType" + typeSigniture
                    , TypeAttributes.Public |
                    TypeAttributes.Class |
                    TypeAttributes.AnsiClass |
                    TypeAttributes.BeforeFieldInit
                    , typeof(object), new[] { typeof(INotifyPropertyChanged) });
          return tb;
        }
        private static void CreateProperty(
                TypeBuilder tb, string propertyName, Type propertyType, MethodInfo onPropertyChangedMethod)
        {
          var fieldBuilder = tb.DefineField("_" + propertyName,
                                propertyType,
                                FieldAttributes.Private);
    
          var propertyBuilder =
            tb.DefineProperty(
              propertyName, PropertyAttributes.HasDefault, propertyType, null);
          var getPropMthdBldr =
            tb.DefineMethod("get_" + propertyName,
              MethodAttributes.Public |
              MethodAttributes.SpecialName |
              MethodAttributes.HideBySig,
              propertyType, Type.EmptyTypes);
          var getIL = getPropMthdBldr.GetILGenerator();
          getIL.DeclareLocal(propertyType);
          var getEnd = getIL.DefineLabel();
          getIL.Emit(OpCodes.Nop);
          getIL.Emit(OpCodes.Ldarg_0);
          getIL.Emit(OpCodes.Ldfld, fieldBuilder);
          getIL.Emit(OpCodes.Stloc_0);
          getIL.Emit(OpCodes.Br_S, getEnd);
          getIL.MarkLabel(getEnd);
          getIL.Emit(OpCodes.Ldloc_0);
          getIL.Emit(OpCodes.Ret);
          var setPropMthdBldr =
            tb.DefineMethod("set_" + propertyName,
             MethodAttributes.Public |
             MethodAttributes.SpecialName |
             MethodAttributes.HideBySig,
             null, new[] { propertyType });
          setPropMthdBldr.DefineParameter(1, ParameterAttributes.None, "value");
          var setIL = setPropMthdBldr.GetILGenerator();
          var isNotEqual = setIL.DefineLabel();
          setIL.DeclareLocal(typeof(bool));
          setIL.Emit(OpCodes.Nop);
          setIL.Emit(OpCodes.Ldarg_1);
          setIL.Emit(OpCodes.Ldarg_0);
          setIL.Emit(OpCodes.Ldfld, fieldBuilder);
          var inequality = propertyType.GetMethod("op_Inequality");
          if (inequality != null)
          {
            setIL.Emit(OpCodes.Call, inequality);
            setIL.Emit(OpCodes.Ldc_I4_0);
          }
          setIL.Emit(OpCodes.Ceq);
          setIL.Emit(OpCodes.Stloc_0);
          setIL.Emit(OpCodes.Ldloc_0);
          setIL.Emit(OpCodes.Brtrue_S, isNotEqual);
          setIL.Emit(OpCodes.Nop);
          setIL.Emit(OpCodes.Ldarg_0);
          setIL.Emit(OpCodes.Ldarg_1);
          setIL.Emit(OpCodes.Stfld, fieldBuilder);
          setIL.Emit(OpCodes.Ldarg_0);
          setIL.Emit(OpCodes.Ldstr, propertyName);
          setIL.Emit(OpCodes.Call, onPropertyChangedMethod);
          setIL.Emit(OpCodes.Nop);
          setIL.Emit(OpCodes.Nop);
          setIL.MarkLabel(isNotEqual);
          setIL.Emit(OpCodes.Ret);
          propertyBuilder.SetGetMethod(getPropMthdBldr);
          propertyBuilder.SetSetMethod(setPropMthdBldr);
        }
      }
    }

    個々のファイルを簡単に補足します。(デフォルトで作成されるソリューション名.Webのプロジェクト等は割愛します。)

    MainPage.xamlとMainPage.xaml.csは、VS2010のC#でSilverlightアプリケーション新規作成を行った際にデフォルトで作成されるファイルに、DataGridや確認用ボタン類を付け加えたファイルです。

    DataSourceCreator.csは、動的に生成したコレクションデータからDataGridのItemsSource形式データを生成するクラスファイルで、
    http://blog.bodurov.com/How-to-Bind-Silverlight-DataGrid-From-IEnumerable-of-IDictionary
    で紹介されている
    http://www.bodurov.com/files/DataSourceCreator_INotifyPropertyChanged.zip
    をそのまま使用しています。

    【問題の概要】

    上記を実行して表示された画面に「データ生成」ボタンがあります。
    それをクリックすると、GRIDにデータが生成されます。
    5列目にチェックボックスが表示されますが、
    最終目標はチェックボックスがチェックされたものをC#側で識別し、
    チェックされた行データに対して何らかの処理を行う、ということです。

    まず、GRIDの上に「行選択チェック...」というボタンがありますが、
    これは5列目のチェックボックスのうち、チェックされたものを表示しようとしています。
    最初は、DataGridのItemsSourceをチェックすれば良いのかなと思い、以下のようなコードを考えました。
    (MainPage.xaml.csのBtnCheckRowSelectData_Click関数の前半です。)

    //dataGrid1.ItemsSourceから5列目がチェックされたデータを検索
    foreach (Object rowdata in dataGrid1.ItemsSource)
    {
        Type row_type = rowdata.GetType();
        bool b = false;
        //このようなキャストを行いたい
        //b = ((row_type)(rowdata)).COL5;
        if (b == true)
        {
            //選択された行データに対する処理
        }
    }

    上記の
    bool b = false;
    の行にブレークポイントを設定して変数rowdataをデバッグウィンドウ(ローカル変数タブ)で確認すると次のような情報が表示されます。

    + rowdata {com.bodurov.TempType1132379999} object {com.bodurov.TempType1132379999}

    左端の+をクリックしてメンバーを確認すると次のような情報が表示されます。

       COL0 0 int
       COL1 1 int
       COL2 "2列目データ1" string
       COL3 10000.1    decimal
       COL4 "     10,000.10" string
       COL5 true bool
      +パブリックでないメンバー       

    上記の「COL5 true bool」は、GRID上のチェックボックスの状態と連動しています。
    また、デバッグウィンドウで上記の「COL5 true bool」を選択し、マウス右クリックで「ウォッチ式の追加」を選択すると
    ウォッチ式タブに
    ((com.bodurov.TempType1132379999)(rowdata)).COL5
    という名前で追加されます。

    上記の
    com.bodurov.TempType1132379999
    は動的データ型です。

    このことからブレークポイントの直前に
    Type row_type = rowdata.GetType();
    として動的データ型を取得し、それでキャストすればrowdata変数の各々のメンバーが参照できるのかなと考え、
    次のようなキャスト(上記ソースではコメント行です)を行おうとしました。
    b = ((row_type)(rowdata)).COL5;
    ところが、このようなキャストは行えず、コンパイルエラーとなってしまいます。

    動的データ型でメンバーを参照する方法を調べたのですが、結局わからずじまいで、別の方法を考えました。

    別の方法とは「DataGridの情報を直接参照しよう」というものです。
    (MainPage.xaml.csのBtnCheckRowSelectData_Click関数の後半です。)
    この方法は、最初は成功したように思えたのですが、
    「(スクロールバー操作で表示しなければならない)非表示の行はチェックできない」
    という致命的な欠点が判明しました。
    (非表示の行は存在しない、とみなされるようです。
    上記ソースのチェックは初期表示されたGRIDの先頭行から行っているので、
    ソート機能等を用いて初期表示された行が非表示になるとチェックは行われません。)

    【希望事項】

    次のいずれかを希望します。

    (1)MainPage.xaml.csのBtnCheckRowSelectData_Click関数の前半に記した方法で、
    rowdata変数(DataGridのItemsSourceをforeach検索時の変数)のメンバー(上記で記したCOL5等)を
    参照する方法があれば教えて頂けないでしょうか?

    (2)MainPage.xaml.csのBtnCheckRowSelectData_Click関数の後半に記した方法で、
    DataGridのItemsSource情報のうち、非表示になっている情報も
    DataGridに必ず展開するという方法があれば教えて頂けないでしょうか?

    (3)今回私が試みている方法以外で、動的列を持つDataGridの表示や、
    その編集内容の確認を行う良い方法はないでしょうか?
    ちなみに私が動的列にこだわるのは、サーバーを非Windows系サーバー(Linux系等)にした場合も含め、サーバーサイドも含めた全体の生産性・保守性を高めたいと考えているからです。(ブラウザとサーバー間の論理的なインターフェース設計を上手に行えば、お互いの依存度を低下させられると考えています。)
    そのため、大変恐縮ですが、サーバーサイドに関しては「Windows系サーバーに依存しない方法」でのアドバイスをお願いします。

    以上よろしくお願いします。

    2011年2月8日 18:14

回答

  • y_maeyamaさま

    ご返信ありがとうございます。
    この件、質問後にいろいろと調べたところ、MainPage.xaml.cs内に

    using System.Reflection;

    を追加し、BtnCheckRowSelectData_Click関数を次のように変更して解決しました。

        private void BtnCheckRowSelectData_Click(object sender, RoutedEventArgs e)
        {
          Type row_type;
          PropertyInfo pi;
          bool b;
          string s = "";
          int select_no;
          //dataGrid1.ItemsSourceから5列目がチェックされたデータを検索
          foreach (Object rowdata in dataGrid1.ItemsSource)
          {
            row_type = rowdata.GetType();
            pi = row_type.GetProperty("COL5");
            b = (bool)(pi.GetValue(rowdata, null));
            if (b == true)
            {
              //選択された行データに対する処理
              select_no = (int)(row_type.GetProperty("COL1").GetValue(rowdata, null));
              if (s.Length == 0)
              {
                s = "5列目で選択されたNo.(1列目)";
              }
              else
              {
                s = s + " と ";
              }
              s = s + select_no.ToString();
            }
          }
          if (s.Length == 0)
          {
            s = "5列目は未選択です";
          }
          MessageBox.Show(s);
        }

    現時点でサーバーはLinux/Apache/PHP/PostgreSQL、
    サーバーの処理結果はJSON形式で返すことを前提にしており、
    XMLを使用する予定はありませんが、機会があればXML/Linqにもチャレンジしたいと思います。

    ありがとうございました。

    • 回答としてマーク VB6おやじ 2011年2月9日 7:01
    2011年2月9日 6:59

すべての返信

  • TwitterはサーバがJavaだかで作られていますが、SilverlightでTwitterクライアントを作成することができます(クロスドメインの問題でアウト・オブ・ブラウザになりますが)。よって、サーバサイド技術がなんであっても基本Silverlightは問題ありません。例えば、

     

    1.クライアントがサーバにリクエストを出す。

    2.サーバがXMLを返す

    3.クライアントでLinqなどを使用しXMLデータをパースしてIEnumerable型に変換する。

    4.データグリッドにセットする。

    と言った構造で、サーバーサイドに関しては「Windows系サーバーに依存しない方法」を実現できると思います。

    参考になれば幸いです。

     

    2011年2月9日 2:44
  • y_maeyamaさま

    ご返信ありがとうございます。
    この件、質問後にいろいろと調べたところ、MainPage.xaml.cs内に

    using System.Reflection;

    を追加し、BtnCheckRowSelectData_Click関数を次のように変更して解決しました。

        private void BtnCheckRowSelectData_Click(object sender, RoutedEventArgs e)
        {
          Type row_type;
          PropertyInfo pi;
          bool b;
          string s = "";
          int select_no;
          //dataGrid1.ItemsSourceから5列目がチェックされたデータを検索
          foreach (Object rowdata in dataGrid1.ItemsSource)
          {
            row_type = rowdata.GetType();
            pi = row_type.GetProperty("COL5");
            b = (bool)(pi.GetValue(rowdata, null));
            if (b == true)
            {
              //選択された行データに対する処理
              select_no = (int)(row_type.GetProperty("COL1").GetValue(rowdata, null));
              if (s.Length == 0)
              {
                s = "5列目で選択されたNo.(1列目)";
              }
              else
              {
                s = s + " と ";
              }
              s = s + select_no.ToString();
            }
          }
          if (s.Length == 0)
          {
            s = "5列目は未選択です";
          }
          MessageBox.Show(s);
        }

    現時点でサーバーはLinux/Apache/PHP/PostgreSQL、
    サーバーの処理結果はJSON形式で返すことを前提にしており、
    XMLを使用する予定はありませんが、機会があればXML/Linqにもチャレンジしたいと思います。

    ありがとうございました。

    • 回答としてマーク VB6おやじ 2011年2月9日 7:01
    2011年2月9日 6:59