Error message in a property - wpf

This is my problem. If a user type a number in the textbox is all right, but if he type a char, I don't see the messagebox () in the property.
Why ?
<TextBox HorizontalAlignment="Left"
TabIndex="12"
Text="{Binding Time_HH, UpdateSourceTrigger=PropertyChanged,StringFormat='{}{##}'}"
FlowDirection="RightToLeft"
MaxLength ="2"
Height="30"
Width="30" />
And this is the property
Private _Time_HH As Integer
Public Property Time_HH() As Integer
Get
Return _Time_HH
End Get
Set(value As Integer)
For i = 0 To Len(value.ToString)
If IsNumeric(value.ToString(i)) = False Then
MessageBox.Show("Error")
value = 0
End If
Next
_Time_HH = value
OnPropertyChanged("Time_HH")
End Set
End Property

Your Time_HH property is an Integer, there's no way it's gonna contain a non-numeric character.
At most, what will happen is that your Binding will fail due to the type mismatch (is your TextBox showing a red outline?)
If you wanna check if your user enters non-numeric characters, you have to use a type that allows so: a String.
Try this:
Private _Time_HH As Integer
Public Property Time_HH() As String
Get
Return _Time_HH.ToString()
End Get
Set(value As String)
For i = 0 To Len(value)
If IsNumeric(value.ToString(i)) = False Then
MessageBox.Show("Error")
value = 0
End If
Next
_Time_HH = Integer.Parse(value)
OnPropertyChanged("Time_HH")
End Set
End Property
If you need to use the numeric value, use the Integer field. You could create a second property of type Integer that simply exposes the field, if you want to use it for another Binding or something like that (remember to raise the OnPropertyChanged for that property too, then, on the Time_HH setter)
CAUTION - The code above will raise an Exception if the user types something like "00,01-2,0". IsNumeric returns True for all the characters in that String, but that doesn't mean it is a correct number.
In my opinion, it would be better to do this:
Private _Time_HH As Integer
Public Property Time_HH() As String
Get
Return _Time_HH.ToString()
End Get
Set(value As String)
Dim int As Integer
If Integer.TryParse(value, int) = False Then
MessageBox.Show("Error")
End If
_Time_HH = int
OnPropertyChanged("Time_HH")
End Set
End Property
Sorry if I did some mistakes, I usually code in C# and my VB is pretty rusty :P

Related

How to declare variable for DataGridviewColumn Type?

This must be simple, but I couldn't figure it out even after reading Microsoft's specification.
How do I store DataGridViewColumn type in a declared variable? It has to provide a collection of types, similar to declaring variable for DataGridViewContentAlignment:
Dim GridAlignment as DataGridViewContentAlignment ' declare
GridAlignment = DataGridViewContentAlignment.MiddleCenter ' set
So it should be something like:
Dim GridColType1 as DataGridViewColumn ' declare variable (how?)
Dim GridColType2 as DataGridViewColumn ' declare variable (how?)
' To use variable like this:
GridColType1 = DataGridViewColumn.DataGridViewTextBoxColumn ' set it as TextBox column
GridColType2 = DataGridViewColumn.DataGridViewComboBoxColumn ' set it as ComboBox column
At this stage, no DataGridView exist, it's a definition to store for dynamic generation and I need the correct declaration in order to use it in PropertyGrid.
EDIT:
For another idea, here is what VS has in GUI when adding/editing DataGridView Column:
So it's the ColumnType declaration with the collection shown in the listbox, I'm looking for.
OK, it seems the best and easiest way is to define custom Enum.
Public Enum DataGridViewColumnType
NotSetError = 0
TextBoxColumn = 1
ImageColumn = 2
CheckBoxColumn = 3
ComboBoxColumn = 4
ButtonCOlumn = 5
End Enum
Then I can set a variable of that type:
Public GridColType As DataGridViewColumnType
Then it can be used in property:
<CategoryAttribute("Grid"), DefaultValueAttribute(""), DescriptionAttribute("Select column type"), DisplayName("Grid Column Type")>
Public Property PropGriColType() As DataGridViewColumnType
Get
Return GridColType
End Get
Set(ByVal Value As DataGridViewColumnType)
GridColType = Value
End Set
End Property
The result is then as desired:

Setting a string to be empty if value is null

I am not sure how to search for the issue I am trying to solve here. In the program I am writing (in VB.Net) I am trying to assign values pulled from a database to different variables in a structure.
Now my issue is that sometimes, some of the values pulled from the database are NULL, for example not every phone number has an extension. This is what I have for my code at the moment:
Structure CustomerContact
Public _name As String
Public _email As String
Public _fax As String
Public _phone1 As String
Public _phone2 As String
Public _phone3 As String
Public _ext1 As String
Public _ext2 As String
Public _ext3 As String
Public _type1 As String
Public _type2 As String
Public _type3 As String
End Structure
Dim contactData As DataTable = CustomerDBFunctions.GetCustomerContacts(Customer)
For Each row As DataRow In contactData.Rows
If contacts.Count < 1 Then
contacts.Add(New CustomerContact With {
._name = row.Item("FullName").ToString() & " (" & row.Item("ContactType").ToString() & ")",
._email = row.Item("Email").ToString(),
._fax = row.Item("Fax").ToString(),
._phone1 = row.Item("Phone").ToString(),
._ext1 = row.Item("Extension").ToString(),
._type1 = row.Item("PhoneType").ToString()})
End If
Next
Right now I am getting an error when the value in the database is NULL because it can't assign a NULL value to a string. I'd like to in the instances where a NULL value is present instead set the value of the variable to "" instead. I am just unsure how to code this.
Technically, the problem isn't that the column is null. String is a reference type, so it can but null (though, if it was null, you wouldn't be able to call ToString on it anyway). What's actually going on is that ADO.NET always returns DBNull.Value for all columns where the row contains a null value.
You could check it, like this:
If row.Item("Phone") <> DBNull.Value Then
customerContact._phone1 = row.Item("Phone")?.ToString()
End If
Note, I used ?. instead of . when calling ToString just in case the column actually is null rather than DbNull.Value. The reason for this is that I don't know what kind of code you're using to fill that DataTable. If it's ADO.NET that's filling it, it'll never be null, but if it's custom code that populates it via some other means, it might get actual nulls in it.
Since you are using a DataTable to return the value, it has a convenient IsNull method that you can use, which cleans up the code a little bit:
If Not row.IsNull("Phone") Then
customerContact._phone1 = row.Item("Phone")?.ToString()
End If
Obviously, if you're doing this a lot, it would be good to wrap it up into a reusable function. Also, if you want to shorten it up into one line, you could do so like this:
._phone1 = If(row.IsNull("Phone"), row.Item("Phone")?.ToString(), Nothing)
String concatenation can be used to convert Nothing to "" (other alternatives in my answer here)
._fax = row!Fax & "",

TextBox not updating properly when decimal point is input

I'm having some strange behavior that I can't quite figure out. I have a text box bound to a string property (that represents a numeric value). The getter should be formatting my string to include 4 decimal digits. This much works but gives the user non-intuitive surprise when entering a number with decimal values.
Example: If the user wanted to enter 5.1, they type 5 -> . -> 1 resulting in the TextBox formatting to
"5.0000"->"5..0000"->"5.1.0000"
So I added additional logic to replace any sequential decimal points with a single decimal point. This is where my strange issue have started appearing. A user can enter "5.....1" and the UI does not seem to update with the removal of the sequential decimal points. However typing any character between those decimal points triggers it to correctly remove sequential decimal points:
Example: Typing a 2 before the last decimal point ("5....2.1") results
in "5.2.1", which is what I expect.
If I step trough the code, the returned value always seems to be correctly formatted with no sequential decimal points. However the UI still displays "5.....1". The UI doesn't seem to update if I edit the decimal points directly (either typing more or deleting existing ones), however typing or deleting a character in between them triggers the expected update.
This is the property in the ViewModel:
Private _valueString As String
Public Property ValueString As String
Get
Dim formattedString As String = Double.NaN.ToString
Dim tempDouble As Double
Dim rgx As New Regex("\.+")
If Double.TryParse(_ValueString, tempDouble) Then
formattedString = tempDouble.ToString("F4")
formattedString = rgx.Replace(formattedString, ".")
Else
formattedString = rgx.Replace(_valueString, ".")
End If
Return formattedString
End Get
Set(value As String)
Dim rgx As New Regex("\.+")
_ValueString = rgx.Replace(value, ".")
NotifyPropertyChanged("ValueString")
End Set
End Property
XAML:
<TextBox Text="{Binding ValueString, UpdateSourceTrigger=PropertyChanged}" />
Any ideas on what is causing it to not update? Why are users able to enter multiple decimal points in a row?
Try using Text="{Binding ValueString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Apart from that I think you should just bind the Textbox to a double Property and use a validator so that input thats unable to parse as double is not set at all.
I would set your updatesourcetrigger to LostFocus (default) and set up the following in the textbox's textchanged event. This will allow you to continue entering numbers after the decimal place. If there is no decimal place on the end it will update just like if you had set the trigger to PropertyChanged.
The update source function is manually doing what the trigger would fire.
For the sake of this example my textbox is called txbAmount.
VB
Private Sub txbAmount_TextChanged(sender As Object, e As TextChangedEventArgs)
If Me.txbAmount.Text IsNot Nothing Then
If Me.txbAmount.Text.Count() > 0 Then
If Me.txbAmount.Text.Last() <> "."C Then
Me.txbAmount.GetBindingExpression(TextBox.TextProperty).UpdateSource()
End If
End If
End If
End Sub
C#
private void txbAmount_TextChanged(object sender, TextChangedEventArgs e)
{
if (this.txbAmount.Text != null)
{
if (this.txbAmount.Text.Count() > 0)
{
if (this.txbAmount.Text.Last() != '.')
{
this.txbAmount.GetBindingExpression(TextBox.TextProperty).UpdateSource();
}
}
}
}

Creating Property Get/Let procedures for a private array

I am trying to assign a number of values to a private array in a class module using public Property Get/Let procedures. But when I try to get the values out, the array is showing as empty. Why is this?
Here's the relevant code:
Private pdt_RentStepDate(23) As Date
Public Property Get dt_RentStepDate(ByRef d1 As Integer) As Date
dt_RentStepDate(d1) = pdt_RentStepDate(d1)
End Property
Public Property Let dt_RentStepDate(ByRef d1 As Integer, something As Date)
pdt_RentStepDate(d1) = something
End Property
In your Property Get procedure, don't specify an index when you're assigning the return value:
Public Property Get dt_RentStepDate(ByRef d1 As Integer) As Date
' dt_RentStepDate(d1) = pdt_RentStepDate(d1) ' <-- WRONG
dt_RentStepDate = pdt_RentStepDate(d1) ' <-- RIGHT
End Property
If you specify an index, then instead of assigning the return value for the Property Get, you're actually calling the Property Let with the arguments d1 and pdt_RentStepDate(d1). (This has no effect because the Property Let ends up setting pdt_RentStepDate(d1) to itself.) Since no return value is assigned, the Property Get always returns an empty Date.

ReadOnly Property Gets "Unintentional" Change

I understand that List, Array, Object etc. types "copied" by reference. However my natural and ordinary intend is to just have a "copy" of it in this context where I intentionally use ReadOnly instead of Read/Write property. In below sample the ReadOnly 'Extensions' property get change through 'm_extensions' reference change. Regardless, I think this behavior is incorrect and I have to do extra work to prevent ReadOnly properties from being overwritten. Is there any built in keyword to use for 'm_extensions' value protection?
Public Classs A
' more properties and methods here...
Private m_extensions() As String = {"*.abc", "*.def"}
Public ReadOnly Property Extensions() As String()
Get
Return m_extensions
End Get
End Property
End Class
Public Classs B
' more stuff here...
Private Function BuildFilter() As String
Dim l() As String = A.Extensions
Dim s As String = String.Empty
For m As Integer = 0 To l.Length - 1
Select Case l(m).ToLower
Case "*.*" : s = "All Files"
Case "*.abc" : s = "ABC File"
Case "*.def" : s = "DEF File"
Case Else : s = "XYZ File " + m.ToString
End Select
l(m) = String.Format("{1} ({0})|{0}", l(m), s)
Next
Return String.Join("|", l)
End Function
End Class
Readonly modifier means that anything using the property cannot change the reference that you protected this way (e.g. cannot set it to Nothing). It doesn't prevent changing the values in the array returned from that property.
One way around it could be to copy the array inside the property. This will prevent modifications of the original array:
Public ReadOnly Property Extensions() As String()
Get
Return m_extensions.Clone()
End Get
End Property

Resources