i'm currently working on a project (WOL) where i have a program that launch as a window form and have few components which one of them is a chart form.
Basically, the program can re-launch multiple times as the user wants but i'm trying to clear/reset the chart form (that is in Pie style) at each launch = delete the previous datas of the chart.
My code is way too long so i will post only the concerned parts
$WOL_W = New-Object System.Windows.Forms.Form
.... (skipping useless code part)
function Chart {
param(
[Parameter(Mandatory)]
[PSCustomObject[]]
$Results
)
$G_Graphique = New-object System.Windows.Forms.DataVisualization.Charting.Chart
$G_Graphique.Location = New-Object System.Drawing.Point(300,350)
$WOL_W.Controls.Add($G_Graphique)
$Wol_W.Add_Shown({$Graphique.Activate()})
$G_Graphique.Anchor = [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Right -bor [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left
# Objet Zone Graphique (ZG_)
$ZG_GraphArea = New-Object System.Windows.Forms.DataVisualization.Charting.ChartArea
$G_Graphique.ChartAreas.Add($ZG_GraphArea)
[void]$G_Graphique.Titles.Add("RĂ©ussite du WOL")
$G_Graphique.BackColor = [System.Drawing.Color]::White
$G_Graphique.BorderColor = 'Black'
$G_Graphique.BorderDashStyle = 'Solid'
[void]$G_Graphique.Series.Add("Data")
$G_Graphique.Series["Data"].ChartType = [System.Windows.Forms.DataVisualization.Charting.SeriesChartType]::Pie
$G_Graphique.Series["Data"].Points.DataBindXY($Results.Keys,$Results.Values)
}
Note : i've tried to use the followings command in another function (function of the creation for the main window) :
$G_Graphique.ChartAreas.Clear()
$Wol_W.Controls.Remove($G_Graphique)
Which failed as i want.
Related
Good day everyone. I'm new to powershell so I don't know what's wrong with this. I have this script to open multiple MS Access at once as you see in the script and it is save in my local drive. If I run this script in VS Code editor, the script is fine and two application is launch. Now if I run this script using mouse Right-Click and Run with powershell. At runtime, both application is visible but after the script completed/done, only one application is running and the other is closed.
$accessMenu = New-Object -ComObject Access.Application
$AccessPath1 = "G:\access1.MDB"
$accessMenu.OpenCurrentDatabase($AccessPath1, $false)
$accessMenu.Visible = $true
$accessLink = New-Object -ComObject Access.Application
$AccessPath2 = "G:\access2.accdb"
$accessLink.OpenCurrentDatabase($AccessPath2, $false)
$accessLink.Visible = $true
Am I missing something here? Thanks in advance for sharing your idea's.
Here is a VBScript that will open multiple dbs. It utilizes Windows Shell object. Create a text file and change the extension to vbs. Double click the file to run.
Dim objFSO1, objFS02, oShell1, oShell2
Set objFSO1 = CreateObject("Scripting.FileSystemObject")
Set oShell1 = CreateObject("WScript.Shell")
oShell1.Run """G:\access1.MDB"""
Set objFSO2 = CreateObject("Scripting.FileSystemObject")
Set oShell2 = CreateObject("WScript.Shell")
oShell2.Run """G:\access2.accdb"""
The only way I can get multiple databases to open and include a password is in VBA.
Option Compare Database
Option Explicit
Dim accdbObj1 As Access.Application
Dim accdbObj2 As Access.Application
____________________________________________________________________________
Sub test()
Set accdbObj1 = CreateObject("Access.Application")
accdbObj1.OpenCurrentDatabase "C:\Users\Owner\June\Forums\demofile.accdb", , "test"
accdbObj1.Application.Visible = True
Set accdbObj2 = CreateObject("Access.Application")
accdbObj2.OpenCurrentDatabase "C:\Users\Owner\June\DOT\Projects.accdb"
accdbObj2.Application.Visible = True
End Sub
For future preference:
As per #topsail said, by passing UserControl = $true in the instantiated variable of Access.Application it prevents the closing of object/application upon script termination/complete.
In powershell:
$accessObj = New-Object -ComObject Access.Application -Property #{UserControl = $true}
In VBA:
Dim accdbObj
Set accdbObj = CreateObject("Access.Application")
accdbObj.OpenCurrentDatabase "G:\path\test.mdb", , "password"
accdbObj.Application.Visible = True
accdbObj.UserControl = True
I'm trying to use a 'forward' arrow character on my form button ([char]0x84 from the WingDings 3 font), to match the 'back' arrow ([char]0x83), but for some reason the 'forward' arrow doesn't show on the button. I've tried other HEX values and they work fine. HEX values were sourced from MS Word. Below is my MRE showing correct "back" button, blank "forward" button, plus a spare.
I could just use different symbols, but curious to know if there is a reason for this one character not working.
Add-Type -AssemblyName System.Windows.Forms
$Form = New-Object system.Windows.Forms.Form
$Form.Size = '800,600'
$buttFACE1 = [char]0x83 # Back arrow from WingDings3
$buttFACE2 = [char]0x84 # Forward arrow from WingDings3 <- NOT WORKING
$buttFACE3 = [char]0x86 # Spare button for testing
$navFONT = New-Object System.Drawing.Font("WingDings 3","14",[System.Drawing.FontStyle]::Bold)
#################################################
# NAV BACK BUTTON
$navBACK = New-Object System.Windows.Forms.Button
$navBACK.Location = '10,10'
$navBACK.Size = '30,30'
$navBack.Font = $navFONT
$navBACK.Text = $buttFACE1
$navBACK.Enabled = $True
$Form.Controls.Add($navBACK)
####################################################
# NAV FORWARD BUTTON
$navFORWARD = New-Object System.Windows.Forms.Button
$navFORWARD.Location = '50,10'
$navFORWARD.Size = '30,30'
$navFORWARD.Font = $navFONT
$navFORWARD.Text = $buttFACE2
$navFORWARD.Enabled = $True
$Form.Controls.Add($navFORWARD)
##################################################
# SPARE BUTTON
$navSPARE = New-Object System.Windows.Forms.Button
$navSPARE.Location = '90,10'
$navSPARE.Size = '30,30'
$navSPARE.Font = $navFONT
$navSPARE.Text = $buttFACE3
$navSPARE.Enabled = $True
$Form.Controls.Add($navSPARE)
$Form.ShowDialog()
Thanks for taking the time to read this. This forum has been a massive help since my organisation decided HTA's were too risky (I know, right!).
i have a function that create a textbox (info box) and can be called from other functions or from buttons.
function InfoBox ($x,$y,$text){
$TextboxInfoBox = New-Object System.Windows.Forms.TextBox
$TextboxInfoBox.Location = New-Object System.Drawing.Size($x,$y)
$TextboxInfoBox.Size = New-Object System.Drawing.Size(200,800)
# Readonly Textbox.
$TextboxInfoBox.Enabled = $true
$TextboxInfoBox.Text = $text
$TabPanelButtons.Controls.Add($TextboxInfoBox)
}
After i called the textbox once, i am not able to overwrite it with another info message.
the first time i call with all the parameters ($x, $y & $text).
Infobox -x '100' -y '150' -text 'This is the first message'
The second time i only want to give another text. How can i clear the textbox and give only a new text like:
Infobox -text "This is the second message"
Any Ideas?
As commenter wrote, the function has to output the TextBox object so you can reference it later to change the text:
function InfoBox ($x,$y,$text){
$TextboxInfoBox = New-Object System.Windows.Forms.TextBox
$TextboxInfoBox.Location = New-Object System.Drawing.Size($x,$y)
$TextboxInfoBox.Size = New-Object System.Drawing.Size(200,800)
# Readonly Textbox.
$TextboxInfoBox.Enabled = $true
$TextboxInfoBox.Text = $text
$TabPanelButtons.Controls.Add($TextboxInfoBox)
$TextboxInfoBox # Output
}
Contrary to most other programming languages, in PowerShell you normally don't need to use the return statement, except for early return from a function. Simply referring to a variable by name on its own line will output its value from the function.
Now you can store the output in a variable and change the text:
$box = Infobox -x 100 -y 150 -text 'This is the first message'
$box.Text = 'This is the second message'
I have the following PowerShell code which adds a new line of data into a MS Access database (based on user input) and works perfectly.
if ($NewAccounts ="Y") {
$cursor = 3
$lock = 3
$Ado = New-Object -ComObject ADODB.Connection
$recordset = New-Object -ComObject ADODB.Recordset
$Ado.Open("Provider = Microsoft.ACE.OLEDB.12.0;Data Source=$Source")
$query = "Select * from [Sheet1]"
$recordset.Open($query, $ado, $cursor, $lock)
$recordset.AddNew()
$recordset.Fields.Item("Account") = $AccName
$recordset.Fields.Item("Serial") = $CGBSerial
$recordset.Fields.Item("SAExpiry") = $SAEDate.ToString("dd/MM/yyyy")
$recordset.Fields.Item("SAValidatedPerson") = $SAPerson
$recordset.Fields.Item("DataCollection") = $DCRun
$recordset.Fields.Item("DataCollectionDate") = $DCRunDate
$recordset.Fields.Item("DataCollectionPerson") = $DCPerson
$recordset.Fields.Item("Version") = $Version
$recordset.Fields.Item("VersionDateValidated") = Get-Date -Format d
$recordset.Fields.Item("VersionValidatedPerson") = $logontrim
$recordset.Update()
$recordset.Close()
$ado.Close()
}
However, I cannot seem to update a row in the database that already exists. Is it possible to update a row, rather than creating an entirely new row?
$recordset.AddNew() appends a new empty record to the recordset. To update an existing record you need to navigate to the record you want to modify first, and then change the values of that record.
$recordset.Open($query, $ado, $cursor, $lock)
while ($recordset.Fields.Item('Account').Value -ne $AccName) {
$recordset.MoveNext()
}
$recordset.Fields.Item('Serial') = $CGBSerial
...
$recordset.Update()
$recordset.Close()
However, you can't use MoveNext() with a static cursor, so you need to change the cursor type to adOpenForwardOnly ($cursor = 0).
Alternatively you could use a prepared statement:
$cn = New-Object -ComObject 'ADODB.Connection'
$cn.ConnectionString = "..."
$cn.Open()
$cmd = New-Object -ComObject 'ADODB.Command'
$cmd.CommandText = 'UPDATE [Sheet1] SET Serial=?, SAExpiry=?, ... WHERE Account=?'
$cmd.Parameters.Append($cmd.CreateParameter('#p1', 200, 1, 50, $CGBSerial))
$cmd.Parameters.Append($cmd.CreateParameter('#p2', 7, 1, $null, $SAEDate))
...
$cmd.Execute()
$cn.Close()
I'm trying to add a progress bar to a form in powershell. I do not want to use PowerShell's Write-Progress cmdlet (because when I run the script from command line, it shows a text-based progress bar and I always want a form/graphic based bar).
I've tried this and it seems to work(found online):
[reflection.assembly]::loadwithpartialname("System.Windows.Forms") | Out-Null
[reflection.assembly]::loadwithpartialname("System.Drawing") | Out-Null
$form_main = New-Object System.Windows.Forms.Form
$progressBar1 = New-Object System.Windows.Forms.ProgressBar
$timer1 = New-Object System.Windows.Forms.Timer
$timer1_OnTick = {
$progressBar1.PerformStep()
}
$form_main.Text = 'ProgressBar demo'
$progressBar1.DataBindings.DefaultDataSourceUpdateMode = 0
$progressBar1.Step = 1
$progressBar1.Name = 'progressBar1'
$form_main.Controls.Add($progressBar1)
$timer1.Interval = 100
$timer1.add_tick($timer1_OnTick)
$timer1.Start()
$form_main.ShowDialog()| Out-Null
However, I do not want an event to update the progress bar (as does $timer1_OnTic in the example above) I want to update it myself by making calls throughout my script such as:
$progressBar1.PerformStep()
Or
$progressBar1.Value = 10
So it seems I need some sort of background worker that updates the progress bar whenever I make calls to PerformStep() or change the value of the progressBar
Calling ShowDialog stops all processing inside the script until the form is closed.
If I understand correctly, you should be able to change ShowDialog() to Show(), which will display the Dialog without blocking your script. You can then continue execution and update the progress bar.
You may be disappointed in the lack of interactivity of the form though.
A method I have had some success with is to use a child runspace for the GUI (in this case WPF) so it doesn't lock the script. Data can be accessed in both the parent and sub runspaces via the session state proxy.
e.g.
# define the shared variable
$sharedData = [HashTable]::Synchronized(#{});
$sharedData.Progress = 0;
$sharedData.state = 0;
$sharedData.EnableTimer = $true;
# Set up the runspace (STA is required for WPF)
$rs = [RunSpaceFactory]::CreateRunSpace();
$rs.ApartmentState = "STA";
$rs.ThreadOptions = "ReuseThread";
$rs.Open();
# configure the shared variable as accessible from both sides (parent and child runspace)
$rs.SessionStateProxy.setVariable("sharedData", $sharedData);
# define the code to run in the child runspace
$script = {
add-Type -assembly PresentationFramework;
add-Type -assembly PresentationCore;
add-Type -assembly WindowsBase;
[xml]$xaml = #"
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
MaxHeight="100" MinHeight="100" Height="100"
MaxWidth="320" MinWidth="320" Width="320"
WindowStyle="ToolWindow">
<Canvas Grid.Row="1">
<TextBlock Name="ProgressText" Canvas.Top="10" Canvas.Left="20">Hello world</TextBlock>
<ProgressBar Name="ProgressComplete" Canvas.Top="30" Canvas.Left="20" Width="260" Height="20" HorizontalAlignment="Center" Value="20" />
</Canvas>
</Window>
"#
# process the xaml above
$reader = New-Object System.Xml.XmlNodeReader $xaml;
$dialog = [Windows.Markup.XamlReader]::Load($reader);
# get an handle for the progress bar
$progBar = $dialog.FindName("ProgressComplete");
$progBar.Value = 0;
# define the code to run at each interval (update the bar)
# DON'T forget to include a way to stop the script
$scriptBlock = {
if ($sharedData.EnableTimer = $false) {
$timer.IsEnabled = $false;
$dialog.Close();
}
$progBar.value = $sharedData.Progress;
}
# at the timer to run the script on each 'tick'
$dialog.Add_SourceInitialized( {
$timer = new-Object System.Windows.Threading.DispatherTimer;
$timer.Interface = [TimeSpan]"0:0:0.50";
$timer.Add_Tick($scriptBlock);
$timer.Start();
if (!$timer.IsEnabled) {
$dialog.Close();
}
});
# Start the timer and show the dialog
&$scriptBlock;
$dialog.ShowDialog() | out-null;
}
$ps = [PowerShell]::Create();
$ps.Runspace = $rs;
$ps.AddScript($script).BeginInvoke();
# if you want data from your GUI, you can access it through the $sharedData variable
Write-Output $sharedData;
If you try this code, once the dialog is displayed you can change the progress bar by setting the value of $sharedData.Progress
This has allowed me to write plenty of dialogs for tools, I'm constrained by our infrastructure to use powershell from within a runspace and WPF seems to work much better than forms.
Have a look at Posh Progress Bar it has horizontal, vertical and circle progress bars.