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
Related
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;
}
}
}
I got a datagridview and per Button I can Add items, and now I want to see in the datagridview that something changed.
I tried so many thing. this.Refresh(); this.Invalidate(); nothing works,
I found Application.Restart(); but I am not happy with this solution.
Is there anything else??
var contex = new frachtkostenEntities();
try
{
var x = new Kunden()
{
Kundenname = first,
Zielort = second,
};
contex.Kunden.Add(x);
contex.SaveChanges();
}
Now the new Items are in the Database, but the datagridview stays the same
This is the code to display the database
frachtkostenEntities context = new frachtkostenEntities();
var item = from p in context.Artikel
select new
{
Artikelnummer = p.Artikelnummer,
Bezeichnung = p.Bezeichnung,
};
dgvDisplayDataBase.DataSource = item.ToList();
your problem is that new data has not been uploaded, you can create a function
void LoadData()
{
//code load data from db
}
Then add code to the event AddButton
void button1_Click(object sender, EventArgs e)
{
//code add new record
LoadData();
}
There is no reason to refresh, reload or invalidate when setup properly as per below.
To see immediate change when adding a new item is to setup with a BindingList and a BindingSource. Then add the new item to the BindingList.
Also consider using INotifyPropertyChanged for when a property value changes in the model.
Using the following model
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Newtonsoft.Json;
using North.Interfaces;
namespace North.Models
{
public partial class Contacts : INotifyPropertyChanged
{
private string _firstName;
private string _lastName;
private int? _contactTypeIdentifier;
public int Id => ContactId;
public int ContactId { get; set; }
public string FirstName
{
get => _firstName;
set
{
_firstName = value;
OnPropertyChanged();
}
}
public string LastName
{
get => _lastName;
set
{
_lastName = value;
OnPropertyChanged();
}
}
public int? ContactTypeIdentifier
{
get => _contactTypeIdentifier;
set
{
_contactTypeIdentifier = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Then in the form, load data to the BindingList, add button adds a new record and shows the new primary key.
namespace North.Forms
{
public partial class ContactAddForm : Form
{
private BindingList<Contacts> _bindingList;
private readonly BindingSource _bindingSource = new BindingSource();
public ContactAddForm()
{
InitializeComponent();
dataGridView1.AutoGenerateColumns = false;
Shown += OnShown;
}
private void OnShown(object sender, EventArgs e)
{
using (var context = new NorthwindContext())
{
_bindingList = new BindingList<Contacts>(context.Contacts.ToList());
_bindingSource.DataSource = _bindingList;
dataGridView1.DataSource = _bindingSource;
}
_bindingSource.MoveLast();
}
private void AddNewContactButton_Click(object sender, EventArgs e)
{
// hard coded contact
var newContact = new Contacts()
{
FirstName = "Karen",
LastName = "Payne",
ContactTypeIdentifier = 1
};
// add to list and display in DataGridView
_bindingList.Add(newContact);
// save changes
using (var context = new NorthwindContext())
{
context.Add(newContact).State = EntityState.Added;
context.SaveChanges();
}
// See new primary key
MessageBox.Show("Id " + newContact.ContactId.ToString());
}
}
}
Edit
To see values of the current row in the DataGridView
private void CurrentContactButton_Click(object sender, EventArgs e)
{
Contacts current = _bindingList[_bindingSource.Position];
MessageBox.Show($"{current.ContactId}, {current.FirstName}, {current.LastName}");
}
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 View with a DataGrid in it.
A ViewModel as DataContext where i can access a DataTable in a background object.
The background object has to work with the DataTable and keep it updated.
The user has also be allowed to make changes to that DataTable.
If i create a copy of the DataTable it stops crashing but the user is obviousely not working on the data.
If i leave access open for the user the program crashed inevitabely.
Here is a short program that will crash:
app.cs
public partial class App : Application
{
public App()
{
SomeBackgroundThing background = new SomeBackgroundThing();
MainWindowViewModel viewmodel = new MainWindowViewModel(background);
MainWindowView view = new MainWindowView(viewmodel);
view.Show();
}
}
main xaml
<Window x:Class="NullPointerDataGrid.MainWindowView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DataGrid Name="datagrid" ItemsSource="{Binding Path=table, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
</Grid>
</Window>
and the program code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using System.Data;
using System.Diagnostics;
using System.Timers;
using System.ComponentModel;
namespace NullPointerDataGrid
{
public partial class MainWindowView : Window
{
public MainWindowView(MainWindowViewModel model)
{
DataContext = model;
InitializeComponent();
datagrid.Loaded += new RoutedEventHandler(ScrollToBottom);
datagrid.AutoGeneratedColumns += new EventHandler(StarSizeLastRow);
}
void ScrollToBottom(object sender, RoutedEventArgs e)
{
Debug.WriteLine("TableGrid_ScrollToBottom");
if (datagrid.Items.Count > 0)
{
var border = VisualTreeHelper.GetChild(datagrid, 0) as Decorator;
if (border != null)
{
var scroll = border.Child as ScrollViewer;
if (scroll != null) scroll.ScrollToEnd();
}
}
}
void StarSizeLastRow(object sender, EventArgs e)
{
Debug.WriteLine("TableGrid_StarSizeLastColumn");
try
{
datagrid.Columns[datagrid.Columns.Count - 1].Width = new DataGridLength(1, DataGridLengthUnitType.Star);
}
catch { }
}
}
public class MainWindowViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private SomeBackgroundThing thing;
public DataTable table
{
get
{
lock (thing.table)
{
//DataTable wpfcopy = thing.table.Copy();
return thing.table;
};
}
set
{
Debug.Write("This never happens");
}
}
public MainWindowViewModel(SomeBackgroundThing thing)
{
this.thing = thing;
thing.Changed += new EventHandler(thing_Changed);
}
void thing_Changed(object sender, EventArgs e)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("table"));
}
}
}
public class SomeBackgroundThing : IDisposable
{
public DataTable table;
private DataTable tablecopy;
private System.Timers.Timer timer, slowrowchanger;
public event EventHandler Changed = new EventHandler((o, e) => { ;});
protected void CallChanged(object sender, EventArgs e)
{
Changed(sender, e);
}
public SomeBackgroundThing()
{
CreateTable();
UpdateB(this, null);
tablecopy = table.Copy();
InitAndStartTimer(1);
}
#region timer
private void UpdateA()
{
Boolean haschanged = false;
DataTable newcopy = table.Copy(); ;
if (newcopy.Rows.Count != tablecopy.Rows.Count)
{
Debug.WriteLine("Different ammount of rows");
haschanged = true;
}
else if (newcopy.Columns.Count != tablecopy.Columns.Count)
{
Debug.WriteLine("Different ammount of columns");
haschanged = true;
}
else
{
for (int i = 0; i < newcopy.Rows.Count; i++)
{
for (int j = 0; j < newcopy.Columns.Count; j++)
{
if (newcopy.Rows[i][j].ToString() != tablecopy.Rows[i][j].ToString())
{
Debug.WriteLine(String.Format(
"Element [{0}/{1}]: {2} is different from {3}",
i, j, newcopy.Rows[i][j], tablecopy.Rows[i][j]
));
haschanged = true;
}
if (haschanged) break;
}
if (haschanged) break;
}
}
if (haschanged)
{
tablecopy = newcopy;
}
}
private void InitAndStartTimer(int interval)
{
timer = new System.Timers.Timer();
timer.Interval = interval;
timer.AutoReset = true;
timer.Elapsed += new ElapsedEventHandler((s, e) =>
{
UpdateA();
});
timer.Enabled = true;
slowrowchanger = new System.Timers.Timer();
slowrowchanger.Interval = 3000;
slowrowchanger.AutoReset = true;
slowrowchanger.Elapsed += new ElapsedEventHandler((s, e) =>
{
UpdateB(null, null);
});
slowrowchanger.Enabled = true;
}
public void Dispose()
{
timer.Enabled = false;
slowrowchanger.Enabled = false;
timer.Dispose();
slowrowchanger.Dispose();
}
#endregion
#region editlastrow
void UpdateB(object sender, EventArgs e)
{
Random rnd = new Random();
List<String> cells = new List<string>{
"The SAME",
rnd.Next(0,100).ToString(),
rnd.ToString(),
rnd.NextDouble().ToString()};
lock (table)
{
OverwriteOrAppendLastRow(ref table, cells);
table.AcceptChanges();
}
CallChanged(this, null);
}
private void OverwriteOrAppendLastRow(ref DataTable table, List<string> newrow)
{
if (table.Rows.Count == 0) CreteEmptyRow(ref table);
if (newrow[0].ToString() != table.Rows[table.Rows.Count - 1][0].ToString())
{
Debug.WriteLine(String.Format("Creating row because '{0}' is different from '{1}'", newrow[0], table.Rows[table.Rows.Count - 1][0]));
CreteEmptyRow(ref table);
}
OverwriteLastRow(ref table, newrow);
}
private void OverwriteLastRow(ref DataTable table, List<string> newrow)
{
for (int i = 0; i < newrow.Count() && i < table.Columns.Count; i++)
{
table.Rows[table.Rows.Count - 1][i] = newrow[i];
}
}
private void CreteEmptyRow(ref DataTable table)
{
table.Rows.Add(new String[table.Columns.Count]);
}
#endregion
private void CreateTable()
{
table = new DataTable();
table.Columns.Add("FirstCell", typeof(String));
table.Columns.Add("BananaCell", typeof(String));
table.Columns.Add("CherryCell", typeof(String));
table.Columns.Add("Blue", typeof(String));
Random rnd = new Random();
for (int i = 0; i < 145; i++)
{
table.Rows.Add(new String[]{
rnd.Next().ToString(),
rnd.Next(0,i+1).ToString(),
rnd.ToString(),
rnd.NextDouble().ToString()});
}
}
}
}
How can i stop this multithread crashing?
EDIT:
I don't know if there are more than one reasons for this code to crash. But i did my best to gather some information about one reason to crash:
Nullpointer exception in App.g.cs - the autogenerated portion. The Debugger wont step into it - so i cant say anything about the line it crashes in.
Here is the Exception Detail, sorry for the German.
System.NullReferenceException wurde nicht behandelt.
Message=Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt.
Source=PresentationFramework
InnerException:
The Stacktrace only shows "Externer Code" so no stack to trace.
The thing is that WPF crashes - my code can handle it... somehow i need to capsule WPF so it wont crash, one way to do that is to copy the DataTable - but then i loose the ability to write back that table since its setter is not called when something has gotten edited.
EDIT #2:
I recreated this example to show the error i have in another program and i just found out that what crashes is actually related with the scrollbar. If i change the ammount of displayed data to a low number so that there is no scrollbar, the code will not crash.
The following change to the viewmodel solves the problem.
I am now using a copy for wpf to work on and only note the canges should they occur. This code has an issue with the poorly refined change mechanism - but that is beyond the scope of this question.
public class MainWindowViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private SomeBackgroundThing thing;
private DataTable wpftable;
public DataTable table
{
get
{
lock (wpftable)
{
return wpftable;
}
}
set
{
lock (wpftable)
{
wpftable = value;
}
}
}
public MainWindowViewModel(SomeBackgroundThing thing)
{
wpftable = thing.table.Copy();
this.thing = thing;
thing.Changed += new EventHandler(thing_Changed);
}
void thing_Changed(object sender, EventArgs e)
{
if (PropertyChanged != null)
{
DataTable wpftablecopy = wpftable.Copy();
DataTable thintablecopy = thing.table.Copy();
int rowcount = wpftablecopy.Rows.Count;
for (int col = 0; col < 4; col++)
{
for (int row = 0; row < rowcount; row++)
{
if (wpftablecopy.Rows[row][col] != thintablecopy.Rows[row][col])
wpftable.Rows[row][col] = thintablecopy.Rows[row][col];
}
}
PropertyChanged(this, new PropertyChangedEventArgs("table"));
}
}
}
I have the following code:
DataGridView lGrid = new DataGridView();
BindingSource _bind = new BindingSource();
DataTable Table = new DataTable();
this.Controls.Add(lGrid);
lGrid.AutoGenerateColumns = true;
List<string> ColumnsNames = new List<string>();
ColumnsNames.Add("ID");
ColumnsNames.Add("NAME");
foreach (string Name in ColumnsNames)
Table.Columns.Add(Name);
DataColumn col = Table.Columns["ID"];
DataColumn[] keys = new DataColumn[1];
keys[0] = Table.Columns["ID"];
Table.PrimaryKey = keys;
lGrid.DataSource = _bind;
_bind.DataSource = Table;
int i = lGrid.Columns.Count;
which populates lGrid with columns in datatable just fine with this code executing in the form constructor. However, when I move it to the control constructor binding doesn't work and i = 0. Why is it so and what can i do about it?
Update1
OK. the constructors are the most simple
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
//CODE GOES HERE
}
}
VS
public class mycontrol : Control
{
public mycontrol()
{
//CODE GOES HERE
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
mycontrol ll = new mycontrol();
this.Controls.Add(ll);
}
}
Use think if you
lDataGrid.Bind();
then you will get the Count()
or check the Count on the AfterDataBinding Event.