Config File cannot be saved for second instance WPF - wpf

I am having an issue saving the config file when working with two instances of my program. I was able to reproduce this issue in a simple example project that looks like that:
Class MainWindow
Dim config As System.Configuration.Configuration
Public Sub New()
config = System.Configuration.ConfigurationManager.OpenExeConfiguration(System.Configuration.ConfigurationUserLevel.None)
End Sub
Protected Overrides Sub OnClosing(e As CancelEventArgs)
config.Save(ConfigurationSaveMode.Modified, True)
End Sub
End Class
The first instance is saving the config on closing, but as soon as I am trying to close the second instance, config.Save(ConfigurationSaveMode.Modified, True) is throwing an error saying that the config file was changed by another program. I hope someone is able to help me with that problem. Thanks in advance.
edit: Forgot to delete the MyBase call
edit2: Tried Chillzy suggestion, but it fails as well.
Protected Overrides Sub OnClosing(e As CancelEventArgs)
Dim mdate As String = Date.Now.ToString("yyyyMMdd_HHmmss")
Dim mptpath As String = Path.GetDirectoryName(config.FilePath) & "\" & mdate
config.SaveAs(mdate, ConfigurationSaveMode.Full, True)
File.Delete(fpath)
File.Move(mptpath, fpath)
End Sub

There. You copy load the config file then save as. Re-Read the saveas file as the current config file. on your way out you do the reverse
Imports System.Configuration
Imports System.IO
Public Class Form1
Dim config As System.Configuration.Configuration
Dim fpath As String = ""
Dim mptpath As String = ""
Public Sub New()
config = System.Configuration.ConfigurationManager.OpenExeConfiguration(System.Configuration.ConfigurationUserLevel.None)
fpath = config.FilePath
Dim mdate As String = Date.Now.ToString("yyyyMMdd_HHmmss")
mptpath = Path.GetDirectoryName(config.FilePath) & "\" & mdate & ".config"
config.SaveAs(mptpath, ConfigurationSaveMode.Full, True)
config = System.Configuration.ConfigurationManager.OpenExeConfiguration(mptpath)
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
End Sub
Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
File.Delete(fpath)
config.SaveAs(fpath, ConfigurationSaveMode.Full, True)
File.Delete(mptpath)
End Sub
End Class

You are making a loop by calling the OnClosing at the end of OnClosing
Protected Overrides Sub OnClosing(e As CancelEventArgs)
config.Save(ConfigurationSaveMode.Modified, True)
End Sub

Related

Database records delete as soon as debugging is stopped

When I enter the fields it works and sends the records to the database and if I clear the form and enter another it also sends that one to the database but when I stop debugging, the database is empty.
Is there something wrong with my code?
What should I do to resolve this issue?
• I am using VB.NET with a Microsoft Access database
• There are two pages of code: Control and Create Account Form
Control
Imports System.Data.OleDb
Public Class DBControl
'CREATE YOUR DB CONNECTION
Private DBCon As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;" &
"Data Source=Hotel.mdb;")
'PREPARE DB COMMAND
Private DBCmd As OleDbCommand
'DB DATA
Public DBDA As OleDbDataAdapter
Public DBDT As DataTable
'QUERY PARAMETERS
Public Params As New List(Of OleDbParameter)
'QUERY STATISTICS
Public RecordCount As Integer
Public Exception As String
Public Sub ExecQuery(Query As String)
'RESET QUERY STATS
RecordCount = 0
Exception = ""
Try
'OPEN A CONNECTION
DBCon.Open()
'CREATE DB COMMAND
DBCmd = New OleDbCommand(Query, DBCon)
'LOAD PARAMS INTO DB COMMAND
Params.ForEach(Sub(p) DBCmd.Parameters.Add(p))
'CLEAR PARAMS LIST
Params.Clear()
'EXECUTE COMMAND & FILL DATABASE
DBDT = New DataTable
DBDA = New OleDbDataAdapter(DBCmd)
RecordCount = DBDA.Fill(DBDT)
Catch ex As Exception
Exception = ex.Message
End Try
'CLOSE YOUR CONNECTION
If DBCon.State = ConnectionState.Open Then DBCon.Close()
End Sub
'INCLUDE QUERY & COMMAND PARAMETERS
Public Sub AddParam(Name As String, Value As Object)
Dim NewParam As New OleDbParameter(Name, Value)
Params.Add(NewParam)
End Sub
End Class
Create Account Form
Imports System.Data.OleDb
Public Class Create
Private Access As New DBControl
Private DBCon As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;" &
"Data Source=Hotel.mdb")
Private Sub Create_Load(sender As Object, e As EventArgs) Handles MyBase.Load
If DBCon.State = ConnectionState.Closed Then DBCon.Open() : Exit Sub
End Sub
Private Sub btnCreate_Click(sender As Object, e As EventArgs) Handles btnCreate.Click
AddUser()
End Sub
Private Sub btnCancel_Click(sender As Object, e As EventArgs) Handles btnCancel.Click
txtForename.Clear()
txtSurname.Clear()
txtNumber.Clear()
txtEmail.Clear()
txtPass.Clear()
txtCity.Clear()
End Sub
Private Sub cbxTitle_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cbxTitle.SelectedIndexChanged, txtFirst.TextChanged, txtSurname.TextChanged, txtEmail.TextChanged, txtPass.TextChanged
If Not String.IsNullOrWhiteSpace(cbxTitle.Text) AndAlso Not String.IsNullOrWhiteSpace(txtFirst.Text) AndAlso Not String.IsNullOrWhiteSpace(txtSurname.Text) AndAlso Not String.IsNullOrWhiteSpace(txtEmail.Text) AndAlso txtPass.Text.Length = 8 Then
btnCreate.Enabled = True
End If
End Sub
Private Sub AddUser()
'ADD PARAMETERS
Access.AddParam("#Title", cbxTitle.Text)
Access.AddParam("#Forename", txtForename.Text)
Access.AddParam("#Surname", txtSurname.Text)
Access.AddParam("#Number", txtNumber.Text)
Access.AddParam("#Email", txtEmail.Text)
Access.AddParam("#Pass", txtPass.Text)
Access.AddParam("#City", txtCity.Text)
'EXECUTE INSERT COMMAND
Access.ExecQuery("INSERT INTO Customers([Title], [Forename], [Surname], [Number], [Email], [Pass], [City])" &
"VALUES(#Title, #Forename, #Surname, #Number, #Email, #Pass, #City)")
'REPORT & ABORT ON ERRORS
If Not String.IsNullOrEmpty(Access.Exception) Then MsgBox(Access.Exception) : Exit Sub
DBCon.Close()
End Sub
End Class
Any help is appreciated
Thank you in advance :)
There is a proper way to work with file-based databases in VB.NET.
Add the data file to your project in the Solution Explorer. If you're prompted to copy the file into the project, accept.
Set the Copy To Output Directory property of the data file to Copy If Newer.
Use "|DataDirectory|" to represent the folder path of the data file in your connection string, e.g. Dim connectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=|DataDirectory|\Hotel.mdb;".
Now you have two copies of your database. You have the one in the project folder, with the other source files, and the one in the output folder, with the EXE. The source file should stay clean, unsullied by test data. The copy in the output folder is the one you connect to when you debug so that's the one you need to look in to find any changes you make while debugging/testing.
Because you set it to only copy when the source file is newer than the output file, any changes you make while testing will not be lost unless you actually make a change to the source file, e.g. add a column or a table. By default, that property is set to Copy Always, which means that your source file is copied over the output file every time the project is built, so any time any source file changes and you run the project again. That's why your data "disappears".

Treeview items don't expand

I'm developing a WPF application for my company, and everything needs to look the same way corresponding to our company's look. Therefore I have to make a custom folder explorer, which will feature a treeview of the current directory.
In order to make it easier, I've made the following class, which is basically a TreeViewItem that stores a DirectoryInfo and automaticely browses subfolders when expanded (not to browse everything at once and make the software faster). Here is my code :
Private Class TreeViewPlus
Inherits TreeViewItem
Public dir As IO.DirectoryInfo
Public Sub New()
End Sub
Public Sub New(dir As DirectoryInfo)
Me.dir = dir
Try
If Not dir.EnumerateDirectories Is Nothing Then 'If there are subdirectories, I add an empty item to enable the expansion
Me.Items.Add(New TreeViewPlus)
End If
Catch ex As Exception
End Try
End Sub
Private Sub TreeViewPlus_Expanded(sender As Object, e As RoutedEventArgs) Handles Me.Expanded
Me.Items.Clear()
Try
For Each folder In dir.EnumerateDirectories()
Dim item As TreeViewPlus = New TreeViewPlus(folder)
item.Name = Text.RegularExpressions.Regex.Replace(folder.FullName, "[^a-zA-Z0-9]", "")
item.Header = folder.Name
Me.Items.Add(item)
Next
Catch ex As Exception
End Try
End Sub
End Class
And here is the code where I initialize the first directories: (TRV_Arbre is the name of my TreeView)
Sub New()
...
For Each Drive As IO.DriveInfo In IO.DriveInfo.GetDrives
Dim item As TreeViewPlus = New TreeViewPlus(Drive.RootDirectory)
item.Header = Drive.Name
TRV_Arbre.Items.Add(item)
Next
...
End Sub
The Issue I've got is that the first level of items correctly expand, but not the following ones.
See here : https://youtu.be/E6BJbKal5Sk
I've already debugged my code a little, and it correctly creates the different Items.
Can anyone help me for this ? Thanks in advance.
There is a simple way to solve this problem and that is to Override the OnExpanded Sub on the Base TreeViewItem class instead of implementing your own Expanded method. Then in the end you execute MyBase.OnExpanded(e) method which seems to contain the correct update events to send out to whomever listens. In this case your TreeView.
Protected Overrides Sub OnExpanded(e As RoutedEventArgs)
Me.Items.Clear()
Try
For Each folder In dir.EnumerateDirectories()
Dim item As TreeViewPlus = New TreeViewPlus(folder)
item.Name = Text.RegularExpressions.Regex.Replace(folder.FullName, "[^a-zA-Z0-9]", "")
item.Header = folder.Name
Me.Items.Add(item)
Next
Catch ex As Exception
End Try
MyBase.OnExpanded(e)
End Sub

How to implement a Windows Form in vb2008 in vb6

I have written a Test-Form in vb2008 in order to call it as a mdi-child in vb6:
The code is as followed:
`
Imports System.Runtime.InteropServices
<ComClass(frmTest.ClassId, frmTest.InterfaceId, frmTest.EventsId)> _
Public Class frmTest
Inherits System.Windows.Forms.Form
#Region "COM-GUIDs"
Public Const ClassId As String = ""
Public Const InterfaceId As String = ""
Public Const EventsId As String = ""
#End Region
Public Sub New()
MyBase.New()
InitializeComponent()
End Sub
Public Overloads Sub Show(ByVal MDI As Object)
Me.MdiParent = CType(MDI, System.Windows.Forms.Form)
Me.Show
End Sub
Public Sub SomeText(ByVal Text As String)
MsgBox(Text)
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
MsgBox("Test")
End Sub
End Class
`
In vb6, under references I make a reference to the tlb.
So far, so good.
I open a new project and create a new MDI-Form.
Private Sub Start_Click(Index As Integer)
Dim f As New MyTestLibrary.frmTest
f.Show (Me)
End Sub
Unfortunately, this approach does not work, because it is COM-Component, and an error occurs. Anyone knows a verified way to make a windows .NET form available in VB6 ???
Thank you, in advance.
Stephan
Put your VB 6 in a DLL. Create a public method that invokes a modal dialog from VB 6.
From your .NET program, reference the DLL built in VB 6 (COM tab)
Invoke the method. Your VB6 modal form will appear.
However, some things may not work such as some ActiveX controls embedded in the VB 6 code might have problems.

Update DataGridView with dataview

I have a VB.NET 2010 application that loads data from SQL Server to a datagridview through adapter.fill(). When I update the database it works fine but my problem is that when i use the dataview to filter the data based on combobox selection the method adapter.update(table) does not work!
Is there away to do it with the filter applied?
Private Sub cmbDepartment_SelectedIndexChanged(...)
Handles cmbDepartment.SelectedIndexChanged
Dim filter As String
Try
lblDepartmentId.Text = ds.Tables("department").Rows(cmbDepartment.SelectedIndex)(0)
filter = "dprtId = " & lblDepartmentId.Text
dvSection = New DataView(ds.Tables("section"), filter, "", DataViewRowState.CurrentRows)
table = dvSection.ToTable
dgvSections.DataSource = table
Catch ex As Exception
MsgBox(Err.Description)
End Try
End Sub
Private Sub btnSaveDepartment_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
Handles btnSaveDepartment.Click
Dim cbDep As SqlCommandBuilder
Dim numRows As Integer
Try cbDep = New SqlCommandBuilder(daDep)
Me.Validate() numRows = daDep.Update(ds.Tables("section"))
MsgBox(numRows & " Rows affected.")
Catch ex As Exception
MsgBox(Err.Description)
End Try
End Sub
The way you are currently doing your filtering is by creating a new DataTable and binding this to your DataGridView data source. This breaks the association between the original DataSet and that table so when you call Update your changes are not recovered.
Try changing to something like the code below instead (I've left out the try catch blocks, they don't change the idea that fixes your problem).
When you first set the data source to the grid set it to a form level private dvSection field:
Public Class Form1
' Here we declare some class level variables.
'These are visible to all members of the class
Dim dvSection As DataView
Dim tableAdapter As New DataSet1TableAdapters.CustomersTableAdapter()
Dim ds As New DataSet1()
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' We fill our dataset - in this case using a table adapter
tableAdapter.Fill(ds.Customers)
dvSection = ds.Customers.DefaultView
DataGridView1.DataSource = dvSection
End Sub
' Here is the code to filter.
' Note how I refer to the class level variable dvSection
Private Sub ComboBox1_SelectedIndexChanged(sender As System.Object, e As System.EventArgs) Handles ComboBox1.SelectedIndexChanged
Dim filter As String
lblDepartmentId.Text = ds.Tables("department").Rows(cmbDepartment.SelectedIndex)(0)
filter = "dprtId = " & lblDepartmentId.Text
dvSection.RowFilter = filter
dvSection.RowFilter = filter
End Sub
' And here is the update code referring to the class level table adapter
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
tableAdapter.Update(ds.Customers)
End Sub
End Class
One other approach that might make things easier is to introduce a binding source and set the filter on that. Either was should work fine however.

ListBox ObservableCollection duplicating

I have a WPF application which has a listbox bound to an ObservableCollection which retrieves it's data from a Database. I am attempting to have the ListBox data refreshed every minute through the use of a DispatcherTimer.
Dim dispatcherTimer As DispatcherTimer = New System.Windows.Threading.DispatcherTimer
AddHandler dispatcherTimer.Tick, AddressOf getRoomMeetingDetails
dispatcherTimer.Interval = New TimeSpan(0, 2, 0)
dispatcherTimer.Start()
Which calls the getRoomMeetingDetails method as follows.
Public Sub getRoomMeetingDetails()
If Not My.Settings.rbConn = Nothing And _
Not gl_rmName = Nothing Then
Dim sqlConn As New SqlConnection(My.Settings.rbConn)
Dim sqlquery As String = "SELECT *" & _
"FROM table " & _
Dim sqlCmd As New SqlCommand(sqlquery, sqlConn)
sqlConn.Open()
Dim dr As SqlDataReader
dr = sqlCmd.ExecuteReader
While dr.Read
roomMeetingList.Add(New meetingDetails() With {.eMeetingId = dr.Item("dId")})
End While
End If
End Sub
I then have my two classes for the Collection as follows (I am very new to ObservableCollections and have tried my best to model my code off the MSDN examples, so if this isn't the best method to use to achieve what I am trying to achieve, or can be done easier, please let me know)
Public Class MeetingList
Inherits ObservableCollection(Of meetingDetails)
Private Shared list As New MeetingList
Public Shared Function getList() As MeetingList
Return list
End Function
Private Sub New()
AddItems()
End Sub
Public Shared Sub reset(ByVal rmName As String)
list.ClearItems()
list.AddItems()
End Sub
Private Sub AddItems()
End Sub
End Class
Public Class meetingDetails
Implements INotifyPropertyChanged
Public Sub New()
End Sub
Public Property eID() As String
Get
Return _eID
End Get
Set(ByVal value As String)
_eID = value
OnPropertyChanged("eID")
End Set
End Property
Private _eID As String
Public Event PropertyChanged As PropertyChangedEventHandler _
Implements INotifyPropertyChanged.PropertyChanged
End Class
What is happening is when the DispatcherTimer is called every minute, the ListBox data is duplicated which I believe is because the getRoomMeetingDetails method is adding all of the SQL results on every tick. How can I refresh the ListBox with only new data or data changes from the table?
I am really struggling to work out where I am going wrong and what needs to be added/removed for this to work.
If there is any details I am missing please let me know.
Matt
Either you clear all the data in the listbox before adding them again or you do a check on the collection. I assume your eID is the primary key? the do something like this:
if ( roomMeetingList.Where ( entry => entry.eID == dbID ).Count () == 0 ) {
// add
}
C# code, but it shows the idea
developerFusion's convert made this VB:
If roomMeetingList.Where(Function(entry) entry.eID = dbID).Count() = 0 Then
' Add
End If

Resources