I am trying to make event viewer using datagridview in c#
This is my target
I am already read many source, and still confused to implement it.
Source
add custom DataGridViewColumn with label and button per cell
How to add a Label to a DataGridView cell
Big thanks for the helps
The following shows how to read images from files, place them into a Dictionary then load them into rows in a DataTable, of course for a real app there are more images and logic to assign the images dependent on your logic.
Note that the image cells can not be selected.
The alternative is to create a custom DataGridViewColumn or the following solution.
Backend mockup
using System.Data;
namespace DataGridViewImages.Classes
{
internal class Operations
{
public static Dictionary<int, byte[]> SmallImages()
{
Dictionary<int, byte[]> dictionary = new Dictionary<int, byte[]>
{
{ 1, File.ReadAllBytes("blueInformation_16.png") },
{ 2, File.ReadAllBytes("radiobutton16.png") }
};
return dictionary;
}
public static DataTable Table()
{
DataTable dt = new DataTable();
dt.Columns.Add("image", typeof(byte[]));
dt.Columns.Add("text", typeof(string));
var images = SmallImages();
dt.Rows.Add(images[1], "Some text");
dt.Rows.Add(images[2], "More text");
return dt;
}
}
}
Form code
using DataGridViewImages.Classes;
namespace DataGridViewImages
{
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
Shown += OnShown;
dataGridView1.SelectionChanged += DataGridView1OnSelectionChanged;
dataGridView1.RowHeadersVisible = false;
}
private void DataGridView1OnSelectionChanged(object sender, EventArgs e)
{
if (dataGridView1.CurrentCell.ColumnIndex == 0)
{
dataGridView1.Rows[dataGridView1.CurrentCell.RowIndex]
.Cells[0].Selected = false;
}
}
private void OnShown(object sender, EventArgs e)
{
dataGridView1.DataSource = Operations.Table();
dataGridView1.Columns[0].HeaderText = "";
dataGridView1.Columns[0].Width = 25;
}
}
}
Related
I have to select one brand of moto. If i select "KTM", i want to get Ktm's motos. If i select "HVA", i want HVA's motos. Etc ..
I have a List of models with all models, and in function what i select, i want to add models by this brand and return this in my ComboBox2.
Modele.cs :
class Modele
{
public string NomModele;
public static List<Modele> lesModeles = new List<Modele>() {
// Husqvarna
new Modele() { NomModele = "TE"},
new Modele() { NomModele = "FE"},
// KTM
new Modele() { NomModele = "EXC"},
new Modele() { NomModele = "EXC-F"}
};
public Modele() { }
public Modele(string NomModele)
{
this.NomModele = NomModele;
}
}
Main.cs :
namespace SuiviEntretien
{
public partial class SuiviEntretien : Form
{
public SuiviEntretien()
{
InitializeComponent();
this.lesMarques.Items.AddRange(Marque.lesMarques.Select(x => x.NomMarque).ToArray());
this.lesModeles.Items.AddRange(Modele.lesModeles.Select(x => x.NomModele).ToArray());
}
private void SuiviEntretien_Load(object sender, EventArgs e)
{
}
private void SauvegarderMoto_Click(object sender, EventArgs e)
{
try
{
Moto maMoto = new Moto(
maMarque.Text = lesMarques.SelectedItem.ToString(),
monModele.Text = lesModeles.SelectedItem.ToString()
);
MessageBox.Show("Moto enregistrée avec succès !", "Information");
tabControl1.SelectTab(MaMoto);
}
catch(Exception)
{
MessageBox.Show("Il manque des informations !", "Information");
}
}
}
}
Thanks for further help.
The following answer has been made with some assumptions, those being:
-You have a ComboBox that contains values, when a value is selected another ComboBox needs to re-populate itself with a new list of data.
Depending on the scale of this problem I would recommend two solutions. Move your data into a relational database and access it accordingly, then populate your first ComboBox as a list of all main keys. (One to many methodology) then populate your second ComboBox according to the first ComboBox value.
Assuming you want to build your list dynamically and want to avoid a database then simply use functionality based on if the ComboBox changes.
private void ComboBox1_SelectedIndexChanged(object sender, EventArgs e) {
if (ComboBox1.Text == "KTM")
{
// Populate ComboBox2 with KTM data.
}
else
{
// Populate ComboBox2 with some other data.
}
}
This should help you out.
In C# windows forms application I am looking for a way to bind a grid data view to a flat file that contain simple data to display as I dont want to use a complete database like SQL Server. I need to do add/delete/edit of these data from the grid, how I can do that ?
Please if you have a link to documentation or tutorial give it to me.
Here's example how to do it:
private const string TestDataFilePath = #"C:\test\TestData.xml";
private readonly XmlSerializer _serializer = new XmlSerializer(typeof(DataModel[]));
private DataModel[] Items { get; set; }
public Form1()
{
InitializeComponent();
Load += LoadData;
btnSave.Click += SaveData;
}
private void SaveData( object sender, EventArgs e )
{
using (var file = File.Create(TestDataFilePath))
{
_serializer.Serialize(file, Items);
}
}
private void LoadData( object sender, EventArgs eventArgs )
{
using ( var file = File.Open( TestDataFilePath, FileMode.Open ))
{
Items = (DataModel[])_serializer.Deserialize( file );
}
dataGridView1.DataSource = Items;
}
I want to add a row from my DateTimePicker, TextBoxes and ComboBox (which are on form2) to DGV (form1), and now I get the message:
'Overzicht.File.dt' is inaccessible due to its protection level.
How do I solve this?
This is form1
using System;
using System.Data;
using System.Windows.Forms;
namespace Javell_Administratie_Software
{
public partial class Overzicht : Form
{
public Overzicht()
{
InitializeComponent();
Overzicht1.AutoGenerateColumns = false;
}
public DataTable table
{
set
{
File myFile = new File();
table = myFile.dt;
}
//above at myFile.dt its giving me the error
get
{
return table;
}
}
public class File
{
DataTable dt = new DataTable();
DataSet ds = new DataSet();
public void RekeningenOverzicht()
{
Overzicht oz = new Overzicht();
foreach (DataGridViewColumn col in oz.Overzicht1.Columns)
{
dt.Columns.Add(col.Name);
col.DataPropertyName = col.Name;
}
ds.Tables.Add(dt);
oz.Overzicht1.DataSource = dt;
}
}
public void AddDataTableRow()
{
Toevoegen tv = new Toevoegen();
Object row = new Object[]
{
tv.dateTimePicker1.Value, tv.textBox1.Text,
tv.textBox2.Text, tv.textBox3.Text, tv.textBox4.Text,
tv.textBox5.Text, tv.comboBox1.Text
};
table.Rows.Add(row);
Overzicht1.DataSource = table;
Overzicht1.Update();
tv.Close();
}
public void Toevoegen1_Click(object sender, EventArgs e)
{
Toevoegen tv = new Toevoegen();
tv.Show();
}
}
}
This is form2
using System;
using System.Windows.Forms;
namespace Javell_Administratie_Software
{
public partial class Toevoegen : Form
{
public Toevoegen()
{
InitializeComponent();
}
public void Toevoegen2_Click(object sender, EventArgs e)
{
Overzicht oz = new Overzicht();
oz.AddDataTableRow();
}
}
}
If you want DataTable dt from your class File to be visible you need to set them as such
Public DataTable dt = new DataTable();
Public DataSet ds = new DataSet();
If they arent public they cant be seen outside the class
I'm trying to make a "history page". Reading the urls a user have been as a string and adding those to List and transform it to DataTable but when I click on the show History menu option all I get is column "urls" and an empty cell underneath it. I assume I'm probably also using the Add method inappropriately.
Main form class:
private void showHistoryToolStripMenuItem_Click(object sender, EventArgs e)
{
using (History history = new History())
{
history.ShowDialog();
nonHomepage = URLInput.Text;
if (String.IsNullOrEmpty(nonHomepage))
{
return;
}
else
{
addToList(nonHomepage);
}
}
}
public void addToList(string URLvalue)
{
listH.Add(URLvalue);
}
public List<string> getList()
{
return listH;
}
History form class:
private void History_Load(object sender, EventArgs e)
{
Form1 br = new Form1();
list = br.listH;
DataTable table = ConvertListToDataTable(list);
dataGridView1.DataSource = table;
}
static DataTable ConvertListToDataTable(List<string> l)
{
DataTable table = new DataTable();
//int columns = 0;
table.Columns.Add("urls");
foreach(string s in l)
{
table.Rows.Add(s);
}
return table;
}
Any suggestions? What if I put all those urls in the file and then read from a file and write to a textbox/table? Or maybe I should change the data structure? Go for dictionaries, for example? Thanks in advance.
When you add a table row, you actually have to add a row, not just a string.
foreach(string s in l)
{
var row = table.NewRow();
row[0] = s;
table.Rows.Add(row);
}
return table;
Also, add a breakpoint and make sure your list isn't empty before converting it, and make sure your table is being populated correctly afterwards.
Additionally, from an architectural standpoint, if you only have one column of information, you shouldn't really be using a DataTable, a List<T> will suffice. Is there some reason that you are using a DataTable here?
Your problem is you create an empty Form1 in the private void History_Load(object sender, EventArgs e) and pass in the listH (which is empty) into the method ConvertListToDataTable(list), hence you have empty grid. The solution is you have to change your History initialization or explicitly call some method LoadData to load the actual list, something like this:
Solution 1
public partial class History : Form {
public History(){
InitializeComponent();
}
public Form1 MainForm {get;set;}
private void History_Load(object sender, EventArgs e) {
var list = MainForm == null ? new List<string>() : MainForm.listH;
DataTable table = ConvertListToDataTable(list);
dataGridView1.DataSource = table;
}
//other code ....
}
//Form1 class
private void showHistoryToolStripMenuItem_Click(object sender, EventArgs e) {
//note the MainForm initialization using Property initializer
using (History history = new History {MainForm = this}) {
history.ShowDialog();
nonHomepage = URLInput.Text;
if (String.IsNullOrEmpty(nonHomepage)) {
return;
} else {
addToList(nonHomepage);
}
}
}
Solution 2
//History class
public partial class History : Form {
//define this method to call explicitly before showing your History dialog
public void LoadData(List<string> list){
DataTable table = ConvertListToDataTable(list);
dataGridView1.DataSource = table;
}
//other code ...
}
//Form1 (or Main Form) class
private void showHistoryToolStripMenuItem_Click(object sender, EventArgs e) {
using (History history = new History()) {
history.LoadData(listH);// <---- call this first to load data
history.ShowDialog();
nonHomepage = URLInput.Text;
if (String.IsNullOrEmpty(nonHomepage)) {
return;
} else {
addToList(nonHomepage);
}
}
}
Alternative syntax to SpikeX's answer:
int i = 0;
foreach (string s in l)
{
table.Rows.Add()
tables.Rows[i].SetField("COLUMN NAME", s);
i++
}
I suppose you only have 1 column in your table, so using SetField might be a bit excessive. But when you have multiple columns it's a bit easier to read, rather than having to go back and check which column has which index.
I have a simple C# Windows Forms application which should display a DataGridView. As DataBinding I used an Object (selected a class called Car) and this is what it looks like:
class Car
{
public string color { get; set ; }
public int maxspeed { get; set; }
public Car (string color, int maxspeed) {
this.color = color;
this.maxspeed = maxspeed;
}
}
However, when I set the DataGridView property AllowUserToAddRows to true, there is still no little * which allows me to add rows.
Someone suggested to set carBindingSource.AllowAdd to true, however, when I do that, I get a MissingMethodException which says my constructor could not be found.
Your Car class needs to have a parameterless constructor and your datasource needs be something like a BindingList
Change the Car class to this:
class Car
{
public string color { get; set ; }
public int maxspeed { get; set; }
public Car() {
}
public Car (string color, int maxspeed) {
this.color = color;
this.maxspeed = maxspeed;
}
}
And then bind something like this:
BindingList<Car> carList = new BindingList<Car>();
dataGridView1.DataSource = carList;
You can also use a BindingSource for this, you don't have to but BindingSource gives some extra functionality that can sometimes be necessary.
If for some reason you absolutely cannot add the parameterless constructor then you can handle the adding new event of the binding source and call the Car parameterised constructor:
Setting up the binding:
BindingList<Car> carList = new BindingList<Car>();
BindingSource bs = new BindingSource();
bs.DataSource = carList;
bs.AddingNew +=
new AddingNewEventHandler(bindingSource_AddingNew);
bs.AllowNew = true;
dataGridView1.DataSource = bs;
And the handler code:
void bindingSource_AddingNew(object sender, AddingNewEventArgs e)
{
e.NewObject = new Car("",0);
}
You need to add AddingNew event handler:
public partial class Form1 : Form
{
private BindingSource carBindingSource = new BindingSource();
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
dataGridView1.DataSource = carBindingSource;
this.carBindingSource.AddingNew +=
new AddingNewEventHandler(carBindingSource_AddingNew);
carBindingSource.AllowNew = true;
}
void carBindingSource_AddingNew(object sender, AddingNewEventArgs e)
{
e.NewObject = new Car();
}
}
I recently discovered that if you implement your own binding list using IBindingList you MUST return true in your SupportsChangeNotification in addition to AllowNew.
The MSDN article for DataGridView.AllowUserToAddRows only specifies the following remark however:
If the DataGridView is bound to data, the user is allowed to add rows if both this property and the data source's IBindingList.AllowNew property are set to true.