Iterate Through Devexpres TextEdit Controls in VB.NET - vb.net

Could someone help iterating through DevExpress TextEdit controls within an XTRAFORM in vb.net?
What I am actually trying to do is to intercept any value changes at FormClosing event by using EditValue and OldEditValue properties.
I meight need to tell that my controls are contained in XtraTab and XtraPanel Containers.
the following is what I tried:
Public Function TextEditChangesOccured(frm As XtraForm) As Boolean
Dim result As Boolean
For Each ctrl As BaseEdit In frm.Controls
If TypeOf ctrl Is TextEdit Then
If ctrl.EditValue <> ctrl.OldEditValue Then
result = True
Else
result = False
End If
End If
Next
Return result
End Function
Private Sub MyXtraForm_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
If TextEditChangesOccured(Me) Then
DevExpress.XtraEditors.XtraMessageBox.Show("Changes have occured!", My.Application.Info.AssemblyName, MessageBoxButtons.OK, MessageBoxIcon.Information)
End If
End Sub
but it says unable to cast XtraTab control to TextEdit control.
Your help will be much appreciated.

To make your code works just change your code snippet as follows:
Public Function TextEditChangesOccured(container As Control) As Boolean
Dim result As Boolean
For Each ctrl As Control In container.Controls
Dim bEdit As BaseEdit = TryCast(ctrl, BaseEdit)
If bEdit IsNot Nothing Then
Dim tEdit As TextEdit = TryCast(ctrl, TextEdit)
If tEdit IsNot Nothing Then
result = result Or (bEdit.EditValue <> bEdit.OldEditValue)
End If
Else
result = result Or TextEditChangesOccured(ctrl)
End If
Next
Return result
End Function
To detect changes for all the editors within a Form use the following approach:
Partial Public Class Form1
Inherits Form
Public Sub New()
InitializeComponent()
SubscribeTextEditValueChanged(Me)
End Sub
Private Sub SubscribeTextEditValueChanged(ByVal container As Control)
For Each ctrl As Control In container.Controls
Dim tEdit As TextEdit = TryCast(ctrl, TextEdit)
If tEdit IsNot Nothing Then
AddHandler tEdit.EditValueChanged, AddressOf tEdit_EditValueChanged
Else
SubscribeTextEditValueChanged(ctrl)
End If
Next ctrl
End Sub
Private IsEditValueChanged As Boolean
Private Sub tEdit_EditValueChanged(ByVal sender As Object, ByVal e As EventArgs)
IsEditValueChanged = True
End Sub
Protected Overrides Sub OnClosing(ByVal e As CancelEventArgs)
If IsEditValueChanged Then
' do some stuff
End If
MyBase.OnClosing(e)
End Sub
End Class

Related

Underlines of MaskedTextBox disappear

i have a problem in Windows Forms. I've created a form where I can enter the first and last name and click on the Search-Button to show another form with the following code:
Private Sub btnSearchUser_Click(sender As Object, e As EventArgs) Handles btnSearchUser.Click
If Me._clsfrmChild Is Nothing Then
Me._clsfrmChild = New clsFrmChild
End If
If Me._clsfrmChild.ShowDialog = False Then
Me._clsfrmChild.ShowDialog(Me)
End If
In the second form I have a MaskedTextbox:
Empty MaskedTextBox
Whatever I do, If I close the second form with Visible = False and reopen it again, the MaskedTextBox looks like this:
MaskedTextBox without underlines
I close the second form that way:
Private Sub btnAbort_Click(sender As Object, e As EventArgs) Handles btnAbort.Click
Me.Visible = False
End Sub
Does someone of you know why this problem is caused?
This is really not the way you supposed to work with dialogs. The proper way is this
dim result as DialogResult
using f as Form = New MyForm()
result = f.ShowDialog()
' here you can work with form's public properties etc
end using
' optionally here you can continue massaging the result
if result = DialogResult.Ok then
' do for ok result
else
' do for other result. You can have severul results - add elseif
end if
Here is how to make a dialog with results in general. This will be similar to how MessageBox.Show() works.
Public Class clsFrmChild
Private Sub New()
InitializeComponent()
End Sub
Public Shared Shadows Function Show() As DialogResult
Dim result As DialogResult
Using f As New clsFrmChild()
result = f.ShowDialog()
End Using
Return result
End Function
Private Sub OkButton_Click(sender As Object, e As EventArgs) Handles OkButton.Click
DialogResult = DialogResult.OK
Close()
End Sub
Private Sub CancelButton_Click(sender As Object, e As EventArgs) Handles CancelButton.Click
DialogResult = DialogResult.Cancel
Close()
End Sub
End Class
Note the constructor is privatized so there is no more Me._clsfrmChild = New clsFrmChild. You will see the displaying of the modal dialog is much simpler when called like MessageBox
Private Sub btnSearchUser_Click(sender As Object, e As EventArgs) Handles btnSearchUser.Click
Dim result = clsFrmChild.Show() ' static method call instead of instance method
Select Case result
Case DialogResult.OK
MessageBox.Show("Ok")
Case DialogResult.Cancel
MessageBox.Show("Cancel")
End Select
End Sub
If you are not interested in returning a standard DialogResult, you could change the return type to whatever you like such as a custom class, with more information (such as you want to return a string in addition to DialogResult) i.e.
Public Class clsFrmChildResult
Public Property Text As String
Public Property DialogResult As DialogResult
End Class
...
Public Shared Shadows Function Show() As clsFrmChildResult
Dim result As clsFrmChildResult
Using f As New clsFrmChild()
Dim dr = f.ShowDialog()
result = New clsFrmChildResult With {.Text = TextBox1.Text, .DialogResult = dr}
End Using
Return result
End Function
...
Private Sub btnSearchUser_Click(sender As Object, e As EventArgs) Handles btnSearchUser.Click
Dim result = clsFrmChild.Show()
Select Case result.DialogResult
Case DialogResult.OK
MessageBox.Show("Ok")
Dim myString = result.Text
Case DialogResult.Cancel
MessageBox.Show("Cancel")
End Select
End Sub

Try to read text , put on list , then compare on list and finally replace txt file

Trying to read a .txt file , put items to list, then on textbox change compare if the string exists in the list. Finally write the new list on the same .txt file.
Public Class Form1
Dim stockList As New List(Of String)
private sub
ListBox1.Items.AddRange(IO.File.ReadAllLines("C:\Users\path\file.txt"))
end sub
Private Sub TextBox1_TextChanged(sender As Object, e As EventArgs) Handles
TextBox1.ReadOnlyChanged
Dim text As String = TextBox1.Text
If TextBox1.Text <> "TRAINING" Then
For Each item As Object In ListBox1.Items
If item.ToString = text Then
MsgBox("This code has already been used.", 0, "cheat attempt violation") ' Else alert the user that item already exist
Else
ListBox1.Items.Add(TextBox1.Text)
End If
Next
End If
IO.File.WriteAllLines("C:\Users\path\file.txt", ListBox1.Items.Cast(Of String).ToArray)
End Sub
Instead of using a UI control to store data, you should store the data in a variable. I'm not sure if you really need to show the data in a ListBox, so in the following example code I didn't.
If you use a List(Of String) instead of an array of strings, it is simpler to add another item before saving it.
The Contains method I used in the example can take a second parameter which does the comparison of the item - I guessed that you might want to ignore the case (e.g. "ABC" is the same as "aBc") so I used StringComparer.CurrentCultureIgnoreCase.
I suspect that you want to use the Control.Validating Event instead of the TextChanged event. When the data has been validated, the Control.Validated event handler is used to save it.
I put one TextBox and one Button on the form, so that the focus could change away from the TextBox, e.g. when pressing tab, to fire the validating event.
Imports System.IO
Public Class Form1
Dim dataFile As String = "C:\temp\grains.txt"
Dim alreadyUsed As List(Of String) = Nothing
Sub LoadAlreadyUsed(filename As String)
'TODO: Add exception checking, e.g., the file might not exist.
alreadyUsed = File.ReadAllLines(filename).ToList()
End Sub
Sub SaveAlreadyUsed(filename As String)
File.WriteAllLines(dataFile, alreadyUsed)
End Sub
Function CodeIsAlreadyUsed(newCode As String) As Boolean
Return alreadyUsed.Contains(newCode, StringComparer.CurrentCultureIgnoreCase)
End Function
Private Sub TextBox1_Validating(sender As Object, e As System.ComponentModel.CancelEventArgs) Handles TextBox1.Validating
' Do nothing if the user has clicked the form close button
' See https://stackoverflow.com/questions/15920770/closing-the-c-sharp-windows-form-by-avoiding-textbox-validation
If Me.ActiveControl.Equals(sender) Then
Exit Sub
End If
Dim txt = DirectCast(sender, TextBox).Text
If CodeIsAlreadyUsed(txt) Then
MessageBox.Show("This code has already been used.", "Cheat attempt violation", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
e.Cancel = True
End If
End Sub
Private Sub TextBox1_Validated(sender As Object, e As EventArgs) Handles TextBox1.Validated
' The Validated event is raised before the FormClosing event.
' We do not want to save the data when the form is closing.
If Me.ActiveControl.Equals(sender) Then
Exit Sub
End If
Dim txt = DirectCast(sender, TextBox).Text
alreadyUsed.Add(txt)
SaveAlreadyUsed(dataFile)
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
LoadAlreadyUsed(dataFile)
End Sub
End Class

simple dialog like msgbox with custom buttons (vb)

I want to ask user for example "Do you want to go right or left?".
To have simple Code I use MSGBOX with a prompt like:
"Do you want to go right or left"
Press "YES for 'right' / NO for 'left'"
Then I process Yes/No/Cancel that was pressed. This works but is ugly and in some cases hard to understand.
Also in Addition in some cases I have more than 2 choices - but that is probable another question...
You can create one dynamically
Public Module CustomMessageBox
Private result As String
Public Function Show(options As IEnumerable(Of String), Optional message As String = "", Optional title As String = "") As String
result = "Cancel"
Dim myForm As New Form With {.Text = title}
Dim tlp As New TableLayoutPanel With {.ColumnCount = 1, .RowCount = 2}
Dim flp As New FlowLayoutPanel()
Dim l As New Label With {.Text = message}
myForm.Controls.Add(tlp)
tlp.Dock = DockStyle.Fill
tlp.Controls.Add(l)
l.Dock = DockStyle.Fill
tlp.Controls.Add(flp)
flp.Dock = DockStyle.Fill
For Each o In options
Dim b As New Button With {.Text = o}
flp.Controls.Add(b)
AddHandler b.Click,
Sub(sender As Object, e As EventArgs)
result = DirectCast(sender, Button).Text
myForm.Close()
End Sub
Next
myForm.FormBorderStyle = FormBorderStyle.FixedDialog
myForm.Height = 100
myForm.ShowDialog()
Return result
End Function
End Module
You see you have options as to what buttons are present, the message, and title.
Use it like this
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim result = CustomMessageBox.Show(
{"Right", "Left"},
"Do you want to go right or left?",
"Confirm Direction")
MessageBox.Show(result)
End Sub
End Class
In my example, the prompt is "Do you want to go right or left?" and the options are "Right" and "Left".
The string is returned as opposed to DialogResult because now your options are unlimited (!). Experiment with the size to your suiting.
You need to create your own "custom" msgbox form according to your needs, and its better to create a reusable control - pass your "question" string via the constructor of your custom control.
You need some "way" to get the user decision from out side your custom msgbox - one way is use DialogResult Enum for that.
Here is a basic example i just wrote to demonstrate that, please see the comments i have added inside the code.
create a new project with 2 forms, Form1 will be the main form that will call the custom msgbox and Form2 will be the custom msgbox:
Form1:
Form2:
Code for Form1:
Public Class Form1
Private Sub btnOpenCustomMsgbox_Click(sender As Object, e As EventArgs) Handles btnOpenCustomMsgbox.Click
Dim customMsgbox = New Form2("this is my custom msg, if you press yes i will do something if you press no i will do nothing")
If customMsgbox.ShowDialog() = DialogResult.Yes Then
' do something
MsgBox("I am doing some operation...")
Else
' do nothing (its DialogResult.no)
MsgBox("I am doing nothing...")
End If
End Sub
End Class
Code for Form2:
Public Class Form2
' field that will contain the messege
Private PromtMsg As String
Sub New(ByVal promtmsg As String)
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
' set global field with the argument that was passed to the constructor
Me.PromtMsg = promtmsg
End Sub
Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
' set the msg label
Me.lblPromtMsg.Text = Me.PromtMsg
End Sub
Private Sub btnCustomYes_Click(sender As Object, e As EventArgs) Handles btnCustomYes.Click
' user choosed yes - return DialogResult.Yes
Me.DialogResult = DialogResult.Yes
Me.Close()
End Sub
Private Sub btnCustomNo_Click(sender As Object, e As EventArgs) Handles btnCustomNo.Click
' user choosed no - DialogResult.no
Me.DialogResult = DialogResult.No
Me.Close()
End Sub
End Class
it can be much more sophisticated but if you explore that example i hope you will understand the general idea.

How to Set BackColor of Inactive Control

I have a form that contains several Textboxes and Comboboxes. I have set active control's back color property to skyblue. I want to set the back color of all textboxes and comboboxes to white while they are not active.
There's a Right Way and a wrong way to do this. You are asking for the wrong way. The right way is to derive your own class from TextBox and override the OnEnter and OnLeave methods. Repeat for ComboBox.
But you ask for the wrong way and you are probably trying to add this feature too late so we'll have to slug it out by finding the controls back at runtime. Add a constructor to your form class and make it look like:
Public Sub New()
InitializeComponent()
FindControls(Me.Controls)
End Sub
Private Sub FindControls(ctls As Control.ControlCollection)
For Each ctl As Control In ctls
Dim match As Boolean
If TypeOf ctl Is TextBoxBase Then match = True
If TypeOf ctl Is ComboBox Then
Dim combo = DirectCast(ctl, ComboBox)
If combo.DropDownStyle <> ComboBoxStyle.DropDownList Then match = True
End If
If match Then
AddHandler ctl.Enter, AddressOf ControlEnter
AddHandler ctl.Leave, AddressOf ControlLeave
End If
FindControls(ctl.Controls)
Next
End Sub
Private controlColor As Color
Private Sub ControlEnter(sender As Object, e As EventArgs)
Dim ctl = DirectCast(sender, Control)
controlColor = ctl.BackColor
ctl.BackColor = Color.AliceBlue
End Sub
Private Sub ControlLeave(sender As Object, e As EventArgs)
Dim ctl = DirectCast(sender, Control)
ctl.BackColor = controlColor
End Sub

Trapping WM_SETFOCUS in WindProc VB.NET

I have a user control that inherits from a base user control class. On the user control that inherits this, I place a series of textboxes. At runtime, I need to know when each control gets focus and loses focus. To do this, I overrode WndProc in the base class, and am attempting to capture the messages there. The problem I'm having is that I never receive WM_SETFOCUS or WM_KILLFOCUS within the message loop.
This is the WndProc:
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
Select Case m.Msg
Case WM_SETFOCUS
Debug.WriteLine(m.ToString())
Case WM_KILLFOCUS
Debug.WriteLine(m.ToString())
Case Else
Debug.Print("OTHER: " + m.ToString())
End Select
MyBase.WndProc(m)
End Sub
I get a whole bunch of message for getting text and some other stuff, so I know that I'm getting there. It just never stops on WM_SETFOCUS or WM_KILLFOCUS.
What am I not doing correctly.
Quick example of wiring up all TextBoxes in the UserControl:
Public Class UserControl1
Private Sub UserControl1_Load(sender As Object, e As EventArgs) Handles Me.Load
WireTBs(Me)
End Sub
Private Sub WireTBs(ByVal cont As Control)
For Each ctl As Control In cont.Controls
If TypeOf ctl Is TextBox Then
Dim TB As TextBox = DirectCast(ctl, TextBox)
AddHandler TB.GotFocus, AddressOf TB_GotFocus
AddHandler TB.LostFocus, AddressOf TB_LostFocus
ElseIf ctl.HasChildren Then
WireTBs(ctl)
End If
Next
End Sub
Private Sub TB_GotFocus(sender As Object, e As EventArgs)
Dim TB As TextBox = DirectCast(sender, TextBox)
' ... do something with "TB" ...
Debug.Print("GotFocus: " & TB.Name)
End Sub
Private Sub TB_LostFocus(sender As Object, e As EventArgs)
Dim TB As TextBox = DirectCast(sender, TextBox)
' ... do something with "TB" ...
Debug.Print("LostFocus: " & TB.Name)
End Sub
End Class