How can I call a method on multiple objects in PowerShell? - powershell

I am working with an XAML GUI created in VisualStudio, and I am trying to set up some of the code for the checkboxes. Instead of doing each one separately, I was hoping to do it dynamically since the functions are all the same.
I have searched, but was only able to find a reference to an Invoke-Method function that was written.
Below is what I have so far, just to explain better what I am trying to accomplish.
The Get-Variable line comes back with a half-dozen variables that I am trying to call the same method on.
$vars = Get-Variable WPFVarA*
Foreach ( $var in $vars ) {
$var.Add_Checked.Invoke({$Global:TicketCount++})
$var.Add_Unchecked.Invoke({$Global:TicketCount--})
}

If the checkboxes are wrapped, say with a stackpanel (could be anything) you can create a routed even handler to handle all events, like this (untested):
#Bubble up event handler
[Windows.RoutedEventHandler]$Script:CheckedEventHandler = {
$Global:TicketCount++
}
[Windows.RoutedEventHandler]$Script:UncheckedEventHandler = {
$Global:TicketCount--
}
$StackPanel.AddHandler([Windows.Controls.CheckBox]::CheckedEvent, $CheckedEventHandler)
$StackPanel.AddHandler([Windows.Controls.CheckBox]::UncheckedEvent, $UncheckedEventHandler)

Related

How to check whether an attribute of an object has a value?

I get an error such as "can't call method 'xxxx' on an undefined value" when attempting to check if an object has been created (by the perl module Bio::Perl).
Is there a general way of checking if an attribute has a value or not? I would have liked to do something like:
if ($the_object->the_attribute) {
But as long as the attribute is "undef", calling the method will only give me the error message. I have not been able to find a solution to this problem - which is real, because the object is created by the Bio::Perl module, and some attributes may or may not be set. Maybe I should add that I am not particularly perl-objects-savvy.
edit:
Below is a relevant part of my code. The get_sequence() function is in the Bio::Perl module. On line 13, how can I make sure that there is a value (sequence in this case) before checking the length of it?
my #msgs;
my #sequence_objects;
my $maxlength = 0;
for ( #contigs ) {
my $seq_obj;
try {
$seq_obj = get_sequence( 'genbank', $_ );
}
catch Bio::Root::Exception with {
push #msgs, "Nothing found for $_ ";
};
if ( $seq_obj ) {
my $seq_length = length( $seq_obj->seq );
if ( $seq_length > $maxlength ) {
$maxlength = $seq_length;
}
push #sequence_objects, $seq_obj;
}
}
...
if ($the_object->the_attribute) {
This checks if the return value of the method the_attribute is true. True means that it's not 0, the empty string q{} or undef.
But you said you want to know whether the object exists.
Let's go over some basics first.
# | this variable contains an object
# | this arrow -> tells Perl to call the method on the obj
# | | this is a method that is called on $the_object
# | | |
if ($the_object->the_attribute) {
# ( )
# the if checks the return value of the expression between those parenthesis
It looks like you're confusing a few things.
First, your $the_object is supposed to be an object. It probably came from a call like this:
my $the_object = Some::Class->new;
Or maybe it was returned from some other function call. Maybe some other object returned it.
my $the_object = $something_else->some_property_that_be_another_obj
Now the_attribute is a method (that's like a function) that returns a specific piece of data in your object. Depending on the implementation of the class (the building plan of the object), if that attribute is not set (initialized), it might either just return undef, or some other value.
But the error message you are seeing is not related to the_attribute. If it was, you'd just not call the code in the block. The if check would catch it, and decide to go to else, or do nothing if there is no else.
Your error message says you are trying to call a method on something that is undef. We know you are calling the the_attribute accessor method on $the_object. So $the_object is undef.
The easiest way to check if something has a true value is to just put it in an if. But you already seem to know that.
if ($obj) {
# there is some non-false value in $obj
}
You've now checked that $obj is something that is true. So it could be an object. So you could now call your method.
if ($obj && $obj->the_attribute) { ... }
This will check the true-ness of $obj and only continue if there is something in $obj. If not, it will never call the right hand side of the && and you will not get an error.
But if you want to know whether $obj is an object that has a method, you can use can. Remember that attributes are just accessor methods to values stored inside the object.
if ($obj->can('the_attribute')) {
# $obj has a method the_attribute
}
But that can blow up if $obj is not there.
If you're not sure that $obj is really an object, you can use the Safe::Isa module. It provides a method $_call_if_object1 that you can use to safely call your method on your maybe-object.
$maybe_an_object->$_call_if_object(method_name => #args);
Your call would translate to.
my $the_attribute = $obj->$_call_if_object('the_attribute');
if ($the_attribute) {
# there is a value in the_attribute
}
The same way you can use $_isa and $_can from Safe::Isa.
1) Yes, the method starts with a $, it's really a variable. If you want to learn more about how and why this works, watch the talk You did what? by mst.

Powershell cannot pass object to function

I am unable to pass an object of type [System.Messaging.Message] into a function within my script.
e.g. (outline of code)
function global:CopyQueue() {
$vTotalCountInMSMQ = $global:qSource.GetAllMessages()
foreach ($msg in $vTotalCountInMSMQ)
{
ReadAndCopyMessage $destinationQueue ([REF]$msg)
}
}
Target Function:
function global:ReadAndCopyMessage($destinationQueueName, [REF]$message)
{
$message = $message.BodyStream.Position
.etc.....
}
Unable to access properties (Property 'Position' cannot be found on this object; make sure it exists and is settable.). However, if this code is run within the CopyQueue function, everything works as expected.
I am having trouble to outsource this and process the $msg object out of the loop.
Thanks for your help in advance
similiar questions didn't work:
PowerShell pass by reference not working for me
Powershell argument passing to function seemingly not working
It appears that you shouldn't use [REF] anymore. Also, I must have made the common "," error between parameters before and thus it didn't work.
The code above works fine without [REF]
Call:
ReadAndCopyMessage $destinationQueue $msg
Function:
function global:ReadAndCopyMessage($destinationQueueName, $message)

Unable to modify Coffeescript's global variables in a JQuery listener

Today I was migrating some of my javascript code into coffeescript and got stuck in something really silly but even though I didn't know how to make it work.
I wanted to update the value of a global variable when a click event was triggered, have a look at the code below to see one of my guesses
Here's the code
#activeObject = null
# Some other code
$ ->
$('#header').click ->
if !headerSelected
showMenu '#header-menu', event
else
#activeObject = "#header"
showMenu '#menu-style-header', event
Unfortunately even though the click event was triggered the variable was not getting updated.
I came up with a work around. I created a function that set the value of the variable and called it instead of the assignment and this time it worked.
I just wanted to know why I wasn't able to do it the other way. For me it was a simple operation and it seemed silly to define a new function just for this.
Your problem is that # (AKA this) inside the click handler isn't the same as it is outside so this:
#activeObject = null
and this:
#activeObject = "#header"
are referring to two different activeObjects. You should be able to bind everything with => to get the right this:
$ =>
$('#header').click =>
#...
or better (IMHO), just refer to window.activeObject directly in both places so that it is obvious to everyone that you're referring to a global variable:
window.activeObject = null
$ ->
$('#header').click ->
if !headerSelected
showMenu '#header-menu', event
else
window.activeObject = "#header"
showMenu '#menu-style-header', event
Alternatively, you could stop using globals altogether in favor of, perhaps, a data attribute:
$ ->
$('#header').data 'activeObject', null
$('#header').click ->
if !headerSelected
showMenu '#header-menu', event
else
$(#).data 'activeObject', '#header'
showMenu '#menu-style-header', event
I think the confusion is about the usage of #, which is basically just a shortcut for this.
If you compile your code and see what CoffeeScript compiler it produces, the confusion becomes clear
this.activeObject = null;
$(function() {
return $('#header').click(function() {
if (!headerSelected) {
return showMenu('#header-menu', event);
} else {
this.activeObject = "#header";
return showMenu('#menu-style-header', event);
}
});
});
if activeObject is global you whould reference to it
window.activeObject = null
and
window.activeObject = "#header";
in both occurences in this code, cause one might be tempted to use it without window in second occurence, but that will cause a new local variable to be implecitly defined.
Generally when starting with CoffeeScript, its usefull to try small snipets like this in
http://coffeescript.org/ on the Try Now Tab

update hashtable in enumerator and have it recognize that add?

What is the best way to handle doing an add to a hashtable inside of enumeration of that hashtable so that it includes that added item in the enumeration?
I havo something lke:
foreach ($key in $groups.GetEnumerator() | Sort-Object Name -descending) {
if (something) {
groups.add("test","test2")
}
}
I want it to use the new added item in the enumeration instead of calling the function that has this in it over and over, which was killing my memory resources and cuasing issues with affecting what was getting written out to export-csv call.
I think powershell throws an error when you update a hash you are walking with foreach.
Write your hash's keys into an array:
$stack = #($groups.keys)
Then "shift" a key off that list to work it:
while ($stack) {
$item,$stack = $stack
DoStuffHere()
}
And finally, in DoStuffHere, you'll need to add an item to the HASH and push the new key onto the stack so it gets worked.
$groups.add($newkey,$newval)
$stack = $stack,$newkey
This way, you have no worries about powershell's automatic behavior not working the way you want.

PowerShell function won't return object

I have a simple function that creates a generic List:
function test()
{
$genericType = [Type] "System.Collections.Generic.List``1"
[type[]] $typedParameters = ,"System.String"
$closedType = $genericType.MakeGenericType($typedParameters)
[Activator]::CreateInstance($closedType)
}
$a = test
The problem is that $a is always null no matter what I try. If I execute the same code outside of the function it works properly.
Thoughts?
IMHO that's pitfall #1. If you return an object from the function that is somehow enumerable (I don't know exactly if implementing IEnumerable is the only case), PowerShell unrolls the object and returns the items in that.
Your newly created list was empty, so nothing was returned. To make it work just use this:
,[Activator]::CreateInstance($closedType)
That will make an one item array that gets unrolled and the item (the generic list) is assigned to $a.
Further info
Here is list of similar question that will help you to understand what's going on:
Powershell pitfalls
Avoiding Agnostic Jagged Array Flattening in Powershell
Strange behavior in PowerShell function returning DataSet/DataTable
What determines whether the Powershell pipeline will unroll a collection?
Note: you dont need to declare the function header with parenthesis. If you need to add parameters, the function will look like this:
function test {
param($myParameter, $myParameter2)
}
or
function {
param(
[Parameter(Mandatory=true, Position=0)]$myParameter,
... again $myParameter2)
...
An easier way to work with generics. This does not directly solve the [Activator] approach though
Function test
{
New-Object "system.collections.generic.list[string]"
}
(test).gettype()