Lets say I have a form with 100 textboxes, comboboxes and other controls and I want to check if any of the controls is empty. When I click on 'OK' button, I want the messagebox to show a list of errors example Textbox1 is empty, Textbox30 is empty and such.
I can achieve this by doing the tedious method where I check textbox1 and messagebox is shown, check textbox2 and messagebox is shown again and so on.
I want the messagebox to show only once. How can I achieve this?
What I have did is that I set up an array and store all the error messages to be shown later by selecting (example Msgbox(errMessages(3) + Environment.newline + errMessages(30)) and I know this is not the right way to do it as well.
Please and thank you in advance.
Here is a direct answer to your question:
You can store the empty controls in a list, and at the end create the message like this:
Dim empty_controls = New List(Of Control)
If TextBox1.Text = String.Empty Then
empty_controls.Add(TextBox1)
End If
If TextBox2.Text = String.Empty Then
empty_controls.Add(TextBox2)
End If
Dim result As String = String.Join(
Environment.NewLine,
empty_controls.Select(Function(c As Control) c.Name + " is empty"))
MessageBox.Show(result)
Here is even a better way to detect which text boxes are empty:
Dim empty_controls = New List(Of Control)
//The following line will search through all text boxes on the form
empty_controls.AddRange(
Controls.OfType(Of TextBox).Where(Function(c As Control) c.Text = String.Empty))
//Here you can add other kinds of controls with their own way of determining if they are empty
Dim result As String = String.Join(
Environment.NewLine,
empty_controls.Select(Function(c As Control) c.Name + " is empty"))
MessageBox.Show(result)
Related
Long-time WinForms programmer relatively new to WPF. I'm databinding a DataGrid in Code-Behind using a SQL Query. I reuse the DataGrid because I'm using Ribbon Tabs and reload different data in the grid dependent on the Tab selected. So binding to a static resource is not possible.
I'm trying to open a new window on a double-click event but get the following exceeption - "Unable to cast object of type 'System.Windows.RoutedEventArgs' to type 'System.Windows.Input.MouseButtonEventArgs'."
This used to be a simple thing in WinForms. My code is as follows:
Try
Dim StrRow As DataRowView = MainDataGrid.SelectedItem
Dim CellValue As String = StrRow.Row(0).ToString()
'MsgBox(CellValue)
e.Handled = True
Dim EventDetails = New EventDetails()
EventDetails.Show()
OpenEventDetailsWindow()
Catch ex As Exception
MsgBox("No Event No.for this Event")
End Try
The messagebox shows the proper string (first cell value of selected row and the new window actually pops up right before the exception is thrown. I've seen plenty of posts that say this method should work but it doesn't. I've been so far unsuccessful in fixing this. Thanks in advance.
I am programmatically creating a GridViewColumn in WPF as follows:
Dim oGVCol As GridViewColumn = New GridViewColumn
Dim oHeaderTemplate As DataTemplate
oHeaderTemplate = New DataTemplate
Dim oGridFactory As FrameworkElementFactory = New FrameworkElementFactory(GetType(Grid))
oHeaderTemplate.VisualTree = oGridFactory
oGridFactory.SetValue(Grid.BackgroundProperty, Brushes.Transparent)
oGVCol.HeaderTemplate = oHeaderTemplate
(I have removed irrelevant code to set the content of the grid)
What I can't figure out is how to add a "click" event for the GridViewColumnHeader itself. I can add events to the Grid and any other Controls I added through the Factory objects, no problem. But I'm stuck on how to add an event to the header itself.
If you have a solution in VB.NET, great, but C# is fine too.
One (failed) attempt:
AddHandler TryCast(oGVCol.Header, GridViewColumnHeader).Click, AddressOf HeaderClick
Sadly it turns out that I cannot cast oGVCol.Header to a GridViewColumnHeader.
Ok, it may not be pretty, but I found a pretty decent solution to the problem.
Firstly, when I create the Grid Factory for the root element in the header's Visual Tree, I give it a name
oGridFactory.SetValue(Grid.NameProperty, <column name here>))
(Please note that the Names must have only letters, numbers and underscores so if your data contains column names that don't have those, you'll need to deal with that both here, to convert invalid names to valid ones, and below, to revert them back to their original names if necessary.... I won't detail that functionality here)
Also, I add an event handler to the Root "Grid" in the Template for the column header:
oGridFactory.AddHandler(Grid.SizeChangedEvent,
New SizeChangedEventHandler(AddressOf ColumnHeaderSizeChanged))
The "magic" happens in the procedure ColumnHeaderSizeChanged. This procedure is called both when the grid is Rendered the first time, but also when the user is manually resizing columns.
Signature:
Private Sub ColumnHeaderSizeChanged(sender As Object, e As SizeChangedEventArgs)
I keep a List(Of GridViewColumnHeaders) which is reset to an empty list when I need to replace the Grid with a different one. In the ColumnHeaderSizeChanged event I then do the following:
The first thing we need to do is get to the Root of the controls in the Column Header. For example, your column header may contain a TextBlock to show a column name, and icons to indicate it's been sorted up or down. That sort of thing. When the user clicks on the header they may be clicking on any of those controls, so:
Dim oParent As Object
Dim oColHeader As GridViewColumnHeader = Nothing
Dim sColHeaderName As String = String.Empty
Dim oGWH As Grid = Nothing
oParent = e.OriginalSource 'This may be any of the controls in the header.
If Not oParent Is Nothing Then
Try
While Not oParent.Parent Is Nothing
'So we keep going down the Tree until we hit the Root Parent
'which will be the main Grid created in the Grid Factory
oParent = oParent.Parent
End While
Catch
End Try
End If
'But at this point, if we still have a control, it will be the main Grid
If oParent Is Nothing Then
Exit Sub
End If
If TryCast(oParent, Grid) Is Nothing Then
'what the heck is this? This SHOULD be the Grid at the root of the Visual Tree,
'so if, for whatever reason, this is NOT a Grid, get outta here.
Exit Sub
End If
By this point we're on the main Grid, but now we need to get the GridViewColumnHeader into which this Grid has been created. So now we go to the TemplatedParent
While Not oParent.TemplatedParent Is Nothing
oParent = oParent.TemplatedParent
oColHeader = TryCast(oParent, GridViewColumnHeader)
If Not oColHeader Is Nothing Then
'This procedure is called both when the Grid View is first rendered,
'and when the user is dragging the column width.
If Mouse.LeftButton = MouseButtonState.Pressed Then
'Do something appropriate to when the user is resizing the column
Else
'Do something appropriate to when the grid
'is first Rendered or re-displayed
End If
Exit While
End If
End While
At this point we have the GridViewColumnHeader we need, so we can add it to the List and add a Handler for its Click event. moColHeaders is the List(Of GridViewColumnHeaders)
If Not oColHeader Is Nothing Then
If Not moColHeaders.Contains(oColHeader) Then
oColHeader.Name = <column name here>
moColHeaders.Add(oColHeader) 'Only need to add it once!
AddHandler oColHeader.Click, AddressOf HeaderClick
End If
End If
Now you can code your HeaderClick procedure to handle the GridViewColumnHeader.Click event.
Signature:
Private Sub HeaderClick(sender As Object, e As RoutedEventArgs)
I'm having some trouble looping through the comboboxes on my form and to clear the data sources (using VB.net in Visual Studio 2017)
the code I have so far is this:
==============================
For Each c As Control In Me.Controls.OfType(Of ComboBox)()
If c.Name IsNot Control.Name Then c.datasource = Nothing
Next
==============================
However, get the error " 'DataSource' is not a member of 'control' "
The "Control" is a variable passed to the sub
====================
Public Sub Item_Select(ByRef Field As String, ByRef Control As Control)
====================
and is called like this
==================
Dim Control1 As Control = ComboBox2
Item_Select("Last_Name", Control1)
==================
The datasource is an SQL table
Could anyone point me in the right direction? I don't seem to be getting the right search terms in google for a workable solution!
Thanks so much in advance, and apologies if this post is inappropriate (i have read the guide!)
This woks as a solution!
For Each c As Control In Me.Controls
If TypeOf c Is ComboBox Then
Dim ctrl = DirectCast(c, ComboBox)
ctrl.DataSource = Nothing
End If
Next
Hey all i have created a few text boxes and also buttons to go along with them at run-time.
The code for the button is:
Dim updateButton As New Button
updateButton.Name = "button_" & ticketTheRowNum & "_" & ticketRowNum
updateButton.Content = "UPDATE!"
updateButton.Height = 26
Canvas.SetTop(updateButton, 24 * ticketTheRowNum)
Canvas.SetLeft(updateButton, 330 + lblNotes.Width)
updateButton.Width = lblNotes.Width / 2 - 10
updateButton.Background = New SolidColorBrush(Colors.Green)
Grid.SetRow(updateButton, 0)
Grid.SetColumn(updateButton, 0)
Grid.SetZIndex(updateButton, 2500)
cavTicket.Children.Add(updateButton)
And the code for the text box is this:
Dim txtBlock As New TextBox
txtBlock.Name = "txt_" & ticketTheRowNum & "_" & ticketRowNum
txtBlock.Text = theHeader
txtBlock.TextWrapping = TextWrapping.Wrap
txtBlock.Width = lblNotes.Width - 5
txtBlock.BorderThickness = New Thickness(0)
txtBlock.TextAlignment = TextAlignment.Left
txtBlock.VerticalScrollBarVisibility = ScrollBarVisibility.Auto
tmpExpander.Content = txtBlock
Now the button and text box displays just fine on the WPF but I am unsure of how to hook the button up to have it know whats in the text box and save its content. Currently since I am creating these at run-time I don't have access like I would if its was already on the form before it ran (I would simply just call the buttons click event and then within that I would call the text box content and save it).
If both of your code blocks come from the same class then just add a private property txtBlock (by the way why block and not Box?). So when you will create your TextBox control it will then be visible to all the functions in your class including the button event handler.
But as NETscape said. WPF is all about XAML, binding, and WVVM. So if you want to avoid headaches use them ;)
I m working with WPF DataGrid and I want to retrieve a DataGridCell's value by using the column and the row indexes : all what I could do is this but it didn't work :
myDatGrid.Columns[0].GetCellContent((DataGridRow)myDatGrid.Items[0]);
Could you please show me the way to realize that
Your cells value is going to be contingent on what the given column is bound to. The entire row will be the instance of your model.
Assume we have a collection of Person classes which we are binding to within our DataGrid.
Person p = ((ContentPresenter)myDatGrid.Columns[0].GetCellContent(myDatGrid.Items[0])).Content;
The Content property is going to return the underlying model for the row. If you wanted to obtain a given property you can do so by directly accessing the underyling object which should implement INotifyPropertyChanged, no need to fool with the DataGrid as you would in a WinForms application.
I tried what has been proposed above and it did not work.
I struggled for a whole day trying to bind a ComboBox and select the correct value in a WPF Datagrid when it first loads, so I thought I would share my solution here in hopes that somebody else could benefit. Sorry about the VB, but the concept should work in C# also.
First, if you are trying to get the values populated and selected when the grid first loads, you need to put your code in the correct event handler: LayoutUpdated. In other event handlers, the GetCellContent function returns Nothing (null). You could also put this code in an event handler which handles an event occurring later, such as a Button.Click event, if that meets your requirements.
Second, the code:
For i As Int32 = 0 To DataGrid1.Items.Count - 1
Dim row As DataGridRow
row = DataGrid1.ItemContainerGenerator.ContainerFromItem(DataGrid1.Items(i))
If row IsNot Nothing AndAlso row.Item IsNot Nothing Then
Dim cp As ContentPresenter = DataGrid1.Columns(3).GetCellContent(DataGrid1.Items(i))
If cp IsNot Nothing AndAlso cp.ContentTemplate IsNot Nothing Then
Dim dt As DataTemplate = cp.ContentTemplate
If dt IsNot Nothing Then
Dim cb As ComboBox = dt.FindName("cbVendorNames", cp)
If cb IsNot Nothing Then
cb.ItemsSource = Vendors
cb.DisplayMemberPath = "VendorName"
cb.SelectedValuePath = "AS_ID"
cb.SelectedValue = "" ' set your selected value here
End If
End If
End If
End If
Next
What this code does is (a) loop through all of the rows in the datagrid, (b) get the ContentPresenter for the cell selected (in this case, cell 3 in each row), (c) get the DataTemplate for the ContentPresenter, and (d) finally, get the reference to the ComboBox. This allowed me to bind the ComboBox to an external datasource (a List(Of Vendor) and select its value. This code worked for me.
I do not find this solution particularly elegant or efficient, but it does work. If somebody has a better solution, please post it.
OK I think I got it , I missed some casts that I should put , because the column I'm using is a DataGridComboBoxColumn
I should actually do this :
((ComboBox)(myDatGrid.Columns[0].GetCellContent(
(TestData)myDatGrid.Items[0]))).SelectedValue.ToString());
Now it works.