In my application i have two textboxes that are used to set the price of an entries for children and adults. Everytime the textboxes are changed, the event "textbox_ValueChanged" fires which executes "priceChanged()" and saves the content of my textboxes to a MySQL Database.
The columns that the prices are saved to are type double.
This whole thing works fine for typing integers into "adultPriceTextbox" (for example), but when trying to write a double or float into the textbox there are the following cases:
1. -> User types "5" then "," and then "5"
In this case the program crashes as soon as the second "5" is typed.
The line that crashes is cmd.ExecuteNonQuery(); which saves the value to the database (view code further below).
The error message looks like this
Data truncated for column 'adults' at row 1"
2. -> User types "5" then "." and then "5"
In this case nothing crashes, but the value being saved to the database does not contain the dot. So "5.5" would turn into "55".
This is the code that saves the values to the database. It is inside of a class called "DatabaseInterface":
public static void updatePrice(double _adults, double _children)
{
MySqlConnection cnn = OpenCnn();
MySqlCommand cmd = new MySqlCommand("UPDATE price SET adults = '" + _adults + "', children = '" + _children + "';", cnn);
cmd.ExecuteNonQuery();
}
And this is the code that executes "UpdatePrice":
private void priceChanged()
{
double adultPrice;
double childPrice;
try
{
adultPrice = Convert.ToDouble(adultPriceTextbox.Text);
}
catch
{
adultPrice = 0.0;
}
try
{
childPrice = Convert.ToDouble(childPriceTextbox.Text);
}
catch
{
childPrice = 0.0;
}
DatabaseInterface.updatePrice(adultPrice, childPrice);
}
Note that in this special case, there are two input windows. One that sets the price for children and the other one for adults.
Also my region is Germany, where decimals are written with "," instead of ".".
What would be the most elegant solution to achieve a Textbox where the user can type in integers and floats / doubles?
Addition: Ideas for blocking any alphabetical input into said textboxes are welcome as well, only numbers and "." / "," should be allowed.
Instead of binding to a text/string, just have a property on your binding object (MVVM pattern) that is the expected type of decimal, float or int for the respective type. Then, when you bind your text to that public get/set property, it will only store the value IF it qualifies for that data type, thus NOT applying the value to the property with bad values that cant be converted.
Additionally, instead of sending the data on every key stroke, you could change the binding to only submit after focus changes such as
<TextBox Text="{Binding YourIntegerField, UpdateSourceTrigger=LostFocus}"
Width="40" MaxLength="3" />
<TextBox Text="{Binding YourDecimalField, UpdateSourceTrigger=LostFocus}"
Width="70" MaxLength="6" />
The MaxLength is how many characters you actually allow. For a person's age, you probably cap at 3, but for pricing, your call.
Finally, by building out your strings with quoted values, you are open to SQL injection and should PARAMETERIZE your queries. Learn to do that early on, especially if dealing with actual text-based content and not numbers.
MVVM pattern is (M)odel, (V)iew, (VM)ViewModel. The model is the data where all stuff comes from and goes to for the database. The view is all the presentation stuff to your end-users, hence the Textbox, and all other inputs, buttons, etc. The ViewModel is the glue that ties the pieces together. You can find plenty of reading out there.
Now, the view, you got. The model where the data resides also exists. Your view model is basically the object that allows the back-and-forth exposure. Since you are not showing your actual view (more context of the .xaml and .xaml.cs, harder to add more specifics. But, lets say that your .xaml.cs is directly paired with your .xaml. In the constructor, if you set the DataContext to your .xaml.cs (or other actual object), that is what is being "bound" to. So it might look like
namespace YourApp
{
public partial class YourView
{
public YourView()
{
InitializeComponent();
// HERE, you are basically telling the view that anything with "BINDING"
// you want associated to this object.
DataContext = this;
}
// Now, any publicly prepared properties such as
// int, decimal, date, etc can be exposed as bindable in the view
private int _youngAge = 7;
public int YoungAge
{ get { return _youngAge; }
set { _youngAge = value;
DoSomethingOnceAssigned();
}
}
private int _olderAge = 83;
public int OlderAge
{ get { return _olderAge; }
set { _olderAge = value;
DoSomethingWithOlderAge();
}
}
// similar with any other properties
}
}
Now, in this scenario, since I defaulted the young and old ages, if you run the view and have the bindings to these public properties (not the private), the form SHOULD show those ages respectively. Now, if you edit the details while running the form and change focus to the next field such as by click or tab, it should hit the respective setters which you can break-point on and debug with.
See if that helps you get going some and let me know if anything else to assist.
To both cases, in the priceChanged ignores any not numeric characters, you can do as this, or using a ASCII table instead of regex, exclude the '.' or/and '.' from ignored characters.
Doing the first suggestion, i think that the crashes from the first case will not happen again.
For the second case, you the culture info as suggested Jaime and don't forget to change the ',' to '.' or the inverse before the conversion.
Related
I have a sealed class and in this sealed class I have a "public int shoebox128bit { get; } = 256 / 8;" that gets entered in a byte array. Now, what I'm trying to do is when the user selects "shoebox128bit". I also need to enter a different value in one another byte array. In essence, I need to double the size.
RNGCryptoServiceProvider().GetBytes(shoebox = new byte[shoebox128bit]); // 32
byte[] shoeboxTools = new byte[shoebox128bitDoubled]; // 64
I know this may seem a little confusing but all I'm trying to do is when you select one value that it places that value in a designated spot in the class. Then, that value is doubled and entered that doubled value in another spot in the same class.
User selects shoebox128bit.
Behind the scenes in the class this action happens:
"32" in the RNGCryptoServiceProvider
byte[] shoeboxTools = new byte[64];
Is there a way to achieve this goal?
Thank you for your assistance in advance.
So, if I understand correctly, your sealed class is just a collection of, basically, constants?
Is your example also in the sealed class, or somewhere else?
The first thing that comes to my mind is to expand the property to execute code when the getter is called.
Like this:
private int _shoebox128bit = 256/8;
public int shoebox128bit
{
get
{
shoebox128bitDoubled = _shoebox128bit * 2;
return _shoebox128bit;
}
}
This way each property can be in charge of the action that needs to happen on the related one.
Alternatively, you could make shoebox128bitDoubled into a kind of calculated property.
public int shoebox128bitDoubled => shoebox128bit * 2;
If the second property to get is always the same one, but with a different value based on the selection, you're probably better off with option 1.
I'm making a wild west duelling game based on typing of the dead. You have a word to write in a certain amount of time. You win if you type out the word in time, you lose if you type it incorrectly/press the wrong button or if the time runs out.
Currently I've got everything working fine. A slight issue, however, is with how I'm dealing with displaying the letters you have to type on the screen.
Each character is stored into an array that is looped through and displayed on the screen. When the player presses the correct button, the corresponding display should turn red which it does most of the time. The times where it doesn't is when there are duplicate characters.
For example if I was typing the word 'dentist', when I type the first t, it won't turn red. However, when I get to the second t and press it, both turn red. I assume this is because I'm looping through each displayed character and checking to see if it's relevant input is being pressed and because there's two and I can only type one character at a time one is always false which 'overrides' the one that is true. I'm not sure how to implement a solution with how I'm currently dealing input so any help is appreciated!
Code:
if (Duelling)
{
if (currentWord.Count > 0 && Input.inputString == currentWord[0].ToLower())
{
print(Input.inputString);
string pressedKey = currentWord[0];
currentWord.Remove(currentWord[0]);
}
else if (Input.inputString != "" && Input.inputString != currentWord[0].ToLower())
{
DuelLost();
}
if (currentWord.Count <= 0)
{
DuelWon();
}
foreach(Transform Keypad in keyDisplay.transform)
{
//print(Keypad.Find("KeyText").GetComponent<Text>().text);
Keypad.Find("KeyText").GetComponent<Text>().color = currentWord.Contains(Keypad.Find("KeyText").GetComponent<Text>().text) ? Color.black : Color.red;
}
}
I believe the issue lies in your colour-updating logic. Contains naturally returns true if your array, well, contains the text you're looking for. Since the second T in "dentist" is still present in the array after you type the first one in, the component isn't going to change its colour. When inputting the second T, all instances of Ts are cleared from the list, and since you loop over all of your Text components all the time, both of them will become red.
No offence, but you're going about this rather... crudely. Allow me to suggest a more elegant method:
public String currentWord;
private List<Text> letterViews = new List<Text>();
private int curIndex = 0;
void Start() {
// Populate the list of views ONCE, don't look for them every single time
letterViews = ... // How you do this is entirely up to you
}
void Update() {
// ...
if (Duelling) {
// If we've gone through the whole word, we're good
if (curIndex >= currentWord.Length) DuelWon();
// Now check input:
// Note that inputString, which I've never used before, is NOT a single character, but
// you're using only its first character; I'll do the same, as your solution seems to work.
if (Input.inputString[0] == currentWord[currentIndex]) {
// If the correct character was typed, make the label red and increment index
letterViews[currentIndex].color = Color.red;
currentIndex++;
}
else DuelLost();
}
}
I daresay that this is a much simpler solution. DuelWon and DuelLost shall reset the index to 0, clear the text in all letterViews and turn them back to black, perhaps.
How to populate the list of views: you can make it public and manually link them by hand through the inspector (boring), or you can do it iteratively using Transform.GetChild(index). You've probably got enough Text views to accommodate your longest words; I recommend filling the list up with them all. You only do it once, you lose no performance by doing so, and you can re-use them for any words in your dictionary.
When I initialize a combobox with text contents like so, where eo is some object with a ToString() override:
foreach (EncodingInfo ei in Encoding.GetEncodings()) {
Encoding e = ei.GetEncoding();
encodeObject eo = new encodeObject();
eo.Name = ei.DisplayName;
eo.Value = ei.Name;
int targetIndex = this.targetEncodingBox.Items.Add(eo);
}
I can set this to be the default value by using
this.targetEncodingBox.SelectedIndex = targetIndex
However, when the box is actually sorted, and the data initially entered into the box using the Add() method is not sorted, the default index is kept while the box is re-sorted, resulting in an entirely different value being selected almost all of the time.
A basic solution for this is to look up the generated value that the combobox would display and use FindStringExact:
this.targetEncodingBox.SelectedIndex = this.targetEncodingBox.FindStringExact("utf-8 -- Unicode (utf-8)");
However, this results in other problems. The string in question may depend on the user's operating system' language settings in this particular case. This can't be known beforehand.
Thus another way I've found is to manually find the name of the encoding a second time and set the SelectedIndex after the box is fully populated, using the same convention for concatenating the acronym name and translated name as used in the definition for encodeObject.ToString();.
foreach (EncodingInfo ei in Encoding.GetEncodings()) {
if (ei.Name == "utf-8") {
this.sourceEncodingBox.SelectedIndex = this.sourceEncodingBox.FindStringExact(ei.Name + " -- " + ei.DisplayName);
}
}
Note: the definition of the class encodeObject below:
private class encodeObject {
public string Name;
public string Value;
public override string ToString() {
return Value + " -- " + Name;
}
}
This actually works, and does exactly what I want, yet the solution seems quite clunky to do something that should really be a single call. Is there a better way of achieving this?
As Hans commented you need to create that list and store it to a variable.
Since the available encodings are unlikely to change anyway, this should happen in some class constructor or when you load your settings.
This variable then can be re-used anywhere you need it, it also can be easily updated & sorted as you like.
After this step the rest is trivial, create a variable with a default value/index, and once a ComboBox was assigned this list just set the SelectedValue/SelectedIndex value to your default value/index.
I have a winforms application that is connected to a database which contains a huge amount of measurement data of different datapoints. The data gets updated every few seconds, old values go to an archive table etc. I'm using EF6 for data access.
Now I need to build a GUI that offers some functionality as follows:
The user should be able to define conditions and/or expressions at runtime that then trigger some actions when true. An example in pseudo-code:
if ([Value of Datapoint 203] >= 45 and (TimeStamp of Datapoint 203 < "07:00am")
and (([Value of Datapoint 525] == 1]) or ([Value of Datapoint 22] < 0)])
then set [Value of Datapoint 1234] to ([Value of 203]/4) //or call a method alternatively
or an even simpler example in natural language (differs from the above):
if it is cold and raining, turn on machine XY
where cold and raining are values of certain datapoints and turn on machine is a method with a given parameter XY.
These expressions need to be saved and then evaluated in regular intervals of some minutes or hours. I did not face such a requirement before and I hardly know where to start. What would be the best practice? Is there maybe some sample code you know of? Or are there even controls or libraries for this?
Update: Breaking it down to something more specific:
Suppose I have a class like this:
class Datapoint
{
public int Id { get; set; }
public DateTime TimeStamp { get; set; }
public int Value { get; set; }
}
During runtime I have two objects of this type, DatapointA and DatapointB. I want to enter the following into a textbox and then click a button:
DatapointA.Value>5 && ( DatapointB.Value==2 || DatapointB.Value==7 )
Depending on the actual values of these objects, I want to evaluate this expression string and get a true or false. Is this possible?
I have multiple XAML TextBoxes, each of which manipulate a corresponding value in an array, when the value in the TextBox is changed, using a C# method which dynamically checks which TextBox has called the method.
<TextBox x:Name="_0_0" TextChanged="_x_y_TextChanged"/>
<TextBox x:Name="_0_1" TextChanged="_x_y_TextChanged"/>
<TextBox x:Name="_0_2" TextChanged="_x_y_TextChanged"/>
// And so on.....
each of which manipulate a corresponding value in an array, when the value in the TextBox is changed, using a C# method which dynamically checks which TextBox has called the method.
private void _x_y_TextChanged(object sender, TextChangedEventArgs e)
{
TextBox current = (TextBox)sender;
string currentname = current.Name;
string rowstring = currentname.Substring(1, 1);
string columnstring = currentname.Substring(3, 1);
int row = Convert.ToInt32(rowstring);
int column = Convert.ToInt32(columnstring);
// I've then detected the name of the textbox which has called it...
So this information can be used to dynamically store information from a TextBox in a corresponding array index - or whatever you want to do with it...
My question however, is:
How can I create a method which uses index locations in my array, to call the relevant TextBox and update its text?
Use FindName(string) to find the text box by name as follows (where container is a control that contains all of the text boxes):
private void UpdateTextBox(int row, int column, string text)
{
TextBox textBox = container.FindName("_" + row + "_" + column) as TextBox;
if(textbox != null)
{
textbox.Text = text;
}
}
There are two ways you might go:
If you have a lot of data to manage, or if you can't predict the length of the array, it would be better to bind to a collection instead of manually poking data into and out of an array. If you create a class derived from ObservableCollection instead of using an array the data <> ui relationship is pretty trivial.
if you really need to do this manually, maybe it would be better to stick the index into the 'tag' field of your text boxes. You could (a) see it clearly in your xaml, (b) parse it easily and (c) if you used a variation on the formula here:
Find all controls in WPF Window by type
you could iterate over the textboxes in window and find the right one by looking at its tag index:
foreach (TextBox t in FindVisualChildren<TextBox>(this))
{
if ((int) t.Tag) == my_index )
{
t.Text = "my_text_goes_here";
}
}
I would go in the direction of the answer I gave on this question:
form anchor/dock
In short, I would create a class that holds the actual values and then create a collection that holds information classes.
Then I would not use the event "TextChanged" on the TextBoxes, rather "sniff" for changes on the Dependency Property used to hold the text. This can easily be done in the Dependency Property.
Last, I would use an ItemsControl or ItemsPresenter to show the controls. Number of controls will follow number of items in the collection.
I suggest using MVVM pattern, data template, and ItemsControl for handling this problem effectively.