Void user check RadioButton without disabling it - vb.net

I have four RadioButtons inside a GroupBox and I need to forbid user to change it status but without disabling the control because I need that they look NOT disabled(grayed).
I'm controlling them from code.
I tried to change AutoClick to false but I get more than one RadioButton checked when I change them by code.
Of course I can change all the RadioButtons status every time but that looks a bit messy.

You can use an inherited class for this.
Mark IsReadOnly to true to disable the click.
Class ReadOnlyRadioButton
Inherits System.Windows.Forms.RadioButton
Private mRo as Boolean
Public Property IsReadOnly As Boolean
Get
Return mRo
End Get
Set(ByVal value as String)
mRo = value
End Set
End Property
Protected Overrides Sub OnClick(ByVal e As EventArgs)
If Not Me.IsReadOnly Then
MyBase.OnClick(e)
End If
End Sub
End Class

Related

Call sub on class property change?

I have a custom class which is based off of a TreeNode and this has an Enum property on it called status, setup as shown below.
Public _staus As enumStatus
Public Enum enumStatus
None
Yes
No
End Enum
Basically when I change this property I want to call a sub routine which just changes the text colour of the item - this sub is contained in the class and is simply a select case statement updating the Me.ForeColor property.
This works correctly if I class myClass.ChangeColourBasedOnStatus but how can I make it automatically do this when a property is changed.
I've tried looking at event handlers but I just can't find an explanation that I understand and can get to work.
Any advice is greatly appreciated. :)
Make the field private and add a Property to access it. In the example below I assume that you are using text from a control, but you could modify it to use the enum or other types.
Private _staus As enumStatus
Public Enum enumStatus
None
Yes
No
End Enum
Public Property Status As enumStatus
Get
Return _staus
End Get
Set(value As enumStatus)
_staus = value
ChangeColor(TextBox1.Text)
End Set
End Property
Private Sub ChangeColor(SomeText As String)
Select Case SomeText
Case "" : Me.ForeColor = Color.Black
Case "Stop" : Me.ForeColor = Color.Red
Case "Go" : Me.ForeColor = Color.Green
End Select
End Sub

vb.net - how to change a property of a form element from another module

I am trying to change the background color of a button (cmdLogQry) from a Set-procedure of a public property (LogQry) - according to the new value of the property.
It works if the property is being changed in the code belongig to the form containing the button (in the Click method of the same or even another button). But it does not work if the property is being changed from another module (handler procedure for COM ports DataReceived event). No error message or anything - the LogQry gets its value changed all right, but the color of the button does not change.
What do I do wrong?
Public Class Handler
Private _logQry As Boolean = False
Public Property LogQry() As Boolean
Get
Return _logQry
End Get
Set(ByVal value As Boolean)
_logQry = value
If value Then
frmMain.cmdLogQry.BackColor = Color.Red
Else
frmMain.cmdLogQry.BackColor = Color.Blue
End If
End Set
End Property
Private Sub comPort_DataReceived(ByVal sender As Object, ByVal e As SerialDataReceivedEventArgs)
...
LogQry = Not LogQry ' does NOT change color
...
End Sub
End Class
Public Class frmMain
Private comm As New Handler()
...
Private Sub cmdLogQry_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles cmdLogQry.Click
comm.LogQry = Not comm.LogQry ' does change color
End Sub
...
End Class
This problem is caused by the default instance of the form class created by the VB.NET implementation. More on default instances could be found here and in this answer from Hans Passant.
Essentially when you define a form class, VB.NET compiler creates a default instance of that class named with the same name of the class, but this creates a lot of misunderstanding in an object oriented environment like NET.
To fix your problem you need to implement a constructor in your Handler class that receives the actual instance of frmMain, store it inside a class variable and use that instance when you want to modify something on the actual displayed form
Public Class Handler
Private _logQry As Boolean = False
Private _mainInstance As frmMain
Public Sub New(mainInstance as frmMain)
_mainInstance = mainInstance
End Sub
Public Property LogQry() As Boolean
Get
Return _logQry
End Get
Set(ByVal value As Boolean)
_logQry = value
If value Then
_mainInstance.cmdLogQry.BackColor = Color.Red
Else
_mainInstance.cmdLogQry.BackColor = Color.Blue
End If
End Set
End Property
....
End Class
Now, when you create the Handler instance pass the reference to the current frmMain
Public Class frmMain
Private comm As Handler
...
Private Sub cmdLogQry_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles cmdLogQry.Click
comm = new Handler(Me)
comm.LogQry = Not comm.LogQry ' does change color
End Sub
...
End Class
Keep in mind that this solution creates also problems. It couples the class Handler to your frmMain and the two are now inseparable. Probably a better approach is to create an Event in the Handler class so, every form that wants to be notified could subscribe to the event and receieves the information when needed.

Saving changed values of components on FormClosing

In my application I have a TabControl with some tabs. Each tab contains many components. If the application is closing I want to check If a value of any component has changed. If so, I will ask a user if he wants to save it or not.
I want to know how you solve this situation (because it is standard behaving when application is closing). I thought that I have some flag (bool) and I set an event ValueChanged to each component. If the method handlings this event is fired, this flag is set to true. In case of closing application I will only check if flag is true.
But the problem is that there is more than 30 components and create method handling the event to each component it seems not efectve to me.
You're on the right track wit the boolean flag and the ValueChanged event. As far as I'm aware, that's really the only way to deal with this kind of thing. To accomplish this you could simply writing the handling for the event once and then copy and paste the component over and over as needed.
However, to your question of effectiveness when spread across 30 components, you should consider rolling your own component(s) that inherit from a basal class which exposes an IsDirty property or similar. When closing you could then loop through all controls on your tabs to see any have IsDirty set to true.
Unfortunately, given that you've already created the interface, neither approach will solve your current dilemma.
When you fill in the text/value for each control, also fill in the TAG with the same value. Then you can compare the TAG against the text/value for each control to see if anything changed.
To avoid having to write code for each control (when checking) you cah do a for loop on each control in [Tab Page Name].Controls().
Another way to do this is to extend each control by adding a IsDirty property and overriding the validation event. Then you can set this if it changed. You might also want to have a method to RESET the IsDirty property.
Yet another way, I always bind to a class, it just makes my code less error-prone and gives me intellisense. Also gives you tons of features that you can easily throw in like this. Then just BIND to your custom class.
Here is an example of how I would do this using a custom class. This is just an example of reading a csv and writting it but the key, to answer your question, is in the "Dirty" code.
Imports System.ComponentModel
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
'On form load, open the file and read the data
Using SR As New System.IO.StreamReader("Test.csv")
Do While SR.Peek >= 0
'Put each line into it's own instance of the class
BindingSource1.Add(New MyData(SR.ReadLine))
Loop
End Using
End Sub
Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
'When closing, see if any data has been changed and ask to save
Dim bDirty As Boolean = False
For Each dat As MyData In BindingSource1
If dat.IsDirty Then
bDirty = True
Exit For
End If
Next
If bDirty Then
'Example code for saving
Select Case MessageBox.Show("Do you want to save your changes?", "Save Changes", MessageBoxButtons.YesNoCancel)
Case Windows.Forms.DialogResult.Cancel
e.Cancel = True
Case Windows.Forms.DialogResult.Yes
'Here you should remove the old file, I like to rename it to BAK,
' save the new file, then you can get rid of it.
' Just in case there is a problem saving.
If System.IO.File.Exists("Test.csv") Then System.IO.File.Delete("Test.csv")
Using SW As New System.IO.StreamWriter("Test.csv", False)
For Each dat As MyData In BindingSource1
SW.WriteLine(dat)
Next
End Using
End Select
End If
End Sub
End Class
Public Class MyData
Implements INotifyPropertyChanged
'Event that implements INotifyPropertyChanged. This tells the binding to refresh a property in the UI.
Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
Public Sub New(ByVal SomeDataToLoad As String)
'Take you data and parse it or whatever into the various properties
'This example uses a comma-seperated string
Dim sWords As String() = SomeDataToLoad.Split(",")
_FirstName = sWords(0)
_LastName = sWords(1)
End Sub
''' <param name="PropertyName">Case-Sensative property name</param>
Public Sub ForcePropertyChanged(ByVal PropertyName As String)
RaiseEvent PropertyChanged(Me, New System.ComponentModel.PropertyChangedEventArgs(PropertyName))
End Sub
Private _IsDirty As Boolean
Public ReadOnly Property IsDirty As Boolean
Get
Return _IsDirty
End Get
End Property
''' <summary>Override the ToString method for getting the data back out, in this case as comma seperated again. You can then write this to file or whatever.</summary>
Public Overrides Function ToString() As String
Return FirstName & "," & LastName
End Function
'--Properties you can bind to------------------------------------------------
Private _FirstName As String
Public Property FirstName As String
Get
Return _FirstName
End Get
Set(value As String)
_FirstName = value
_IsDirty = True
ForcePropertyChanged("FirstName")
End Set
End Property
Private _LastName As String
Public Property LastName As String
Get
Return _LastName
End Get
Set(value As String)
_LastName = value
_IsDirty = True
ForcePropertyChanged("LastName")
End Set
End Property
End Class
I did not go into how to bind here, you can find that all over the web, but I did put the data in a BindingSource so the rest will be easy. Notice that when the form is closing, I can loop through EVERY record to see if there were changes, easily. If you only had a single record, you wouldn't even have to loop, just ask if it is dirty.

borderless button in visual basic.net?

HEY there I was asking about how to make a borderless button in vb.net I can always set the style to flat and make the background color as transparent but always I always get the button focused and shows in a shape of a button which ruins my button style that I want it
here is a class I used earlier but it didn't work
Public Class ButtonEx
Inherits Button
Private _ShouldShowFocus As Boolean = False
Public Property ShouldShowFocus() As Boolean
Get
Return _ShouldShowFocus
End Get
Set(ByVal value As Boolean)
_ShouldShowFocus = value
End Set
End Property
Protected Overrides ReadOnly Property ShowFocusCues() As Boolean
Get
Return _ShouldShowFocus
End Get
End Property
End Class
You could subclass the standard button and override the OnPaint method to achieve a borderless button.
Class BorderlessButton
Inherits Button
Protected Overrides Sub OnPaint(ByVal pe As PaintEventArgs)
MyBase.OnPaint(pe)
pe.Graphics.DrawRectangle(New Pen(BackColor, 5), ClientRectangle)
End Sub
End Class
I'm assuming you've tried out the Button.FlatAppearance properties and none of these help to solve your problem.
Public Class ButtonNoFocusCues
Inherits System.Windows.Forms.Button
Protected Overrides ReadOnly Property ShowFocusCues As Boolean
Get
Return False
End Get
End Property
End Class
You can use label that is clickable so it will be no border just add hover code.....
Also you can use bootstrap or css if your using asp.net

How do you handle PropertyChange for a boolean value?

I'm creating a program in which I have a publicly defined boolean value
Public boolOverallStatus As Boolean = True
and I need to execute some code whenever the boolean value changes. In previous applications, an actual form item change handled this, but it can be changed by several different subs.
How would I handle this? I'm looking through msdn, but it's rather confusing.
In a nutshell: How to execute code when the event of a boolean value changing occurs.
Make it a property instead.
Private _boolOverallStatus As Boolean = True
Property boolOverallStatus As Boolean
Get
Return _boolOverallStatus
End Get
Set(ByVal value as Boolean)
If value <> _boolOverallStatus Then
_boolOverallStatus = value
'// handle more code changes here.'
End If
End Set
End Property
I use the following pattern which resembles what Microsoft do for most of their Changed events.
Class MyClass
Public Property OverallStatus As Boolean
Get
Return _OverallStatus
End Get
Set (value As Boolean)
If _OverallStatus = value Then Exit Property
_OverallStatus = value
OnOverallStatusChanged(EventArgs.Empty)
End Set
End Property
Private _OverallStatus As Boolean = False
Protected Overridable Sub OnOverallStatusChanged(e As EventArgs)
RaiseEvent OverallStatusChanged(Me, e)
End Sub
Public Event OverallStatusChanged As EventHandler
End Class
In VB, you can handle the event using the WithEvents and Handles keywords:
Class MyParent
Private WithEvents myObject As New MyClass()
Private Sub myobject_OverallStatusChanged(sender As Object, e As EventArgs) Handles myObject.OverallStatusChanged
' TODO: Write handler.
End Sub
End Class
The OnOverallStatusChanged function is useful for inheriting classes to get first shot at responding to the change.
Class MyOtherClass
Inherits MyClass
Protected Overrides Sub OnOverallStatusChanged(e As EventArgs)
' TODO: Do my stuff first.
MyBase.OnOverallStatusChanged(e)
End Sub
End Class
Use public properties instead of public variables. You can then put logic in the Set method of the property to execute whenever the property is.. well set.
http://msdn.microsoft.com/en-us/library/65zdfbdt%28v=VS.100%29.aspx
Private number As Integer = 0
Public Property MyNumber As Integer
' Retrieves number.
Get
Return number
End Get
' Assigns to number.
Set
CallLogicHere()
number = value
End Set
End Property
You could also define an event, which is fired each time the status changes. The advantage is, that changes can be handled by the parts of the application that depend on this status. Otherwise the logic would have to be implemented together with the status.
Other parts of the application can subscribe the event with AddHandler.
Public Class OverallStatusChangedEventArgs
Inherits EventArgs
Public Sub New(newStatus As Boolean)
Me.NewStatus = newStatus
End Sub
Private m_NewStatus As Boolean
Public Property NewStatus() As Boolean
Get
Return m_NewStatus
End Get
Private Set
m_NewStatus = Value
End Set
End Property
End Class
Module modGlobals
Public Event OverallStatusChanged As EventHandler(Of OverallStatusChangedEventArgs)
Private m_boolOverallStatus As Boolean = True
Public Property BoolOverallStatus() As Boolean
Get
Return m_boolOverallStatus
End Get
Set
If Value <> m_boolOverallStatus Then
m_boolOverallStatus = Value
OnOverallStatusChanged(Value)
End If
End Set
End Property
Private Sub OnOverallStatusChanged(newStatus As Boolean)
RaiseEvent OverallStatusChanged(GetType(modGlobals), New OverallStatusChangedEventArgs(newStatus))
End Sub
End Module