I am trying to enable and disable line points on a chart with checkboxes.
However when I am clicking the checkboxes the x axis which are set to DateTime are changing! It is quite annoying. Here is an example of the pictures before and afterwards below.
This is the image after selecting the check box.
I would like to try and stop this from happening and add check boxes for each series so I can enable and disable the lines. Then the user can just view one at a time.
The code is below:
private void radCheckBox1_ToggleStateChanged(object sender, Telerik.WinControls.UI.StateChangedEventArgs args)
{
if (radCheckBox1.Checked == false)
{
chart1.Series["Series1"].Points.Clear();
}
else
{
for (int x = 0; x < HRM.Active.DataRows.Count; x++)
{
chart1.Series["Series1"].Points.AddXY(x,
HRM.Active.DataRows.ElementAt(x).Speed);
}
}
}
The simplest way to make Series disappear and come back again is like so:
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
Series sz = chart1.Series["Zeroes"];
sz.Enabled = checkBox1.Checked;
}
One more note on the code and the images you posted:
chart1.Series["Series1"].Points.AddXY(x, HRM.Active.DataRows.ElementAt(x).Speed);
is not guaranteed to add the X-Values as the dates they may be. Instead you might need to use this conversion:
chart1.Series["Series1"].Points.AddXY(x.ToOADate(),
HRM.Active.DataRows.ElementAt(x).Speed);
but not knowing anything about your actuall data, I can't be sure about this. But do keep in mind: DateTimes in Chart are not very intuitve, as the values will internally always be mapped to double.
But using the Enabled property you don't need all the messy clearing and re-adding any DataPoints anyway!
Related
I am having trouble making code for my progress bar, basically I have a datagridview that has to be filled by the user and I would like for my progressbar to increase only when the checkboxes from column 2 are checked (or to decrease when they are unchecked), I've tried the following code so far in the void norms_TableDataGridView_CurrentCellDirtyStateChanged
int cnt = this.norms_TableDataGridView.Rows.Count;
int i = 0;
string A = this.norms_TableDataGridView.Rows[i].Cells[2].Value.ToString(); //"Approved" Cells
for (i = 0; i < cnt; i++)
{
if (A == "True")
{
s_Checks++;
}
}
progressBar.Value = s_Checks * (progressBar.Maximum / TOTAL_CHECKBOXES);
This is making the progress bar increase at a higher rate than it should because, do to the for cycl,e it is counting more than one time the same checked checkbox. Any suggestion would be appreciated since my newbie idea is clearly failing
I am confident there are numerous ways to do this. Below is one possible way to manage the progress bar such that the “checked” check boxes in a grid’s column dictate the progress bars current value.
If no check boxes are checked, then the progress bar would be at zero. If all the check boxes are checked, then the progress bar would be full. If half the check boxes are checked, then the progress bar would show half filled.
It is unclear if the user is allowed to “add” rows. I will assume that the rows are fixed when the grid is loaded and the user is not able to “add” rows to the grid. Otherwise, the code would have to “update" the maximum value for the progress bar when the row is added.
A small trace of the current code...
The current posted code is a little odd and is doing a lot of unnecessary work. For starters, the for loop looks odd in a sense that this code is executed in the grids CurrentCellDirtyStateChanged event… So, this event fires every time a single cells value becomes dirty or is changed. So technically, only ONE (1) cell has changed. It seems odd to loop through all the check box values when only a single check box value may have changed.
I assume this is because the code is not keeping track of the progress bars current value. So, this value needs to be computed every time a single check box value changes. I am confident, there is an easier approach to change the progress bars value even without keeping track of its current value. I am thinking to add 1 to the progress bar when a check box is checked and subtract 1 when it is unchecked.
In addition, it is odd the way the current code is “checking” the value. There is a line before the for loop…
string A = this.norms_TableDataGridView.Rows[i].Cells[2].Value.ToString();
Then, in the for loop there is a “check” to see if A is “True”?
if (A == "True")
This is odd because A never changes in the loop. If it is true, then it will ALWAYS be true in the loop. Same for false. Therefore, s_Checks++ will always either be zero or equal to cnt when the for loop exits.
To fix this, you would need to “change” A to the next check box value with each iteration of the loop. However, even with this fix, the code is still checking values that have not changed since it was last checked.
Again, there may be an easier approach.
A different approach....
I am assuming that when the grid is loaded, that all the check boxes are NOT checked and the progress bar would start at zero. In addition, when the grid is loaded with data, the progress bars Maximum value will be set to the number of (fixed) rows in the grid.
With this approach, the code will “add” 1 to the progress bars value when a check box is changed from “unchecked” to “checked.” And, the code will “subtract” 1 from the progress bars value if a “checked” box is “unchecked.”
To avoid errors, and to keep it simple, the code simply checks to make sure that adding 1 to the progress bars value does not go over its Maximum value. In addition, a check to make sure that subtracting 1 does not go below the Minimum value.
So, this approach would go something like below. As soon as the grids data is loaded where all the check boxes are NOT checked, we would set the progress bars, min and max values. Something like…
private void Form2_Load(object sender, EventArgs e) {
dataGridView1.DataSource = YourDataSource;
progressBar1.Maximum = dataGridView1.Rows.Count;
progressBar1.Minimum = 0;
}
Instead of using the grids CurrentCellDirtyStateChanged event, I used the grids CellContentClick event.
One issue with using the CellContentClick event is that when it fires, the cells value has not changed yet. We could easily reverse the checking logic, however, to keep the code in a readable state, I will call the grids CommitEdit to update the changes in the grid.
In the event, the code checks if the check box is checked… in this example, the check box is in column 1.
(bool)dataGridView1.Rows[e.RowIndex].Cells[1].Value == true
If the check box went from “unchecked” to “checked,” then the code will add 1 to the progress bars value and subtract 1 from its value if the check box went from “checked” to “unchecked.”
private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e) {
if (e.ColumnIndex == 1) {
dataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit);
if ((bool)dataGridView1.Rows[e.RowIndex].Cells[1].Value == true) {
if (progressBar1.Value < progressBar1.Maximum) {
progressBar1.Value++;
}
}
else {
if (progressBar1.Value > 0) {
progressBar1.Value--;
}
}
}
}
I hope this makes sense.
Filipe,
I'm a little uncertain about your code, but normally I would handle your situation by doing the following:
using System.Linq;
var checkedRows = norms_TableDataGridView.Rows.Cast<DataGridViewRow>()
.Count(r => Convert.ToBoolean(r.Cells[2].Value));
double allRows = norms_TableDataGridView.Rows.Count;
progressBar.Value = Math.Round((checkedRows / allRows) * 100,0);
Whenever you are using windows forms, and you decide that you need to read or write directly to the cells of a DataGridView, you should sit down, take deep breaths, and think again if it is wise to directly access the cells instead of using databinding.
People hesitate to use DataBinding, because they think it is difficult, or they don't really know how to use it.
However if you use Databinding to display your data, you separate the data from how it is displayed. This enables you to change your display, without changing your data. You can reuse your data for other purposes, and You can unit test your data without the need of a DataGridView.
Apparently you have a DataGridView that shows rows of similar items. Let's say Reports. You'll need a class Report. I don't know much about the report, only that apparently it has something that can be displayed as checked/not checked: a Boolean property.
The other properties I just invented for the example.
public class Report
{
public int Id {get; set;}
public string Title {get; set;}
public string Author {get; set;}
public DateTime PublicationDate {get; set;}
public bool IsRead {get; set;} // will be a checkbox in the datagridview
}
Using visual studio designer you have added a DataGridView, and some columns. Maybe all columns, or maybe you are not interested in the PublicationDate, or the Author.
DataGridView reportView = new DataGridView();
DataGridViewColumn columnId = new DataGridViewColumn()
columnId.DataPropertyName = nameof(Report.Id);
DatagridViewColumn columnTitle = new DataGridViewColumn();
columnTitle.DataPropertyName = nameof(Report.Title);
DataGridViewCheckBoxColumn columnIsRead = new DataGridViewCheckBoxColumn();
columnIsRead.DataPropertyName = nameof(Report.IsRead);
// TODO: add the columns to reportView.Columns.
Now suppose you have a method to fetch the reports:
private IEnumerable<report> FetchReports()
{
...
}
Now all you have to do is assign the fetched reports to the DataSource:
private void DisplayReports()
{
this.reportView.DataSource = this.FetchReports();
}
And presto! All reports are in the DataGridView.
However, this is one direction. If the operator changes anything, then they are not reflected in the original data. If you want two way changes, you need to use an object that implement IBindingList, like BindingList.
Good practice is to add the following lines to your Form:
public BindingList<Report> DisplayedReports
{
get => (BindingList<Report>)this.reportView.DataSource;
set => this.reportView.DataSource = value;
}
public Report CurrentReport => (Report)this.reportView.CurrentRow?.DataBoundItem;
public IEnumerable<Report> SelectedReports => this.reportView.SelectedCells
.Cast<DataGridViewCell>()
.Select(cell => (Report)cell.OwningRow.DataBoundItem)
.Distinct();
The Distinct in the end is in case you have selected two cells in one row.
Usage:
private void Form_Load(object sender, ...)
{
// initialize the datagridview
this.DisplayedReports = new BindingList<Report>(this.FetchReports().ToList());
}
While the operator add / remove / changes displayed Reports, the BindingList is automatically update.
After a while the operator indicates that he finished editing:
private void ButtonOk_Clicked(object sender, ...)
{
ICollection<Report> reports = this.DisplayedReports;
// Todo: if desired: find out which reports are added / removed / changed
this.ProcessEditedReports(reports);
}
Note: because you separated the view from your data, the operator can rearrange columns, sort the rows etc, without you having to care about on what row the Report with Id 25 is shown.
I'm trying to find a a way to be able to essentially dynamically generate code based on an input.
For example I could type something like:
int Number = 22;
Button<Number>.Text = "X";
So in this case it would set button22 to have its text be an "X".
And I could change it so that I could input, for example 24 into the program and it would then set button24 to be an "X", instead of setting up a bunch of if statements to cover every potential button press.
For further context I have a Grid of 64 buttons and I need to be able to edit them individually to show to the user which buttons have been pressed, it is possible to do it with a lot of if statements but I thought it might be worth trying to find a more elegant solution.
You could have a list of buttons:
private List<Button> _buttons = new List<Button>();
Populate it like this:
for (int i = 0; i < 10; i++)
{
var b = new Button();
b.Text = $"Button #{i}";
b.Click += HandleButtonClick;
}
And you could even set an event handler on one of its events which doesn't even need to use the list (the sender is the source of the event):
private void HandleButtonClick(object sender, EventArgs e)
{
(sender as Button).Text = "X";
}
Buttons have a Tag property that can be used to hold arbitrary data about a button, this is described for WinForms, WPF and UWP.
Simple usage that is similar to OP's requirement is demonstrated in this SO post
This situation is in a practical sense the very reason that .Tag exists at all in user interface controls pretty much from the birth of c#.
So you do not need to use a custom class for a button, just simply assign your value to the .Tag property on the Button class that you are creating programmatically:
in this example a list is used to create the buttons and separate the creation from the layout, it is not necessary to do this, but may be useful. Instead, you could assign this button to it's parent container and/or set the layout margins or coordinates without keeping a reference to the Button object at all.
If OP updates the post to include implementation examples, we can update this response with more specific and complete code.
private List<Button> _buttons = new List<Button>();
// ... iteration or switching logic
var nextButton = new Button
{
Text = "x",
Tag = 22
};
nextButton.Click += DynamicButton_Click;
_buttons.Add(nextButton);
// ... later push the buttons into the parent container or bind to the UI
Then the button click handler you can access this Tag property:
this is presented from WinForms, the only difference in UWP or WPF is the method signature, change EventArgs to RoutedEventArgs
private void DynamicButton_Click(object sender, EventArgs e)
{
if(int.TryParse((sender as Button).Tag?.ToString(), out int buttonValue))
{
// use buttonValue
Console.Out.WriteLine(buttonValue);
}
else
{
// Otherwise, sender was not a button, or the button did not have an integer tag value
// either way, handle that error state here...
}
}
Using these concepts, once the buttons are created, let's say in some simple grid alignment, you could allow the user to set this Tag value at runtime if you have a TextBox (or other) input field that can be accessed from the code.
I recommend that you use MVVM style bindings for this rather than directly referencing a TextBox control, but this is simply to demonstrate the point.
private void DynamicButton_Click(object sender, EventArgs e)
{
// assign the string value from the ButtonValueTextbox control to this button
string value = this.ButtonValueTextBox.Text;
if(sender is Button button)
{
button.Tag = value;
}
else
{
// Otherwise, sender was not a button
// handle the error state here if you need to...
}
}
Now that each button has a tag, you could easily add logic to maintain unique tag values by iterating through the other buttons and clearing the tag if it was previously assigned to a different button.
Maybe you could keep a List of Button References:
var myButtons = new List<Button>();
myButtons.Add(firstButton);
myButtons.Add(secondButton);
// ... etc
// ... then somewhere else
int number = 3;
myButtons[number].Text = "xxx";
I'm trying to create a Connect 4 game using textboxes and buttons.
If you click on the button, the textbox background color is filled in and the text will either be filled with x or y.
private void button2_Click(object sender, EventArgs e)
{
Output_textBox.AppendText("You have inserted in Column 2");
Output_textBox.AppendText(Environment.NewLine);
TextBox[] boxes = { textBox21, textBox22, textBox23, textBox24, textBox25, textBox26 };
for (int i = 0; i < 6; i++)
{
if (boxes[i].BackColor != SystemColors.HotTrack)
{
boxes[i].BackColor = SystemColors.HotTrack;
boxes[i].Text = "Y";
}
}
}
(edited on 11/6/2013 2:25pm)
The Code above doesn't seem to work even though it seems to make perfect sense to me that it should.
What i want is for the textbox to turn one by one. So if i click on the button once. textbox11 changes first. Then if textbox11 is filled, textbox12 is filled next when i click on the button again etc.
The coloring needs to be from bottom to top in a column
About me: I'm new to coding. Sorry for the trouble
Thank you in advanced
I'm using a CheckedListBox control in a small application I'm working on. It's a nice control, but one thing bothers me; I can't set a property so that it only checks the item when I actually check the checkbox.
What's the best way to overcome this?
I've been thinking about getting the position of the mouseclick, relative from the left side of the checkbox. Which works partly, but if I would click on an empty space, close enough to the left the current selected item would still be checked. Any ideas regarding this?
I know this thread's a bit old, but I don't think it's a problem to offer another solution:
private void checkedListBox1_MouseClick(object sender, MouseEventArgs e)
{
if ((e.Button == MouseButtons.Left) & (e.X > 13))
{
this.checkedListBox1.SetItemChecked(this.checkedListBox1.SelectedIndex, !this.checkedListBox1.GetItemChecked(this.checkedListBox1.SelectedIndex));
}
}
(With the value of CheckOnClick = True).
You could use that thingy with the rectangle, but why make it more complex the it needs to.
Well, it is quite ugly, but you could calculate mouse hit coordinates against rectangles of items by hooking on CheckedListBox.MouseDown and CheckedListBox.ItemCheck like the following
/// <summary>
/// In order to control itemcheck changes (blinds double clicking, among other things)
/// </summary>
bool AuthorizeCheck { get; set; }
private void checkedListBox1_ItemCheck(object sender, ItemCheckEventArgs e)
{
if(!AuthorizeCheck)
e.NewValue = e.CurrentValue; //check state change was not through authorized actions
}
private void checkedListBox1_MouseDown(object sender, MouseEventArgs e)
{
Point loc = this.checkedListBox1.PointToClient(Cursor.Position);
for (int i = 0; i < this.checkedListBox1.Items.Count; i++)
{
Rectangle rec = this.checkedListBox1.GetItemRectangle(i);
rec.Width = 16; //checkbox itself has a default width of about 16 pixels
if (rec.Contains(loc))
{
AuthorizeCheck = true;
bool newValue = !this.checkedListBox1.GetItemChecked(i);
this.checkedListBox1.SetItemChecked(i, newValue);//check
AuthorizeCheck = false;
return;
}
}
}
Another solution is to simply use a Treeview.
Set CheckBoxes to true, ShowLines to false, and ShowPlusMinus to false and you have basically the same thing as a CheckedListBox. The items are only checked when the actual CheckBox is clicked.
The CheckedListBox is much more simplistic, but the TreeView offers a lot of options that can potentially be better suited for your program.
I succesfully used this property:
CheckedBoxList.CheckOnClick
https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.checkedlistbox.checkonclick?view=netframework-4.7.2
The text for a checkbox in a CheckedListBox is rendered by default is to place an HTML label after the checkbox input and set the label's "for" attribute to the ID of the checkbox.
When a label is denoting an element that it is "for," clicking on that label tells the browser to focus on that element, which is what you're seeing.
Two options are to render your own list with separate CheckBox controls and text (not as the Text property of the CheckBox, as that does the same thing as the CheckBoxList) if the list is static or to use something like a Repeater if the list is dynamic.
Try this. Declare iLastIndexClicked as a form-level int variable.
private void chklst_MouseClick(object sender, MouseEventArgs e)
{
Point p = chklst.PointToClient(MousePosition);
int i = chklst.IndexFromPoint(p);
if (p.X > 15) { return; } // Body click.
if (chklst.CheckedIndices.Contains(i)){ return; } // If already has focus click anywhere works right.
if (iLastIndexClicked == i) { return; } // native code will check/uncheck
chklst.SetItemChecked(i, true);
iLastIndexClicked = i;
}
Just checking to see if the user clicked in the leftmost 15 pixels of the checked list (the check box area) works at all times except re-checking a currently selected item. Storing the last index and exiting without changing lets the native code handle that properly, trying to set it to checked in that case turns it on and it just turns back off when the "ItemCheck" code runs.
I would like to add a backcolor for specific line depending of a Property of the object binded.
The solution I have (and it works) is to use the Event DataBindingComplete but I do not think it's the best solution.
Here is the event:
private void myGrid_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
{
for (int i = 0; i < this.myGrid.Rows.Count; i++)
{
if((this.myGrid.Rows[i].DataBoundItem as MyObject).Special)
{
this.myGrid.Rows[i].DefaultCellStyle.BackColor = Color.FromArgb(240, 128, 128);
}
}
}
Any other option that would be better?
You can also attach an event handler to RowPostPaint:
dataGridView1.RowPostPaint += OnRowPostPaint;
void OnRowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e)
{
MyObject value = (MyObject) dataGridView1.Rows[e.RowIndex].DataBoundItem;
DataGridViewCellStyle style = dataGridView1.Rows[e.RowIndex].DefaultCellStyle;
// Do whatever you want with style and value
....
}
I don't really work with WinForms that much, but in ASP you would use the 'ItemDataBound' method. Is there something similar in windows forms for a DataGrid?
If so, in that method, the event arguments would contain the item that was databound, along with the DataGrid row. So the general code would look something like this (syntax is probably off):
if(((MyObject)e.Item.DataItem).Special)
e.Item.DefaultCellStyle.BackColor = Color.FromArgb(240, 128, 128);
I would suggest a few things:
look at modifying your rows at _OnRowDatabound
Do not set color in your code!!! This would be a big mistake. Use the attributes property and set the cssclass. Wag of the finger to people still doing this.
Let me know if you struggle with the implementation and i'll post a snippet.