Converting Integers to Binary in Power Apps

Say what?!

This is the 2nd part of a series that should probably be called “Silly Challenges in Power Apps for Nerds”. You can find the first part here: Converting Binary to Integers in Power Apps.

Once again I will vaguely allude to the fact that there are good reasons to do this and all shall be revealed in the fullness of time. In the meantime, if you need it – here ya go!

When it comes to math, you have many terrible tool options. Thanks for choosing Power Apps!

Let’s do it!

Converting numeric values to their binary equivalent (as text) is a little more challenging than the reverse. This isn’t really because the concept is much harder, it’s more that Power Apps makes us work a little harder. But speaking of concepts, here’s Chris’ 2 minute summary of binary to integer math:

Some Background

The main thing we need to know is that binary digits (1 or 0) indicate if the value they represent are “on” or “off”. In our case, when the digit is “on” (1) then the value it represents will be added with all the other “on” values and this total will be the number we’re looking for.

Something to keep in mind that we didn’t have to directly worry about when we were going from binary to number is the “size” of the numeric data type. This just means the number of digits (bits). The bigger the number, the more bits required. For instance, 255 in binary is 11111111. 8 digits = 8 bits. More on this in a moment.

The Code

Once again, a beautiful screen was created in my Canvas Power App:

We’ve got a textbox called txtToBinary and a label under Result that is set to a variable called binaryResult

Here’s what is in the OnSelect for the button:

If(CountRows(binaryDigits)=0,
ClearCollect(binaryDigits,[]);
ForAll(Sequence(31,30,-1),
Collect(binaryDigits, {
index: Value,
worth: Power(2,Value)
})
)
);
ClearCollect(intToBinary,[]);
With({
firstDigit: CountRows(binaryDigits) – 1, //starting point depending on the number of binary digits
input: Value(txtToBinary.Text)
},
ForAll(binaryDigits,
With({
remainder: If(index = firstDigit, input, Last(intToBinary).remaining) //what's left to process (starts with input and decreases)
},
Collect(intToBinary,{
binary: If(remainder >= worth, "1", "0"), //Actual binary digit (left to right)
remaining: If(remainder >= worth, remainder – worth, remainder) //process what's left
})
)
)
);
Set(binaryResult,
With({
full: Concat(intToBinary,binary)
},
Right(full, Len(full) – Find("1",full) + 1) //trims leading zeros (unnecessary really)
)
);

Let’s look at what’s happening line by line:

  • Line 1 – In order to run through our conversion, we need all the positional values figured out ahead of time (so we can loop through them). This is a quick check to make this all a single chunk of code.
    • Basically, we are saying only initialize this collection if it’s empty (checked using the CountRows function). If you are only doing binary to numbers calculations infrequently then this is fine. More likely, however, you could just remove this condition and put lines 2-8 in your App.OnStart.
  • Line 2Clearing/Initializing our binaryDigits collection
  • Line 3 – We start a ForAll loop against an arbitrary collection we generate with Sequence.
    • As mentioned previously, this combination acts as a backwoods for loop. This is even more obvious here because of the parameters. The first number (31) is the total number of rows we want. The second number (30) is what the first number should be. The final number (-1) is the steps between the row values.
    • We are saying give us a collection of numbers from 30 to 0.
    • You might be surprised to see 31 instead of 32. The reason for this is that normal integers are 32-bits but only 31 of those are bits that represent the numeric value and the other is the sign (+ or -). This allows you to calculate the binary equivalent of numbers up to 2,147,483,647.
  • Line 4 – We store a record in our binaryDigits collection for every time we loop through our temporary collection from Sequence
  • Line 5 – The index value is set to the number we get from Sequence. Later we’ll use this value to determine if we’re on the first digit or not (this allows us to change the “size” of our temporary collection without having to update our conversion code).
  • Line 6 – We store the positional value (worth) as a Power of 2 of the Sequence number. This is because of this handy chart:
  • The resulting collection will look something like this:
This was for an 8-bit number: Sequence(8,7,-1) and only shows the first 5 (of 8) rows
  • Line 11 – We are using a temporary collection (intToBinary) to store results as we process the binaryDigits collections. So, we clear it out before starting.
  • Lines 12-15 – Using a With function we setup a couple of local variables
    • firstDigit is set to the total number of rows in the binaryDigits collection minus one. This allows us to know what the “size” of the number will be without having to hardcode it.
    • input is set to the Text Input’s Value so that we don’t have to reference it directly each time making our code more portable.
  • Line 16 – We start a ForAll loop against every row in our binaryDigits collection we just generated above.
  • Lines 17-19 – We use another With function to setup the remainder. We get this by using the Last function to pull in our most recently added item in the collection (the last iteration of the loop) unless this is the first iteration (index = firstDigit) in which case we use the full value of the input.
  • Lines 20-23 – We are calculating which positional values to add together position by position. This requires us to remember what the value of the input is as it changes (subtracting the “on” values), but there is no way to set results from within a ForAll. Fortunately, you can call Collect within a ForAll so we’re doing exactly that and storing our results and our progress as we go.
  • Line 21 – This is the actual binary digit and we are determining if it should be “on” (1) or “off” (0). We do this by looking to see if the positional value (worth) is greater than or equal to the remaining value. If it is, then we turn it “on” (set it to “1”) otherwise we turn it “off” (set it to “0”).
  • Line 22 – To calculate the additional bits, we need to know what’s left (remaining) of the original input. So if we just stored a 1, we subtract the positional value (worth) and store that for calculating the next digit. If it was a 0, then we just pass the value on unchanged.
  • Line 28 – We’ve been calculating as we went but our values are spread across our temporary collection. So we need to extract that value out and save it. In this case we are setting it to a global variable that is referenced by our result label.
  • Lines 29-31 – We use another With function to make it easier to reference our full binary value. If we just want the raw value then this could be skipped and you could just set the binaryResult to this directly. But, we want to trim all the leading zeros to make it easier to read.
    • We use the Concat function to slap all the binary columns of our temporary collection into a single line of text. This works because we evaluated from left to right meaning our calculated digits are already in order.
  • Line 32 – We use a combination of the Right, Len, and Find functions to calculate where the first “1” is in our result and cut off all the extra zeros before that.

Perhaps looking inside the intToBinary collection will help make sense of what we built:

There you have it! Now you can FINALLY convert numbers to binary strings. Wowee!

Looking for more exciting low level looping in Power Apps? Come back for Part 3: Getting the Binary Value of an ASCII Character in Power Apps!

Converting Binary to Integers in Power Apps

But… why?

I promise there are legitimate reasons why you might have to work with binary integers in Power Apps and that’ll be more obvious near the end of this series which should probably be titled “Doing low level operations in Power Apps for masochists”. For now, know it’s possible and if you need to do it here’s the guide.

Let’s do it!

Converting binary values (as text) to their numeric values is “relatively” straight-forward (at least for this series). There are a bunch of guides on how to convert from base 2 to base 10 conceptually and if you’re interested in all the details, go check em out! For our purposes, here’s a basic overview (don’t weep hardcore computer scientists):

A Brief but Boring Background of Some Binary Stuff

Binary digits (1 or 0) indicate if the value it represents is “on” or “off”. In this case, “on” (1) means that value should be included when calculating the result. The values start with 1 and double each time so that the values are 1, 2, 4, 8, 16… etc. to whatever “size” for the data type (read right to left):

As you can see from the image above, the values (1,2,4,8,16…) are really just increasing powers of 2. We’re going to use that fact to our advantage.

So to convert binary to a number, add the values to each other whenever there’s a 1 in the corresponding positions.

The Actual Expression

I went all out in my Canvas Power Apps design skills and created the following screen:

I’ve got a textbox called txtFromBinary and a label under Result that is set to a variable called intResult

Here’s the contents of the OnSelect for the button:

ClearCollect(binaryToInt,[]);
With({
input: txtFromBinary.Text
},
ForAll(Sequence(Len(input)),
With({
binaryDigit: Right(Left(input,Len(input)-(Value-1)),1) //Grab the right most digit
},
Collect(binaryToInt,{
binaryDigit: binaryDigit, //Not necessary (debug)
value:If(binaryDigit = "1", Power(2,Value-1), 0), //Not necessary (debug)
runningTotal:If(binaryDigit = "1", Power(2,Value-1), 0) + If(Value=1, 0, Last(binaryToInt).runningTotal)
})
)
)
);
Set(intResult,Last(binaryToInt).runningTotal);

Let’s look at what’s happening line by line:

  • Line 1 – We are using a temporary collection (binaryToInt) to store intermediate results as we process the binary text character by character. So, we clear it out before starting.
  • Lines 2-4 – Using the With function we can setup a local variable called input which we can reference rather than the Text Input’s Value each time (and it gets cleaned up at the end)
  • Line 5 – We start a ForAll loop against an arbitrary collection we generate with Sequence.
    • We’re going to see this combination over and over in this series because this is Power Apps’ janky version of a for loop. In this case, we are using it to let us run through a collection that is exactly as long as our binary string (which we get by using the Len function). This makes working with each individual character one at a time possible.
  • Lines 6-8 – Again using the With function (which I absolutely love) we setup a local variable. This time we’re extracting the exact character, binaryDigit, we want to work with.
    • In their infinite wisdom, Power Apps didn’t feel the need to implement a substring function so we’ve got an overly complicated combination of Right and Left to grab a single character. The exact character is determined by the use of the Value keyword which in this case references the number from the Sequence function we used earlier.
  • Line 9 – We are calculating a running total of the final value but there’s no way to set results from within a ForAll. Fortunately, you can call Collect within a ForAll so we’re doing exactly that and storing our calculations as we go.
  • Line 10 – This is just for debugging and isn’t used at all but helps us verify we pulled the right bit
  • Line 11 – Also just for debugging, it helps us identify that we calculated the positional value correctly
  • Line 12 – This is the actual calculation where we are adding 2 numbers together.
    • The first is the positional value for a bit which is 0 when the bit is “off” (0) and calculated as a Power of 2 when “on” (1). We use the Value keyword which references the number from the Sequence function used earlier. However, we have to subtract one because Sequence starts with 1 (rather than 0).
    • The second number is the running total we’ve been calculating on the previous bits. We get this by using the Last function to pull in our most recently added item in the collection (the last iteration of the loop) unless this is the first iteration (Value=1) in which case it’s just 0.
  • Line 17 – Since we were calculating as we went, we know that the very last value of our temporary collection is the final result. So we grab it and set it to a global variable to be referenced in our label.

Looking in our temporary collection, binaryToInt might make it a little easier to understand:

Or maybe it didn’t?! Oh well!

The good news is that it works – so copy and paste as you please. And for those of you who can’t get enough of doing things in Power Apps that were not intended to be done in Power Apps, be sure to come back for Part 2: Converting Integers to Binary in Power Apps!

Custom Icon Buttons in Power Apps with Hover Color

Using icons for buttons is a very common scenario in Power Apps and Microsoft has provided the Icon control to accomplish just that. But… there are only 106 across 4 categories which isn’t a whole lot when it comes to icons. So if you are making anything of any complexity you’ve likely already run out. Fortunately, that’s

Easiest Solution

Just use their stupid icons

Custom Icons

But what if you don’t want to use one of the very few icons they’ve provided? What if you found Flicon and would like your app to match the rest of Office 365 and use the Fluent / UI Fabric icons? Or perhaps Font Awesome or the Noun Project or something your kid drew?

Fortunately, there are still options! There is a pretty easy option laid out in the OKish Solution below and then a PITA Solution that works but there are several steps involved. I’ve included both, but I definitely think the PITA Solution is the way to go until Microsoft figures out something better.

OK-ish Solution

Use images but be satisfied with Border or Fill effects

PITA Solution That Works

What if you want more than just a changing background or border? You know, like how the icons themselves work in Power Apps? For this, we’ll need a custom icon as an SVG. These steps could be adapted to work with a PNG image, but SVG is very common among icon sets and provides a better result overall.

1. Get an SVG Icon

First step, get your icon! Font Awesome and the Noun Project both provide all of their icons in SVG format. However, I recommend using the Fluent Icons provided by Microsoft since that’s what’s being used everywhere else. For that, let’s head over to Flicon.io.

If you haven’t used Flicon.io before, just search for an icon you want to use or browse with the categories. Once you’ve got an icon in mind, hover over it and switch to the Export tab. You can mess around with colors if you’d like, but it isn’t really necessary for what we’re doing. Just click Save as SVG:

2. Edit that Icon

We need to edit the SVG file before it’s ready to be used. There are plenty of tools out there to do that, but I use a free, open-source tool called Inkscape. You can download it directly from the site or just install it from the Microsoft Store.

Open your icon in Inkscape. One thing you’ll notice is that most of the icons are square, but the image isn’t always centered. Don’t worry, we’ll account for that to ensure our icon is a centered square.

In Power Apps we can use an SVG as the Image for an Image control. The Image control has HoverFill as we showed above. We’re going to take advantage of that and create an inverse of our icon. This means we’ll be creating an image that is the background and leaves the part of the icon we want displayed transparent. This allows us to set the “color” of the icon using the Fill and HoverFill properties!

3. Create a Box

In Inkscape, draw a box (fill color doesn’t matter, but it shouldn’t have a stroke). The size doesn’t matter just yet. Grab the square tool and draw something. Don’t worry about making it perfectly square.

Click on your icon and look at the Width and Height displayed. Take note of the bigger of the two (mine happen to be the same):

Click on the square you just drew and put the value you just noted for both its width and height. Now you have a box sitting somewhere near your icon that is the same size as your icon’s largest dimension. If you want additional padding for your icon, add that to the box’s dimensions.

Now we’re going to line them up. Open the Align and Distribute panel (Object > Align and Distribute). Select both the box and your icon. Ensure the Relative to dropdown is set to Page then click the Center on Vertical Axis button followed by the Center on Horizontal Axis button. Your box should be totally covering your icon now.

4. Cut a Hole in a Box

With both the box and the icon selected (just select all if you clicked off of them), perform an exclusion (Path > Exclusion). You now have one object and it is the negative space around your icon (remember the color doesn’t matter).

You’ll notice we’ve got an extra space around the icon and the document size (seen as a black square above). This is because our icon wasn’t perfectly centered before. If your icon looks good, skip ahead. To fix it, however, go to Document Properties (File > Document Properties). In the Custom Size options group, expand the Resize page to content section and click Resize page to drawing or selection.

5. Optimize Your SVG

Let’s save our SVG. Although we can use the default format, there’s a lot of extra stuff added that we don’t need. So let’s Save as (File > Save As…) and change the type to Optimized SVG. In the dialog that pops up, here are the options I’ve chosen with the goal of reducing SVG complexity/length:

Let’s open up our SVG file using a text editor like Notepad. You should see some XML with a viewBox attribute and one or more paths. Let’s do a find for double quotes and replace them all with single quotes.

6. Use Your Icon in Power Apps

Although we can add an SVG file as media and use it that way, I like to have a little more control. So, let’s add an Image control to your App. In the Image property we’re going to replace SampleImage with some text. The first bit of text is just a string:

"data:image/svg+xml;utf8, "

This will let us use the SVG text directly. To do that we need to add the EncodeUrl function. So connect the text above with an & and enclose the SVG text (copied from notepad) in double quotes inside of the EncodeUrl function (don’t forget your closing double quote and parenthesis):

The icon is showing, wowee! Now we need to make that fill match the background (the black stuff shown above). So we can edit our SVG string to add fill='white' (or a HEX or RGBA value that matches your background which is white in my case).

Where did that icon go?! It’s still there, it’s just being sneaky.

7. Add Some Color

Now for the magic! Set the Fill property for the Image control (shown as Color in the properties window for some reason) and your icon shows up! WOWEE!

Now set the HoverFill to some other color and hold Alt to see the magic.

Hold down Alt to test it right in the Editor

Limitations

This is not a perfect solution and the fact that this is our best option is pretty frustrating. But… it gets the job done but with a few notable issues when compared to using the native icons:

  • That’s an annoying amount of work for each icon
  • No option to have the pointer cursor even when using it with an OnSelect action
  • Images sometimes get weird borders depending on the scaling
  • You need to keep your image ratio the same to avoid the background leaking out (set the Width to Self.Height and control just the Height)

Making a Game in PowerApps – PowerPush: Dungeon Explorer 3000!

During my day job I create PowerApps fairly regularly for both clients and internal use. The platform has come a long way in a very short amount of time and has gone from interesting idea to amazing enterprise accelerator. So what did I decide to do with all that enterprise capability? Create a game of course!

PowerPush: Dungeon Explorer 3000!

PowerPush is a Sokoban clone made entirely in PowerApps. It has 6 levels, 4 playable characters, music, sound effects, supports right and left handed players, and has only 4 screens.

Fair warning, there’s a lot of sweet techno loops

PowerPush is open source (in as much as PowerApps can be) and you can import it into your tenant to give it a whirl and take a peek under the hood. There are no data connections at all, so it really is as simple as importing and playing.

Get it here: PowerPush.zip

Why?

To push myself and learn some stuff I wouldn’t necessarily learn by creating another form wrapper app. Believe me, I learned a lot! Number one thing I learned was that PowerApps really isn’t a great platform for making games. However, I also learned several really cool techniques (“wonky hacks” might be a better term for several of them). I’ll talk more about some of these below but I’ll also be following up with some individual tips and tricks in the next few weeks.

How it works

There are a lot of pieces to this app, and the best way to understand it is to just download it and take a look. But here are some details about each of the screens:

Screen 1: Main Menu

When the app starts, several global variables and collections are initialized. The most important of which is the levels global variable. This is an array of level objects that define the tiles to be used and details about where the crates start and where they should end up:

The levels object is later processed into a set of context variables and collections to manage current state for the active level and provide details about which image to use for each tile and whether those tiles are solid, etc.

The main menu has some animation based on a timer, but otherwise there are simple controls to set global variables used on other screens (like the character).

Screen 2: Level Start

The level start screen simply shows the current level details along with a randomly selected “tip”. This screen uses the fade navigation to create a nice transition into the level. You can also click anywhere on the screen to skip the automatic navigation based on a short timer.

One of the key things it does, however, is to process the level object into some key collections:

Set(activeLevel,Last(FirstN(levels,currentLevel)).Value);
Set(tiles,{
    background: ForAll(activeLevel.background,{
        img: LookUp(imgMap,id=Left(Value,2),img),
        solid: Right(Value,1) = "1"
    })
});
ClearCollect(crates, ForAll(activeLevel.start.crates, {x:Value.x, y:Value.y}));
ClearCollect(torches, ForAll(activeLevel.start.crates, { on: false}));
UpdateContext({tip:First(Shuffle(tips)).Value})

More details will later be pulled out of the activeLevel object to set up initial state for the player, but the info above determines how many crates are visible and where they are along with key information about the tiles. By using an image map collection we can use the strings setup in the level (first 2 characters are the tile image and the last indicates if the tile should be “solid”).

This step greatly simplifies level design by making it a simple array of strings so that you can almost visualize the level while you design it. By processing it here, we get easily usable values in a collection that tiles can reference details from by index.

This is how we can have one screen for the main game with any number of levels! This lets us simply bind to a data model and write logic (movement) to update that model. This is the area that PowerApps really shines.

Additionally, we could easily provide multiple image maps to create “themes” for levels to allow the same logic to apply a completely different tile set. The tile objects could also be extended to provide an additional layer of tiles for foreground elements if we wanted using nearly the same logic.

Screen 3: Main Game

This is where all the levels are shown. The tile system is based on 140 images laid out in 10 rows of 14 columns. Each of these tile image controls hijack their own BorderThickness property to store their “index” value. This works because the BorderStyle is set to None (so that value isn’t used).

This “index” value is used to position each of the individual tiles (the number of tiles, size, rows, columns, etc. can all be changed through some simple global variables). The “index” is also used to pull what the image should be by referencing the tiles object’s background array (shown above).

While not easy, you can reference an item within a collection by index by using this non-obvious formula:

Last(FirstN(tiles.background,tile043.BorderThickness)).img

The actual movement logic is handled in a single spot. There aren’t really global functions in PowerApps, but I’ve got something pretty close in that I’ve added a timer with a Duration value of 0. Then I set the AutoStart to a context variable. In the OnTimerEnd action I update any variables as needed and finally set that same AutoStart context variable to false.

So now to call that “global function” I simply need to call an UpdateContext function. In that call I update any “parameter” variables the global function will reference and set the AutoStart variable to true. For instance, here is the OnSelect for the Left button of the virtual D-Pad:

UpdateContext({requestedMove:{x:-1,y:0,rot:ImageRotation.Rotate180},evaluateMove:true})

Just to show you the power of the “global function” approach, here is the OnSelect for the Down button of the virtual D-Pad:

UpdateContext({requestedMove:{x:0,y:1,rot:ImageRotation.Rotate90},evaluateMove:true})

Both of these calls are nearly identical with the only exception being the “parameter” variable requestedMove.

The key part, however, is that evaluateMove:true. This will cause the timer to start and because it has a Duration of 0 it immediately performs it’s OnTimerEnd actio. This was really important because the move evaluation logic is pretty complicated and I really didn’t want to cut and paste it with some minor tweaks between 4 different buttons. That would be pretty hard to maintain.

The actual move evaluation uses a series of If statements and looks at the tiles collection to determine the current positioning of objects to determine if the requested position of the player is open and what should happen (push a crate, play a grunt since he hit a wall, etc.). It’s pretty convoluted, but you can see that some pretty complex logic can be performed to update our positioning elements (which specific controls like the player or the crates are bound too). Because of the power of PowerApps, all of the controls redraw themselves automatically just by our editing the model!

"Handle movement";
"If no wall in this direction";
If(!Last(FirstN(tiles.background,(playerY+requestedMove.y)*14+playerX+requestedMove.x+1)).solid,
    "If no crate in this direction";
    If(CountIf(crates,x=playerX+requestedMove.x And y=playerY+requestedMove.y) = 0,
	    "No crate, just move the player";
        UpdateContext({playerX:playerX+requestedMove.x, playerY:playerY+requestedMove.y, playerRot:requestedMove.rot, playerPushing:false,playSndWalk:true});
        Reset(audWalk),
		"There is a crate, see if it is up against a wall";
		If(!Last(FirstN(tiles.background,(playerY+requestedMove.y*2)*14+playerX+(requestedMove.x*2)+1)).solid,
			"Not against a wall, check for another crate";
			If(CountIf(crates,x=playerX+(requestedMove.x*2) And y=playerY+(requestedMove.y*2)) = 0,
				"Push that crate!";
				UpdateIf(crates, x=playerX+requestedMove.x And y=playerY+requestedMove.y, {x:playerX+(requestedMove.x*2),y:playerY+(requestedMove.y*2)});
				UpdateContext({playerX:playerX+requestedMove.x, playerY:playerY+requestedMove.y, playerRot:requestedMove.rot, playerPushing:true,playSndWalk:true,playSndPush:true});
                Reset(audWalk);
                Reset(audPush),
				"Can't move the crate or player (crate against a crate), but update player look";
				UpdateContext({playerRot:requestedMove.rot, playerPushing:true, playSndGrunt:true});
                Reset(audGrunt)
			),
			"Can't move the crate or player (crate against a wall), but update player look";
			UpdateContext({playerRot:requestedMove.rot, playerPushing:true, playSndGrunt:true});
            Reset(audGrunt)
		)
    ),
	"Can't move the player (wall), but update player look";
	UpdateContext({playerRot:requestedMove.rot, playerPushing:false});
    If(Rand()<.5,
        UpdateContext({playSndHuh1:true});
        Reset(audHuh1),
        UpdateContext({playSndHuh2:true});
        Reset(audHuh2)
    )
);

"Check crate positions";
ClearCollect(prevTorches, torches);
ClearCollect(torches, ForAll(crates, {on:CountIf(activeLevel.start.targets, x=Value.x And y=Value.y)>0}));
If(CountRows(Filter(torches,on=true))  CountRows(Filter(prevTorches,on=true)),
    UpdateContext({playSndIgnite:true});
    Reset(audIgnite)
);

"Check for Win";
If(CountRows(Filter(torches,on=true)) = CountRows(activeLevel.start.targets),
    UpdateContext({finalDescent:true});
    Reset(audMusicWin)
);
UpdateContext({evaluateMove:false})

Screen 4: Credits

This is a really simple screen that has a gallery bound to a custom credits collection. Then the Y value of the gallery is set based on a timer value to provide the scrolling animation. The timer is on a loop, so the scrolling is too.

This screen really exists to thank the tile artists (used with permission, but still greatly appreciated) and to make my kids happy (they’re the testers listed above).

Lessons Learned

I’m going to post a few different techniques I used as separate articles over the next few weeks, but there are several pain points I identified that I’d love to see solved in the future. Here’s a few:

Need for a this operator

You refer to a control by it’s name in your formulas. This works great. Even better, if you copy one or more controls they’ll get renamed and all the formulas updated. This is super powerful. But… if you suddenly need to update that formula on multiple controls (say 140 tiles), you can’t just select them all and update their formula because all of those formulas are now unique because they refer to specific controls. Simply introducing a this keyword that would refer to the current control in it’s own functions would be awesome.

Related Idea Entry – Go Vote!

Additional Idea Entry – Go Vote!

Audio should pause in the editor

Currently, if you loop and play your audio file it will continue to play even in the editor. This is weird behavior since, for instance, the timer stops running in the editor. This means if you do something like add really annoying techno music (see video above), you’ll never escape it.

Related Idea Entry – Go Vote!

True Image Rotation is needed

Right now you can flip an image control horizontally or vertically and you can rotate in 90 degree increments. It would be far more powerful if you could specify an exact degree of rotation. This would make animations far better.

Related Idea Entry – Go Vote!

Custom Object Props Please

To simplify things, I shoved an index variable for the tiles, crates, torches, etc. into their border width property. This works because I wasn’t using this property (BorderStyle: None). This made creating multiple controls that knew what variables to pull from far simpler. However, it would be even better if we could just associate custom properties of a given type directly on controls instead of hijacking an unused property (since this could have unintended consequences).

Related Idea Entry – Go Vote!

Reference Media by Name (text)

When you assign an image to an image control you do so by the Media variable. This is true for other controls as well. This is fine, but it can make dynamic assignments difficult since the control will need to be able to reference (know about) the actual media variables directly. I solved this using an image map collection so that I could build strings and then find the referenced image that way. This works pretty well, but it would be even easier if there were just a Media(“MediaName”) function.

Related Idea Entry – Go Vote!

Support for Key Down Events

PowerPush was designed for touch controls on a phone using a simulated d-pad. This works, but is clunky for the web. It would be far nicer to move with the arrow keys. Unfortunately, there isn’t a key down event to do this.

Related Idea Entry – Go Vote!

Update

I demoed the game on the SharePoint Patterns and Practices General Development call on 1/24/2019. Check it out here: