I would like to create in PowerShell a Windows form that uses a ListBox that selects data for editing. As an entry in the ListBox is selected, textboxes below are bound to properties in the selected entry, allowing the editing of that entry. Sounds simple, and almost is, if only the ListBox would always show the current state of the data.
A stripped down version of the code produces a form that looks like this image below. The textboxes at the bottom of the form appear to work perfectly, that is, the test I've done show the bound data is changing as you type in them. But the content of the ListBox is never refreshed or updated, and so far all attempts at fixing this seem to either not work, cause other problems.
Initially I was creating a class in PowerShell for holding the data, but switched to a C# class:
Add-Type -ReferencedAssemblies "System.Windows.Forms" -Language CSharp -TypeDefinition #'
using System;
public class VectorData {
double _degrees = 0.0d;
double _distance = 0.0d;
public double Degrees {
get {
return this._degrees;
}
set {
this._degrees = value;
}
}
public double Distance {
get {
return this._distance;
}
set {
this._distance = value;
}
}
public string Display {
get {
return "" + this._degrees + "\u00B0 \u2192 " + this._distance + "\u0027";
}
}
public VectorData(double degrees, double distance){
this._degrees = degrees;
this._distance = distance;
}
}
'#
I've tried all three of these examples for creating the list that can be bound to the ListBox, but none of them update the ListBox's content as the data changes.
#$Vectors = [System.Collections.Generic.List[VectorData]]::new()
$Vectors = [System.ComponentModel.BindingList[VectorData]]::new()
#$Vectors = [System.Collections.ObjectModel.ObservableCollection[VectorData]]::new()
This is the core code, $lbxVectors is the ListBox. $tbxDegrees and $tbxDistance are textboxes.
function SetVectorsDataSource {
$lbxVectors.DataSource = $null
$lbxVectors.DataSource = $Vectors
$lbxVectors.DisplayMember = 'Display'
}
function BindTextBoxes {
$VD = $Vectors[$lbxVectors.SelectedIndex]
$null = $txbDegrees.DataBindings.Clear()
$null = $txbDegrees.DataBindings.Add('Text', $VD, "Degrees")
$null = $txbDistance.DataBindings.Clear()
$null = $txbDistance.DataBindings.Add('Text', $VD, "Distance")
}
$null = $Vectors.Add([VectorData]::new(45, 20))
$null = $Vectors.Add([VectorData]::new(193, 32))
$null = $Vectors.Add([VectorData]::new(155, 18))
SetVectorsDataSource
BindTextBoxes
$lbxVectors.Add_SelectedIndexChanged({
BindTextBoxes
})
Calling the SetVectorsDataSource function from the textboxes' TextChanged event seems to overwrite what you type with the original data, so that doesn't work:
$txbDegrees.Add_TextChanged({
# SetVectorsDataSource
})
$txbDistance.Add_TextChanged({
# SetVectorsDataSource
})
So, does anyone have any idea on how to keep the ListBox up-to-date with the current state of the data?
FYI: Here is the full code if someone wants to try it:
using namespace System.Windows.Forms
Add-Type -AssemblyName System.Windows.Forms
Add-Type -ReferencedAssemblies "System.Windows.Forms" -Language CSharp -TypeDefinition #'
using System;
public class VectorData {
double _degrees = 0.0d;
double _distance = 0.0d;
public double Degrees {
get {
return this._degrees;
}
set {
this._degrees = value;
}
}
public double Distance {
get {
return this._distance;
}
set {
this._distance = value;
}
}
public string Display {
get {
return "" + this._degrees + "\u00B0 \u2192 " + this._distance + "\u0027";
}
}
public VectorData(double degrees, double distance){
this._degrees = degrees;
this._distance = distance;
}
}
'#
#$Vectors = [System.Collections.Generic.List[VectorData]]::new()
$Vectors = [System.ComponentModel.BindingList[VectorData]]::new()
#$Vectors = [System.Collections.ObjectModel.ObservableCollection[VectorData]]::new()
#region Build form and controls
function NextCtrlY { param ( [Control]$Control ) return $Control.Location.Y + $Control.Size.Height }
$ClientWidth = 200
$lbxVectors = [ListBox]#{
Location = "12, 12"
Name = "lbxVectors"
Size = "$($ClientWidth - 24), 120"
TabIndex = 0
}
$TextBoxWidth = ($ClientWidth - (12 + 6 + 12))/2
$LeftTxBxX = 12 + $TextBoxWidth + 6
$lblDegrees = [Label]#{
AutoSize = $true
Location = "12, $(NextCtrlY $lbxVectors)"
Name = "lblDegrees"
TabIndex = 1
Text = "Degrees:"
}
$lblDistance = [Label]#{
AutoSize = $true
Location = "$LeftTxBxX, $(NextCtrlY $lbxVectors)"
Name = "lblDistance"
TabIndex = 2
Text = "Distance:"
}
$txbDegrees = [TextBox]#{
Location = "12, $(NextCtrlY $lblDegrees)"
Name = "txbDegrees"
Size = "$TextBoxWidth, 20"
TabIndex = 3
Text = ""
}
$txbDistance = [TextBox]#{
Location = "$LeftTxBxX, $($txbDegrees.Location.Y)"
Name = "txbDistance"
Size = "$TextBoxWidth, 12"
TabIndex = 4
Text = ""
}
$ListBoxTestForm = [Form]#{
ClientSize = "$ClientWidth, $($(NextCtrlY $txbDegrees) + 12)"
FormBorderStyle = 'FixedDialog'
MaximizeBox = $false
MinimizeBox = $true
Name = 'ListBoxTestForm'
StartPosition = 'CenterScreen'
Text = "ListBox Test"
}
$ListBoxTestForm.Controls.Add($lbxVectors)
$ListBoxTestForm.Controls.Add($lblDegrees)
$ListBoxTestForm.Controls.Add($lblDistance)
$ListBoxTestForm.Controls.Add($txbDegrees)
$ListBoxTestForm.Controls.Add($txbDistance)
#endregion
function SetVectorsDataSource {
$lbxVectors.DataSource = $null
$lbxVectors.DataSource = $Vectors
$lbxVectors.DisplayMember = 'Display'
}
function BindTextBoxes {
$VD = $Vectors[$lbxVectors.SelectedIndex]
$null = $txbDegrees.DataBindings.Clear()
$null = $txbDegrees.DataBindings.Add('Text', $VD, "Degrees")
$null = $txbDistance.DataBindings.Clear()
$null = $txbDistance.DataBindings.Add('Text', $VD, "Distance")
}
$null = $Vectors.Add([VectorData]::new(45, 20))
$null = $Vectors.Add([VectorData]::new(193, 32))
$null = $Vectors.Add([VectorData]::new(155, 18))
SetVectorsDataSource
BindTextBoxes
$lbxVectors.Add_SelectedIndexChanged({
BindTextBoxes
})
$txbDegrees.Add_TextChanged({
# SetVectorsDataSource
})
$txbDistance.Add_TextChanged({
# SetVectorsDataSource
})
$null = $ListBoxTestForm.ShowDialog()
As I was about to post this question, I looked at the list of "Similar questions" and found this C# question/answer.
Replaced the core code with the following and it works!:
$VectorBindingSource = [BindingSource]::new()
$VectorBindingSource.DataSource = $Vectors
$lbxVectors.DataSource = $VectorBindingSource
$lbxVectors.DisplayMember = "Display"
$lbxVectors.ValueMember = "Display"
$null = $txbDegrees.DataBindings.Add("Text", $VectorBindingSource, "Degrees", $true, [DataSourceUpdateMode]::OnPropertyChanged)
$null = $txbDistance.DataBindings.Add("Text", $VectorBindingSource, "Distance", $true, [DataSourceUpdateMode]::OnPropertyChanged)
$null = $Vectors.Add([VectorData]::new(45, 20))
$null = $Vectors.Add([VectorData]::new(193, 32))
$null = $Vectors.Add([VectorData]::new(155, 18))
NOTE: Requires BindingList, doesn't work with generic list or ObservableCollection:
$Vectors = [System.ComponentModel.BindingList[VectorData]]::new()
Related
I'm trying to replicate the following JS-function into Powershell. It must match the exact same properties as I'm not able to change the other end where those values will be used.
// Function to encrypt values with Salt
function encryptMetadata(encryptStr, saltValue) {
var encrypted = CryptoJS.AES.encrypt(encryptStr.toString(), saltValue,
{
keySize: 256 / 8,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
return encrypted.toString();
};
I tried to work with AesManaged and the following code, however, I'm failing to build the key.
function Create-AesManagedObject($key, $IV) {
$aesManaged = New-Object "System.Security.Cryptography.AesManaged"
$aesManaged.Mode = [System.Security.Cryptography.CipherMode]::CBC
$aesManaged.Padding = [System.Security.Cryptography.PaddingMode]::PKCS7
$aesManaged.BlockSize = 128
$aesManaged.KeySize = 256
if ($IV) {
if ($IV.getType().Name -eq "String") {
$aesManaged.IV = [System.Convert]::FromBase64String($IV)
}
else {
$aesManaged.IV = $IV
}
}
if ($key) {
if ($key.getType().Name -eq "String") {
$aesManaged.Key = [System.Convert]::FromBase64String($key)
}
else {
$aesManaged.Key = $key
}
}
$aesManaged
}
function Encrypt-String($key, $unencryptedString) {
$bytes = [System.Text.Encoding]::UTF8.GetBytes($unencryptedString)
$aesManaged = Create-AesManagedObject $key
$encryptor = $aesManaged.CreateEncryptor()
$encryptedData = $encryptor.TransformFinalBlock($bytes, 0, $bytes.Length);
[byte[]] $fullData = $aesManaged.IV + $encryptedData
$aesManaged.Dispose()
[System.Convert]::ToBase64String($fullData)
}
$hashedUsername = Encrypt-String $salt $inputUsername
Could anyone help me out on how to replicate this CryptoJS functionality in PS?
Thanks in advance!
I'm trying to call this class function:
public function scan(&$Iterator, $pattern = null, $count = null)
{
return $this->__call('scan', array(&$Iterator, $pattern, $count));
}
From my class:
$itScan = NULL;
while($arr_keys = $this->redisClient->scan($itScan, '', 10000)) {
foreach($arr_keys as $str_key) {
echo "Here is a key: $str_key\n";
}
}
I understand this is something related to the & pointer but I can't figure who to call it from inside my class.
Thank you!
This is the proper way to iterate over your results :
$i = null;
$allResults = [];
do {
$someResults = $redis->scan($i, "*", 10000);
if (!empty($someResults)) {
$allKeys = array_merge($allKeys, $someResults);
}
}
while ($i);
I keep getting errors when running the code below. I don't understand what I'm doing wrong
ERROR 1: You cannot call a method on a null-valued expression. At
line:25 char:9
ERROR 2: Cannot index into a null array. At line:43 char:1
CODE:
Class Step {
[int]$StepNumber = 0
[string]$Name = ''
[string]$ScriptFile = ''
[int]$status = 0 # 0 = 'InComplete', 1 = 'Complete', 2 = 'Failed', -1 = 'Skipped'
[string]$DependencyStepNumber = -1
Step([string]$Name, [int]$StepNumber, [string]$ScriptFile) {
$this.Name = $Name
$this.StepNumber = $StepNumber
$this.ScriptFile = $ScriptFile
}
}
Class Guide {
[int]$StepNumberCounter = 0
[string]$Name = ''
[Step[]]$Steps
[int]AddStep([string]$Name, [string]$ScriptFile) {
$newStepNumber = $this.GetNewStepNumber()
$newStep = [Step]::new($Name, $newStepNumber, $ScriptFile)
line-25>>> $this.Steps.Add($newStep)
return $newStepNumber
}
[int]GetStepName([int]$StepNumber) {
return $this.Steps[$StepNumber-1]
}
[int]GetNewStepNumber() {
return $this.StepNumberCounter += 1
}
}
$Guide = [Guide]::new()
$Guide.AddStep('Step One','ScriptOne.ps1')
$Guide.AddStep('Step Two','ScriptTwo.ps1')
line-43>>> Write-Host $Guide.Steps[0]
Write-Host $Guide.Steps[1]
Array list has better performance, see below revised code
Class Step {
[int]$StepNumber = 0
[string]$Name = ''
[string]$ScriptFile = ''
[int]$status = 0 # 0 = 'InComplete', 1 = 'Complete', 2 = 'Failed', -1 = 'Skipped'
[string]$DependencyStepNumber = -1
Step([string]$Name, [int]$StepNumber, [string]$ScriptFile) {
$this.Name = $Name
$this.StepNumber = $StepNumber
$this.ScriptFile = $ScriptFile
}
}
Class Guide {
[int]$StepNumberCounter = 0
[string]$Name = ''
[System.Collections.ArrayList]$Steps = #()
[int]AddStep([string]$Name, [string]$ScriptFile) {
$newStepNumber = $this.GetNewStepNumber()
$newStep = [Step]::new($Name, $newStepNumber, $ScriptFile)
$this.Steps.Add($newStep)
return $newStepNumber
}
[int]GetStepName([int]$StepNumber) {
return $this.Steps[$StepNumber-1]
}
[int]GetNewStepNumber() {
return $this.StepNumberCounter += 1
}
}
$Guide = [Guide]::new()
$Guide.AddStep('Step One','ScriptOne.ps1')
$Guide.AddStep('Step Two','ScriptTwo.ps1')
Write-Output $Guide.Steps[0]
Write-Output $Guide.Steps[1]
To add an element to an array, we use the += operator.
$this.Steps += $newStep
Add is reserved for ArrayList:
$al = New-Object System.Collections.ArrayList
$al.Add('Example')
I have 2 signature document and the first signer already signed. The document is still in process. So I went to correct the second signer. I realised the envelopeID changed after the correction.
What will it happen to the old envelope? Voided?
function getDocuments($pdfbytes) {
$documents = array();
$id = 1;
$d = new Document();
$d->PDFBytes = $pdfbytes;
$d->Name = "Demo Document";
$d->ID = $id++;;
$d->FileExtension = "pdf";
array_push($documents, $d);
return $documents;
}
function buildEnvelope($pdfbytes) {
$envelope = new Envelope();
$envelope->Subject = $_SESSION["Taskmaster"];
$envelope->EmailBlurb = "Please Sign by logining ";
$envelope->AccountId = $_SESSION["AccountID"];
$envelope->Recipients = constructRecipients();
$envelope->Tabs = addTabs(count($envelope->Recipients));
$envelope = processOptions($envelope);
$envelope->Documents = getDocuments($pdfbytes);
return $envelope;
}
function constructRecipients() {
$recipients = array();
$r = new Recipient();
$r->UserName =$_SESSION["Secretaryname"];
$r->Email = $_SESSION["Secretaryemail"];
$r->RequireIDLookup = false;
$r->ID = 1;
$r->Type = RecipientTypeCode::Signer;
$r->RoutingOrder = "2";
// if(!isset($_POST['RecipientInviteToggle'][$i])){
$r->CaptiveInfo = new RecipientCaptiveInfo();
$r->CaptiveInfo->ClientUserId = 2;
$r->CaptiveInfo->embeddedRecipientStartURL=SIGN_AT_DOCUSIGN;
// }
array_push($recipients, $r);
if(empty($recipients)){
$_SESSION["errorMessage"] = "You must include at least 1 Recipient";
//header("Location: error.php");
exit;
}
return $recipients;
}
function sendNow($envelope) {
$api = getAPI();
$csParams = new CreateAndSendEnvelope();
$csParams->Envelope = $envelope;
//print_r($csParams);
try {
$status = $api->CreateAndSendEnvelope($csParams)->CreateAndSendEnvelopeResult;
//echo "Result for Create and Send <br>";
//print_r($status);
if ($status->Status == EnvelopeStatusCode::Sent) {
addEnvelopeID($status->EnvelopeID);
$correct = new Correction;
$correct->EnvelopeID = $status->EnvelopeID;
$correct->RecipientCorrections = addRecipientCorrection();
$correctparams = new CorrectAndResendEnvelope();
$correctparams->Correction = $correct;
//print_r($correctparams);
//Send
$response = $api->CorrectAndResendEnvelope($correctparams);
//print_r($response);
//exit;
$_SESSION["EnvelopeID"]=null;
$_SESSION["Direct"]="Yes";
header("Location: getstatusanddocs.php?envelopid=" . $status->EnvelopeID .
"&accountID=" . $envelope->AccountId . "&source=Document");
}
} catch (SoapFault $e) {
$_SESSION["errorMessage"] = $e;
header("Location: error.php");
}
}
function addRecipientCorrection(){
$correction = new RecipientCorrection();
$correction->PreviousUserName = "xxxxxx";
$correction->PreviousEmail = "xxxxxxx";
$correction->PreviousSignerName = $correction->PreviousUserName;
$correction->PreviousRoutingOrder = "2";
$correction->CorrectedUserName = $_SESSION["Secretaryname"];
$correction->CorrectedEmail = $_SESSION["Secretaryemail"];
$correction->CorrectedSignerName = $correction->CorrectedUserName;
return $correction;
}
//========================================================================
// Main
//========================================================================
loginCheck();
if($_SESSION["Taskmaster"]=="xxxxx"){
$api = getAPI();
$RequestPDFParam = new RequestPDF();
$RequestPDFParam->EnvelopeID = $_SESSION["EnvelopeID"];
$result = $api->RequestPDF($RequestPDFParam);
$envPDF = $result->RequestPDFResult;
//file_put_contents("./Cert/".$_SESSION["EnvelopeID"].".pdf", $envPDF->PDFBytes);
//echo "Stop here";
//exit;
$envelope = buildEnvelope($envPDF->PDFBytes);
sendNow($envelope);
}else{
echo "You have no power";
}
EnvelopeId does not change after correction. The envelopeID never changes. New envelopes can be created, but the original GUID that was used will keep living in the system.
I have a dif called cdefualt that has some inputs from a form inside of it and I want to do something like this to clone it and change that input names:
var i = 2;
function add() {
var item = $('#cdefault').clone();
item.attr({'style': ''});
$xpto = 'gtitle'+i;
$xpto2 = 'gmessage'+i;
item.id = $xpto;
$('#'+$xpto+' input[id="gtitle1"]').attr('name', $xpto);
$('#'+$xpto+' textarea[id="gmessage1"]').attr('name',$xpto2);
$(item).appendTo('#ccontainer');
i++;
}
But this doesnt work. I've tried this already as well but it only works twice (for the original and first clone):
var i = 2;
function add() {
var item = $('#cdefault').clone();
item.attr({'style': ''});
$xpto = 'gtitle'+i;
$xpto2 = 'gmessage'+i;
$('#cdefault input[id="gtitle1"]').attr('id', $xpto);
$('#cdefault textarea[id="gmessage1"]').attr('id',$xpto2);
$('#cdefault input[name="gtitle1"]').attr('name', $xpto);
$('#cdefault textarea[name="gmessage1"]').attr('name', $xpto2);
$(item).appendTo('#ccontainer');
i++;
}
Even tryed this way:
function add() {
$xpto = 'gtitle'+i;
$xpto2 = 'gmessage'+i;
var div = document.getElementById('cdefault');
clone = div.cloneNode(true); // true means clone all childNodes and all event handlers
clone.id = $xpto;
clone.style.display = '';
$("#"+$xpto+" input[id='gtitle1']").attr('name', $xpto);
$("#"+$xpto+" textarea[id='gmessage1']").attr('name',$xpto2);
document.getElementById('ccontainer').appendChild(clone);
i++;
}
http://jsfiddle.net/Theopt/xNfSd/
fixed. changed cdefault id to id0 and this java script:
var i = 2;
var c = 0;
function add() {
$xpto = 'gtitle'+i;
$xpto2 = 'gmessage'+i;
var klon = $( '#id'+ c );
klon.clone().attr('id', 'id'+(++c) ).insertAfter( '#inserthere' );
document.getElementById('id'+(c)).style.display = '' ;
$("#id"+(c)+" input[id='gtitle1']").attr('name', $xpto);
$("#id"+(c)+" textarea[id='gmessage1']").attr('name',$xpto2);
i++;
}