When I call myControl.ValidateChildren() it returns false. How can I find out, which of the children failed to validate?
(I'm working with WinForms for the first time and am confronted with a huge pile of messy legacy code.)
The only way to do that seems to be to debug through the Validating event handlers. (See Hans' comment on the question.)
With some Reflection it is possible to call the same internal functions that .net calls inside ValidateChildren.
This allows to retrieve the validation result of each control and find the one that causes the problem.
I wrote this small function for simple use:
Public Function GetFirstInvalidControl(container As ContainerControl) As Control
Dim getStyle = GetType(Control).GetMethod("GetStyle", Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance)
Dim isInvalid = GetType(Control).GetMethod("PerformControlValidation", Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance)
Dim parents = New Queue(Of Control)
parents.Enqueue(container)
While parents.Any
For Each control As Control In parents.Dequeue.Controls
If CBool(getStyle.Invoke(control, {ControlStyles.Selectable})) AndAlso CBool(isInvalid.Invoke(control, {True})) Then
Return control
End If
parents.Enqueue(control)
Next
End While
Return Nothing
End Function
Related
I've seen a few other posts about this but I seem to be confused since I've seen it done several ways and haven't gotten it correct any of the ways, so I thought I would ask specifically for my case so I can learn what I am doing wrong. After learning and switching to C# for most of my programming, VB.net seems so clunky in its syntax with a lot of things, especially lambda and "on-the-fly" functions.
I have a long running task for a football game I am working on where it generates players. Its an async method utilizing the Task.Factory.StartNew method.
Here is the pertinent code:
Private Sub CreateDraft_Click(sender As Object, e As RoutedEventArgs) Handles CreateDraft.Click
TimeIt(Sub() ReallyGenNewPlayers())
End Sub
'Uses a stopwatch to time how long it takes and sends to console.
Private Sub TimeIt(MyAction As Action)
Dim SW As New Stopwatch
SW.Start()
MyAction()
Console.WriteLine($"Total Time Generating Players: {SW.Elapsed} seconds")
SW.Stop()
End Sub
Private Async Sub GenNewPlayersASync()
Dim myvalue = 0
'Generate the Players on an Async Thread
Dim x As Integer
For i As Integer = 1 To NumPlayers
x = i 'avoids issue with using the iteration variable inside the delegate
CollegePlayers.GenDraftPlayers(i, MyDraft, DraftDT, DraftClass, PosCount)
'Prog_ValueChanged(DBNull.Value, New RoutedPropertyChangedEventArgs(Of Double))
'Calls a delegate to update the progress bar to avoid having the variable locked by the background thread
Dispatcher.Invoke(Sub()
worker.ReportProgress((x / NumPlayers) * 100)
End Sub)
Next i
End Sub
'Creates a task to run the player generation code and wait til its finished
Private Sub ReallyGenNewPlayers()
Dim mytask = Task.Factory.StartNew(Sub() GenNewPlayersASync())
Task.WaitAll(mytask)
End Sub
So here is what I would like to do:
I have a progressbar I created in XAML that has a Progress_Changed Event. This is what I have for it so far based on another post, but the issue is when I have to call the function inside GenNewPlayersAsync() where it wants a RoutedPropertyChangeEventArgs as a double which I'm not exactly sure what to do...I tried creating one using .New(old value, new value) but that didn't work either and threw an error.
Public Async Sub Prog_ValueChanged(sender As Object, e As RoutedPropertyChangedEventArgs(Of Double))
Dim progress = New Progress(Of Integer)(Function(percent)
Prog.Value = e.NewValue
Return Prog.Value
End Function)
Await Task.Run(Sub() DoProcessing(progress))
End Sub
Public Sub DoProcessing(progress As IProgress(Of Integer))
Dim i As Integer = 0
While i <> 100
Thread.Sleep(100)
' CPU-bound work
If progress IsNot Nothing Then
progress.Report(i)
End If
End While
End Sub
I would like for the progressbar to be bound to an INotifyChanged Property and update itself automatically when the value gets changed. None of it seems to be working. The UI is still unresponsive and when I set different parts to ASync, I start getting Exceptions where it appears certain parts of the generation algorithm aren't working as they are returning null...very confused with all of this, and I am sure the answer is probably pretty simple...
If you guys could give several examples of different ways to get this to work so I can learn different methods and maybe state the pros and cons of the method I would greatly appreciate it...
Developers know that WinForms ToolStrip control usage may cause managed memory leaks if we do not force it to release some stuff manually. I mean the internal event handler of the system static Microsoft.Win32.SystemEvents.UserPreferenceChanged event. To release the resources properly, we need an explicit call of the ToolStrip Dispose method as it is described, for instance, in this or this SO posts.
However, this does not help if we use a ToolStrip from a descendant of System.ComponentModel.Component - at least, in my case. Here is the corresponding part of code:
Private Class DropDownFilterBox
Inherits System.ComponentModel.Component
Private WithEvents fToolStripMain As New AutoFilterToolStrip
Private WithEvents fToolStripOKCancel As New AutoFilterToolStrip
Private WithEvents fContextMenuStripCustomFilterOperators As New ContextMenuStrip
Private WithEvents fToolStripDropDownCustomFilterDatePicker As New ToolStripDropDown
Private WithEvents fToolStripControlHostCustomFilterDatePicker As New AutoFilterToolStripControlHostDatePicker
.......................
Public Overloads Sub Dispose()
fToolStripMain.Dispose()
fToolStripOKCancel.Dispose()
fContextMenuStripCustomFilterOperators.Dispose()
fToolStripDropDownCustomFilterDatePicker.Dispose()
fToolStripControlHostCustomFilterDatePicker.Dispose()
MyBase.Dispose()
End Sub
End Class
The AutoFilterToolStrip is defined like this:
Private Class AutoFilterToolStrip
Inherits ToolStrip
......................
End Class
, but this should not matter in our context.
I even call DropDownFilterBox.Dispose manually to clean up the used resources when it is needed, but it seems this does not have any effect.
Some developers recommend hiding ToolStrips (set the Visible property to False) as the UserPreferenceChanged event handler should be removed by ToolStrip automatically in this case. Yes, the internal HookStaticEvents method which should do the work is called at that. but this also does not help.
I even tried to detach the problem event handler manually using reflection:
RemoveHandler Microsoft.Win32.SystemEvents.UserPreferenceChanged, _
DirectCast( _
System.Delegate.CreateDelegate(GetType(Microsoft.Win32.UserPreferenceChangedEventHandler), fToolStripMain, "OnUserPreferenceChanged"), _
Microsoft.Win32.UserPreferenceChangedEventHandler _
)
, but this also does not have any effect.
Any ideas on how to overcome this memory leak problem in our case and why the explicit call of ToolStrip.Dispose may not work in our case?
We develop in VB.NET 2010 for .NET Framework 4+ (client profile).
The missing part of my code was the following:
Dim myOverflowButton As ToolStripOverflow
myOverflowButton = DirectCast(fToolStripMain.OverflowButton.DropDown, ToolStripOverflow)
If (myOverflowButton IsNot Nothing) Then
myOverflowButton.Dispose()
End If
myOverflowButton = DirectCast(fToolStripOKCancel.OverflowButton.DropDown, ToolStripOverflow)
If (myOverflowButton IsNot Nothing) Then
myOverflowButton.Dispose()
End If
ToolStrip creates the so called overflow button automatically, and it also subscribes to the UserPreferenceChanged event which may cause memory leaks! Some more info about this can be found on SO here: ToolStrip memory leak.
Now the full listing of my component's Dispose method looks like this:
Public Overloads Sub Dispose()
With fContainer.Controls
.Remove(fToolStripMain)
.Remove(fToolStripOKCancel)
End With
fToolStripMain.Visible = False
fToolStripOKCancel.Visible = False
fContextMenuStripCustomFilterOperators.Visible = False
fToolStripDropDownCustomFilterDatePicker.Visible = False
fToolStripControlHostCustomFilterDatePicker.Visible = False
fToolStripMain.Dispose()
fToolStripOKCancel.Dispose()
fContextMenuStripCustomFilterOperators.Dispose()
fToolStripDropDownCustomFilterDatePicker.Dispose()
fToolStripControlHostCustomFilterDatePicker.Dispose()
Dim myOverflowButton As ToolStripOverflow
myOverflowButton = DirectCast(fToolStripMain.OverflowButton.DropDown, ToolStripOverflow)
If (myOverflowButton IsNot Nothing) Then
myOverflowButton.Dispose()
End If
myOverflowButton = DirectCast(fToolStripOKCancel.OverflowButton.DropDown, ToolStripOverflow)
If (myOverflowButton IsNot Nothing) Then
myOverflowButton.Dispose()
End If
' Dispose calls for other used components
MyBase.Dispose()
End Sub
I also should admit that the technique of finding memory leaks in managed code, described in the CodeProject article Memory Leak Detection in .NET, helped me a lot in finding the solution - BTW, spending no penny for buying commercial memory profilers.
We use the CellFormatting event to colour code cells in various grids all over our application.
We've got some generic code which handles export to Excel (and print) but it does it in Black & White. Now we want to change this and pick up the colour from the grids.
This question & answer has helped (and it works) ... except there's a problem with larger grids that extend beyond a single screen. The portions of the grid which haven't yet been displayed on screen are (logically) never getting their CellFormatting code fired, and so their underlying colour never gets set. As a result, in Excel, the colour coding fizzles out half way down the page.
Seems there are three solutions:
1) Tell the user he has to scroll to all parts of the grid before doing an Export to Excel. Ha! Not a serious solution
2) Programmatically scroll to all parts of the grid before doing an Export to Excel. Only slighly less horrible than (1)
3) In our Export to Excel code, fire something at the top which tells the DataGridView to paint/format its entire area e.g.
MyDataGridView.FormatAllCells()
Is there something that does something like this???
Oh, and there is a fourth option but this will involve touching a massive amount of existing code:
4) Stop using CellFormatting event, format the cells at load time. Problem with this is we'd have to retool every grid in our application since CellFormatting is the way we've done it since year dot.
As noted in the other answers, accessing the DataGridViewCell.FormattedValue is indeed an easy way to force the CellFormatting event to be (re-)called for a specific cell. In my case, however, this property was also leading to undesirable side-effects involving the auto-resizing of the columns. While searching a long time for a workable solution, I finally encountered the following magic methods that work perfectly: DataGridView.Invalidate(), DataGridView.InvalidateColumn(), DataGridView.InvalidateRow(), and DataGridView.InvalidateCell().
These 4 methods force the CellFormatting event to be re-called only for the specified scope (cell, column, row, or whole table), and also without causing any nasty auto-resizing artifacts.
I have a possible solution - In your export function access the Cell.FormattedValue property of each cell. According to Microsoft, this forces the CellFormatting event to fire.
Assuming, as #DavidHall suggests, there is no magic .FormatAllCells our only option is to stop using CellFormatting.
However, new problem here is that applying cell style formatting during load doesn’t seem to have any effect. Lots of posts out there if you Google it. Also they point out that if you put the same code under a button on the form and click it after loading (instead of in the load, the code will work ... so the grid has to be visible before styling can apply). Most advice on the topic suggests you use ... drumroll ... CellFormatting. Aargh!
Eventually found a post which suggests using the DataBindingComplete event of the grid. And this works.
Admittedly, this solution is a variant of my unwanted option "4".
I had the same problem and I've ended up with something quite similar to your solution #4.
like you, I've used the DataBindingComplete event. but, Since I've used Extension method, the changes in the existing code are bearable:
internal static class DataGridViewExtention
{
public static void SetGridBackColorMyStyle(this DataGridView p_dgvToManipulate)
{
p_dgvToManipulate.RowPrePaint += p_dgvToManipulate_RowPrePaint;
p_dgvToManipulate.DataBindingComplete += p_dgvToManipulate_DataBindingComplete;
}
// for the first part - Coloring the whole grid I used the `DataGridView.DataBindingComplete` event:
private static void p_dgvToManipulate_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
{
foreach (DataGridViewRow objCurrRow in ((DataGridView)sender).Rows)
{
// Get the domain object from row
DomainObject objSelectedItem = (DomainObject)objCurrRow.DataBoundItem;
// if item is valid ....
if objSelectedItem != null)
{
// Change backcolor of row using my method
objCurrRow.DefaultCellStyle.BackColor = GetColorForMyRow(objSelectedItem);
}
}
}
// for the second part (disabling the Selected row from effecting the BackColor i've setted myself, i've used `DataGridView.RowPrePaint` event:
private static void p_dgvToManipulate_RowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e)
{
// If current row about to be painted is selected by user
if (((DataGridView)sender).Rows[e.RowIndex].Selected)
{
// Get current grid row
var objGridRow = ((DataGridView)sender).Rows[e.RowIndex];
// Get selectedItem
DomainObject objSelectedItem = (DomainObject)objGridRow.DataBoundItem;
// if item is valid ....
if (objSelectedItem != null && objSelectedItem.ObjectId != 0)
{
// Set color for row instead of "DefaultCellStyle" (same color as we used at DataBindingComplete event)
objGridRow.DefaultCellStyle.SelectionBackColor = GetColorForMyRow(objSelectedItem);
}
// Since the selected row is no longer unique, we need to let the used to identify it by making the font Bold
objGridRow.DefaultCellStyle.Font = new Font(((DataGridView)sender).Font.FontFamily, ((DataGridView)sender).Font.Size, FontStyle.Bold);
}
// If current row is not selected by user
else
{
// Make sure the Font is not Bold. (for not misleading the user about selected row...)
((DataGridView)sender).Rows[e.RowIndex].DefaultCellStyle.Font = new Font(((DataGridView)sender).Font.FontFamily,
((DataGridView)sender).Font.Size, FontStyle.Regular);
}
}
}
A possible solution if you do want to reuse the formatting provided during the Cellformatting-event (e.g. the cellstyle-elements like fontbold and backgroundcolor). These cellstyles seem to be only available between the 'cellformatting' and 'cellpainting' events but not in the datagridview-cell's style itself..
Capture the cellstyles during the cellformatting-event with a second handler like this:
in the exportmodule add a shared list, array or dictionary to store the cellstyles:
Dim oDataGridFormattingDictionary as Dictionary(Of String, DataGridViewCellStyle) = nothing
initialize the dictionary and add a second handler to the datagridview in your printing or export-code. In vb.net something like this:
oDataGridFormattingDictionary = New Dictionary(Of String, DataGridViewCellStyle)
AddHandler MyDatagridviewControl.CellFormatting, AddressOf OnPrintDataGridView_CellFormatting
Add the code for the handler
Private Sub OnPrintDataGridView_CellFormatting(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellFormattingEventArgs)
If e.RowIndex > -1 AndAlso e.ColumnIndex > -1 AndAlso Not e.CellStyle Is Nothing Then
If Not oDataGridFormattingDictionary Is Nothing andalso oDataGridFormattingDictionary.ContainsKey(e.RowIndex & "_" & e.ColumnIndex) = False Then
oDataGridFormattingDictionary.Add(e.RowIndex & "_" & e.ColumnIndex, e.CellStyle)
End If
End If
End Sub
Very important: to make sure the original cellformating-event (AND the second cellformatting-handler after that) are actually called you have to request the formattedvalue for each cell that you want to print (e.g.
oValue = Datagridview.rows(printRowIndex).Cells(printColumnIndex).FormattedValue)
!
When printing you can now check if the cell has formatting. E.g.:
if not oDataGridFormattingDictionary is nothing andalso oDataGridFormattingDictionary.ContainsKey(printRowIndex & "_" & printColumnIndex) Then
... the cellstyle is accesible via:
oDataGridFormattingDictionary(printRowIndex & "_" & printColumnIndex)
end if
at the end of the export or printcode remove the handler and set the dictionary to nothing
RemoveHandler DirectCast(itemToPrint.TheControl, DataGridView).CellFormatting, AddressOf OnPrintDataGridView_CellFormatting
oDataGridFormattingDictionary = nothing
I'm trying to update a dependancy property in VB.Net 4.0 inside of an Async callback. I feel like I am doing this correctly but I'm still getting the "The calling thread cannot access this object because a different thread owns it." error. Does someone see a better way of using delegates in VB.Net 4.0?
Private WithEvents myObj as CallingObject
Private Delegate Sub MyErrorDel(ByVal strError as string)
Public Property ErrorMessage As String
Get
Return CStr(GetValue(ErrorMessageProperty))
End Get
Set(ByVal value As String)
SetValue(ErrorMessageProperty, value)
End Set
End Property
Private Sub MySub()
myObj.CallFuncAsync()
End Sub
Private Sub DisplayError(ByVal strError as String)
'Set Dependancy Property value Bound to UI Textbox
ErrorMessage = strError
End Sub
Private Sub myObj_CallFuncCompleted(Byval sender as Object, ByVal e as CallFuncEventArgs)
'Call delegate and pass in error string as argument
Dim delError as MyErrorDel
delError = New MyErrorDel(AddressOf DisplayError)
delError("An error occured")
Me.Dispatcher.Invoke(delError, System.Windows.Threading.DispatcherPriority.Normal, Nothing)
End Sub
Whenever ErrorMessage gets set inside of DisplayError an exception gets thrown, even though I am using the dispatcher to call DisplayError.
If anyone see any issues with the way I am trying to access Dependancy Properties from a async callback I would really appreciate the feedback.
Thanks for the help!
Oh and I'm pretty new at blogging about code issues. Any suggestions about how to better formulate this question would be welcome as well.
The problem might be that at the call to Me... you are already accessing an object owned by another thread, try to store a reference to the dispatcher beforehand or possibly use Application.Current.Dispatcher.
Since you didn't indicate the offending line, I suspect the problem here is that you're invokng your delegate in the line delError("An error occured") rather than waiting until you get to the dispatcher. Consider changing your CallFuncCompeted implementation to
Me.Dispatcher.Invoke(AddressOf DisplayError, "An error occureed")
I'm trying to make sure that I don't leave any loose ends open in my application and am concerned about a few but might get my answer from this one. I've "overriden" some functions so that way I can try and keep all the resources as clean and free as possible. So in this instance, I have a function called ExecuteReader which returns a DbDataReader as normal, but all I had to pass to it was a SQL string rather than recreating a DbCommand every time. I want to make sure that even though I'm unable to call dbCommand.Dispose() that it is actually doing so. Any and all help is appreciated.
Public Function ExecuteReader(ByVal strSQL As String) As DbDataReader
Dim dbCommand = _dbConnection.CreateCommand()
dbCommand.CommandText = strSQL
dbCommand.Prepare()
Return dbCommand.ExecuteReader()
End Function
I was thinking of using a using statement, but I remember seeing a thread where someone said they think it was causing them problems having a return in the using statement. Also, I'm not sure if this should be community wiki or not. If it should be, let me know. Thanks.
Code Update:
Here's an example of how I'm using it.
Public Sub RevertDatabase()
'This function can be used whenever all changes need to be undone, but was created'
'for saving the data as a .out file. It sets all changes back to their original value.'
'Set the data reader to all parts and columns that were changed.'
_dbReader = ExecuteReader("SELECT PART_ID, PART_PREV_VALUE, REPORT_COLUMN_NAME FROM REPORTS WHERE PART_PREV_VALUE NOT NULL")
'Create an instance of the Command class.'
Dim cmd = New Command()
While _dbReader.Read()
'For each part and columns that has been changed, set the values in the'
'new cmd variable and then update the value using the same function'
'that is used whenever a value is changed in the data grid view.'
cmd.CommandString = _dbReader("REPORT_COLUMN_NAME").ToString().Replace("_", " ")
cmd.Value = _dbReader("PART_PREV_VALUE").ToString()
cmd.ID = _dbReader("PART_ID").ToString()
UpdateValue(cmd)
End While
'Close the reader.'
_dbReader.Close()
End Sub
In here, I set the _dbReader to what I'd get from the function and eventually I close the _dbReader. I do not close the connection as I don't open it each time I make a query. This is a SQLite database that only one user will be using at a time (small application with very very very little likeliness it will grow) so I didn't think it necessary to close and open the connection all the time. Maybe I'm wrong, not sure. Using it this way though, it is potentially ok for cleaning resources?
It's a bad idea passing back DbDataReader as this requires the stream to be kept open and relies on calling code to do the right thing by disposing the reader. It's also makes it difficult to close the underlying command and connection objects. One way to facilitate this if you really want to expose the reader, is to use CommandBehavour. This can be used to close the underlying connection when the reader itself is closed.
dbCommand.ExecuteReader(CommandBehavour.CloseConnection)
As DbConnection, DbCommand and DbDataReader are Disposable you need to refactor the code to allow for these to be cleaned up when the code has finished with them. One way is to achieve this is to implement IDisposable within your own class and bubble dispose though to any encapsulated objects. The calling code can then implement using to ensure resources are freed.
Using helper As New DatabaseHelper()
Using reader As IDataReader = helper.LoadSomeDataReader()
' do something with reader
End Using
End Using
UPDATE:
Your second block of code would become more like:
Public Sub RevertDatabase()
Using _dbReader As IDataReader = ExecuteReader(...)
While _dbReader.Read()
Using cmd As New Command()
cmd.CommandString = _dbReader("REPORT_COLUMN_NAME").ToString().Replace("_", " ")
cmd.Value = _dbReader("PART_PREV_VALUE").ToString()
cmd.ID = _dbReader("PART_ID").ToString()
UpdateValue(cmd)
End Using
End While
End Using
End Sub
You should always open and close the connection, rather then just leaving it open. It's could practice and although highly unlikely in a very small application, you could run into issues. You also shouldn't be keeping a reference to the _dbReader IMO.
Returning an IDisposable instance from a function is perfectly fine. The ownership of the instance is being transferred from the function to the caller and it is now the caller's responsibility to call Dispose. This is the exact same principal in play when calling IDbCommand.ExecuteReader.
You should definitely be using the Using statement. It makes the code more readable since it automatically inserts the correct try-finally block that calls the Dispose method. I do not know the details of the issue you were referring to, but you should not have any problems yourself since you are not doing anything out of the ordinary.
Public Sub RevertDatabase()
Using dbReader As DbDataReader = ExecuteReader(...)
// Use the data reader here.
End Using
End Sub