0

I have an xml file which acts as a data table. It holds various types of data. The data types could be different in any of the columns. An example would be: row(1)String, int, date; row(2)Binary, int, date; row(3)String, binary, int.

Which leads me to the problem i am trying to solve, how can I parse the cell contents and place a button in cells where the data type is binary or byte type.

This has been tricky because not only are all the cell contents of different types but the cells are not assigned to columntypes automatically, it must be done manually.

Any ideas on how to accomplish this?

P.S. I think that using the CellValidating event I might be able to get the behavior I want, however I am at a loss for how to validate that a string is actually binary data...

EDIT: Ok so I have decided that the easiest way to get to the desired result is to make the user choose the data type they wish to record. And then later check for that data type in each column and then place a button if that data type exists in a column.

I have a "TableFactory" class which makes the table that will be serialized and stored in xml. In this class I have added a "ColumnDataTypes" enum and a section to handle the types I want to allow, only 3 right now, Integer, String, and File(byte[]). The problem with this new approach is that in order to properly store the byte array I have to convert it to a Base64String. This however changes the data type to a string instead of a byte, which places me back at the beginning again...How do I parse the column data and logically determine that it is a byte array and not just a string. Once I know it is a byte array I can place a button in the cell that contains the byte array. Does anyone have any ideas as to how to accomplish this?

Table Factory Class:

    using System.Data;
    using System.Xml;
    using System.IO;
    using System.Xml.Serialization;
    using XML_Database.usrctrls;

    namespace XML_Database.data
    {
    public class TableFactory
{
    //string _tableName;
    DataTable _dt;

    //public string TableName { get { return _tableName; } set { _tableName = value; } }

    public TableFactory(string tableName)
    {
        if (this._dt == null)
        {
            this._dt = new DataTable(tableName);
        }
    }

    public DataTable Table
    {
        get { return this._dt; }
        set { this._dt = value; }
    }

    public void NewColumn(string ColumnName, ColumnTypes colType)
    {
        if (!this._dt.Columns.Contains(ColumnName))
        {
            switch (colType)
            {
                case ColumnTypes.String:
                    this._dt.Columns.Add(ColumnName, typeof(String));
                    break;
                case ColumnTypes.Integer:
                    this._dt.Columns.Add(ColumnName, typeof(Int32));
                    break;
                case ColumnTypes.Binary:
                    this._dt.Columns.Add(ColumnName, typeof(Byte[]));
                    break;
            }
        }
    }

    public void DeleteColumn(string ColumnName)
    {
        if (_dt.Columns.Contains(ColumnName))
        {
            _dt.Columns.Remove(ColumnName);
        }
    }

    public void SaveTable(string Path)
    {
        data.encryptFiles._SALT = data.dboptions.Salt();
        data.encryptStrings._SALT = data.dboptions.Salt();
        string tablePath = Path + "\\" + _dt.TableName + ".xml";
        DataSet ds = new DataSet();
        XmlDocument xDoc = new XmlDocument();
        ds.Tables.Clear();
        ds.Tables.Add(_dt.Copy());

        XmlElement xE = (XmlElement)Serialize(ds);
        string strXml = xE.OuterXml.ToString();

        xDoc.LoadXml(strXml);
        xDoc.Save(tablePath);
        if (data.dboptions.DBEncryptionOptions())
        {
            File.Delete(Path + "\\" + _dt.TableName + "_enc.xml");
            data.encryptFiles.EncryptFile(tablePath, tablePath.Replace(".xml", "_enc.xml"), data.encryptStrings.Decrypt(data.dboptions.Pwd(), data.dboptions.Salt()));
        }
    }

    public void LoadTable(string Path)
    {
        string tablePath = Path + "\\" + _dt.TableName + ".xml";
        XmlDocument xDoc = new XmlDocument();
        if (File.Exists(tablePath))
        {
            if (_dt.TableName.Contains("_enc"))
            {
                MemoryStream ms = new MemoryStream();
                data.encryptFiles._SALT = data.dboptions.Salt();
                data.encryptStrings._SALT = data.dboptions.Salt();
                data.encryptFiles.DecryptToMemory(tablePath, out ms, data.encryptStrings.Decrypt(data.dboptions.Pwd(), data.dboptions.Salt()));
                using (ms)
                {
                    xDoc.Load(ms);
                }
                DataSet ds = (DataSet)Deserialize(xDoc.DocumentElement, typeof(DataSet));
                _dt = ds.Tables[0];
            }
            else
            {
                xDoc.Load(tablePath);
                DataSet ds = (DataSet)Deserialize(xDoc.DocumentElement, typeof(DataSet));
                _dt = ds.Tables[0];
            }
        }
    }

    private object Deserialize(XmlElement xmlElement, System.Type type)
    {
        Object transformedObject = null;
        try
        {
            Stream memStream = StringToStream(xmlElement.OuterXml);
            XmlSerializer serializer = new XmlSerializer(type);
            transformedObject = serializer.Deserialize(memStream);
        }
        catch (Exception)
        {

        }
        return transformedObject;
    }

    private Stream StringToStream(string p)
    {
        MemoryStream memStream = null;
        try
        {
            byte[] buffer = Encoding.UTF8.GetBytes(p);
            memStream = new MemoryStream(buffer);
        }
        catch (Exception)
        {

        }
        finally
        {
            memStream.Position = 0;
        }
        return memStream;
    }

    private XmlElement Serialize(object TransformObject)
    {
        XmlElement serializedElement = null;
        try
        {
            MemoryStream memStream = new MemoryStream();
            XmlSerializer serializer = new XmlSerializer(TransformObject.GetType());
            serializer.Serialize(memStream, TransformObject);
            memStream.Position = 0;
            XmlDocument xDoc = new XmlDocument();
            xDoc.Load(memStream);
            serializedElement = xDoc.DocumentElement;
        }
        catch (Exception)
        {

        }
        return serializedElement;
    }
}
}

Any help is greatly appreciated! :)

EDIT: I am able to add Images but not regular files. Ideally adding both files and images is the desirable functionality. But so far only images will go into my Byte array column. I can store files if I change the column data type to string and store converted Base64String, but I still can't figure out how to parse the data to confirm that it is a file and not just text...

EDIT: Ok I am closer to solving this problem, but not sure how to get over the hump...I can get a button to show up but not in the correct cell. Here is some more code to assist anyone in helping me answer this question...

private void dataGridView1_RowsAdded(object sender, DataGridViewRowsAddedEventArgs e)
    {
        button1.ForeColor = Color.Red;
        try
        {
            for (int i = 0; i < dataGridView1.RowCount; i++)
            {
                foreach (DataGridViewCell cell in dataGridView1.Rows[i].Cells)
                {
                    if (cell.Value != null)
                    {
                        if (TryParseBinary(cell.Value.ToString()))
                        {
                            var buttonCell = new DataGridViewButtonCell();
                            buttonCell.Value = "Export File";
                            buttonCell.Style.Alignment = DataGridViewContentAlignment.MiddleCenter;
                            dataGridView1.Rows[i].Cells[cell.ColumnIndex] = buttonCell;
                        }
                    }
                }
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show("Grid Validation Error: " + ex.Message);
        }
    }

    private bool TryParseBinary(string p)
    {
        try
        {
            string path = "C:\\Temp\\" + DateTime.Now.Ticks.ToString() + ".test";
            byte[] bytes = Convert.FromBase64String(p);
            File.WriteAllBytes(path, bytes);

            //File.Delete(path);
            return true;
        }
        catch (Exception ex)
        {
            MessageBox.Show("Error: " + ex.Message);
            return false;
        }
    }

The "TryParseBinary" is the problem. It needs to create a valid file to return "true", but I'm not sure how to make it smart enough to tell if what it just wrote is a valid file...

jforward5
  • 93
  • 1
  • 12
  • the point of a Gridview is that rows have values of various types where each column has value of the same type. – Aniket Inge Jan 14 '13 at 21:32
  • the way around this would be to generate raw `
    ` `` manually
    – Aniket Inge Jan 14 '13 at 21:33
  • I am creating a xml database program, for fun (I know it sounds off the wall or outlandish). And the xml file is created by the user. They create the columns and put the data into the tables. They can drag and drop files into the tables and it stores the information into the xml file. For this reason I am using the datagridview. It makes viewing the user content easy...or so I thought lol...btw it is a windows forms application, not web based... – jforward5 Jan 14 '13 at 21:41
  • Gridview is converted to tables when rendered – Aniket Inge Jan 14 '13 at 21:42
  • well connect the dots :P Gridview expects each column to be of a specific type, the output however is just plain table. So you can now generate a table from XML, do your Javascript magic of Drag+Drop, then a save can write the data back to the XML. – Aniket Inge Jan 14 '13 at 21:46
  • Oh wait, its not web? My bad then ! – Aniket Inge Jan 14 '13 at 21:47
  • I have the drag drop part done, I have the save to xml done. The part that is a problem is displaying a button for columns that hold binary data. I have to figure out a way to parse each column and have the program decide if it is binary or not, and then display a button for exporting the file from the xml to a path the user chooses... – jforward5 Jan 14 '13 at 21:49
  • try overriding the RowsAdded method, see how if that helps. – Aniket Inge Jan 14 '13 at 21:51
  • And do what exactly? Currently I am using the rows added to "intuitively" change a "save" button's color (prompting the user to click it) when items have been added to the grid... – jforward5 Jan 14 '13 at 21:57
  • Another strange thing I just noticed is that when I drop a file into the datagridview if the file is not an image then I get an error. It tries to render the "file" as if it were an image. – jforward5 Jan 15 '13 at 22:12

1 Answers1

0

Ok so I was able to create a solution to my problem. It isn't exactly what I wanted but it does the job. I made the "DragDrop" method for my datagridview check for the existence of a "File" and "MIME" columns in the datatable. I then create those columns if they do not exist, if they do I add the file bytes converted to Base64String to the "File" column and the file extension to the "MIME" column. I then have the "RowsAdded" event change the "File" cells to buttons. However currently it doesn't keep the text "Export File", but I will keep working on that part, it seems like a simple fix. Below is my code:

    private void dataGridView1_DragDrop(object sender, DragEventArgs e)
    {
        try
        {
            Point cursorPosition = dataGridView1.PointToClient(Cursor.Position);
            DataGridView.HitTestInfo info = dataGridView1.HitTest(cursorPosition.X, cursorPosition.Y);
            tableFactory = new data.TableFactory(TablesDropDown.SelectedValue.ToString());
            tableFactory.LoadTable(dbPath);

            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);

                foreach (DataColumn col in tableFactory.Table.Columns)
                {
                    if (col.ColumnName != "MIME" && col.ColumnName != "File")
                    {
                        tableFactory.NewColumn("MIME", ColumnTypes.String);
                        tableFactory.NewColumn("File", ColumnTypes.String);
                        tableFactory.SaveTable(dbPath);
                        ColumnsDataGrid();

                        dataGridView1.Rows[info.RowIndex].Cells["File"].Value = Convert.ToBase64String(GetFile(files));
                        FileInfo f = new FileInfo(files[0]);
                        dataGridView1.Rows[info.RowIndex].Cells["MIME"].Value = f.Extension;
                    }
                    else
                    {
                        dataGridView1.Rows[info.RowIndex].Cells["File"].Value = Convert.ToBase64String(GetFile(files));
                        FileInfo f = new FileInfo(files[0]);
                        dataGridView1.Rows[info.RowIndex].Cells["MIME"].Value = f.Extension;
                    }
                }
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show("This was not a valid file to store in the database. Error Msg: " + ex.Message);
        }
    }


    private byte[] GetFile(string[] files)
    {
        byte[] bytes = null;
        foreach (string file in files)
        {
            FileInfo fileInfo = new FileInfo(file);
            if (File.Exists(file))
            {
                bytes = File.ReadAllBytes(file);
            }
        }
        return bytes;
    }


    private void dataGridView1_RowsAdded(object sender, DataGridViewRowsAddedEventArgs e)
    {
        button1.ForeColor = Color.Red;
        try
        {
            for (int i = 0; i < dataGridView1.RowCount; i++)
            {
                foreach (DataGridViewColumn col in dataGridView1.Columns)
                {
                    if (col.Name == "File")
                    {
                        var buttonCell = new DataGridViewButtonCell();
                        buttonCell.Value = "Export File";
                        buttonCell.Style.Alignment = DataGridViewContentAlignment.MiddleCenter;
                        dataGridView1.Rows[i].Cells[col.Index] = buttonCell;
                    }
                }
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show("Grid Validation Error: " + ex.Message);
        }
    }

If you see something that needs adjustment or could be done better, I am open to suggestions! :)

jforward5
  • 93
  • 1
  • 12