I'm using WinForms TreeView and reaction to AfterLabelEdit event. Here's the snippet of the code:
if (e.Label.Contains("|"))
{
if (WantAutofix())
{
label = e.Label.Replace('|', '_');
}
else
{
e.CancelEdit = true;
e.Node.BeginEdit();
return;
}
}
The problem is that when user doesn't want automatic fix of bad character, node doesn't stay in edit mode. Any way to fix this?
A few things to keep in mind:
The AfterLabelEdit event always ends edit mode after it is raised, even if you call BeginEdit in the middle of your event handler. You can use TreeView.BeginInvoke to "leapfrog" this by having EditMode start up again after the TreeView does its thing. (NOTE: this does not create a new thread or race condition, it simply delays the method for 1 window message.) There is more information on some of the issues with this event here (though it suggests what I think is a worse solution).
e.Label is null if the user didn't make any changes, so when we "leapfrog" with BeginInvoke, it is as if the user didn't make any changes, so we also need to handle that case.
BeginInvoke is an acceptable workaround in this case, you should find it to be very reliable in this situation.
This works very well for me, tested with .NET 2.0:
private void treeView1_AfterLabelEdit(object sender, NodeLabelEditEventArgs e)
{
//we have to handle both the first and future edits
if ((e.Label != null && e.Label.Contains("|") || (e.Label == null && e.Node.Text.Contains("|"))))
{
if (WantAutofix())
{
e.CancelEdit = true;
if(e.Label != null)
e.Node.Text = e.Label.Replace('|', '_');
else
e.Node.Text = e.Node.Text.Replace('|', '_');
}
else
{
//lets the treeview finish up its OnAfterLabelEdit method
treeView1.BeginInvoke(new MethodInvoker(delegate() { e.Node.BeginEdit(); }));
}
}
}
private bool WantAutofix()
{
return MessageBox.Show("You entered a |, you want me to AutoFix?", String.Empty, MessageBoxButtons.YesNo) == DialogResult.Yes;
}
Use EndEdit and Replace the "bad character" if user want automatic fix
You could try making the BeginEdit() occur asynchronously:
private void treeView1_AfterLabelEdit(object sender, NodeLabelEditEventArgs e)
{
if (e.Label.Contains("|"))
{
if (WantAutofix())
{
}
else
{
e.CancelEdit = true;
BeginInvoke(new ActionDelegate(new NodeBeginEditAsync(e.Node).Execute));
return;
}
}
}
public class NodeBeginEditAsync
{
private readonly TreeNode _node;
public NodeBeginEditAsync(TreeNode node)
{
_node = node;
}
public void Execute()
{
_node.BeginEdit();
}
}
public delegate void ActionDelegate();
That way the CancelEdit is given a chance to complete before a new BeginEdit tries to take over.
try this...
TreeNode node = tv.SelectedNode;
if (tv.SelectedNode.Parent == null)
{
node.TreeView.LabelEdit = false;
}
else
{
node.Text = FieldName.Text;
if (node == null) { return; }
node.TreeView.LabelEdit = true;
node.BeginEdit();
}
Related
I would like my application to to check if all 5 textboxes are numeric, and if the values are all true. Display the Talley. If not, I don't want my method to execute and right now it is. The way I have IsValid method coded, to me seems like it should work. I guess if someone could point me in the right direction, I'm sure there is a simple way of doing something such as this but I haven't found it. Thanks in advance to anyway that takes their time to look at this.
I have tried several variations of the example here, but haven't been able to do what it needs to do.
private void btnCalculate_Click(object sender, EventArgs e)
{
if (IsValid()) // if everything passes it will hit this
{
double total;
double hospitalCharge;
hospitalCharge = CalcStayCharges() * Convert.ToDouble(txtDays.Text);
total = hospitalCharge + CalcMiscCharges();
CalcTotalCharges(total);
lblDisplay.Text = "The total is " + CalcTotalCharges(total).ToString("c");
}
//else return;
// IsNumber(txtDays.Text.ToString()); // testing somehing
}
private double CalcStayCharges()
{
double hosipalCharge = 350;
return hosipalCharge;
}
private double CalcMiscCharges()
{
double Totalcharges;
Totalcharges = Convert.ToDouble(txtLab.Text) + Convert.ToDouble(txtMedication.Text) + Convert.ToDouble(txtRehab.Text) + Convert.ToDouble(txtSurgical.Text);
return Totalcharges;
}
private double CalcTotalCharges(double total)
{
return total;
}
private bool IsNumber(TextBox myNumber)
{
if (double.TryParse(Convert.ToString(myNumber), out double n))
{
// MessageBox.Show("string");
return false;
}
// MessageBox.Show("number"); // this is how you can test a bool value
return true;
}
private bool IsValid()
{
bool validation = false;
foreach (Control ctl in this.Controls)
{
if (ctl is TextBox)
{
TextBox tb = ctl as TextBox;
IsNumber(tb);
if (IsNumber(tb) == true)
{
validation = true;
}
else if (IsNumber(tb) == false)
{
validation = false;
}
}
}
return validation;
}
The problem is that your loop will override validation with whatever value it has last. So, as it stands, you code will only really validate the last text box.
The usual way to handle this is to make an assumption that it is valid. Loop through everything, and only set it if it is invalid. like this.
private bool IsValid()
{
bool validation = true;
foreach (Control ctl in this.Controls)
{
if (ctl is TextBox)
{
TextBox tb = ctl as TextBox;
IsNumber(tb);
if (IsNumber(tb) == false)
{
validation = false;
}
}
}
return validation;
}
Nick, please check this answer and you will understand.
Basically he is getting all controls from the Page of type DropDownList (in your case TextBox). And then he iterates each control and validates them. If you want something more complex, when iterating the list of controls, you can validate its properties and process the controls with the specified properties that you gave to them, when you declared them in the page.
I rephrased your IsValid statement. Please check it out
private bool IsValid()
{
bool validation = true;
foreach (Control ctl in this.Controls)
{
if (ctl is TextBox)
{
TextBox tb = ctl as TextBox;
if (IsNumber(tb) == false)
{
validation = false;
break;
}
}
}
return validation;
}
Improved version (basic syntax)
private bool IsValid()
{
bool validation = true;
foreach (TextBox tb in this.Controls.OfType<TextBox>())
{
if (!IsNumber(tb))
{
validation = false;
break;
}
}
return validation;
}
Another variation of IsValid. It uses a pattern matching if and plays logic games to get rid of the validation variable:
private bool IsValid()
{
foreach (var control in this.Controls)
{
if (control is TextBox textBox)
{
//the first test to fail cause the function to return false
//no need to test the rest of the controls
if (!IsNumber(textBox))
{
return false;
}
}
}
//nothing returned false, so it must be valid
return true;
}
Notice that true and false only appear in the return statements. You don't need to test Booleans for truthiness or falsehood, they are true or false all on their own. If you want to test a bool b for true-ness:
if (b) { /* code goes here */ }
of for false-ness
if (!b) { /* other code */ }
The other thing you might do if you have a lot of controls on the page and only a few of them are text boxes is keep a separate collection of textbox controls. You could have a List<TextBox> and populate it once at page load time.
Why for-each? :) use Linq (more declarative way) programming!
More of a condensed form and readable.
private bool IsValid()
{
return this.Controls.OfType<TextBox>().Where(t =>
IsNumber(t.Text)).ToList().Any();
}
You can use your IsNumber(...) to validate the text box values.
So you can use the validate method as below
if (!IsValid()) return;
Hope this helps.
Cheers!
In a Windows Forms application, i have a ComboBox element and need to run some code pieces periodically when ComboBox element changes.
The problem with code is, when ComboBox Text changes A to B or vice versa ,event handler triggers and previous while(true) loops are still running.
When event handler is triggered, needs to run one infinite loop.
private void comboBox_SelectedIndexChanged(object sender, EventArgs e)
{
string status = comboBox.SelectedItem.ToString();
Task.Run(async () =>
{
while (true)
{
// Running on ui thread
datagrid.BeginInvoke((MethodInvoker)delegate
{
status = comboBox.SelectedItem.ToString();
});
if (status == "A")
{
Work();
}
else if (status == "B")
{
anotherWork();
}
else if (status == "C")
{
someWork();
break;
}
await Task.Delay(3000);
}
});
}
Tried with ManualResetEvent, CancellationTokenSource, and bool check but none of them solved the problem.
What is the best practice to prevent repeated while loops?
Edit: Implemented timer already but wanted to while loop trick. I was doing bool check in a wrong way, Owen s answer is accepted.
Add a bool to check whether or not the loop is running:
private bool _loopRunning;
private void comboBox_SelectedIndexChanged(object sender, EventArgs e)
{
if(_loopRunning)
{
return;
}
_loopRunning = true;
string status = comboBox.SelectedItem.ToString();
Task.Run(async () =>
{
// stuff
_loopRunning = false;
});
}
This is the form code and every time I test some inputs, the application will open and then immediately close and I can't figure out what is causing it.
namespace Assignment2
{
public partial class IsmClassForm : Form
{
public IsmClassForm()
{
InitializeComponent();
}
private void IsmClassForm_Load(object sender, EventArgs e)
{
}
protected Student m_student;
protected Course m_course;
protected IsmClassForm m_next;
protected EnrollmentForm m_home;
public bool TestPrerequisites()
{
if (!m_student.Record.MeetsPrerequisites(m_course.Number))
{
MessageBox.Show("The registrar reports that you don't meet the prerequisites for " + m_course.Prefix + m_course.Number.ToString());
m_student.PridePoints = m_student.PridePoints - 5;
m_student.Record.Remove(m_course);
return false;
}
return true;
}
public string Description
{
get
{
return textDescription.Text;
}
set
{
textDescription.Text = value;
}
}
public string Title
{
get
{
return this.Text;
}
set
{
this.Text = value;
}
}
public string Welcome
{
get
{
return labelWelcome.Text;
}
set
{
labelWelcome.Text = value;
}
}
public bool Initialize(Student student, int course, IsmClassForm next, EnrollmentForm home)
{
if (student == null) return false;
m_student = student;
m_next = next;
m_home = home;
m_course = m_student.Record.FindEnrolled(course);
if (m_course == null)
{
return false;
}
labelCourse.Text = m_course.Prefix + "-" + m_course.Number.ToString();
return TestPrerequisites();
}
public enum DropMode
{
FreeDrop, PayDrop, Withdraw, NoDrop
};
DropMode mState = DropMode.FreeDrop;
public DropMode Drop
{
get
{
return mState;
}
set
{
mState = value;
UpdateDrop();
}
}
public void UpdateDrop()
{
switch (Drop)
{
case DropMode.FreeDrop:
buttonDrop.Text = "Drop";
break;
case DropMode.PayDrop:
buttonDrop.Text = "Drop";
break;
case DropMode.Withdraw:
buttonDrop.Text = "Withdraw";
break;
case DropMode.NoDrop:
buttonDrop.Text = "Done";
break;
}
}
protected void buttonDrop_Click(object sender, EventArgs e)
{
switch (Drop)
{
case DropMode.FreeDrop:
m_student.PridePoints = m_student.PridePoints - 5;
m_student.Record.Remove(m_course);
m_course = null;
break;
case DropMode.PayDrop:
m_student.PridePoints = m_student.PridePoints - 10;
m_student.WealthPoints = m_student.WealthPoints - 500;
m_student.Record.Remove(m_course);
m_course = null;
break;
case DropMode.Withdraw:
m_student.PridePoints = m_student.PridePoints - 50;
m_student.WealthPoints = m_student.WealthPoints - 500;
m_course.Grade = "W";
break;
case DropMode.NoDrop:
m_student.WealthPoints = m_student.WealthPoints - 500;
break;
}
Close();
}
protected void IsmClassForm_FormClosed(object sender, FormClosedEventArgs e)
{
if (e.CloseReason == CloseReason.UserClosing)
{
//The student not having a grade suggest the buttons were ignored
if (m_course != null && m_course.Grade == null)
{
m_course.Grade = "F";
m_student.PridePoints = m_student.PridePoints - 100;
m_student.WealthPoints = m_student.WealthPoints - 500;
}
if (m_next != null) m_next.Show();
else if (m_home != null) m_home.Show();
}
}
And here are some test inputs:
static void TestIsmClassForm()
{
Student tjt1 = new Student("Travis Todd");
tjt1.Record = new Transcript();
tjt1.Record.Add(new Course(1, 3113, "B", false));
tjt1.Record.Add(new Course(1, 3232, "C", false));
tjt1.Record.Add(new Course(2, 3113, "A", true));
tjt1.Record.Add(new Course(2, 3232, null, true));
tjt1.Record.Add(new Course(2, 4220, null, true));
IsmClassForm f4220 = new IsmClassForm();
IsmClassForm f3232 = new IsmClassForm();
IsmClassForm f4212 = new IsmClassForm();
f4212.Initialize(tjt1, 4212, f3232, null);
f3232.Initialize(tjt1, 3232, f4220, null);
f4220.Initialize(tjt1, 4220, null, null);
f4212.Show();
}
This does use some other classes in the project and their forms, but I the other functions all work and these is the only problem I have found so far. Am I missing something glaringly obvious?
Thank you,
Travis
You have two ways to achieve this;
Given your entry method:
public static void Main()
{
TestIsmClassForm();
}
You can use Application.Run or Form.ShowDialog:
static void TestIsmClassForm()
{
...All of your original code...
Application.Run(f4212.Show());
//OR
f4212.ShowDialog()
}
What is happening right now is that Form.Show is non-blocking - the application calls it, continues on out of the method, and closes.
Application.Run will show the form and wait until it closes before exiting the application. Form.ShowDialog() will block the calling method until the form is closed.
Application.Run is preferred because it guarantees the form used as the application host is marshalled in the "Main" or "GUI" thread. ShowDialog() makes no guarantee when run direct from Main()(Application.MessageLoop is false) and it is possible for some surprisingly annoying threading bugs to happen - so the majority of the time Application.Run is the best way to achieve what you are doing.
Make sure there is a Program.cs (that's the default name) file in your project. It should have void Main(string[] args) method, which will construct an instance of the form (namely, form1) and do Application.Run(form1).
Place breakpoints in IsmClassForm_Load and IsmClassForm_FormClosed to catch the moments of the form opening and closing.
The reason the form disappears is that the variable/object that contains the form goes out of scope and is disposed off at the end of your test block.
i.e. As soon as the 'code' reaches the closing '}' all of those form (and the Student) variables will be disposed of.
You could replace the .Show(), with .ShowDialog() which will cause the code to stop until the form is manually closed.
MSDN: ShowDialog
MSDN: Variable and Method Scope
I'm trying to understand what's happening here. I have a CheckedListBox which contains some ticked and some un-ticked items. I'm trying to find a way of determining the delta in the selection of controls. I've tried some cumbersome like this - but only works part of the time, I'm sure there's a more elegant solution. A maybe related problem is the myCheckBox_ItemCheck event fires on form load - before I have a chance to perform an ItemCheck. Here's what I have so far:
void clbProgs_ItemCheck(object sender, ItemCheckEventArgs e)
{
// i know its awful
System.Windows.Forms.CheckedListBox cb = (System.Windows.Forms.CheckedListBox)sender;
string sCurrent = e.CurrentValue.ToString();
int sIndex = e.Index;
AbstractLink lk = (AbstractLink)cb.Items[sIndex];
List<ILink> _links = clbProgs.DataSource as List<ILink>;
foreach (AbstractLink lkCurrent in _links)
{
if (!lkCurrent.IsActive)
{
if (!_groupValues.ContainsKey(lkCurrent.Linkid))
{
_groupValues.Add(lkCurrent.Linkid, lkCurrent);
}
}
}
if (_groupValues.ContainsKey(lk.Linkid))
{
AbstractLink lkDirty = (AbstractLink)lk.Clone();
CheckState newValue = (CheckState)e.NewValue;
if (newValue == CheckState.Checked)
{
lkDirty.IsActive = true;
}
else if (newValue == CheckState.Unchecked)
{
lkDirty.IsActive = false;
}
if (_dirtyGroups.ContainsKey(lk.Linkid))
{
_dirtyGroups[lk.Linkid] = lkDirty;
}
else
{
CheckState oldValue = (CheckState)e.NewValue;
if (oldValue == CheckState.Checked)
{
lkDirty.IsActive = true;
}
else if (oldValue == CheckState.Unchecked)
{
lkDirty.IsActive = false;
}
_dirtyGroups.Add(lk.Linkid, lk);
}
}
else
{
if (!lk.IsActive)
{
_dirtyGroups.Add(lk.Linkid, lk);
}
else
{
_groupValues.Add(lk.Linkid, lk);
}
}
}
Then onclick of a save button - I check whats changed before sending to database:
private void btSave_Click(object sender, EventArgs e)
{
List<AbstractLink> originalList = new List<AbstractLink>(_groupValues.Values);
List<AbstractLink> changedList = new List<AbstractLink>(_dirtyGroups.Values);
IEnumerable<AbstractLink> dupes = originalList.ToArray<AbstractLink>().Intersect(changedList.ToArray<AbstractLink>());
foreach (ILink t in dupes)
{
MessageBox.Show("Changed");
}
if (dupes.Count() == 0)
{
MessageBox.Show("No Change");
}
}
For further info. The definition of type AbstractLink uses:
public bool Equals(ILink other)
{
if (Object.ReferenceEquals(other, null)) return false;
if (Object.ReferenceEquals(this, other)) return true;
return IsActive.Equals(other.IsActive) && Linkid.Equals(other.Linkid);
}
There's little point that I see to do this in the ItemCheck event. Just calculate the delta when you save. Cuts out a bunch of code and trouble with spurious events.
I want to make an error label come up when my flowLayoutPanel is empty, but i don't know how to check that the flowLayoutPanel is empty. This is my current code:
private void flowLayoutPanel1_ControlRemoved(object sender, ControlEventArgs e)
{
if (flowLayoutPanel1.Controls == null)
{
customtoolwarning.Visible = true;
}
else
{
customtoolwarning.Visible = false;
}
}
Please Help,
Thanks
private void flowLayoutPanel1_ControlRemoved(object sender, ControlEventArgs e)
{
if (flowLayoutPanel1.Controls.Count > 0)
{
customtoolwarning.Visible = true;
}
else
{
customtoolwarning.Visible = false;
}
}
The problem you're running into is you're checking Controls for null to determine if it's empty. The Controls property won't ever be null but instead will be non-null and have 0 length when empty. For example
if (flowLayoutPanel1.Controls.Count == 0) {
// It's empty
}
lblNoContacts.Visible = (flowLayoutPanel.Controls.Count == 0) ? true : false;