Winforms TextBox - Allow numeric or 1 decimal place only - winforms

How can I prevent users from entering anything other than a numeric value or a decimal value with 1 decimal place?
The user should be allowed to enter any length of characters (if decimal value, before the decimal).

Try using Regex. This pattern should work: Regex match = new Regex(#"^[1-9]\d*(\.\d{1})?$"), put that in your validating event of the textbox. If its no match, Undo() or delete the Textbox.Text property.
Regex match = new Regex(#"^[1-9]\d*(\.\d{1})?$");
private void textBox1_Validating(object sender, CancelEventArgs e)
{
if (!match.IsMatch(textBox1.Text))
{
textBox1.Undo();
}
}
To actually undo the input immediatly, you have to use
private void textBox1_KeyUp(object sender, KeyEventArgs e)
{
if (!match.IsMatch(textBox1.Text))
{
textBox1.Undo();
}
}
Because if you use KeyDown, the TextBox has no Undo State.
2nd Edit: If you want both cases to match, you have to do the check in the Validating Event or a similar one. Since the regex uses "$" to make sure, no characters are added in the end, you cannot enter "." or else you'd end up having a number like 1. which would require additional checking.

Maybe a little late to the party on this one, but I extended a simple textbox to force the entry to always be formatted decimal.. Simple but effective
Imports System.Runtime.InteropServices
Imports System.Drawing.Imaging
Imports System.ComponentModel
Imports System.Text.RegularExpressions
<ToolboxBitmap(GetType(System.Windows.Forms.TextBox))> _
Public Class NumericTextBox
Inherits TextBox
Dim _TextBoxValue As String
Dim _CaretPosition As Integer
Protected Overrides Sub OnKeyDown(e As KeyEventArgs)
MyBase.OnKeyDown(e)
_TextBoxValue = Me.Text
_CaretPosition = Me.SelectionStart
End Sub
Protected Overrides Sub OnKeyUp(e As KeyEventArgs)
MyBase.OnKeyUp(e)
If (Me.Text.Length = 0) Or (Me.Text = _TextBoxValue) Then Exit Sub
If IsNumeric(Me.Text) Then
If Me.Text.EndsWith(".") Then
Me.Text = Convert.ToDecimal(Me.Text) & "."
Else
Me.Text = Convert.ToDecimal(Me.Text)
End If
Else
Me.Text = _TextBoxValue
End If
Me.SelectionStart = _CaretPosition + 1
End Sub
End Class

Regex match = new Regex(#"^[1-9]\d*(.\d{1})?$");
Working Correctly

Related

(VB.NET) display the lower half of a textfile to a listbox

I have to make a application that organizes a list of runners and their teams. In the following text file, I have to remove the top half of the text file (the top half being the listed teams) and display only the bottom half (the runners)in a listbox item.
The Text file:
# School [School Code|School Name|Coach F-Name|Coach L-Name|AD F-Name|AD L Name]
WSHS|Worcester South High School|Glenn|Clauss|Bret|Zane
WDHS|Worcester Dorehty High School|Ellsworth|Quackenbush|Bert|Coco
WBCHS|Worcester Burncoat High School|Gail|Cain|Kevin|Kane
QRHS|Quabbin Regional High School|Bob|Desilets|Seth|Desilets
GHS|Gardner High School|Jack|Smith|George|Fanning
NBHS|North Brookfield High School|Hughe|Fitch|Richard|Carey
WHS|Winchendon High School|Bill|Nice|Sam|Adams
AUBHS|Auburn High School|Katie|Right|Alice|Wonderland
OXHS|Oxford High School|Mary|Cousin|Frank|Daughter
# Roster [Bib #|School Code|Runner's F-Name|Runner's L-Name]
101|WSHS|Sanora|Hibshman
102|WSHS|Bridgette|Moffitt
103|WSHS|Karine|Chunn
104|WSHS|Shanita|Wind
105|WSHS|Fernanda|Parsell
106|WSHS|Albertha|Baringer
107|WSHS|Carlee|Sowards
108|WDHS|Maisha|Kleis
109|WDHS|Lezlie|Berson
110|WDHS|Deane|Rocheleau
111|WDHS|Hang|Hodapp
112|WDHS|Zola|Dorrough
113|WDHS|Shalon|Mcmonigle
I have some code that reads each row from the text file as an array and uses boolean variables to determine where to end the text file. This worked with displaying only the teams, which I've managed to do. But I now need to do the opposite and display only the players, and I'm a bit stumped.
My Code:
Private Sub btnLoadTeams_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnLoadTeam.Click
' This routine loads the lstTeam box from an ASCII .txt file
' # School [School Code | Name | Coach F-Name| Coach L-Name | AD F-Name | AD L-Name]
Dim strRow As String
Dim bolFoundCode As Boolean = False
Dim bolEndCode As Boolean = False
Dim bolFoundDup As Boolean = False
Dim intPosition As Integer
Dim intPosition2 As Integer
Dim strTeamCodeIn As String
Dim textIn As New StreamReader( _
New FileStream(txtFilePath.Text, FileMode.OpenOrCreate, FileAccess.Read))
' Clear Team listbox
lstTeam.Items.Clear()
btnDeleteRunner.Enabled = True
Do While textIn.Peek <> -1 And Not bolEndCode
Me.Refresh()
strRow = textIn.ReadLine.Trim
If Not bolFoundCode Then
If "# SCHOOL " = UCase(Mid(strRow, 1, 9)) Then
bolFoundCode = True
End If
Else
If Mid(strRow, 1, 2) <> "# " Then
For Each item As String In lstTeam.Items
intPosition = InStr(1, strRow, "|")
strTeamCodeIn = Mid(strRow, 1, intPosition - 1)
intPosition2 = InStr(1, item, strTeamCodeIn)
If intPosition2 > 0 Then
bolFoundDup = True
MsgBox("Found Duplicate School Code: " & strTeamCodeIn)
End If
Else
bolEndCode = True
Next
If Not bolFoundDup Then
lstTeam.Items.Add(strRow)
Else
lstTeam.Items.Add("DUPLICATE School Code: " & strRow)
lstTeam.Items.Add("Please correct input file and reload teams")
bolEndCode = True
End If
End If
End If
Loop
End Sub
Ive put bolEndCode = True in between the part that reads the mid section of the text file, but all Ive managed to display is the following in the listbox:
# Roster [Bib #|School Code|Runner's F-Name|Runner's L-Name]
Any help or hints on how I would display just the runners to my "lstPlayers" listbox would be greatly appreciated. I'm a beginner programmer and We've only just started learning about reading and writing arrays in my .NET class.
First I made 2 classes, one Runner and one School. These have the properties available in the text file. As part of the class I added a function that overrides .ToString. This is for he list boxes that call .ToString for display.
Next I made a function that reads all the data in the file. This is very simple with the File.ReadLines method.
Then I created 2 variables List(Of T) T stands for Type. Ours Types are Runner and School. I used List(Of T) instead of arrays because I don't have to worry about what the size of the list is. No ReDim Preserve, just keep adding items. The FillList method adds the data to the lists. First I had to find where the schools ended and the runners started. I used the Array.FindIndex method which is a bit different because the second parameter is a predicate. Check it out a bit. Now we know the indexes of the lines we want to use for each list and use a For...Next loop. In each loop an instance of the class is created and the properties set. Finally the new object is added to the the list.
Finally we fill the list boxes with a simple .AddRange and the lists.ToArray. Note that we are adding the entire object, properties and all. The neat thing is we can access the properties from the listbox items. Check out the SelectedIndexChanged event. You can do the same thing with the Runner list box.
Sorry, I couldn't just work with your code. I have all but forgotten the old vb6 methods. InStr, Mid etc. It is better when you can to use .net methods. It makes your code more portable when the boss says "Rewrite the whole application in C#"
Public Class Runner
Public Property BibNum As Integer
Public Property SchoolCode As String
Public Property FirstName As String
Public Property LastName As String
Public Overrides Function ToString() As String
'The listbox will call .ToString when we add a Runner object to determin what to display
Return $"{FirstName} {LastName}" 'or $"{LastName}, {FirstName}"
End Function
End Class
Public Class School
Public Property Code As String
Public Property Name As String
Public Property CoachFName As String
Public Property CoachLName As String
Public Property ADFName As String
Public Property ADLName As String
'The listbox will call .ToString when we add a School object to determin what to display
Public Overrides Function ToString() As String
Return Name
End Function
End Class
Private Runners As New List(Of Runner)
Private Schools As New List(Of School)
Private Function ReadData(path As String) As String()
Dim lines = File.ReadLines(path).ToArray
Return lines
End Function
Private Sub FillLists(data As String())
Dim location = Array.FindIndex(data, AddressOf FindRosterLine)
'The first line is the title so we don't start at zero
For index = 1 To location - 1
Dim SplitData = data(index).Split("|"c)
Dim Schl As New School
Schl.Code = SplitData(0)
Schl.Name = SplitData(1)
Schl.CoachFName = SplitData(2)
Schl.CoachLName = SplitData(3)
Schl.ADFName = SplitData(4)
Schl.ADLName = SplitData(5)
Schools.Add(Schl)
Next
For index = location + 1 To data.GetUpperBound(0)
Dim SplitData = data(index).Split("|"c)
Dim Run As New Runner
Run.BibNum = CInt(SplitData(0))
Run.SchoolCode = SplitData(1)
Run.FirstName = SplitData(2)
Run.LastName = SplitData(3)
Runners.Add(Run)
Next
End Sub
Private Function FindRosterLine(s As String) As Boolean
If s.Trim.StartsWith("# Roster") Then
Return True
Else
Return False
End If
End Function
Private Sub FillListBoxes()
Dim arrRunners As Runner() = Runners.ToArray
Dim arrSchools As School() = Schools.ToArray
ListBox1.Items.AddRange(arrSchools)
ListBox2.Items.AddRange(arrRunners)
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim arrRunner = ReadData("Runners.txt")
FillLists(arrRunner)
FillListBoxes()
End Sub
Private Sub ListBox1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ListBox1.SelectedIndexChanged
Dim Schl = DirectCast(ListBox1.SelectedItem, School)
TextBox1.Text = Schl.CoachLName
TextBox2.Text = Schl.Code
End Sub

Eliminating adding CRLF to end of text while copying from DataGrid

If you copy text from WPF DataGrid cell (Ctrl+C), from unknown reason there is always end of line (CRLF) added to end of copied text. This can prevent correct pasting of copied text to some applications.
I tried to intercept copying using behavior and also by directly hooking to DataGrid's CopyingRowClipboardContent, both without success.
Protected Sub OnCopyingRowClipboardContent(sender As Object, e As DataGridRowClipboardEventArgs)
Dim cellContent = e.ClipboardRowContent(DirectCast(sender, DataGrid).CurrentCell.Column.DisplayIndex)
e.ClipboardRowContent.Clear()
e.ClipboardRowContent.Add(cellContent)
If Clipboard.ContainsText(TextDataFormat.UnicodeText) OrElse
Clipboard.ContainsText(TextDataFormat.Text) Then
Dim clipboardText = Clipboard.GetText()
Dim length = clipboardText.Length
If length >= 2 Then
If clipboardText(length - 1) = vbLf AndAlso clipboardText(length - 2) = vbCr Then
clipboardText = clipboardText.Substring(0, length - 2)
Clipboard.SetText(clipboardText)
End If
End If
End If
End Sub
Is there any way to copy text without added CRLF?
The easiest way to fix this is probably to create a custom DataGrid class that overrides the OnExecutedCopy method:
public class CustomDataGrid : DataGrid
{
protected override void OnExecutedCopy(ExecutedRoutedEventArgs args)
{
base.OnExecutedCopy(args);
string text = Clipboard.GetText();
if(!string.IsNullOrEmpty(text))
Clipboard.SetText(text.Replace("\r\n", string.Empty));
}
}

Validation in Lost focus event

we are having wpf pages which has a menu items on the top with select,save,clear,exit items. Below the menu we have got a textbox which accepts the supplier code. In the lost focus of the textbox we have to validate the supplier code is correct and at the same time if user clicks the exit menu item this validation should not happen. this has been easily achieved by checking the tab index in the windows application but in WPF pages we dont know how to achieve.Below code is done in the windows application.same functionality we need in the wpf pages.
we need code in c#
Private Sub txtSupp_Leave(ByVal sender As Object, ByVal e As System.EventArgs) Handles txtSupp.Leave
If Me.ActiveControl Is Nothing Then Exit Sub
If Me.ActiveControl.TabIndex <= txtSupp.TabIndex And _
Me.ActiveControl.Parent.TabIndex <= txtSupp.Parent.TabIndex Then Exit Sub
Dim lsErrmsg As String
cowSIMaintain.Validate_Supp(Me, lsErrmsg)
End Sub
private void txtSupp_LostFocus(object sender, RoutedEventArgs e)
{
var focusedControl = Keyboard.FocusedElement;
if (focusedControl.GetType() == typeof(Button))
{
var but = focusedControl as Button;
if (but == null)
{
return;
}
if (but.Name == "ImTheExitButton")
{
return;
}
}
string lsErrmsg = null;
cowSIMaintain.Validate_Supp(this, lsErrmsg);
}
Sorry forgot I left you hanging.. Hope you get this in time to be useful, It should do what you need, or at least point you in the right direction.

How to cancel BackgroundWorker in WPF without DoEvents

I have a search box that works great in WinForms, but is giving me trouble in WPF.
It works by starting a search each time a letter is pushed, similar to Google.
If (txtQuickSearch.Text.Length >= 3) Or (e.Key = Key.Enter) Then
If SearchWorker.IsBusy Then
SearchWorker.CancelAsync()
Do While SearchWorker.IsBusy
'Application.DoEvents()
'System.Threading.Thread.Sleep(500)
Loop
End If
doSearchText = txtQuickSearch.Text
SearchWorker.RunWorkerAsync()
End If
Every time a key is pushed it cancels the current searchWorker then restarts it. In WinForms the Do while searchworker.isbusy doevents loop worked great, but since I don't have access to that anymore, I need to figure out a better way to do it. Sleep() deadlocks it, and I've tried just doing i+=1 as a way to pass time until it's not busy, but that doesn't work either...
What should I do?
Update: Here's what I changed it to. It works, but the cancel part doesn't seem to ever trigger, this doesn't seem to be running async...
Imports System.ComponentModel
Imports System.Collections.ObjectModel
Imports System.Threading
Imports System.Threading.Tasks
Public Class QuickSearch
Private doSearchText As String
Private canceled As Boolean
Private curSearch As String
Dim searchResults As New ObservableCollection(Of ocSearchResults)
'Task Factory
Private cts As CancellationTokenSource
Private searchtask As Task(Of ObservableCollection(Of ocSearchResults))
Private Sub txtQuickSearch_KeyDown(ByVal sender As System.Object, ByVal e As System.Windows.Input.KeyEventArgs) Handles txtQuickSearch.KeyDown
If e.Key = Key.Enter Then
curSearch = ""
End If
If ((txtQuickSearch.Text.Length >= 3) Or (e.Key = Key.Enter)) And Not txtQuickSearch.Text = curSearch Then
If Not cts Is Nothing Then
cts.Cancel()
ColorChecker.CancelAsync()
Try
' searchtask.Wait()
Catch ex As AggregateException
MsgBox(ex.InnerException.Message)
End Try
cts = New CancellationTokenSource
Else
cts = New CancellationTokenSource
End If
Dim cToken = cts.Token
Me.Height = 400
doSearchText = txtQuickSearch.Text
'This always completes fully before continuing on to tRunWorkerComplete(searchtask.Result) '
searchtask = Task(Of ObservableCollection(Of ocSearchResults)).Factory.StartNew(Function() tPerformSearch(cToken), cToken)
Try
tRunWorkerCompleted(searchtask.Result)
Catch ex As AggregateException
' MsgBox(ex.InnerException.Message)
End Try
Else
If Not cts Is Nothing Then
cts.Cancel()
End If
searchResults.Clear()
End If
End Sub
Function tPerformSearch(ByVal ct As CancellationToken) As ObservableCollection(Of ocSearchResults)
On Error GoTo sError
canceled = False
If curSearch = doSearchText Then
canceled = True
Return Nothing
End If
curSearch = doSearchText
Dim SR As New ObservableCollection(Of ocSearchResults)
Dim t As ocSearchResults
Dim rSelect As New ADODB.Recordset
Dim sSql As String = "SELECT DISTINCT CustomerID, CustomerName, City, State, Zip FROM qrySearchFieldsQuick WHERE "
Dim sWhere As String = "CustomerName Like '" & doSearchText & "%'"
SR.Clear()
With rSelect
.Open(sSql & sWhere & " ORDER BY CustomerName", MyCn, ADODB.CursorTypeEnum.adOpenStatic, ADODB.LockTypeEnum.adLockReadOnly)
Do While Not .EOF
If ct.IsCancellationRequested Then ' This never shows true, the process always returns a value, as if it wasn't async'
canceled = True
Return Nothing
End If
Do While IsDBNull(.Fields("CustomerID").Value)
.MoveNext()
Loop
t = New ocSearchResults(.Fields!CustomerID.Value, .Fields!CustomerName.Value.ToString.Trim, .Fields!City.Value.ToString.Trim, .Fields!State.Value.ToString.Trim, .Fields!Zip.Value.ToString.Trim)
If Not SR.Contains(t) Then
SR.Add(t)
End If
aMoveNext:
.MoveNext()
Loop
.Close()
End With
Return SR
Exit Function
sError:
MsgBox(ErrorToString, MsgBoxStyle.Exclamation)
End Function
Sub tRunWorkerCompleted(ByVal SR As ObservableCollection(Of ocSearchResults))
If canceled Then
Exit Sub
End If
If cts.IsCancellationRequested Then
Exit Sub
End If
searchResults.Clear()
For Each t As ocSearchResults In SR
searchResults.Add(t)
Next
ColorChecker = New BackgroundWorker
ColorChecker.WorkerReportsProgress = True
ColorChecker.WorkerSupportsCancellation = True
ColorChecker.RunWorkerAsync(searchResults)
lblRecordCount.Text = "(" & searchResults.Count & ") Records"
progBar.Value = 100
Exit Sub
sError:
MsgBox(ErrorToString)
End Sub
I don't know enough VB to give you any well written sample code, but if you're on .Net 4.0 I suggest switching to the System.Threading.Tasks namespace, which has cancellation abilities.
If (txtQuickSearch.Text.Length >= 3) Or (e.Key = Key.Enter) Then
If TokenSource Is Not Nothing Then
TokenSource.Cancel()
TokenSource = New CancellationTokenSource()
End If
Task.Factory.StartNew(SomeSearchMethod, txtQuickSearch.Text, TokenSource.Token)
End If
I am not sure a BackgroundWorker is flexible enough to provide an elegant solution for this type of background processing anyway. I think what I would do is to create a single dedicated thread for doing the searching. This thread would use the producer-consumer pattern for accepting work items and processing them.
The following code is a rough sketch of how I see this strategy working. You would call the SearchAsync method to request a new search. That method accepts a callback that gets invoked when and if the search operation found something. Notice that the consumer code (in the Run method) cancels its current search operation if another search request is queued. The effect is that the consumer only ever processes the latest request.
Public Class Searcher
Private m_Queue As BlockingCollection(Of WorkItem) = New BlockingCollection(Of WorkItem)()
Public Sub New()
Dim t = New Thread(AddressOf Run)
t.IsBackground = True
t.Start()
End Sub
Public Sub SearchAsync(ByVal text As String, ByVal callback As Action)
Dim wi = New WorkItem()
wi.Text = text
wi.Callback = callback
m_Queue.Add(wi)
End Sub
Private Sub Run()
Do While True
Dim wi As WorkItem = m_Queue.Take()
Dim found As Boolean = False
Do While Not found AndAlso m_Queue.Count = 0
' Continue searching using your custom algorithm here.
Loop
If found Then
wi.Callback()
End If
Loop
End Sub
Private Class WorkItem
Public Text As String
Public Callback As Action
End Class
End Class
Here is where the elegance happens. Look at how you can implement the logic from the UI thread now.
If (txtQuickSearch.Text.Length >= 3) Or (e.Key = Key.Enter) Then
searcher.SearchAsync(txtQuickSearch.Text, AddressOf OnSearchComplete)
End If
Note that OnSearchComplete will be executed on the worker thread so you will need to call Dispatcher.Invoke from within the callback if you want to publish the results to a UI control.
You can simulate a DoEvents in WPF by doing (in C#):
Dispatcher.Invoke(DispatcherPriority.Background, new Action(() => {}));

WinForm DateTimePicker blues. Is it me?

I want to be able to accept a NULL date using the DateTimePicker control.
The "Checked" property appears to be intended to specify whether the control "holds a date" or now. However, when "unchecked", the date still appears, though it appears disabled. To me, this is distracting. If the intent of the unchecked checkbox is to indicate that there is no date value, why is there ANY date value disable or otherwise that appears in the textbox? It seems to me that if the control is unchecked, the textbox should be EMPTY and that seeing a dimmed date value when the user really wants "no value" is distracting.
If the user toggled the checkbox on, then I would like the ability to place a default value into the textbox.
I am considering creating a usercontrol that toggles between a dateTimePicker control and a textBox, but I hate to go through this trouble.
I tried looking a Telerik's DateTimePicker but trying to get decent null handling functionality out of that control seems worse. I'd love to see a working example of what one of you think is a user-friendly code example with either the std MS or Telerik DateTimePicker control that accepts null input.
I've looked at a few opensource controls, but every time they fix one issue, they introduce others.
EDIT:
See my answer below. It seems to work fine, now I just want to make it part of every DateTimePicker's behavior.
I had the same problem. Well, actually I'm smart enough to understand, but my users had a problem.
I solved by removing the checkbox, and adding 2 radio buttons. Looks something like this now:
(using pseudo UI)
O No value entered
O | 1/1/2010 |V|
The top radiobutton is checked when there is no value (null), the bottom one when there is a value. I do not hide, or disable the bottom control, and users seem to understand.
The downside is, that it takes a lot more space.
PS: Next thing users will complain about is using the scroll-wheel when a combo-box has focus.
Klugey, but it seems to get the job done. If the checkbox is not checked, assume a NULL value.
Private Sub DateTimePicker1_ValueChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles DateTimePicker1.ValueChanged
If DateTimePicker1.Checked Then
DateTimePicker1.Format = DateTimePickerFormat.Short 'Or whatever the original format was
Else
DateTimePicker1.Format = DateTimePickerFormat.Custom
DateTimePicker1.CustomFormat = " "
End If
End Sub
OK, the next question...How do I roll this behavior into a subclassed DateTimePicker? What I want to do is to capture the original values of the Format and CustomFormat properties as set in the Properties window. But, this clearly isn't the way to do it.
Here's my feeble attempt:
Public Class NullableDateTimePicker
Inherits DateTimePicker
Private _OriginalFormat As DateTimePickerFormat
Private _OriginalCustomerFormat As String
Private Sub NullableDateTimePicker_ValueChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.ValueChanged
If Me.Checked Then
Me.Format = _OriginalFormat
Me.CustomFormat = _OriginalCustomerFormat
Else
Me.Format = DateTimePickerFormat.Custom
Me.CustomFormat = " "
End If
End Sub
Private Sub _DP_FormatChanged(ByVal sender As Object, ByVal e As System.EventArgs)
Static Count As Integer
If Count = 0 Then
_OriginalFormat = Me.Format
_OriginalCustomerFormat = Me.CustomFormat
End If
Count += 1
End Sub
Public Sub New()
AddHandler MyBase.FormatChanged, AddressOf _DP_FormatChanged
End Sub
End Class
I realise this is many years after your initial question but here's a subclass of the Telerik RadDateTimePicker that does what you were asking for:
Imports Telerik.WinControls.UI
Public Class DateTimePickerWithNull
Inherits Telerik.WinControls.UI.RadDateTimePicker
Private ReadOnly _calendar As RadCalendar
Sub New()
Dim calendarBehavior As RadDateTimePickerCalendar = Me.DateTimePickerElement.GetCurrentBehavior()
calendarBehavior.DropDownMinSize = New Size(220, 150)
_calendar = calendarBehavior.Calendar
_calendar.ShowFooter = True
AddHandler _calendar.ClearButton.Click, AddressOf ClearButton_Click
AddHandler _calendar.TodayButton.Click, AddressOf TodayButton_Click
End Sub
Private Sub ClearButton_Click(sender As Object, e As EventArgs)
'Do this to put the calendar away
_calendar.SelectedDate = _calendar.FocusedDate
'Then clear
Me.SetToNullValue()
End Sub
Private Sub TodayButton_Click(sender As Object, e As EventArgs)
_calendar.SelectedDate = _calendar.FocusedDate
End Sub
End Class
To get the value of the picker:
If DateTimePicker1.Value.Date = DateTimePicker1.NullDate Then
Label1.Text = "Null"
Else
Label1.Text = DateTimePicker1.Value.ToLongDateString
End If
A bit tricky to get right. This looked good:
Imports System.ComponentModel
Public Class MyDateTimePicker
Inherits DateTimePicker
Implements ISupportInitialize
Public Sub New()
Me.ShowCheckBox = True
Me.NullDate = True
End Sub
Private CustomFormatBacking As String = ""
Private FormatBacking As DateTimePickerFormat = DateTimePickerFormat.Long
<DefaultValue(True)> _
<Bindable(True)> _
Public Property NullDate() As Boolean
Get
Return Not Me.Checked
End Get
Set(ByVal value As Boolean)
Me.Checked = Not value
End Set
End Property
<DefaultValue("")> _
<Localizable(True)> _
<RefreshProperties(RefreshProperties.Repaint)> _
Public Shadows Property CustomFormat() As String
Get
Return CustomFormatBacking
End Get
Set(ByVal value As String)
CustomFormatBacking = value
If DesignMode Or Not NullDate Then MyBase.CustomFormat = value
End Set
End Property
<RefreshProperties(RefreshProperties.Repaint)> _
Public Shadows Property Format() As DateTimePickerFormat
Get
Return FormatBacking
End Get
Set(ByVal value As DateTimePickerFormat)
FormatBacking = value
If DesignMode Or Not NullDate Then MyBase.Format = value
End Set
End Property
<DefaultValue(true)> _
<Bindable(True)> _
<DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
Public Shadows Property Checked() As Boolean
Get
Return MyBase.Checked
End Get
Set(ByVal value As Boolean)
MyBase.Checked = value
End Set
End Property
Private Sub updateNullState()
If NullDate and Not DesignMode Then
MyBase.CustomFormat = " "
MyBase.Format = DateTimePickerFormat.Custom
Else
MyBase.CustomFormat = CustomFormatBacking
MyBase.Format = FormatBacking
End If
End Sub
Public Sub BeginInit() Implements System.ComponentModel.ISupportInitialize.BeginInit
End Sub
Public Sub EndInit() Implements System.ComponentModel.ISupportInitialize.EndInit
updateNullState()
End Sub
Protected Overrides Sub OnValueChanged(ByVal eventargs As System.EventArgs)
MyBase.OnValueChanged(eventargs)
updateNullState()
End Sub
End Class

Resources