When an item is selected in a ComboBox, some files are copied which match the selected name. This takes some time and the screen is just frozen (with the list dropped down) during that time, I would like the dropped down combobox list to go up and a show a Progress Bar while the files get copied. I tried adding a Progress Bar but it doesn't show up.
$ComboBox.Add_SelectionChanged(
{
$progressBar.Visibility = "Visible" #Show an Indeterminate Progress Bar at the beginning
if($ComboBox.SelectedItem)
{
$selectedName = $ComboBox.SelectedItem
Copy-Item -Path $storePath\* -Destination $tempPath -Filter $selectedName*
$fileList = (Get-ChildItem -Path $tempPath).Name | Select-String $selectedName
$ListBox.Items.Clear()
foreach($file in $fileList)
{
$ListBox.Items.Add($fileList)
}
}
$progressBar.Visibility = "Hidden" #Hide the Progress Bar once done
})
As a workaround, I used 'DropDownClosed' to trigger the Copy, while using 'SelectionChanged' to trigger the Progress Bar. The problem now is that the Progress Bar gets stuck. Now I need another way to run the ProgressBar in a separate Thread.
$ComboBox.Add_SelectionChanged(
{
$progressBar.Visibility = "Visible" #Show an Indeterminate Progress Bar at the beginning
})
$ComboBox.Add_DropDownClosed(
{
if($ComboBox.SelectedItem)
{
$selectedName = $ComboBox.SelectedItem
if(!((Get-ChildItem -Path $tempPath).Name | Select-String $selectedName))
{
Copy-Item -Path $storePath\* -Destination $tempPath -Filter $selectedName*
}
$fileList = (Get-ChildItem -Path $tempPath).Name | Select-String $selectedName
$ListBox.Items.Clear()
foreach($file in $fileList)
{
$ListBox.Items.Add($fileList)
}
}
$progressBar.Visibility = "Hidden" #Hide the Progress Bar once done
})
Related
I am trying to change checkbox content property during its execution. If Test1 is selected and button clicked it should show immediately Test 1 Running (same applies for Test2 if selected). Once test finishes it should show Test 1 finished. Right now there are no interim changes until button task completes which ends with Test 1 finished. I was reading about WPF Toolkit BusyIndicator (cannot implement as it is closed network where I am running this) and also async-await but not sure how to implement solution powershell/xaml way.
Add-type -AssemblyName PresentationFramework, PresentationCore
$ToolboxXml = #"
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Toolbox"
WindowStartupLocation = "CenterScreen"
Height ="300"
Width ="250"
Topmost="True"
FontFamily="Arial"
WindowStyle="SingleBorderWindow">
<Grid>
<StackPanel Margin="30 10 30 0">
<StackPanel Orientation="vertical">
<CheckBox Name="chkbx1" Content="Test1"/>
<CheckBox Name="chkbx2" Content="Test2"/>
</StackPanel>
<StackPanel>
<Button Name="button1" Content="Run Tests"/>
<Label Name="label2" />
</StackPanel>
</StackPanel>
</Grid>
</Window>
"#
$ToolboxGui = $ToolboxXml -replace "x:N",'N'
$Form = [Windows.Markup.XamlReader]::Parse(([string]$ToolboxGui))
[xml]$xaml = $ToolboxGui
$xaml.SelectNodes("//*[#*[contains(translate(name(.),'n','N'),'Name')]]").ForEach{
Set-Variable -Name $_.Name -Value $Form.FindName($_.Name) -Force
}
$label2.content = "Test(s) not running"
$button1.add_Click({
If ($chkbx1.IsChecked){
$Script:chkbx1.content = "Test 1 Running"
Start-Sleep -Seconds 5
$Script:chkbx1.content = "Test 1 Finished"
}
If ($chkbx2.IsChecked){
$Script:chkbx2.content = "Test 2 Running"
Start-Sleep -Seconds 5
$Script:chkbx2.content = "Test 2 Finished"
}
$Script:label2.content = "Tests Finished"
})
[void]$Script:label2
$async = $Form.Dispatcher.InvokeAsync({
$Form.ShowDialog() | Out-Null
})
$async.Wait() | Out-Null
I am attempting to adapt this WPF Popup implementation to implement a messaging system. The goal is a popup any time I need to send a message, and the popup can be closed by the user by double clicking the message, and the message also goes away after a set time.
What I have now is this
using assembly PresentationFramework
using assembly System.Windows.Forms
using assembly System.Drawing
$icon = [System.Drawing.Icon]::ExtractAssociatedIcon("$pshome\powershell.exe")
[xml]$xaml = '<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="window" WindowStyle="None" Height="200" Width="400"
ResizeMode="NoResize" ShowInTaskbar="False">
<Grid Name="grid" Background="#313130" Height="200" Width="400">
<Label Name="label" Content="Messanger Test" Foreground="White" FontSize="18" Margin="10,10,0,15"/>
<TextBox x:Name="Message" Height = "50" FontSize="18" Margin="10,10,0,15" />
</Grid>
</Window>'
$window = [Windows.Markup.XamlReader]::Load([System.Xml.XmlNodeReader]::New($xaml))
$window.Left = [System.Windows.SystemParameters]::WorkArea.Width-$window.Width
$window.Top = 0
$message = $Window.FindName('Message')
# Close the window if it's double clicked
$window.Add_MouseDoubleClick({
$window.Hide()
})
$messageCount = 1
do {
if ((Get-Random -Minimum:0 -Maximum:100) -le 30) {
$messageString = "($messageCount) $(Get-Date -format 'HH:mm:ss')"
$message.Text = $messageString
Write-Host $messageString
$messageCount ++
$window.Show()
Start-Sleep -s:10
$window.Hide()
}
Start-Sleep -s:5
} while ($messageCount -le 5)
This partially works, in that the first message pops up, and it will hide after 10 seconds. However, double clicking to hide doesn't work, nor do subsequent shows happen. I know the criteria are being met, as the console shows each new time message.
So...
What is wrong with my MouseDoubleClick event, and
What is keeping messages after the first from showing?
The Start-Sleep you are using makes the code wait while not processing other events, like the MouseDoubleClick.
In order to have a window keep responding and at the same time wait for a certain period of time, you need to add a System.Windows.Forms.Timer object.
This Timer has a Tick event in which you can stop it from running in order to proceed with the code.
I would suggest something like this:
Add-Type -AssemblyName PresentationFramework
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
$icon = [System.Drawing.Icon]::ExtractAssociatedIcon("$pshome\powershell.exe")
[xml]$xaml = '<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="window" WindowStyle="None" Height="200" Width="400"
ResizeMode="NoResize" ShowInTaskbar="False">
<Grid Name="grid" Background="#313130" Height="200" Width="400">
<Label Name="label" Content="Messenger Test" Foreground="White" FontSize="18" Margin="10,10,0,15"/>
<TextBox x:Name="Message" Height = "50" FontSize="18" Margin="10,10,0,15" />
</Grid>
</Window>'
$window = [Windows.Markup.XamlReader]::Load([System.Xml.XmlNodeReader]::New($xaml))
$window.Left = [System.Windows.SystemParameters]::WorkArea.Width-$window.Width
$window.Top = 0
$window.Topmost = $true
$message = $Window.FindName('Message')
# create a Timer object to use instead of Start-Sleep
# old PowerShell: $timer = New-Object System.Windows.Forms.Timer
$timer = [System.Windows.Forms.Timer]::new()
$timer.Add_Tick({
Write-Host ">> Timer Tick.."
$timer.Stop()
})
# Close the window if it's double clicked
$window.Add_MouseDoubleClick({
Write-Host ">> Mouse Double Click.."
$timer.Stop()
})
$maxLoops = 5
for ($messageCount = 1; $messageCount -le $maxLoops; $messageCount++) {
$messageString = "($messageCount) $(Get-Date -format 'HH:mm:ss')"
$message.Text = $messageString
Write-Host $messageString
$window.Show()
# start the timer to fire after 10 seconds and then disable itself
$timer.Stop()
$timer.Interval = 10000
$timer.Start()
# while the Tick event did not occur, respond to other events
# such as e mouse double-click on the window
while ($timer.Enabled) { [System.Windows.Forms.Application]::DoEvents() }
# the timer tick event happened or the user double-clicked the window
$window.Hide()
# end of the loop reached, no use waiting some more..?
if ($messageCount -ge $maxLoops) { break }
# start the timer to fire after (random) 1 - 5 seconds
$interval = (Get-Random -Minimum 1 -Maximum 5)
$timer.Stop()
$timer.Interval = $interval * 1000
$timer.Start()
Write-Host ">> Loop wait $interval seconds.."
while ($timer.Enabled) { [System.Windows.Forms.Application]::DoEvents() }
}
# we're done, clean-up
$timer.Dispose()
$window.Close()
I have following code which is (almost) working as expected:
Add-Type -AssemblyName System.Windows.Forms
class MyForm : System.Windows.Forms.Form {
MyForm($mystuff) {
#Do-Stuff
$this.Add_Load( $this.MyForm_Load )
}
$MyForm_Load = {
$mlabel = [System.Windows.Forms.Label]::new()
$mlabel.Name = "label"
$mlabel.Text = "disabled"
$mbutton = [System.Windows.Forms.Button]::new()
$mbutton.Name = "button"
$mbutton.Location = [System.Drawing.Point]::new(100,100)
$mbutton.Add_Click( $this.mbutton_click )
$this.Controls.Add($mlabel)
$this.Controls.Add($mbutton)
# ----------------------------------------------
# Now $this.controls has something. We can now access it.
# ----------------------------------------------
if ($this.controls["label"].text -eq "enabled"){
$mbutton.text = "disable"
}else{
$mbutton.text = "enable"
}
}
$mbutton_click = {
if ($this.Parent.Controls["label"].Text -eq "enabled"){
$this.Parent.Controls["label"].Text = "disabled"
$this.Parent.Controls["button"].Text = "enable"
}
else{
$this.Parent.Controls["label"].Text = "enabled"
$this.Parent.Controls["button"].Text = "disable"
}
}
}
$foo = [MyForm]::new("test")
$foo.ShowDialog()
but when I replace following section:
$mbutton_click = {
if ($this.Parent.Controls["label"].Text -eq "enabled"){
$this.Parent.Controls["label"].Text = "disabled"
$this.Parent.Controls["button"].Text = "enable"
}
else{
$this.Parent.Controls["label"].Text = "enabled"
$this.Parent.Controls["button"].Text = "disable"
}
}
For this (missing Parent):
$mbutton_click = {
if ($this.Controls["label"].Text -eq "enabled"){
$this.Controls["label"].Text = "disabled"
$this.Controls["button"].Text = "enable"
}
else{
$this.Controls["label"].Text = "enabled"
$this.Controls["button"].Text = "disable"
}
}
Then my script stops working and I see following error on console:
The property 'Text' cannot be found on this object. Verify that the property exists and can be set.
Why$MyForm_Load works without Parent but $mbutton_click requires Parent? Isn't both $MyForm_Load and $mbutton_click part of same object? How does Parent works in System.Windows.Forms?
That's because in the event handler $this is bound to the sender of the event (the button in this case) instead of your class instance. So something like this should also work:
$mbutton_click = {
if ($this.Text -ne "enable") {
$this.Parent.Controls["label"].Text = "disabled"
$this.Text = "enable"
}
else{
$this.Parent.Controls["label"].Text = "enabled"
$this.Text = "disable"
}
}
Like #mhu says, it's because one is bound to the Form Load event vs an individual control object.
A Form is a class. That means a Form has:
Properties
Properties are objects in the class that can be referenced. They can be simple strings like .Name, or complex like .Controls which return a complex Control.ControlCollection object.
Methods:
Calling a method calls a single function definition that is fully defined at compile time. Calling a method e.g. MyForm.ShowDialog() calls that individual .ShowDialog() function.
Events:
Sometimes we want to do something, but we can't fully define the method call at compile time. But, at the same time, we really like the convenience of calling something that is defined like a method. This is where Event's can be used.
First. We think of a method call for something useful that we want to happen, like MyForm.Load(), and that's all we have to define at compile time. Right now we don't know what we want to do. We know that we want to be able to load a form, but we don't know what it will do or look like. So we put this in as a placeholder that we can call.
After some thought, we figure out what we want to do, and how we want things to look like and we build a function that does something useful. We then subscribe this function to an Event. This is like connecting it.
In the first case:
MyForm($mystuff) {
$this.Add_Load( $this.MyForm_Load )
}
We are registering MyForm_Load to the MyForm.Load event:
MyForm.Load -> MyForm_Load
This means that when we call MyForm.Load() it will call the connected function MyForm_Load that we wrote, and will execute it as if we wrote it as a real method at compile time.
Therefore inside MyForm_Load, $this refers to the MyForm Form object. i.e. No .parent needed, because you are the form.
Therefore to access the MyForm.Controls property, you can access it directly.
MyForm.Load -> MyForm_Load
MyForm.Controls
The second:
$MyForm_Load = {
$mlabel = [System.Windows.Forms.Label]::new()
$mlabel.Name = "label"
$mlabel.Text = "disabled"
$mbutton = [System.Windows.Forms.Button]::new()
$mbutton.Name = "button"
$mbutton.Location = [System.Drawing.Point]::new(100,100)
$mbutton.Add_Click( $this.mbutton_click )
$this.Controls.Add($mlabel)
$this.Controls.Add($mbutton)
}
Adds Controls to the Form.Controls object:
MyForm.Load -> MyForm_Load
MyForm.Controls
|-> mlabel
|-> mbutton
The mbutton control has a click event attached:
$MyForm_Load = {
...
$mbutton.Add_Click( $this.mbutton_click )
...
}
$mbutton_click = {
...
$this.Parent.Controls["label"].Text = "disabled"
...
}
So it now looks like:
MyForm.Load -> MyForm_Load
MyForm.Controls
|-> mlabel.Text
|-> mbutton.Click -> mbutton_click
So to go from MyForm_Load to mlabel.Text is:
$this .Controls["label"] .Text
(MyForm).Controls[(mlabel)].Text
Whereas from mbutton_click, the mbutton doesn't have any controls inside it. You have to go "up" a level to the form to get the mlabel control:
$this .Parent .Controls["label"] .Text
(mbutton).(MyForm).Controls[(mlabel)].Text
in my app I have multiple user rol0 (admin, distributor, buyer). By default, I show 3 menu items. But if the user in his list of articles has articleAA i show him the new item in the menu only for this article and another default item. Same, if he has articleBB I show him menu item for this article. On login, i chech this and show menu
$scope.allArticles = allArticlesFactory.get();
$scope.allArticles.$promise.then(function(data) {
angular.forEach(data, function(key, value){
switch (key.type){
case 'articleAA':
$scope.articleAA= true;
break;
case 'articleBB':
$scope.articleBB= true;
break;
default:
$scope.articleAA= "";
$scope.articleBB= "";
}
});
});
})
In a menu with ng-if, I check value
<li ng-if="articleAA === true"><a class="aktivniItem" ui-sref="articleAA">articleAA</a></li>
<li ng-if="articleBB === true"><a class="aktivniItem" ui-sref="articleBB">articleBB</a></li>
PROBLEM: when I log in like admin I get all menu item and this is ok.
When I log in like a buyer who has articleAA and articleBB i get all menu items and this is ok.
But, when I log in like another buyer who doesn't have any of this article, this two menu is still here. When I refresh, this special menu item disappears.
I try on top of controller put
$scope.articleAA= "";
$scope.articleBB= "";
and also
$scope.articleAA= false;
$scope.articleBB= false;
But, same problem is here. Next user always get menu from previous user, but if he refresh, everything is ok.
default menu
| Home | Articles | Info |
if user have articleAA
| Home | Articles | Info | ArticleAA |
if user have articleBB
| Home | Articles | Info | ArticleBB |
if user have articleAA and articleBB
| Home | Articles | Info | ArticleAA | ArticleBB |
Thnx
Thank you for taking the time to help me.
I am using PowerShell to build a GUI and I would like to override default system colors.
For example, when a control is highlighted (TextBox or ComboBox), form shows system colors. I would like to change the color to use AliceBlue. So far I have tried following codes, but to no avail:
[System.Drawing.SystemColors]::Highlight = 'AliceBlue'
[System.Drawing.SystemColors]::HighlightText = 'AliceBlue'
[System.Drawing.SystemColors]::ScrollBar = 'AliceBlue'
[System.Drawing.SystemColors]::Control = 'AliceBlue'
[System.Drawing.SystemColors]::HotTrack = 'AliceBlue'
[System.Drawing.SystemColors]::Window = 'AliceBlue'
[System.Drawing.SystemColors]::WindowFrame = 'AliceBlue'
The documentation says that those properties you are trying to set are readonly.
You can do this by invoking the user32.dll SetSysColors function:
$signature = #'
[DllImport("user32.dll")]
public static extern bool SetSysColors(
int cElements,
int [] lpaElements,
uint [] lpaRgbValues);
'#
$type = Add-Type -MemberDefinition $signature `
-Name Win32Utils `
-Namespace SetSysColors `
-PassThru
$color = [Drawing.Color]::AliceBlue
# For RGB color values:
# $color = [Drawing.Color]::FromArgb(255,255,255)
$elements = #('13')
$colors = [Drawing.ColorTranslator]::ToWin32($color)
$type::SetSysColors($elements.Length, $elements, $colors)
Where the 13 element represents COLOR_HIGHLIGHT, which is the colour of an item selected in a control.
After running the above code, here is the result:
ComboBox
TextBox
You can see that the colour of the actual text has changed and it is barely visible. To change this, simply run:
$color = [Drawing.Color]::Black
$elements = #('14')
$colors = [Drawing.ColorTranslator]::ToWin32($color)
$type::SetSysColors($elements.Length, $elements, $colors)
Where 14 represents COLOR_HIGHLIGHTTEXT, which is the colour of the text of an item selected in a control.
To see more about SetSysColors check PInvoke. Also, go here to find more color codes.
I don't know if it is possible to set the highlight colour for only the PowerShell GUI and nothing else using WinForms or SetSysColor, but one way you might consider is by using a WPF TextBox instead of WinForms. This way you can use SelectionBrush and SelectionOpacity:
[xml]$xaml = #"
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="Window"
Title="Initial Window"
WindowStartupLocation = "CenterScreen"
ResizeMode="NoResize"
SizeToContent = "WidthAndHeight"
ShowInTaskbar = "True"
Background = "lightgray">
<StackPanel >
<Label Content='Type in this textbox' />
<TextBox x:Name="InputBox"
Height = "50"
SelectionBrush= "Green"
SelectionOpacity = "0.5" />
</StackPanel>
</Window>
"#
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
$Window=[Windows.Markup.XamlReader]::Load( $reader )
$Window.ShowDialog() | Out-Null