color whole row instead of single cell - c#

Ive been trying to change the background color of a row in the Compact Framework DataGrid and have found little success since the DataGrid on .NET CF is limited compared to its Windows Forms counterpart. My only bit of success in achieving my goal is I have now been able to change the background color of a single cell depending on its values. I couldnt manipulate the code I got from Googling since I am not that good in C#. However, this is the code that I have:
namespace GridColor
{
public delegate void CheckCellEventHandler(object sender, DataGridEnableEventArgs e);
public class DataGridEnableEventArgs : EventArgs
{
private int _column;
private int _row;
private bool _meetsCriteria;
public DataGridEnableEventArgs(int row, int col, bool val)
{
_row = row;
_column = col;
_meetsCriteria = val;
}
public int Column
{
get { return _column; }
set { _column = value; }
}
public int Row
{
get { return _row; }
set { _row = value; }
}
public bool MeetsCriteria
{
get { return _meetsCriteria; }
set { _meetsCriteria = value; }
}
}
public partial class ColumnStyle : DataGridTextBoxColumn
{
//public event CheckCellEventHandler CheckCellEquals;
public event CheckCellEventHandler CheckCellContains;
private int _col;
public ColumnStyle(int column)
{
_col = column;
}
protected override void Paint(Graphics g, Rectangle Bounds, CurrencyManager Source, int RowNum, Brush BackBrush, Brush ForeBrush, bool AlignToRight)
{
bool enabled = true;
if (CheckCellContains != null)
{
DataGridEnableEventArgs e = new DataGridEnableEventArgs(RowNum, _col, enabled);
CheckCellContains(this, e);
if (e.MeetsCriteria)
//g.DrawRectangle(new Pen(Color.Red, 2), Bounds.Y + 1, Bounds.Width - 2, Bounds.Height - 2);
BackBrush = new SolidBrush(Color.PaleGreen);
}
base.Paint(g, Bounds, Source, RowNum, BackBrush, ForeBrush, AlignToRight);
}
}
}
Now for my form, I have this:
namespace GridColor
{
public partial class Form1 : Form
{
DataSet ds;
SqlDataAdapter da;
private List<string> compareValues = new List<string>();
public Form1()
{
InitializeComponent();
try
{
addGridStyle(ref dataGrid1);
compareValues.Add("OK");
compareValues.Add("Filling");
}
catch (Exception ex)
{
MessageBox.Show("Error: " + ex.ToString());
}
}
private void addGridStyle(ref DataGrid dg)
{
DataGridTableStyle dtStyle = new DataGridTableStyle();
dtStyle.MappingName = "Test";
string connString = "Data Source=192.168.2.16,1433;Initial Catalog=TestDB;User ID=sa;Password=ABC12abc;";
SqlConnection conn = new SqlConnection(connString);
conn.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = conn;
cmd.CommandType = CommandType.Text;
cmd.CommandText = "SELECT * FROM Test";
ds = new DataSet();
da = new SqlDataAdapter(cmd);
da.Fill(ds, "Test");
for (int i = 0; i < ds.Tables["Test"].Columns.Count; i++)
{
ColumnStyle myStyle = new ColumnStyle(i);
myStyle.MappingName = ds.Tables["Test"].Columns[i].ToString();
if (i == 1)
{
if (ds.Tables["Test"].Columns[i].DataType == System.Type.GetType("System.String"))
myStyle.CheckCellContains += new CheckCellEventHandler(myStyle_CheckCellContains);
}
dtStyle.GridColumnStyles.Add(myStyle);
}
dg.TableStyles.Add(dtStyle);
}
public void myStyle_CheckCellContains(object sender, DataGridEnableEventArgs e)
{
try
{
if (compareValues.Contains((string)dataGrid1[e.Row, e.Column]))
e.MeetsCriteria = true;
else
e.MeetsCriteria = false;
}
catch (Exception ex)
{
e.MeetsCriteria = false;
}
}
private void Form1_Load(object sender, EventArgs e)
{
dataGrid1.DataSource = ds.Tables["Test"];
}
}
}
In what part of my code should I change so that If a cell meets the criteria, its whole row will be colored instead of only it's own cell?

Alright I went back and found my code from years ago where I did this on the desktop, before the more advanced DataGridView came out, etc.
First of all there is this tutorial from Microsoft Customizing the Windows Forms DataGrid, which explains how to highlight an entire row.
I looked at my code and I had to add a custom column style for each column, fire an event to the main form which I handled, and then determined the proper color for that record. Then, I set the args.Color property and the DataGridColumn would draw the correct color. So yes, you have to actually have each column be your custom formatable class, then your application logic can handle the event, get the record data and determine the color
** Update: here's a simple example **
public partial class Form1 : Form
{
FormattableTextBoxColumn firstNameColumn = new FormattableTextBoxColumn();
FormattableTextBoxColumn lastNameColumn = new FormattableTextBoxColumn();
public Form1()
{
InitializeComponent();
// add first name col
firstNameColumn.MappingName = "FirstName";
dataGridTableStyle1.GridColumnStyles.Add(firstNameColumn);
firstNameColumn.SetCellFormat += new FormatCellEventHandler(ColumnSetCellFormat);
// add last name col
lastNameColumn.MappingName = "LastName";
lastNameColumn.SetCellFormat += new FormatCellEventHandler(ColumnSetCellFormat);
dataGridTableStyle1.GridColumnStyles.Add(lastNameColumn);
// This just sets up a dummy data source, since I don't have a database in this example
List<PersonTest> peopleList = new List<PersonTest>();
peopleList.Add(new PersonTest
{
FirstName = "Alan",
LastName = "QQQQQ",
HighlightPerson = true
});
peopleList.Add(new PersonTest
{
FirstName = "John",
LastName = "Smith",
HighlightPerson = false
});
BindingSource peopleDataSource = new BindingSource();
peopleDataSource.DataSource = peopleList;
dataGridTableStyle1.MappingName = peopleDataSource.GetListName(null);
dataGrid1.DataSource = peopleDataSource;
}
// I'll cache this brush in the form, just make sure to dispose it (see designer.cs disposing)
SolidBrush highlightBrush = new SolidBrush(Color.Yellow);
// here is the event you can handle to determine the color of your row!
private void ColumnSetCellFormat(object sender, DataGridFormatCellEventArgs e)
{
if ((e.Source.List[e.Row] as PersonTest).HighlightPerson)
e.BackBrush = highlightBrush;
}
// example test class
public class PersonTest
{
public String FirstName { get; set; }
public String LastName { get; set; }
public bool HighlightPerson { get; set; }
}
}
And the custom data grid column
public class FormattableTextBoxColumn : DataGridTextBoxColumn
{
public event FormatCellEventHandler SetCellFormat;
protected override void Paint(Graphics g, Rectangle bounds, CurrencyManager source, int rowNum, Brush backBrush, Brush foreBrush, bool alignToRight)
{
DataGridFormatCellEventArgs e = new DataGridFormatCellEventArgs(rowNum, source);
e.ForeBrush = foreBrush;
e.BackBrush = backBrush;
OnSetCellFormat(e);
base.Paint(g, bounds, source, rowNum, e.BackBrush, e.ForeBrush, alignToRight);
}
private void OnSetCellFormat(DataGridFormatCellEventArgs e)
{
FormatCellEventHandler handler = SetCellFormat;
if (handler != null)
handler(this, e);
}
}
You'll also need this DataGridCellEventArgs.cs
public delegate void FormatCellEventHandler(object sender, DataGridFormatCellEventArgs e);
public class DataGridFormatCellEventArgs : EventArgs
{
public int Row;
public CurrencyManager Source;
public Brush BackBrush;
public Brush ForeBrush;
public DataGridFormatCellEventArgs(int row, CurrencyManager manager)
{
this.Row = row;
this.Source = manager;
}
}
Here's an example project for you:
DataGridTest.zip

I haven't worked with CF, but I thought I would throw this out there... If you can access the cell, wouldn't the row be it's NamingContainer? If so you could drill up to the row and apply a style or add an attribute with a CSS class.

Related

Why can't I create an array of an object I created?

The objective is to create a spreadsheet kind of program such as Microsoft Excel. I have created a cell class which I want to create multiple instances of. Is the syntax incorrect or am I missing something logically?
What I did so far:
- Create a cell class.
- Initialized array, only to get an error.
public partial class form_welcomeScreen : Form
{
Label[] cellLetters = new Label[26];
Label[] cellNumbers = new Label[26];
Cell cell[] = new Cell[26];
char cellLetter = 'A';
int cellNumber = 1;
public form_welcomeScreen()
{
InitializeComponent();
}
private void Btn_newSheet_Click(object sender, EventArgs e)
{
for (int i = 0; i < 26; i++)
{
Cell cell = new Cell();
pnl_main.Controls.Add(cell.createCell());
cell.CellLetter = cellLetter;
cell.CellNumber = cellNumber;
cellLetter++;
cellNumber++;
}
}
}
class Cell : System.Windows.Forms.TextBox
{
private char cellLetter;
private int cellNumber;
private string cellID;
public char CellLetter
{
get { return cellLetter; }
set { cellLetter = value; }
}
public int CellNumber
{
get { return cellNumber; }
set { cellNumber = value; }
}
public string CellID
{
get { return cellID; }
set { cellID = CellLetter + Convert.ToString(CellNumber); }
}
public TextBox createCell()
{
TextBox cell = new TextBox();
cell.AcceptsReturn = true;
cell.Name = cellID;
cell.Size = new System.Drawing.Size(50, 25);
return cell;
}
I expect an array to be created so I can be able to create a whole spreadsheet of cells rather than just the one.
The line:
Cell cell[] = new Cell[26];
gives you mismatched types (cell vs cell[]). It should be:
Cell[] cell = new Cell[26];

How can I get the usercontrol to pass the values from the form it is initiated in to another one?

I am creating this simple video game ordering application. I have create an userControl named productControl. Code:
public partial class productControl : UserControl
{
private string pName;
private float pPrice;
private string pDesc;
private string pImgUrl;
public productControl() => InitializeComponent();
public string getName
{
get => pName;
set
{
pName = value;
productName.Text = pName;
}
}
public float getPrice
{
get => pPrice;
set
{
pPrice = value;
productPrice.Text = pPrice.ToString("c");
}
}
public string getDescription
{
get => pDesc;
set
{
pDesc = value;
descriptionText.Text = pDesc;
}
}
public string getImage
{
get => pImgUrl;
set
{
pImgUrl = value;
prodImage.Load(pImgUrl);
prodImage.SizeMode = PictureBoxSizeMode.StretchImage;
}
}
public int getProdId { get; set; }
private void buyButton_Click(object sender, EventArgs e)
{
MessageBox.Show(pPrice.ToString("c"));
orderConfirmation o = new orderConfirmation();
o.Show();
}
}
And a Windows Forms named accountMain. Which retrieves data from SQL and calls the productControl to fill it up.
private string productRetriever = "SELECT prodId, prodName, prodPrice, prodImg, prodDesc FROM [dbo].[products]";
private void accountMain_Load(object sender, EventArgs e)
{
createFunctions();
}
private void createFunctions()
{
connection.Open();
SqlCommand retProduct = new SqlCommand(productRetriever, connection);
panel1.Controls.Clear()
using (var reader = retProduct.ExecuteReader())
{
int count = 0;
while(reader.Read())
{
p[count] = new productControl();
p[count].Name = count.ToString();
p[count].getProdId = (int)reader[0];
p[count].getName = (string)reader[1];
p[count].getPrice = (float)reader[2];
p[count].getImage = (string)reader[3];
p[count].getDescription = (string)reader[4];
panel1.Controls.Add(p[count]);
count++;
}
}
connection.Close();
}
Above I am using a while loop to populate the panel1 with productControl inside of the accountMain Form. How can I get userControl to hide the accountForm and show another form (passing the values into the new form like title and price) when I click the "Buy" button which is inside the userControl.
Also I am new to implementing SQL into C# is it the correct way to populate the panel1 in accountMain using executeReader() or is there a better approach of retrieving data from SQL and showing it in userControl?

How to get an image to display in Picturbox from a Listbox selected item

Everything works fine until I actually call the SetPicture() Method. Any ideas as to why the image will not display when the selected item is changed. Right now i only have one image preset until i figure out my problem. Thanks for any help
namespace CelestialWindowsApp
{
public partial class Form1 : Form
{
public static List CelestialLibrary = new List(); private static CelestialBody celestialBody; private static string name; private static string description; private static string image; public static CelestialBody NewPlanet { get { return celestialBody; } set { celestialBody = value; } }
public Form1()
{
InitializeComponent();
PopulateLibrary();
}
public void PopulateLibrary()//Runs when the Form 1 is loaded. Adds preset item into the ListNox.
{
CelestialLibrary = new List<CelestialBody>();
CelestialBody cb1 = new CelestialBody("Earth", "Our Home", #"C:\Users\Cassidy\documents\visual studio 2015\Projects\CelestialWindowsApp\CelestialWindowsApp\ImageResources\earthimage.jpg");
CelestialBody cb2 = new CelestialBody("Mars", "4th planet from the sun.");
CelestialBody cb3 = new CelestialBody("Venus", "2nd planet from the son");
CelestialLibrary.Add(cb1);
CelestialLibrary.Add(cb3);
lbLibrary.DataSource = CelestialLibrary;
foreach (var p in CelestialLibrary)
{
lbLibrary.DisplayMember = "ShowBodies";
}
}
public void AddItem() // Adds the new Item to my library and then adds to and updates the ListBox;
{
CelestialBody cb;
name = txtboxName.Text;
description = txtboxDescription.Text;
image = txtboxImagePath.Text;
cb = new CelestialBody(name, description, image);
CelestialLibrary.Add(cb);
MessageBox.Show(name + " has been added to the Library.");
txtboxName.Text = null;
txtboxDescription.Text = null;
lbLibrary.DataSource = null;
lbLibrary.DataSource = CelestialLibrary;
lbLibrary.DisplayMember = "ShowBodies";
}
public void DisplayItemInfo()//Displays the current selected item information.
{
List<CelestialBody> bodyList = (List<CelestialBody>)lbLibrary.DataSource;
CelestialBody currentItem = bodyList[lbLibrary.SelectedIndex];
foreach (CelestialBody cb in bodyList)
{
if (currentItem.MyId == cb.MyId)
celestialBody = new CelestialBody(cb.Name, cb.Description, cb.ImagePath);
{
txtboxItemInfo.Text = celestialBody.Description;
SetPicture(celestialBody.ImagePath);
}
}
}
public void SetPicture(string image)
{
if (picboxBodyImage.Image != null)
{
picboxBodyImage.Image.Dispose();
}
picboxBodyImage.ImageLocation = image;
}
private void btnAddNewBody_Click(object sender, EventArgs e)
{
AddItem();
}
private void lbLibrary_SelectedIndexChanged(object sender, EventArgs e)
{
DisplayItemInfo();
}
}
}
Did you check the path in variable image. Assuming path is correct, Try below
picboxBodyImage.Image = Image.FromFile(image);

Make auto generated column readonly in DataGridView

I have a DataGridView whose DataSource is a DataTable with five columns. If I attempt to access a column's ReadOnly property, like so:
datagridview.Columns[1].ReadOnly = true;
It throws a NullReferenceExcpetion.
I understand this is due to how the framework manages its auto generated columns, as noted by the answer to this question.
My question is: How do I make a column(s) readonly when the data source is auto generated?
Can't really say why it's not working, but a simple test with this code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
dataGridView1.AutoGenerateColumns = true;
dataGridView1.DataSource = GenerateData();
dataGridView1.Columns[0].ReadOnly = true;
}
private List<DataSourceTest> GenerateData()
{
return new List<DataSourceTest>()
{
new DataSourceTest(1, "A"),
new DataSourceTest(2, "B"),
new DataSourceTest(3, "C"),
new DataSourceTest(4, "D"),
new DataSourceTest(5, "E"),
new DataSourceTest(6, "F"),
};
}
}
public class DataSourceTest
{
public DataSourceTest(int id, string name) { ID = id; Name = name; }
public int ID { get; set; }
public string Name { get; set; }
}
and making the gridview EditMode set to EditOnEnter so we can easily check if it's readonly or not, shows that it does the job well.
But if you still have issues, the best bet is to use an event, and the closest event for your question is the DataBindingComplete that will fire after the binding is done, so on that time, you will have full access to all your columns as they already bind to the gridview object.
double click on the event in the GridView control and add your readonly setter:
private void dataGridView1_DataBindingComplete(
object sender, DataGridViewBindingCompleteEventArgs e)
{
dataGridView1.Columns[0].ReadOnly = true;
}
In true TEK fashion, I figured out a solution to my own question:
To do this, you need to make use of the ColumnAdded event
datagridview.ColumnAdded += dataGridView_ColumnAdded;
Then in the event, you can check a column by name:
private void dataGridView_ColumnAdded(object sender, DataGridViewColumnEventArgs e)
{
if (e.Column is DataGridViewColumn)
{
DataGridViewColumn column = e.Column as DataGridViewColumn;
column.ReadOnly = true;
if (column.Name == "first_name")
{
column.ReadOnly = false;
}
}
}
Make column read-only when column has been generated
private void Form1_Load(object sender, EventArgs e)
{
List<Student> allStudent = new List<Student>();
for (int i = 0; i < 10; i++)
{
allStudent.Add(new Student { Name = "Student" + i, Roll = i + 1 });
}
dataGridView1.AutoGenerateColumns = true;
dataGridView1.DataSource = allStudent;
//Edited to show column count
MessageBox.Show("Column count is " + dataGridView1.Columns.Count);
foreach (DataGridViewColumn column in dataGridView1.Columns)
{
column.ReadOnly = true;
}
}
public partial class Student
{
public string Name { get; set; }
public int Roll { get; set; }
}

Event Handlers & delegates (Simple Question)

I have a simple application. Here's how it works. I have a class (MyForm) that inherits from Windows.Forms. It has a button, a label and a textbox. It looks like a chat window.
There's another class (Cliente) that takes an array of strings and it returns a List with a MyForm instance for each element in the array.
I have a third class (Prueba) that makes use of the previous two classes to test them. This class creates four instances of MyForm, and displays them. (I will omit some code and functionality because I know it works correctly.)
I need to be able to type something in one window and when click on the button, it should broadcast this message and display it in all the other windows.
I know I have to use event handlers and delegates, but after hours of looking at tutorials everywhere I can't figure out what to put where.
Would you please help me? If you can point me to a good tutorial or example it'd be enough, but if you can be more specific on my code, it'd be great.
(I can't figure out how to make one instance of MyForm be aware of the other instances, who should be the listener here? I was thinking that Client, but I can't see how to do it.)
Any help will be appreciated!
//MyForm
namespace Dia26 {
//public delegate void ChangedEventHandler(object sender, EventArgs e);
public class MyForm : System.Windows.Forms.Form {
public Button btn = new Button();
public TextBox textbox = new TextBox();
public Label label = new Label();
public Button btnEnviar = new Button();
public delegate void OwnerChangedEventHandler(string newOwner); //~
public event OwnerChangedEventHandler OwnerChanged;
protected void btn_Click(object sender, System.EventArgs e) {
this.Close();
}
protected void btnEnviar_Click(object sender, System.EventArgs e) {
label.Text += textbox.Text + "\n";
textbox.Text = "";
if (this.OwnerChanged != null) {
this.OwnerChanged("something?");
}
}
public MyForm() {
btn.Text = "cerrar";
btn.Left = 400;
btn.Top = 280;
btn.Click += new EventHandler(this.btn_Click);
btnEnviar.Click += new EventHandler(this.btnEnviar_Click);
textbox.Left = 15;
textbox.Top = 20;
textbox.Width = 330;
label.Left = 15;
label.Top = 50;
label.AutoSize = false;
label.Height = 210;
label.Width = 450;
label.BackColor = Color.White;
btnEnviar.Left = 350;
btnEnviar.Top = 17;
btnEnviar.Text = "Enviar";
this.Controls.Add(textbox);
this.Controls.Add(label);
this.Controls.Add(btn);
this.Controls.Add(btnEnviar);
this.SuspendLayout();
this.Name = "MyForm";
this.ResumeLayout(false);
return;
}
}
}
//Cliente.cs
namespace Dia26Prueba {
public class Cliente {
public int creadas;
public int nocreadas;
public List<MyForm> MostrarVentanas(out bool error, ref int creadas, params string[] nombres) {
List<MyForm> list = new List<MyForm>();
int bienCreadas = 0;
foreach (string str in nombres) {
if (str.Length >= 1) {
MyForm mf = new MyForm();
mf.Text = str;
//mf.OwnerChanged += new OwnerChangedEventHandler(mf_OwnerChanged);
list.Add(mf);
mf.Show();
bienCreadas++;
}
}
error = (bienCreadas == creadas);
nocreadas = bienCreadas - creadas;
creadas = bienCreadas;
return list;
}
public void ModificarPosicionYMedidas(MyForm mf, int x = 262, int y = 209, int width = 500, int height = 350) {
mf.Left = x;
mf.Top = y;
mf.Width = width;
mf.Height = height;
}
}
}
// Prueba
namespace Dia29 {
class Prueba {
static void Main(string[] args) {
Cliente cliente = new Cliente();
int n = 4;
Console.WriteLine(cliente.Autor);
if (args.Length != n) {
return;
}
int InstanciasCreadas = n;
bool HayErrores;
List<Dia26.MyForm> list;
list = cliente.MostrarVentanas(
creadas: ref InstanciasCreadas,
error: out HayErrores,
nombres: new string[] { "FirstWindow", "2nd", "3rd", "4th" });
cliente.ModificarPosicionYMedidas(list.ElementAt<MyForm>(0), 0, 0, 512, 384);
cliente.ModificarPosicionYMedidas(list.ElementAt<MyForm>(1), 512, 0, 512, 384);
cliente.ModificarPosicionYMedidas(list.ElementAt<MyForm>(2), 0, 384, 512, 384);
cliente.ModificarPosicionYMedidas(list.ElementAt<MyForm>(3), 512, 384, 512, 384);
for (int i = 0; i < n; i++) {
// .....
Application.Run(list.ElementAt<MyForm>(i));
}
Console.ReadLine();
}
}
}
Here is a small sample. I'm using a interface to remove the coupling between the MainWindow and the ChatWindows.
public class ChatEventArgs : EventArgs
{
public string ChatEventArgs(string message)
{
Message = message;
}
public string Message { get; private set; }
}
public interface IChatMessageProvider
{
event EventHandler<ChatEventArgs> MessageArrived;
void TriggerEvent(object source, ChatEventArgs args);
}
public class MainWindow : IChatMessageProvider
{
public event EventHandler<ChatEventArgs> MessageArrived = delegate{};
public void AddChatWindow()
{
ChatWindow window = new ChatWindow(this);
window.Show();
}
public void TriggerEvent(object source, ChatEventArgs args)
{
MessageArrived(source, args);
}
}
public class ChatWindow :
{
IChatMessageProvider _provider;
public ChatWindow(IChatMessageProvider provider)
{
_provider = provider;
provider.MessageArrived += OnMessage;
}
public void OnMesage(object source, ChatEventArgs args)
{
// since we could have sent the message
if (source == this)
return;
myListBox.Items.Add(args.Message);
}
public void SendButton_Click(object source, EventArgs e)
{
_provider.TriggerEvent(this, new ChatEventArgs(Textbox1.Text));
}
}
There are actualy multiple ways to do it.
Simply make method on Cliente and call it from Prueba. This is simplest and most intuitive solutoin.
Add event to Prueba, pass instance of Prueba to Cliente and let Cliente register to this event.
Use some kind of global static messenger class. Either using events, or simple message passing.

Categories

Resources