1

EFを使用して、1対多のフラグを含むプロセスのリストを返しています。フラグは一意であり、要件に応じて増減する場合があります。データ構造は大まかに次のように変換されます。

public enum FlagTypes
{
    OnlyOnWeekends,
    OnlyOnHolidays
}

public class Process
{
    public DateTime Date { get; set; }
    public String Description { get; set; }
    public Dictionary<FlagTypes, Flag> Flags { get; set; }
}

public class Flag
{
    public FlagTypes Type { get; set; }
    public bool Enabled { get; set; }
}

これを次のようにDataGridViewに表示したいと思います。

Date | Description | OnlyOnWeekends | OnlyOnHolidays [|... more flags as needed]

..編集可能にしながら。

DataGridViewの制限を回避して、カスタム列とセルを使用してテーブルを表示することができました

public class EnumerationColumn : DataGridViewColumn
{
    public FlagTypes EnumerationType { get; set; }

    public EnumerationColumn(FlagTypes enumerationType)
        : base(new EnumerationCell())
    {
        EnumerationType = enumerationType;
    }
    
    public override DataGridViewCell CellTemplate
    {
        get
        {
            return base.CellTemplate;
        }
        set
        {
            // Ensure that the cell used for the template is a EnumerationCell.
            if (value != null &&
                !value.GetType().IsAssignableFrom(typeof(EnumerationCell)))
            {
                throw new InvalidCastException("Must be a EnumerationCell");
            }
            base.CellTemplate = value;
        }
    }
    
    public class EnumerationCell : DataGridViewCheckBoxCell
    {
        private EnumerationColumn Parent
        {
            get
            {
                var parent = base.OwningColumn as EnumerationColumn;
                if(parent == null)
                {
                    throw new NullReferenceException("EnumerationCell must belong to a EnumerationColumn");
                }
                return parent;
            }
        }
        
        private Dictionary<FlagTypes, Flag> GetFlags(int rowIndex)
        {
            var flags = base.GetValue(rowIndex) as Dictionary<FlagTypes, Flag>;
            return flags ?? new Dictionary<FlagTypes, Flag>();
        }
        
        protected override object GetValue(int rowIndex)
        {
            var flags = GetFlags(rowIndex);
            if (flags.ContainsKey(Parent.EnumerationType))
            {
                return flags[Parent.EnumerationType].Enabled;
            }
            return false;
        }
    }
}

そして列を作成する

grid.AutoGenerateColumns = false;
grid.DataSource = processes; // List<Process>

var dateCol = new CalendarWidgetColumn();
dateCol.DataPropertyName = "Date";
dateCol.HeaderText = "Date";

var descCol = new DataGridViewTextBoxColumn();
descCol.DataPropertyName = "Description";
descCol.HeaderText = "Description";

grid.Columns.Add(dateCol);
grid.Columns.Add(descCol);
        
foreach(string name in Enum.GetNames(typeof(FlagTypes)))
{
    FlagTypes flag;
    if(FlagTypes.TryParse(name, out flag))
    {
        var enumCol = new EnumerationColumn(flag);
        enumCol.DataPropertyName = "Flags";
        enumCol.HeaderText = String.Format("{0}?", name);
        grid.Columns.Add(enumCol);
    }
}

DataSourceに保存するための呼び出しをインターセプトする方法がわからないため、bool(チェックボックス値)をディクショナリ(Flags DataProperty)に設定しようとして例外がスローされます。CellValuePushedイベントを見てきましたが、それは事後に発生します。何か案は?

それとも、これにすべて一緒に取り組むためのより簡単な方法ですか?

(最終的には、プロセスリストをBindingListでラップして、DataGridViewから直接新しい行を作成できるようにします)


解決策(以下の@Erez Robinsonによって提案されたように)

ステップ1:基礎となる辞書に簡単にアクセスできるようにDataStructureを変更する

public enum FlagType
{
    OnlyOnWeekends,
    OnlyOnHolidays
}

public class Process
{
    public DateTime Date { get; set; }
    public String Description { get; set; }
    public Dictionary<FlagType, Flag> Flags { get; set; }

    public bool this[FlagType flagType]
    {
        get
        {
            if(!Flags.ContainsKey(flagType))
            {
                Flags.Add(flagType, new Flag(flagType, false));
            }
            return Flags[flagType].Enabled;
        }
        set
        {
            Flags[flagType].Enabled = value;
        }
    }
}

public class Flag
{
    public FlagType Type { get; set; }
    public bool Enabled { get; set; }

    public Flag(FlagType flagType, bool enabled)
    {
        Type = flagType;
        Enabled = enabled;
    }
}

ステップ2:ITypedListから派生するコンテナーを作成します

class ProcessCollection : List<Process>, ITypedList
{
    protected IProcessViewBuilder _viewBuilder;

    public ProcessCollection(IProcessViewBuilder viewBuilder)
    {
        _viewBuilder = viewBuilder;
    }

    #region ITypedList Members

    protected PropertyDescriptorCollection _props;

    public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
    {
        if (_props == null)
        {
            _props = _viewBuilder.GetView();
        }
        return _props;
    }

    public string GetListName(PropertyDescriptor[] listAccessors)
    {
        return ""; // was used by 1.1 datagrid
    }

    #endregion
}

ステップ3:動的プロパティの作成をViewBuilderに移動します

public interface IProcessViewBuilder
{
    PropertyDescriptorCollection GetView();
}

public class ProcessFlagView : IProcessViewBuilder
{
    public PropertyDescriptorCollection GetView()
    {
        List<PropertyDescriptor> props = new List<PropertyDescriptor>();
        
        props.Add(new DynamicPropertyDescriptor(
            typeof(Process),
            "Date",
            typeof(DateTime),
            delegate(object p)
            {
                return ((Process)p).Date;
            },
            delegate(object p, object newPropVal)
                {
                    ((Process)p).Date = (DateTime)newPropVal;
                }
        ));

        props.Add(new DynamicPropertyDescriptor(
            typeof(Process),
            "Description",
            typeof(string),
            delegate(object p)
            {
                return ((Process)p).Description;
            },
            delegate(object p, object newPropVal)
                {
                    ((Process) p).Description = (string) newPropVal;
                }
        ));

        foreach (string name in Enum.GetNames(typeof(FlagType)))
        {
            FlagType flag;
            if (FlagType.TryParse(name, out flag))
            {

                props.Add(new DynamicPropertyDescriptor(
                              typeof (Process),
                              name,
                              typeof (bool),
                              delegate(object p)
                                  {
                                      return ((Process) p)[flag];
                                  },
                              delegate(object p, object newPropVal)
                                  {
                                      ((Process) p)[flag] = (bool) newPropVal;
                                  }
                              ));
            }
        }

        PropertyDescriptor[] propArray = new PropertyDescriptor[props.Count];
        props.CopyTo(propArray);
        
        return new PropertyDescriptorCollection(propArray);
    }
}

ステップ4:コレクションをDataGridViewにバインドします

ProcessCollection processes = new ProcessCollection(new ProcessFlagView());

// Add some dummy data ...
processes.Add( ... );

// If you want a custom DataGridViewColumn, bind it before you bind the DataSource
var dateCol = new CalendarColumn();
dateCol.DataPropertyName = "Date";
dateCol.HeaderText = "Date";
grid.Columns.Add(dateCol);

grid.DataSource = processes;
4

1 に答える 1

1

DataGridViewから派生し、newキーワードを使用してDataSourceプロパティをシャドウすることができます。

しかし、私はあなたがあなたの概念を変える必要があると思います。
datagridviewには触れません。

これらのフラグを個別のプロパティとして公開します。これらのクラスをバインディングリストに追加します。
リフレクションを使用して、動的にプロパティを作成できます。

この記事を読んでください、それはあなたを正しい方向に向けます:ITypedList

于 2012-05-31T18:19:37.827 に答える