Odd issue with WPF controls in Add_Click not working up top - wpf

First off, I'm a PowerShell guy, no experience in C# or with WPF applications aside from incorporating .NET WPF application templates from Visual Studio, so I apologize if my terminology is wrong or I'm not explaining this well.
I've built a small handful of GUI tools for my organization to use, pretty basic stuff and I haven't had any issues like this before - the workflow is essentially my Add_Click script block is supposed to start off by changing a progress bar with value of 0, to indeterminate ($true) and set a progress bar label content to "Running" → some proprietary functions that connect to our environment's API kick off → outputs results and the progress bar label content is set to "Complete" and the progress bar changes to not indeterminate ($false) with a value of 100.
The very bizarre issue that I cannot figure out, is the progress bar indeterminate doesn't get set to $true, the label doesn't change...but the functions all execute just fine, and the progress bar label at the end DOES change, and the progress bar at the end changes as well. Below is the sample code (full code not included because of the proprietary functions that wouldn't make sense, and the rest is just template code that imports the XAML).
XAML starts with $var_progLabel.Content set to "Ready", $var_progBar.IsIndeterminante set to $false.
$var_buttonRun.Add_Click({
# THESE TWO CONTROLS DON'T EXECUTE
$var_progLabel.Content = "Running..."
$var_progBar.IsIndeterminate = $true
<#
API CALL HERE TO GET DATA AND STORE IN $statusOutput - ALL RUNS SUCCESSFULLY
#>
# ALL BELOW RUNS SUCCESSFULLY
$var_progLabel.Content = "Complete!"
$var_progBar.Value = 100
$var_progBar.IsIndeterminate = $false
$statusOutput | Out-GridView
})
So TL;DR I guess, the top 2 controls in my Add_Click script block don't do anything, but everything else below does, include the same controls called at the bottom of the Add_Click. I'm just wondering if someone can educate me on the limitations of an Add_Click script block, or how/the order it processes data?

Not entirely sure, but it seems your issue is that your ProgressBar is blocked by another process, since both are running on the same thread. what you could
try is to run the API CALL on another thread as a Task:
private async Task<resultType> DoAPICallAsync()
{
//API CALL HERE TO GET DATA AND STORE IN $statusOutput - ALL RUNS SUCCESSFULLY
}
private async Button_Click(object sender,Arg...)
{
//Initialize your progress bar
await DoAPICallAsync();
//Set Progress bar to complete values
}

Related

How to call a method of a WPF form element running in another runspace in Powershell

I'm playing around with using runspaces in PowerShell to enhance performance of a small GUI application.
What I've got so far is a runspace that has 2 purposes: First, it holds all my other runspaces and second, it parses my GUI xml file and displays it.
It also adds the nodes to a sychronized hashtable, so I have it accessible across all runspaces. As you can imagine I have some buttons in this GUI which trigger actions when clicked; pretty simple stuff and actually it's working great so far. I can exchange data between the runspaces and I am also able to update the GUI when a certain button is clicked.
However, I am not able to call the addChild() method on an empty stackpanel in my xml file. What I basically want to do is just to add checkboxes to the stackpanel (named "CheckboxViewer").
$checkbox = New-Object System.Windows.Controls.CheckBox
$checkbox.Content = "Hello world"
$syncHash.checkbox = $checkbox
$syncHash.CheckboxViewer.Dispatcher.Invoke(
[Action]{
#this is working:
$syncHash.CheckboxViewer.Background = 'Black'
#this is not working
$syncHash.CheckboxViewer.AddChild($syncHash.checkbox)
}, "Normal")
The error message I receive is the typical error message one gets when trying to access another runspace directly (without using the dispatcher):
Exception calling "AddChild" with "1" argument(s): "The calling thread cannot access this object because a different thread owns it.
Any ideas what I am doing wrong here? Any kind of help would be much appreciated, thanks!
Try with this :
$syncHash.Window.Dispatcher.Invoke([action]{
$checkbox = New-Object System.Windows.Controls.CheckBox
$checkbox.Content = "Hello world"
$syncHash.CheckboxViewer.AddChild($checkbox)
}, "Normal")
To add a control, you must declare it inside the [action]

Redirect output of another PowerShell script to a TextBox on the fly

Hi I have a ps script that displays a GUI using WPF with a [System.Windows.Controls.TextBox]. On the GUI I will call some other ps scripts by clicking on various buttons and so I would like to redirect their outputs to the TextBox on the fly.
There are two things I could not get it to work:
First I tried this with cmdlets to update the Text property of the TextBox, which worked, but only after the cmdlet finished. So I need a way to update the Text on the fly.
Next is I tried the same with calling other ps scripts, unlike the cmdlets, I could not capture their outputs, even after they finished. I am guessing because these scripts are using Write-Host instead of Write-Output? Otherwise, I also need a way to capture the outputs of ps scripts to update the Text on the fly.
I am also thinking it would be much easier if there is a PowerShell cmd control that I can add to the GUI, so I don't have to do the above to achieve what I want. This control can execute the ps scripts and print the outputs there, better yet, read the user interactive input. Will pick this as the answer if this is possible.
Without sample code, this is a little hard to answer.
Usually textblocks are for displaying text and textboxes are for inputting text, I'd probably not use the same box for both.
If you want to update the textblock or box on the fly, wpf has a timer where you can watch for variables and put them in the UI, this is particularly useful if you have your UI and logic in different threads. So you can get the script you have kicked off to update a varible and the timer watches the variable for it to change and will up date the UI.
Add-Type -AssemblyName windowsbase
$updateBlock = {
$wpftextbox.text = $yourVaribletoWatchGoesHere
}
$timer = new-object System.Windows.Threading.DispatcherTimer
# Which will fire 100 times every second
$timer.Interval = [TimeSpan]"0:0:0.01"
# And will invoke the $updateBlock
$timer.Add_Tick($updateBlock)
# Now start the timer running
$timer.Start()
if ($timer.IsEnabled)
{
Write-Output 'UI timer started'
}
else
{
$clock.Close()
Write-Output 'UI update timer did not start'
}
You are correct, if they are using write-host their output will be hard to capture. You should edit the scripts so they use write-output, not write-host. This is a good example of why that is best practice.
I'm not sure what you mean by powershell cmd control, but as far as I know there is no shortcuts here.

How can I make a marque in the progress bar using Powershell Studio 2012?

Is anyone familiar with how to make the progress bar show a "moving" effect in a marque-style while performing a script task in Powershell Studio 2012?
I do not want it to display a percentage or something. When I hit a button it will start to load.... and when finished it will stop. The most convenient method would be to have two functions, "Load" and "Done".
Is this possible?
You could use Write-Progress -Activity "Doing stuff" -Status "Working" -PercentComplete $X, then vary X from 25-99 so the progress bar does that funky fill/reset thing.
The default Write-Progress cmdlet doesn't have a marquee style though.
lets say you got a button that runs a code and perform some kind of task....
Example:
$buttonStart_Click={
$progressbar1.MarqueeAnimationSpeed = 5 #this set the speed of the animation to 5
#your code here...
$progressbar1.MarqueeAnimationSpeed = 0 #this will stop the animation
}
One crucial thing though, if your code will do some "heavy" tasks, most likely the form and the progressbar with it will freeze until it finishes the task.
so no point of having a bar is it.
simple and lazy solution will be this line:
[System.Windows.Forms.Application]::DoEvents()
this will unfreeze the form while the code runs the task.
check the Sapien's spotlight of the bar.

Watin AttachTo: Timeout while waiting for frame document becoming available

I am trying to make WatiN attach to an IE popup window (IE 10).
This popup contains a frameset --> a single frame --> a pdf document.
My goal is to save this pdf to my disk.
Dim winExists = IE.Exists(Of IE)(Find.ByUrl(Function(url) url.Contains("__ADFvDlg")))
If winExists Then 'this evaluates to true
Dim win = IE.AttachTo(Of IE)(Find.ByUrl(Function(url) url.Contains("__ADFvDlg"))) ' Timeout while waiting for frame document becoming available
End If
1) I have tried using the above code inline or in a STA thread
2) When coded inline, its parent thread is also STA
3) I have tried to increase the default timeout to 8 minutes, same result after 8 minutes have passed
There is no other option for me than to parse this particular popup, since it is a site built with Oracle ADF and, apart from the fact that it is A MESS, it is very strange at times...this popup has a URL that somehow works only once. If I try to use it in another window, no pdf is returned. The same happens when I refresh the popup.
I cannot fetch the PDF in the Temporary Internet Files since it is not there (I suppose this is because the website works under SSL).
Any guidelines or solutions even outside WatiN's scope is more than welcome since I've hit a brick wall.
Technologies: VS2012, WPF
Thanks a lot in advance.
I found it easiest when I tried the same thing by making the pop-up show up as a new tab. That way I could attach to it's URL. From there I would use
File.WriteAllText(fileName, responseDownLoad.Content.ReadAsStringAsync().Result);
Where responseDownload will be a HttpResponseMessage

Dynamic Hyperlink in Livecycle Form

I am trying to figure out how to make a hyperlink in a Livecycle Form which points to a URL which will change on different days that the form is rendered. For example on one day I might want the hyperlink to point to:
mywebsite/mypage?option=XXX
and on another day I want it to point to:
mywebsite/mypage?option=YYY
The XXX and YYY can be passed into the form's data pretty easily as XML, but I just don't know how to make it so that the hyperlink is changed to correspond to this.
Any suggestions?
This can be accomplished with JavaScript in LiveCycle Designer. The following script, placed on the Form's docReady event will let you dynamically change the URL of a text object.
form1::docReady - (JavaScript, client)
// If this code is running on the server, you don't want it to run any code
// that might force a relayout, or you could get stuck in an infinite loop
if (xfa.host.name != "XFAPresentationAgent") {
// You would load the URL that you want into this variable, based on
// whatever XML data is being passed into your form
var sURL = "www.stackoverflow.com"; // mywebsite/mypage?option=xxx
// URLs are encoded in XHTML. In order to change the URL, you need
// to create the right XHTML string and push it into the Text object's
// <value> node. This is a super simple XHTML shell for this purpose.
// You could add all sorts of markup to make your hyperlink look pretty
var sRichText = "<body><p>Foo</p></body>";
// Assuming you have a text object called "Text1" on the form, this
// call will push the rich text into the node. Note that this call
// will force a re-layout of the form
this.resolveNode("Text1").value.exData.loadXML(sRichText, false, true);
}
There are a couple of caveats: URLs in Acrobat are only supported in Acrobat 9.0 and later. So if someone using an older version of Acrobat opens your form, the URLs won't work.
Also, as you can see from the "if (xfa.host.name !=...)" line, this code won't run properly if the form is being generated on the server, because forcing a re-layout of a form during docReady can cause problems on certain older versions of the LiveCycle server. If you do need to run this script on the server, you should probably pick a different event then form::docReady.
I a number of complaints from users in WorkSpace that clicking links opened them in the same tab so they lost their WorkSpace form, and there's no option to change that in Designer 11. I think the solution I came up with for that would work for you too.
I made buttons with no border and no background, and in their click event have this line (in Javascript, run at client)
app.launchURL("http:/stackoverflow.com/", true);
It would be easy to add some logic to choose the right URL based on the day and it doesn't cause any form re-rendering.
In some spots where the hyperlink is in line with other text, I leave the text of the link blue and underlined but with no hyperlink, and just place the button (no background, no border, no caption) over it. Does require positioned and not flowed subforms for that to work, so depending on your layout it could get a little clunky.
Wow, just realized I am super late to the party. Well, for anyone using ES4 facing a similar problem . . .
Ended up using a 3rd party component to manipulate the PDF's hyperlinks...wish there was a better solution as this one costs about $1000.

Resources