So in my project I have a few textBoxes that hold the coordinates of two corners (Latitude & Longitude). The textBoxes are updated by a timer (which gets a value from a server and sets the textBoxes if the value received is different from the current value). Problem is, I want the textBoxes to be available for manual editing; However if I'm in the middle of typing the number and the timer checks the current value he sees that it's a different thing from what the server returned and changes it immediately. Is there a way to check if the textBox is being edited at the moment, or a better way to solve this solution?
code (samples, the code is the same for the two corners):
if (northEastLatitude != double.Parse(neLatTB.Text)) //neLatTB is the textBox
neLatTB.Text = northEastLatitude.ToString();
else //No answer returned from the server so we need to reset the textBoxes
{
northEastLatitude = 0;
northEastLongitude = 0;
if(neLatTB.Text != "0")
neLatTB.Text = northEastLatitude.ToString();
if(neLngTB.Text != "0")
neLngTB.Text = northEastLongitude.ToString();
}
in addition, I have functions for TextChanged events for all of the textBoxes (so that when I set the coordinates manually it uploads them to the server). Is there any way to prevent this function from being called whenever I press the dot key? apparently it calls the event too (marks the ending of the text entering).
It really depends on your design but if you want to use TextBox to show updatable values and also make it possible to edit you have to suppress code from your timer to execute. WinForms TextBox doesn't have an option to show you if text is changing programmatically or by user interaction. You have to somehow make it by yourself.
There is plenty of ways to do that of courde. One way is to use Enter/Leave events to detect when your TextBox gets or loses focus. But there will be a need to click somwhere out from the control after edit.
Another one, and probably desired by you would be using TextChanged event preventing your timer from updating field until text in TextBox will be typed in full. I would do it something like that:
Fisrtly I would declare two bool variables for blocking parts of code from execution:
private bool _isDirty; // used when user types text directly
private bool _suppresTextChanged; // used when timer updates value programmatically
After that I would write TextBox.TextChanged event listener:
private void neLatTBTextChanged(object sender, EventArgs args)
{
if(_suppressTextChanged)
return;
_isDirty = true; // toggle dirty state
if(/* text has good format */)
{
// Upload changes to server
_isDirty = false; // end manual edit mode
}
}
And inside timer method i would set:
_suppresTextChanged = true; // on the beginning
if (northEastLatitude != double.Parse(neLatTB.Text)) //neLatTB is the textBox
neLatTB.Text = northEastLatitude.ToString();
else //No answer returned from the server so we need to reset the textBoxes
{
northEastLatitude = 0;
northEastLongitude = 0;
if(neLatTB.Text != "0")
neLatTB.Text = northEastLatitude.ToString();
if(neLngTB.Text != "0")
neLngTB.Text = northEastLongitude.ToString();
}
_suppresTextChanged = false; // after edit was made
Personally i think this design can lead to a lot of problems (consider what to do when user stops typing and leave the TextBox in _isDirty state etc...). Instead of using just TextBox I would add a Label to store data from timer (and probably data the user will type) and left TextBox just for entering user specific values.
Related
I googled a few things before posting, but I couldn't find anything like this. Basically, I want to take text from a textbox, save as a variable (say history1) to then be able to call that in the future to display the text. I can do that, but what I'm stuck with is that I want 3 variables (history1, history2 and history3, for example) and each time the button is pressed the string is moved to the next variable.
For example, the button is pressed, the text is saved as variable history1. The text is changed and the button is pressed again, the text from history1 is moved to variable history2, and the new text is saved as history1. This would only need to work for 3 instances though, not infinitely, so when text is stored in history3 and the button is pressed the text is just overwritten.
The way I had thought of approaching this was:
string history1;
string history2;
string history3;
for (int i = 1; i < 4; i++)
{
history1 = txtOutput.Text;
btnToFile_Click()
{
history2=history1;
btnToFile_Click()
{
history3=history2;
}
}
}
However, this isn't going to work because the btnToFile_Click doesn't take any arguements. Is there an easier way to go about this or just a way to fix the method not taking arguements ?
Thanks in advance!
Make sure that you delcare history1, history2, and history3 on the form level (not inside any method).
Then, have the following code inside the handler of the click event of the button:
history3 = history2;
history2 = history1;
history1 = txtOutput.Text;
You don't need to call the btnToFile_Click() method multiple times in your loop, just move the text from end textbox to another in reverse order. Nor do you need a loop because you only have three textboxes.
Why reverse order? So you move the value to the next textbox before it is overwritten by the new value.
So:
history3 = history2;
history2 = history1;
history1 = txtOutput.Text;
btnToFile_Click() is a Click event handler for btnToFile (a button). You're not supposed to call that method yourself, it's called by the UI framework (say WPF or WinForms etc.). By the way, it does receive a parameter, then event source (since you can assign the same event handler to multiple buttons and do something based on which one sent the event)
You can try saving in a string array and move the strings within it when you call the button clicked event
Currently I have some buttons on my Winform that need to be disabled/enabled at various points depending what the user clicks.
The first draft I made was
button1.Enabled = false;
button2.Enabled = false;
To disable 2 buttons, which is obviously a horrible way of doing this, as there are currently a lot more than 2 and possibly more to come as this is still in development. So I need to have a way of easily changing a selection of buttons on the form.
Then I came up with this
private enum Buttons { Button1, Button2 } // etc with all buttons - that are named :)
private void DisableButtons(params Buttons[] buttons)
{
foreach (Buttons button in buttons)
{
switch (button)
{
case Buttons.Button1:
button1.Enabled = false;
break;
case Buttons.Button2:
button2.Enabled = false;
break;
}
}
}
Which I still wasn't overly happy with. I could scrap the switch-case and foreach for
private void DisableButtons(params Buttons[] buttons)
{
button1.Enabled = buttons.Contains(Buttons.Button1) ? false : true;
}
for each button but I just think there must be a better way.
Any ideas on how I could do this more efficiently?
Thanks
You can shorten your last code line to:
button1.Enabled = !buttons.Contains(Buttons.Button1);
Alternative solution
Or you can use the Tag property of each button to set a enum value for each button.
button1.Tag = Buttons.Button1;
button2.Tag = Buttons.Button2;
button3.Tag = Buttons.Button3;
// etc
Than you can do it for all buttons in a for loop:
var buttons = <all buttons, todo>
foreach (var button in buttons) {
button.Enabled = !button.Contains((Buttons)button.Tag));
}
I would suggest that you don't infact want to make a function that flexibly enables and disables any combination of buttons because you don't yet percieve how your Form will work. Granted, this may save you a few lines of code but, it won't impart any contextual information to the next developer that maintians your code. Neither will it run faster than directly setting the state of the controls.
I woud make a single function that is called whenever your Form changes state, that takes all possible parameters that pertain to your Forms state. Then I would decode those parameters and explicitly setup the state of the controls on the form, by name, in a single pass, using tranditional switch and if statments.
This central function will make it clear to you and future developers how the state of you form changes and how controls are expected to behave. It won't slow down the performance of you code with an unecessary level of abstraction.
Is there a way to make a control dependent on another control? I have a combo box and a button, and I need the button to be enabled if and only if there is an item selected in the combo box.
I know I can set the Enabled property of the button inside the SelectedIndexChanged callback, but then it will require some code, and besides there's an issue with what initial state the button would have. So I'm looking for something that wouldn't require manually handing events, is this possible?
Thanks!
No, there is no way in winforms to do this without code. What I usually do is to collect all such state-setting code into one specific method:
private void SetControlStates()
{
theButton.Enabled = theComboBox.SelectedIndex >= 0;
// code for other controls follow here
}
Then I trigger this method from all over the place, as soon as there is an interaction that may lead to the state changing (including the last thing I do when the form has finished loading; that takes care of initial state). If you want to avoid unnecessary assignments, just add code to check the value first:
private void SetControlStates()
{
bool buttonEnabled = theComboBox.SelectedIndex >= 0;
if (theButton.Enabled != buttonEnabled) theButton.Enabled = buttonEnabled;
// code for other controls follow here
}
Woot, first Stack Overflow post! I've been asked to work on a desktop application to improve an inventory process for my company. I dabbled with WPF in school and I figured I'd start there. After researching some, I learned about MVVM, put a design together, and forged ahead. Finally, I'm stuck and looking for some help and also a sanity check to see if I'm on the right path.
I have single-column DataGrid bound to an observable collection. Users of the application use a scan gun to enter values in. One potential value that I catch in my "Cell" model object is a "MoveNextColumn" value. This raises a custom event in my model that is handled in the View Model. The handler is supposed to simulate blank entries for all remaining rows in that column, set focus on the last row, and wait for input before moving on. So here is what I have so far:
private void dummyCell_MoveToNextColumn(object sender, RoutedEventArgs e) {
e.Handled = true;
// Cell is the model object containing the parsing rules and raising events
var lSender = sender as Cell;
var gridItems = ViewGridReference.Items;
var lastItem = gridItems[gridItems.Count - 1];
if (lSender == lastItem) {
// We are at the bottom of the column
// Move the program on to the next column
CurrentColumn++;
OnPropertyChanged("ItemPositions");
} else {
// Simulate "empty position" input for this cell and all cells down the column
// Cells are validating themselves as the simulation progresses
foreach (Cell item in ViewGridReference.Items) {
item.ActualItemCode = string.Empty;
}
// ViewGridReference is a reference to my DataGrid set from the view
ViewGridReference.Focus();
ViewGridReference.SelectedIndex = gridItems.Count - 1;
ViewGridReference.CurrentCell = new DataGridCellInfo(lastItem, ViewGridReference.Columns[0]);
((DataGridCell)ViewGridReference.SelectedItem).Focus();
}
}
All of this seems to be working as expected: all rows receive blank input and are validated (I use color properties in the cell to which the view binds to signify the validity of the entry).
Unfortunately, though the focus is on the last row as desired, it is not editable and the user cannot submit another "MoveNextColumn" value which would move the program on. The goal here is to minimize any keyboard interaction. Everything should be done with scan guns and barcodes.
Any ideas on how to make the selected cell editable after this code executes?
Any "hey, your design sucks" feedback would be cool too. This is new to me and I'm open to constructive criticism.
I have made some progress with this. The entire grid was left at an uneditable state in the code above. This now leaves focus on the last cell in my column and allows me to submit input with the scan gun.
This seems to work, but I'd still appreciate some feedback on whether there is a better way.
private void dummyCell_MoveToNextColumn(object sender, RoutedEventArgs e) {
e.Handled = true;
// Cell is the model object containing the parsing rules and raising events
var lSender = sender as Cell;
var gridItems = ViewGridReference.Items;
var lastItem = gridItems[gridItems.Count - 1];
if (lSender == lastItem) {
// We are at the bottom of the column
// Move the program on to the next column
CurrentColumn++;
OnPropertyChanged("ItemPositions");
} else {
// Simulate "empty position" input for this cell and all cells down the column
// Cells are validating themselves as the simulation progresses
foreach (Cell item in ViewGridReference.Items) {
item.ActualItemCode = string.Empty;
}
ViewGridReference.SelectedIndex = gridItems.Count - 1;
ViewGridReference.CurrentCell = new DataGridCellInfo(lastItem, ViewGridReference.Columns[0]);
(ViewGridReference.ItemsSource as ListCollectionView).EditItem(ViewGridReference.SelectedItem);
((DataGridCell)ViewGridReference.SelectedItem).Focus();
}
}
Updated 12/2/2010
Hey, there is an important update to this. The first thing to note is that text entry is being done with a scan gun in my scenario, so 'Enter' keys are sent down with each pull of the trigger. It shoots down each character followed by the Enter key all at once.
WPF sees this enter and wants to set the focus to the DataGridCell directly beneath the cell in which the Enter key input was received. The code above sets the focus to the last cell, but then the Enter key event still fires and is handled by DataGrid after this code is run. The effect is that the focus is reset back to the subsequent cell, not the last cell like I want.
So I need to either figure out how to eat the Enter key for just that scan, or I need to break how WPF handles Enter keys. The last line up there actually throws an exception. We are trying to use a Model class (Class.cs) as a DataGridCell, and there is nothing to handle that cast. Because of that, the Focus() method tries to operate on a null object and we get a NullReferenceException. This was really confusing me because Visual Studio 2010 would sometimes break to alert me about this, but sometimes it wouldn't. However, if I run the executable outside of Visual Studio, it works just fine. That's because unhandled, non-fatal exceptions are ignored and the Enter key behavior fails to operate as normal.
So it works, but in a pretty gross way. I either need to figure out how to do one-time handling of the Enter key and override the default WPF handler, or just leave it like it is and grimace.
I have a text box that is going to be populated with a comma spereated list that is driven by a CheckedListBox control.
The idea is that as the user checks items off in the list, they will appear in the text field above. I have this working to the point where if I check an item and then click somewhere else inside the control then the text ends up in the textbox. I am capturing the click event on my control.
If I use the item_checked event then the list in the text box isn't updated until I check a second item (at which point in time only the first item that was checked is displayed in the text box.) Is there anyway around this? Reading on MSDN doesn't seem to show any other events that would be applicable.
I'm using .net 1.1.
This is the method that is run on the event trap.
Private Sub FillCheckedTagsTextBox()
txtSelectedTags.Text = ""
Dim tagChecked As Object
For Each tagChecked In cltTagSelection.CheckedItems
txtSelectedTags.Text = txtSelectedTags.Text + tagChecked.ToString() + ", "
Next
End Sub
Thanks,
Mike
Ouch 1.1? Is your employer trying to kill you? I'd try to push up to 2.0 if I could.
To double check when you say the "Checked" event do you mean CheckedChanged? In 2.00 this works fine on desktop. Is it a bug in 1.1?
If it is a bug (check your own code first before deciding this! Then check it again!) then I can suggest trying to capture the Leave event which occurs when a control loses focus. Failing this you could databind a business object to the .Checked property and then fire your own event when your value changes. E.G.
public class MyValues
{
private bool _check;
public bool Check
{
get
{
return _check;
}
set
{
if(_check != value)
{
_check = value;
// todo: raise event!
}
}
}
}