简体   繁体   中英

Create a DataGridView, that has the second column of type DataGridViewComboBoxColumn with cell values of type ComboboxColorItem using my DataSource

I have decided to re-word this question. Here goes ...

I have a XML file:

<?xml version="1.0" standalone="yes"?>
<GenioCodes>
  <Code Layer="BI" Colour="1" />
  <Code Layer="BP" Colour="1" />
  <Code Layer="BS" Colour="1" />
  <Code Layer="C" Colour="1" />
  <Code Layer="CC" Colour="1" />
  <Code Layer="CR" Colour="1" />
</GenioCodes>

I read it into a DataSet and set it as a DataSource on the DataGridView object:

m_dataSet.ReadXml(textBoxXML.Text);
m_dataGridView.DataSource = m_dataSet.Tables[0];

The DataGridView needs to have two columns:

Column 1 : This is a default string column and should be bound to the Layer attribute.

Column 2 : This needs to be a DataGridViewComboBoxColumn column and should be bound to the Colour attribute.

The cell objects for column 2 need to be of type ComboboxColorItem .The class:

public class ComboboxColorItem
{
    public string Name { get; set; }
    public ushort Index { get; set; }
    public Color Value { get; set; }

    public ComboboxColorItem(string Name, ushort Index, Color Value)
    {
        this.Name = Name;
        this.Index = Index;
        this.Value = Value;
    }
    public override string ToString()
    {
        return Name;
    }

    static public ComboboxColorItem Create(ushort iColourIndex)
    {
        OdCmColor oColour = new OdCmColor();

        oColour.setColorIndex(iColourIndex);

        ComboboxColorItem oColorItem = new ComboboxColorItem(
            oColour.colorNameForDisplay(),
            iColourIndex,
            Color.FromArgb(oColour.red(), oColour.green(), oColour.blue()));

        oColour.Dispose();

        return oColorItem;
    }
}

So, as you can see, the Colour attribute in the XML is just a number. But we can create a cell item from that using the static ComboboxColorItem.Create method.

How do I put this all together? How can I create a DataGridView, that has the second column of type DataGridViewComboBoxColumn with cell values of type ComboboxColorItem using my DataSource ?

Note: I can change the structure of the XML file if required.

based on your questions I have created a working examle with a DataSet. IMO, it is simpler to work with primitive type in ComboBox column.

GetColorFromCode should use OdCmColor to create a color and text for ComboBox items. I don't have access to that class.

this example lacks custom cell painting

public partial class DgvForm : Form
{
    private string xml = 
@"<?xml version='1.0' standalone='yes'?>
<GenioCodes>
  <Code Layer='BI' Colour='1' Value='qwerty'/>
  <Code Layer='BP' Colour='2' />
  <Code Layer='BS' Colour='3' Value='Hello'/>
  <Code Layer='C' Colour='4' />
  <Code Layer='CC' Colour='1' />
  <Code Layer='CR' Colour='1' />
</GenioCodes>";

    DataSet m_dataSet = new DataSet();

    public DgvForm()
    {
        InitializeComponent();

        // reading xml from string
        var reader = XmlReader.Create(new StringReader(xml));            
        m_dataSet.ReadXml(reader);
        m_dataGridView.DataSource = m_dataSet.Tables[0];

        var column = m_dataGridView.Columns["Colour"];
        int idx = column.Index;
        // removing text column
        m_dataGridView.Columns.RemoveAt(idx);

        // adding comboBox column
        var cbo = new DataGridViewComboBoxColumn
        {
            Name = "Colour",
            DataPropertyName = "Colour",
        };
        // unique color codes for comboBox
        var colorCodes = m_dataSet.Tables[0].AsEnumerable()
                    .Select(r => r["Colour"])
                    .Distinct()
                    .ToList();
        cbo.DataSource = colorCodes;
        // restore column in orignal position
        m_dataGridView.Columns.Insert(idx, cbo);

        m_dataGridView.EditingControlShowing += ComboBoxShowing;
    }
    /// <summary>
    /// Activates custom drawing in comboBoxes
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void ComboBoxShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
    {            
        if (e.Control is ComboBox)
        {
            ComboBox theCB = (ComboBox)e.Control;
            theCB.DrawMode = DrawMode.OwnerDrawFixed;
            try
            {
                theCB.DrawItem -= new DrawItemEventHandler(this.ComboItemDraw);
            }
            catch { }
            theCB.DrawItem += new DrawItemEventHandler(this.ComboItemDraw);
        }
    }

    /// <summary>
    /// Custom drawing for comboBox items
    /// </summary>
    private void ComboItemDraw(object sender, DrawItemEventArgs e)
    {
        Graphics g = e.Graphics;

        Rectangle rDraw = e.Bounds;
        rDraw.Inflate(-1, -1);

        bool bSelected = Convert.ToBoolean(e.State & DrawItemState.Selected);
        bool bValue = Convert.ToBoolean(e.State & DrawItemState.ComboBoxEdit);

        rDraw = e.Bounds;
        rDraw.Inflate(-1, -1);

        if (bSelected & !bValue)
        {
            g.FillRectangle(Brushes.LightBlue, rDraw);
            g.DrawRectangle(Pens.Blue, rDraw);
        }
        else
        {
            g.FillRectangle(Brushes.White, e.Bounds);
        }


        if (e.Index < 0)
            return;
        string code = ((ComboBox) sender).Items[e.Index].ToString();

        Color c = GetColorFromCode(code);
        string s = c.ToString();

        SolidBrush b = new SolidBrush(c);
        Rectangle r = new Rectangle(e.Bounds.Left + 5, e.Bounds.Top + 3, 10, 10);
        g.FillRectangle(b, r);
        g.DrawRectangle(Pens.Black, r);
        g.DrawString(s, Form.DefaultFont, Brushes.Black, e.Bounds.Left + 25, e.Bounds.Top + 1);

        b.Dispose();
    }

    /// <summary>
    /// Returns color for a given code 
    /// </summary>
    /// <param name="code"></param>
    /// <returns></returns>
    private Color GetColorFromCode(string code)
    {
        switch (code)
        {
            case "1": return Color.Green;
            case "2": return Color.Cyan;
            case "3": return Color.Orange;
            case "4": return Color.Gray;
        }
        return Color.Red;
    }
}

Try iterating through each Dataset then bind it to the Datagridview using the .Rows.Add function...

        //Creates and Adds Rows for all Data
        for (int i = 0; i < DataSet.Count; i++)
        {
            DataGridView.Rows.Add(new object[] { columnArray1[i], columnArray2[i] });
        }

Thank you for the answers provided so far, I so appreciate them.

The way I understand it, you can set your DataGridView property AutoGenerateColumns to false . This property is not exposed in the IDE but it is available in code.

If that property is set to false , I understood that we could set the DVG column definitions ourselves and then just use a DataSet / DataSource combination. So I was hoping to avoid reading in the data and then effectively reading the column of data again (as per current answer).

For now, I having simplified my code to do things manually. Thus:

Step 1 - Initialize the DVG:

private void GENIO_Code_Editor_Load(object sender, EventArgs e)
{
    try
    {
        buttonDetect.Enabled = m_dbDatabase != null;

        InitColourComboBoxColumn();

        dataGridView.Columns.Add("Layer", "Layer");
        dataGridView.Columns.Add(cboColumn);

        if (textBoxXML.Text != "")
            ReadXmlToGrid();
    }
    catch(Exception ex)
    {
        MessageBox.Show(ex.ToString());
    }
}

Step 2: - The InitColourComboBoxColumn method:

private void InitColourComboBoxColumn()
{
    try
    {
        cboColumn = new DataGridViewComboBoxColumn();
        cboColumn.Name = "Colour";
        cboColumn.ValueMember = "Name";

        List<ushort> listColours = new List<ushort>();
        listColours.Add(1);
        listColours.Add(2);
        listColours.Add(3);
        listColours.Add(4);
        listColours.Add(5);
        listColours.Add(6);
        listColours.Add(7);
        listColours.Add(8);
        listColours.Add(9);
        listColours.Add(250);
        listColours.Add(251);
        listColours.Add(252);
        listColours.Add(253);
        listColours.Add(254);
        listColours.Add(255);

        foreach (ushort iColourIndex in listColours)
            cboColumn.Items.Add(ComboboxColourItem.Create(iColourIndex));
    }
    catch(Exception ex)
    {
        throw ex;
    }
}

Step 3: Implement the Read / Save to XML methods:

private void ReadXmlToGrid()
{
    try
    {
        XmlDocument doc = new XmlDocument();
        doc.Load(textBoxXML.Text);
        XmlNodeList listCodes = doc.SelectNodes("GenioCodes/Code");

        foreach (XmlNode oCode in listCodes)
        {
            int iRow = dataGridView.Rows.Add();

            dataGridView.Rows[iRow].Cells["Layer"].Value = oCode.Attributes["Layer"].Value;

            ushort iColourIndex = Convert.ToUInt16(oCode.Attributes["Colour"].Value);
            ComboboxColourItem ocbItem2 = null;
            foreach (ComboboxColourItem ocbItem in cboColumn.Items)
            {
                if (ocbItem.Index == iColourIndex)
                {
                    ocbItem2 = ocbItem;
                    break;
                }
            }
            if (ocbItem2 == null)
            {
                ocbItem2 = ComboboxColourItem.Create(iColourIndex);
                cboColumn.Items.Add(ocbItem2);
            }
            dataGridView.Rows[iRow].Cells["Colour"].Value = ocbItem2;
        }
    }
    catch(Exception ex)
    {
        throw ex;
    }
}

private void SaveGridToXml()
{
    XmlDocument doc = new XmlDocument();
    XmlElement codes = doc.CreateElement("GenioCodes");

    foreach(DataGridViewRow row in dataGridView.Rows)
    {
        if(row != null && !row.IsNewRow)
        {
            XmlElement code = doc.CreateElement("Code");
            code.SetAttribute("Layer", row.Cells["Layer"].Value.ToString());

            String strThisColour = row.Cells["Colour"].Value.ToString();
            bool bColourFound = false;
            foreach (ComboboxColourItem ocbItem in cboColumn.Items)
            {
                if (ocbItem.Name == strThisColour)
                {
                    code.SetAttribute("Colour", ocbItem.Index.ToString());
                    bColourFound = true;
                    break;
                }
            }
            if(!bColourFound) // This should not happen
                code.SetAttribute("Colour", "1"); // 1 is red

            codes.AppendChild(code);
        }
    }
    doc.AppendChild(codes);
    doc.Save(textBoxXML.Text);
}

That is it. It works as I need. If there is an alternative where that I can just do:

m_DataSet.ReadXml("myfile.xml");
m_DataGridView.DataSource = m_DataSource;
m_DataSet.WriteXml("myfile.xml");

And it create the type of DVG I am desiring (without re-reading any columns or deleting) then I am interesting in other answers as I considering this just a workaround to my issue.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM