בשנתיים האחרונות שאני שומע רק על Coded UI ב – Visual Studio אבל יש עוד כ”כ הרבה מעבר, במפגש הקרוב אני אדבר על כל יכולות האוטומציה ב – Visual Studio 2012 עבור מפתחים ואנשי אוטומציה.
-
נדבר על Unit Test ויכולת ה – Fakes החדשה שמגיעה עם Visual Studio 2012 (מחליף את Pex & Moles)
-
ממשק Web Test Manager שמגיע עם Update 2, אשר מאפשר עבודה עם בדיקות ללא MTM.
-
חידושים ודרכים יעילות לכתיבת Coded UI ועבודה עם דפדפנים שונים כגון FireFox ו – Chrome.
-
Web Performance Test
-
Load Testing
בנוסף לא כדאי להפסיד הרצאה של ליאור פרידמן שידבר על ניהול פרוייקטי Agile.
פרטים נוספים ורישום בלינק זה:
http://www.eventbrite.com/event/6729031715
אשמח לראותכם במפגש!
I know you think the title is wired, because what is the relation between SignalR (WebSockets) and a specific device such as Samsung Galaxy 3?
ASP.NET SignalR is a new library for ASP.NET developers that makes it incredibly simple to add real-time web functionality to your applications. What is "real-time web" functionality? It's the ability to have your server-side code push content to the connected clients as it happens, in real-time.
While working on a project working with SignalR we faced a problem, the following code start the connection with the hub but the following code isn’t working, neither success or failed message appear.
$.connection.hub.start().done(function () {
alert("Connection Working!");
}).fail(function (e) {
alert("Connection Isn't Working... :-(");
});
Tried to debug the code I didn’t understand why the fail function isn’t invoked… I tired to change the transport layer to polling as part of the testing and it works……
connection.start({ transport: 'longPolling']})
I said to my self this is strange, After all Galaxy 3 supports should WebSockets and beside that this what SignalR should do if the browser isn’t supporting a specific transport layer.
From this part of I’ve understand the problem related to SignalR but still didn’t understand why that same code works on Nexus 4 and some other Android devices.
So I made the following test, I’ve open the Galaxy 3 browser and navigate to “http://www.websocket.org/echo.html” to check support for WebSockets , and surprise something isn’t working as it should – The log field didn’t showed anything (Again on different devices this works just fine).

So first thing I’ve modified jquery.signalR.js file, I’ve added a time out around the WebSockets connection, this solved the problem…
Then after a short search I found this Bug: Persistent Connection fails with IIS8 on Android Stock Browser
And this guy add his fix for the problem here - https://github.com/SignalR/SignalR/commit/6d892a3cdeb94099ed06b978ba66ec13fce521ae
You can download the fix and implementing your own timeout around the connection start.
Enjoy
Couple of days ago Brian Harry post about the new release of Update 2 for Visual Studio and TFS 2012, and today another release of Update 3 that isn’t big a Update 1 and 2 but contains a set of bug fixes that can ease you work.
Before jumping to Update 3, if for some reason you’re not using Update 2 this is the time to read about it and install it!
Update 2

Update 3 – CTP
Installing it should be relatively straight forward – for both VS and TFS, just run the installer. There should be no compatibility breaks so you need not upgrade all of your components at once, though, over time, you should plan to get them all updated
List Of Improvements – Full List
- Technology improvements - Team Foundation Server
- Build settings can be preserved when you upgrade a TFS 2012 instance.
- Improvements are made in the New Build Definition UI for the Continuous Integration (CI) build in Git-based team projects.
- Visual Studio Test
- Web test
- Microsoft Visual Studio 2010 crashes when you create a Web test on a computer that has both Visual Studio 2010 Service Pack 1 (SP1) and Visual Studio 2012 Update 2 installed.
- Coded UI test
- You cannot run coded UI together with the cross browser testing feature by using Selenium components.
- Load test
- When you analyze a load test run, parts of the results are missing from the graph in the default view
- Unit test
- Administration and Operations
- The event log does not include stack traces when the administrator console crashes.
- You cannot configure TFS proxy on a computer that belongs to a separate domain from a TFS 2012 server.
- TFS 2012 backup plan wizard does not report an error when you configure a backup plan by using the Local System account.
- A user who does not have read permission on an area path can receive email notifications of changes to work items under the area path.
- Slow performance when you open a work item that has many customized fields in TFS Web Access.
- Fix adds upload retries for Version Control files that are being checked in or shelved to reduce the number of deceptive errors.
- More
Enjoy
On Sunday I spoke about “Advanced Coded UI Testing” at Sela SDP workshops day, this was great day with many people who showed great interest in Coded UI and Automation using Visual Studio 2012. In this workshops we started from the beginning to advanced Coded UI technics and methodologies of how to write Good code for flexibility and good maintainability of your tests..
I’ve also gave some cool tools for help running Coded UI, tools like:
- MS Test Runner UI – Allow everyone (with Test Agent installed) to load test files and execute tests – Good for manual testers that wants to run automated tests.
- Trx to HTML converter.
- Coded UI Steps Tracker
- and More…
This is my fifth time as VS ALM MVP and still excited like it’s the first time I got this email:
Congratulations! We are pleased to present you with the 2013 Microsoft® MVP Award!
To be a Microsoft MVP is a great honor and I’ll keep work hard for the community: Talking and Writing about new technologies and more.
I also would like to say many thanks to my company Sela, my boss David Bassa and Caro Segal and Ishai Ram who pushed me forward in the technical community, promote many tools we built for the ALM community and helping me keep the MVP Award for another Year!
And I’ll also like to thank You, yes You! reading my blog is big part of my contribution for the community and I hope you keep reading my blog and hopefully enjoy it.
My MVP Profile - https://mvp.support.microsoft.com/profile/Shai.Raiten
זהו החלק האחרון בנושא סופר 8 , אני אמשיך להרחיב בנושא JavaScript ופיתוח מחשקים בכללי אבל היום אני רוצה לסגור את הדברים הבסיסיים בפיתוח משחק סופר 8.
בחלק הקודם פיתוח משחקים ב JavaScript - מבוסס על משחק סופר 8 - חלק 4 דיברנו על איך להפוך את המשחק ליותר אינטרקטיבי ע”י הוספת רקעים ואלמנטים נוספים למשחק, ראינו איך אפשר בצורה קלה ונוחה לטעון את כל הגרפיקה (ועוד) בעזרת ספריית PreloadJS ולהתחיל את המשחק כאשר קיבלנו את כל הדברים בנחוצים למשחק.
בפוסט היום נראה איך אפשר לבצע קפיצה של המשתמש (לא רק אנימציה) וכמובן אחר הנושאים החשובים ביותר- התנגשות עם אובייקטים – Collision.

לדוגמא המלאה
בהמשך מהפוסט הקודם נוסיף את הקוד הבא תחת פונקציית handleComplete על מנת לטעון אבן למשחק שלנו:

case "rock":
var g = new createjs.Graphics()
g.beginBitmapFill(result);
g.drawRect(0, 0, 45, 44)
rock = new createjs.Shape(g);
rock.y = h - 119;
rock.x = w;
rock.height = 44;
rock.width = 45;
break;
כעת נשאר להוסיף אותה לאובייקט Stage על מנת שתופיע במשחק:
stage.addChild(sky, ground, hill, sun, player, rock);
עכשיו שיש אבן במשחק בואו נכניס אפשרות תלקפוץ מעליה, על מנת לבצע זאת נירשם ל – stagemousedown על מנת לתפוס לחיצה של העכבר של המשחק, כאשר נקבל פעולה זאת ננגן את פעולה הקפיצה ונשנה את הערך של isJumping ל – true.
נוסיף את הקוד הבא תחת פונקציית handleComplete לפני הפעלת הטיימר – setFPS.
stage.addEventListener("stagemousedown", function () {
if (isJumping) return;
play("jump_h");
gameOver = false;
isJumping = true;
});
כעת נוסיף את הקוד שישנה את מיקום הדמות על ציר Y כך שנוכל לקפוץ מעל האבן, הקוד הבא אינו הפתרון הנכון יותר אבל לדוגמא שלנו הוא פשוט ועובד, בעיקרון אפשר להוסיף הרבה קוד נוסף לביצוע פעולה זאת על ידי טיימר או מיקום הדמות אבל בשבילנו זה מספיק כרגע.
הקוד בודק שהדמות לא נמצאת באוויר (אין קפיצה כפולה), ומפעיל טיימר של שניה אחת, לאחר אותה שניה הטיימר יחזיר את הדמות לקרקע, אבל לפני זה המאפיין ה – y של השחקן יעלה ב – 4 פיקסלים כלפי מעלה עד לגובה מקסימלי, כאשר הדמות תגיע לגובה מקסימלי הדמות תתחיל בירידה כלפי מטה.
function handleJump() {
if (isJumping) {
if (onTheAir == null) {
onTheAir = setTimeout(function () {
isJumping = false;
player.y = playerBaseY;
onTheAir = null;
goingDown = false;
top = false;
}, 1000);
}
if (goingDown && player.y <= playerBaseY) {
player.y += 4;
}
else {
player.y -= 4;
if (player.y <= maxJumpHeight)
goingDown = true;
}
}
}
על מנת לבצע את הקוד נוסיף קריאה לפונקציית handleJump תחת tick:
function tick() {
handleJump();
...
}
כעת נוכל לבצע קפיצה בעזרת לחיצה על מקש שמאלי על המשחק.
דבר אחרון שנשאר לנו הוא להתמודד עם התנגשות של שני אובייקטים במשחק, כלומר עם השחקן יתנגש באבן אז הדמות תיפול.
ממליץ לכם לקרוא - Object/Object Intersection, ולהתעניין קצת יותר בנושא כי הוא בהחלט קצת מורכב, הרעיון המרכזי הוא לדעת מתי הדמות או אבן מתנגשות אחת בשניה, יש נוסחאות שונות לחשב את זה (תלוי גם עם הדמות מרובעות או מעגליות).
function checkRectIntersection(r1, r2) {
var deltax = r1.x - r2.x;
var deltay = r1.y - r2.y;
var dist = 25;
if (Math.abs(deltax) < dist && Math.abs(deltay) < dist) {
if (Math.sqrt(deltax * deltax + deltay * deltay) < dist) {
return true;
}
}
return false;
}
עכשיו לחלק הקוד האחרון של המשחק, פונקציית HandleCollision אשר תקרא מתוך פונקציית tick:
function HandleCollisions() {
var a = getCollideableItemBounds(player);
var b = getCollideableItemBounds(rock);
var oppss = checkRectIntersection(a, b);
if (oppss && !gameOver) {
console.log(oppss);
gameOver = true;
play("fall_h");
}
}
וסיימנו…. כעת כאשר הדמות תגיע למרחב של האבן המשחק יעצר ודמות תיפול לרצפה, לחיצה על קפיצה תמשיך את המשחק.
לדוגמא המלאה
בפוסט הקודם חלק 3 – פיתוח משחקים ב – JavaScript דיברנו על איך להזיז את הדמות במרחב המשחק ואיך לשנות את כיון התמונה שתתאים לכיון שהדמות הולכת אליו.
בפוסט הזה נדבר על יצירת אווירה (רקע ואלמנטים נוספים) למשחק, לא סתם איך להוסיף תמונות רקע נדבר על איך לגרום לרקע לנוע יחד עם הדמות עלל מנת ליצור תחושה של תנועה מתמשכת.
בפוסט זה נשתמש בספריה נוספת שנקראת – PreloadJS, שגם שייכת לסט הספריות של CreateJS. (עד עכשיו השתמשנו ב – EASEJS שגם משתייכת לשם)
ספריית PreloadJS, מאפשרת טעינה של קבצי מוסיקה, תמונות ברקע ומאפשרת לנו לשלוט מתי כל חלקי המשחק נטענו בהצלחה.
נוסיף לדף שלנו את הקריאה הבאה, אשר תאפשר לנו להשתמש ביכולות הטעינה של PreloadJS.
<script type="text/javascript" src="js/preloadjs-0.3.0.min.js"></script>
כעת נוסיף פונקצייה חדשה בשם start ונוסיף אליה את הקוד הבא:
function start() {
manifest = [
{ src: "assets/sky.png", id: "sky" },
{ src: "assets/ground.png", id: "ground" },
{ src: "assets/logo.png", id: "sun" },
{ src: "assets/hill.png", id: "hill" }
];
loader = new createjs.LoadQueue(false);
loader.onFileLoad = handleFileLoad;
loader.onComplete = handleComplete;
loader.loadManifest(manifest);
}
עפי שניתן לראות בדוגמא מעלה, אנחנו מגדירים קובץ manifest (כמובן זה לא חייב להיות השם), בו אנחנו מגדירים את כל התכנים שאנו מעוניינים לטעון עבור המשחק.
כאשר ההורדה תסתיים נעבור לפונקציית handleComplete, עבור כל תוכן שנטען בהצלחה נגיע ל – handleFileLoad ובה נוסיף את התוכן החדש למערך התמונות שלנו.
var assets = [];
function handleFileLoad(event) {
assets.push(event.item);
}
לפני שנמשיך לאיך מציגים את התמונות הנוספות אציג את הרקעים שנשתמש בהם:
אחרי שכל הקבצים נטענו והוכנסו למערך assets, אשר להתחיל למקם אותם על המסך.
נוסיף את פונקציית handleComplete, ונרוץ על כל האובייקטים שהוכנסו ל – assets, ספריית PreloadJS לא רק טענה את התמונות ברקע אלא גם שייכה אותם לקבוצות לפי סוג הקובץ וכל אובייקט שנטען קיבל מאפיין ייחודי (id), בשורות הראשונות אנחנו נוציא את מאפיין ה – id של כל אובייקט שנטען ונשתמש בפונקציית getResult על מנת לקבל את האובייקט.
כאשר נגיע לאובייקט – ground, אנחנו רוצים לצייר רצפה לאורך כל אורך המשחק שלנו, אז ראשית ניצור אובייקט חדש מסוג – Shape ונצייר עליו את התמונה, שימו לב שאורך התמונה ה – w שמציין את אורך המשחק (Canvas), התוצאה שאנחנו נראה לא תהיה מריחה של התמונה ל – 600 פיקסלים אלא שיכפול של התמונה לאורך זה.
לבסוף נוסיף את אובייקט ה – ground ל – stage (כפי שעשינו בפוסטים קודמים) ונתחיל את שעון המשחק.
דוגמא 1
function handleComplete() {
buildPlayerSprite();
for (var i = 0; i < assets.length; i++) {
var item = assets[i];
var _id = item.id;
var result = loader.getResult(_id);
if (item.type == createjs.LoadQueue.IMAGE) {
var bmp = new createjs.Bitmap(result);
}
switch (_id) {
case "ground":
ground = new createjs.Shape();
var g = ground.graphics;
g.beginBitmapFill(result);
g.drawRect(0, 0, w, 79);
ground.y = h - 79;
break;
}
}
stage.addChild(ground, player);
player.gotoAndPlay("walk_h");
var fieldValue = id("fps");
var fps = parseInt(fieldValue.value);
createjs.Ticker.setFPS(fps);
createjs.Ticker.useRAF = true;
createjs.Ticker.addEventListener("tick", tick);
}
דוגמא 1

הבעיה כרגע אין לי תחושה של התקדמות, השחקן מתקדם אבל הרצפה לא, על מנת לעשת זאת נצטרך להזיז את הרצפה יחד עם הדמות על מנת ליצור את האפקט.
בתוך פונקציית tick, נוסיף את השורה הבאה – אשר תזיז את הרצפה 10 פיקסלים לאחור.
function tick() {
var outside = w + 20;
var position = player.x + player.vX;
player.x = (position >= outside) ? -200 : position;
ground.x = (ground.x - 10);
}
stage.update();
}
דוגמא – 2
עכשיו אפשר לראות את הרצפה מתקדמת יחד עם תזוזת הדמות אבל היא חתוכה בהתחלה שלה, מה שצריך לעשות הוא להגדיל את רוחב הרצפה בצורה הבאה:
case "ground":
ground = new createjs.Shape();
var g = ground.graphics;
g.beginBitmapFill(result);
g.drawRect(0, 0, w+330, 79);
ground.y = h - 79;
break;
פשוט הוספתי עוד 330 פיקסלים לרוחב התמונה, אבל גם זה לא פתור את הבעיה, אחרי הכל הרצפה עדיין תעלם מהמסך, נוסיף את השורה הבא תחת פונקציית tick ובעזרת מודולו של תוספת הרוחב שהוספנו נדאג שה – x לעולם לא יצא מגבולות המסך.
ground.x = (ground.x - 10) % 330;
דוגמא - 3
כעת שהבנו את הדוגמא הבסיסית נוסיף את שאר האובייקט למשחק:
* שימו לב שנרשמתי ל - stage.addEventListener("stagemousedown, אשר יאפשר לנו לדעת מתי לחצו על ב – Canvas ונוכל לבצע פעולות קפיצה.
function handleComplete() {
buildPlayerSprite();
for (var i = 0; i < assets.length; i++) {
var item = assets[i];
var _id = item.id;
var result = loader.getResult(_id);
if (item.type == createjs.LoadQueue.IMAGE) {
var bmp = new createjs.Bitmap(result);
}
switch (_id) {
case "sky":
var g = new createjs.Graphics()
g.beginBitmapFill(result);
g.drawRect(0, 0, w * 2, h)
sky = new createjs.Shape(g);
break;
case "ground":
ground = new createjs.Shape();
var g = ground.graphics;
g.beginBitmapFill(result);
g.drawRect(0, 0, w + 330, 79);
ground.y = h - 79;
break;
case "hill":
hill = new createjs.Shape(new createjs.Graphics().beginBitmapFill(result).drawRect(0, 0, w, 159));
hill.x = 0;
hill.scaleX = 3;
hill.y = 163;
break;
case "sun":
var g = new createjs.Graphics();
g.beginBitmapFill(result);
g.drawRect(0, 0, 129, 129);
sun = new createjs.Shape(g);
sun.x = w;
sun.y = 37;
break;
}
}
stage.addChild(sky, ground, hill, sun, player);
player.gotoAndPlay("walk_h");
stage.addEventListener("stagemousedown", function () {
play("jump_h");
});
var fieldValue = id("fps");
var fps = parseInt(fieldValue.value);
createjs.Ticker.setFPS(fps);
createjs.Ticker.useRAF = true;
createjs.Ticker.addEventListener("tick", tick);
}
לבסוף נשנה את פונקציית tick, שתזיז את שאר האובייקטים במשחק.
ב – tick יש חישוב של outside , כלומר מתי האובייקט נמצא מחוץ למסך, כאשר נראה שאובייקט יצא החוצה נוכל להחזיר אותו ע”י שינוי מאפיין - x
function tick() {
if (_action.indexOf("walk") != -1 || _action.indexOf("jump") != -1) {
var outside = w + 20;
var position = player.x + player.vX;
player.x = (position >= outside) ? -200 : position;
sky.x = (sky.x - 5) % w;
hill.x = (hill.x - 2) % w;
ground.x = (ground.x - 10) % 330;
sun.x = (sun.x - 1);
if (sun.x <= -135) { sun.x = outside + 50; }
}
stage.update();
}
לדוגמא המלאה
בפוסט הקודם חלק 2 בפיתוח משחקים ב – JavaScript דיברנו על מה זה Sprite ואיך בעזרת ספרייה EASEJS להציג רצף תמונות מאותו מקבץ תמונות על מנת להציג אנימציה למשתמש.
בפוסט הזה נדבר על איך להזיז את הדמות במרחב על פי הפעולה שהדמות מבצעת.
המשימה הראשונה שלנו היא להזיז את הדמות במרחב המשחק, על מנת לבצע זאת נשנה את פונקציית tick באופן הבא:
ראשית, נגדיר תחום משחק – תחום המשחק ימנע מהדמות שלנו לבצע מגבולות המשחק.
בתוך פונקציית tick נוסיף את הקוד הבא:
if (player.x >= id("gameCanvas").width - playerWH) {
// You reached the end - We need to walk left
}
if (player.x < playerWH) {
// You reached the end - We need to walk right
}
הקוד הנ”ל יבוצע כאשר מאפיין ה – x של הדמות שלנו (player) גדול מרוחב אובייקט ה – gameCanvas, וכאשר מאפיין ה – x של הדמות קטן מרוחב הדמות.
עכשיו נזיז את הדמות במרחב לפי תנאים אלו:
if (player.x >= id("gameCanvas").width - playerWH) {
// You reached the end - We need to walk left
player.direction = -90;
}
if (player.x < playerWH) {
// You reached the end - We need to walk right
player.direction = 90;
}
הקוד הנ”ל משנה את כיון הדמות ל – 90 מעלות או 90- מעלות לפי כיון ההליכה של הדמות.
מה שנשאר לנו עכשיו זה להזיז את הדמות ע”י שינוי מאפיין ה – x של אובייקט player, נוסיף את הקוד הבא לאחר לפי הקריאה ל – stage.update, הקוד אחראי על שינוי מאפיין ה – x בהתאם לכיון הדמות.
vX – תאוצה על ציר – x , ניתן לשינוי וישפיע את התקדמות הדמות במרחב.
player.direction == 90 ? player.x += player.vX : player.x -= player.vX;
עכשיו עומדת לפנינו בעיה, כאשר הדמות זזה לכיון ימין של המסך היא לא מתהפכת.

דוגמא באתר
אפשר ליצור גרפיקה תואמת לכיוונים השונים אבל זה לא פתרון יעיל, גם שימוש ב – scaleX ישפיע באופן רע על ביצועי המשחק, על מנת להפוך את הדמות נשתמש בפונקציית addFlippedFrames שכל מטרתה היא הפיכת הדמות בצורה אופקית ו\או אנכית.
נוסיף את השורה הבאה לפני קריאה ל - player = new createjs.BitmapAnimation.
//( spriteSheet horizontal vertical both )
createjs.SpriteSheetUtils.addFlippedFrames(playerSprite, true, false, false);
פונקציית addFlippedFrames תשכפל את כל התמונות ב – Sprite ותהפוך אותם בצורה אופקית ו\או בצורה אנכית.
כעת בקריאה לפעולה של הדמות (Jump, Walk וכו) נוכל לראות שכל הפעולות של הדמות (animations) שוכפלו ונוסף לשום הפעולה – h_.
על מנת לבצע הליכה שמאלה נקרא לפונקציית gotoAndPlay ונעביר את הפעולה עם סיומת h_
function tick() {
if (_action.indexOf("walk") != -1) {
if (player.x >= id("gameCanvas").width - playerWH) {
// You reached the end - We need to walk left
player.direction = -90;
player.gotoAndPlay("walk")
}
if (player.x < playerWH) {
// You reached the end - We need to walk right
player.direction = 90;
player.gotoAndPlay("walk_h");
}
// Moving the sprite based on the direction & the speed
player.direction == 90 ? player.x += player.vX : player.x -= player.vX;
}
stage.update();
}

דוגמא באתר
בפוסט הקודם חלק 1 – פיתוח משחקים ב – JavaScript דיברנו על פיתוח משחקים ב – JavaScript בעזרת ספרית EASEJS, ספריה המאפשרת לנו לעבוד עם אוביקט Canvas של HTML 5 בצורה קלה ונוחה.
בפוסט הקודם הדגמתי איך בצורה קלה נוכל להשתמש באוביקט Stage על מנת להכיל את כל האלמנטים של המשחק ושימוש ב – Ticker על מנת לעדכן את האלמנטים בתוך לולאת המשחק.
הפוסט הזה ירחיב את הנושא ונתקדם למימוש פעולות של השחקן עד ידי הפעלת אנימציה ספציפית לכל פעולה.
לפני שאנחנו נכנסים לקוד צריך להבין להכיר אוביקט חשוב שנקרא – Sprite:
Sprite הוא תמונה דו מימדית המכילה מקבץ של תמונות (כל מקבץ תמונות מתאר פעולה של השחקן – בדוגמא מטה) שנמצאות בשימוש ולשלב אותם לתמונה אחת גדולה שהיא מעין מפה של כל התמונות בייחד. כך התמונה הגדולה נטענת פעם אחת בלבד בעת העליה והצגת התמונות מתבצעת על ידי הצגת חלק מסויים בלבד של אותה תמונה גדולה לפי מיקום על הציר הרוחבי והאופקי.
בעזרת Sprite נוכל ליצור אנימציה בצורה קלה על ידי איור של המצבים השונים שהמשתמש הולך לראות במשחק, השאלה הגדולה איך הופכים את ה – Sprite למשהו שקל לעבוד איתו במשחק שלנו?

ראשית נתחיל בטעינת התמונה, כאשר טעינת התמונה הסתיימה נקרא לפונקציית start.
<script type="text/javascript">
var stage, player, playerImage, _action;
var playerWH = 64;
var frequency = 4;
function init() {
playerImage = new Image();
playerImage.src = "assets/Player.png";
playerImage.onload = start;
}
</script>
חשוב לבצע טעינה לפני התחלת האנימציה, כפי שאנחנו יודעים Sprite יכול להכיל המון המון תמונות ועל כך משקל התמונה יכול להיות גדול, לכן חשוב לוודא שטעינת התמונה הסתיימה לפני שממשיכים לאנימציה.
בפוסט הקודם השתמשנו ב – Bitmap לטעינת התמונה, אבל עכשיו אנחנו לא מעוניינים להוסיף למשחק את כל התמונה אלא רק חלקים ממנה. על מנת לעשות זאת נשתמש באובייקט SpriteSheet שמגיע כחלק מ – EASEJS וכל מטרתו היא טעינה של Sprite.
כפי שניתן לראות מהדוגמא מטה, אנחנו מתחילים ביצירת Stage שיאכלס את האובייקט שלנו, לאחר מכן אנחנו יוצרים אובייקט SpriteSheet ומגדירים את images לתמונה של השחקן, תחת אובייקט frames נגדיר את גודל הדמות (במקרה שלנו 64x64 פיקסלים), ולבסוף והחשוב ביותר וכאן נראה את הכח של SpriteSheet היא הגדרת המצבים של הדמות.
function start() {
stage = new createjs.Stage(id("gameCanvas"));
var playerSprite = new createjs.SpriteSheet({
animations:
{
"walk": [0, 9, null],
"fall": [10, 21, null],
"jump": [22, 32, null],
"gamgam": [34, 64, null],
"stand": [44, 44, null],
"special_combo": [22, 32, "gamgam"]
},
images: [playerImage],
frames:
{
height: playerWH,
width: playerWH,
regX: 0,
regY: 0,
}
});
}
בדוגמאת הקוד מעלה נוכל לראות הגדרת מצבים של השחקן (קפיצה, נפילה, עמידה וכו) על ידי ציון התחלת רצף התמונות וסופו.
כפי שניתן לראות בתמונה מטה פעולת “הליכה” מתחילה מתמונה מספר 0 עד תמונה 9.
לאחר שהגדרנו את המצבים השונים נוכל פשוט לקרוא לפעולה בשמה, כלומר – נגן walk או אם נגמר הזמן נגן fall.
“שם הפעולה” : [מיקום תמונת ראשונה, מיקום תמונה אחרונה, פעולה הבאה, תדירות]
ראו דוגמא ל – special_combo, אשר מבצע רצף תמונות של קפיצה ולאחר מכן ממשיך עם פעולת gamgam

דוגמאת קוד באתר
לבסוף על מנת שנוכל לנגן את הפעולות נשתמש באובייקט נוסף בשם BitmapAnimation, אובייקט זה יודע לקבל SpriteSheet ומאפשר קריאה לאנימציות בעזרת פונקציית gotoAndPlay.
תחת הקוד הקודם נוסיף יצירה של BitmapAnimation, נגדיר נקודת התחלה של הדמות, נוכל גם לקבוע מאיזה תמונה להתחיל (currentFrame), ולבסוף נקרא לפונקציית gotoAndPlay ונעביר את שם הפעולה שאנחנו מעוניינים לנגן.
player = new createjs.BitmapAnimation(playerSprite);
player.x = id("gameCanvas").width / 2;
player.y = id("gameCanvas").height - playerWH;
//player.currentFrame = 2;
player.gotoAndPlay("walk");
stage.addChild(player);
createjs.Ticker.setFPS(60);
createjs.Ticker.addEventListener("tick", tick);
}

בדוגמא שבאתר תוכלו לשנות הקצב הריענון, תדירות ואת התאוצה של הדמות.
switch (_id) {
case "fps":
createjs.Ticker.setFPS(parseInt(value));
break;
case "velocity":
player.vX = parseInt(value);
break;
case "frequency":
frequency = parseInt(value);
start();
break;
}
דוגמאת קוד באתר
פיתוח משחקים בשבילי זה תחביב ואולי אפילו קצת יותר, אין לי דרך יותר טובה לנסח את זה – זה פשוט מגניב!!!
פיתוח משחקים כמעט תמיד הופך למשימה מאוד מורכבת, ככל שרוצים להגיע לרמה גבוה יותר של משחק, אבל בשביל דברים פשוטים אפשר בקלות להתחיל לפתח משחקים לבד ובכל שפה שתרגישו נוח לפתח, אני חושב שלא קיימת שפה שבה לא נוכל לפתח משחקים (חלק אולי פחות מרשימים – אבל בהחלט אפשרי).
אין מחקר שמוכיח זאת אבל היום השפה הפופולרית ביותר היא כנראה JavaScript, כמובן בגלל האינטרנט וכניסתו של HTML 5 כתקן עולמי וכמובן זה יכול לרוץ על מחשבים, טלפונים מחשבי יד וכמעט כל מכשיר שתומך HTML 5 (זה מתחיל להיות הרבה).
המשחק שפותח עבור מיקרוסופט ישראל – סופר 8 , הינו משחק מבוסס HTML 5 + JavaScript ואני הולך לדבר קצת על איך מתחילים לפתח משחק עם JavaScript ואתה תראו שפיתוח משחקים לא צריך להיות כ”כ מפחיד.
אז איך מתחילים?
-
סוג המשחק – איזה משחק אתם רוצים לפתח? יש המון קטגוריות של משחקים – אני אצמצם את הרשימה בשבילכם – משחקי שולחן או משחקי וידאו.
-
משחקי שולחן – לדוגמא איקס עיגול, פאזל, פוקר הינם משחקים אשר המצב של המשחק נשאר סטטי עד לפעולה מצד המשתמש, המשחק לא משתנה עד שבוצעה פעולה ע”י שחקן אחר או על ידי עצמך.
-
משחקי וידאו – לדוגמא –
סופר 8 פאקמן, Bubbels או כל משחק שמשתנה גם בפעולה משתמש וגם לפי זמן. עוד דוגמא זה משחק יריות – גם אם לא תזוז עדיין יגיעו אליך מפלצות והעולם (המשחק) ימשיך לנוע.
-
שליטה בשפה – נכון אמרתי שזה אל חייב להיות כ”כ מסובך לפתח משחק אבל בשביל לפתח משחק צריך שליטה טובה בשפה שבחרתם.
-
כלי עזר – אחד הדברים שאני ממליץ הוא לא לכתוב הכל בעצמכם, כל המשחקים הגדולים משתמשים בתשתיות לפיתוח משחקים (כאלו שפותחו על ידם או נקנו), אתה יכול לכתוב אבל פשוט חבל על הזמן שלך לבצע דברים שכבר בוצעו אין ספור פעמים ע”י אחרים, לדוגמא – אם אתה רוצה לממש פיזיקה במשחק על מנת להשפיע על אובייקטים שונים – זה ידרוש לא מעט זמן כתיבה או שתוכל להשתמש בספריה קיימת.
לולאת משחק

אחד המרכיבים החשובים ביותר בפיתוח משחקים נקרא לולאת משחק (Game Loop), לולאת המשחק אחראית על ביסוס לוגיקת המשחק ע”י ציור הגרפיקה במשחק. לולאת המשחק מאפשרת למשחק להמשיך להתקדם גם בלי קבלת פעולות חדשות מהמשתמש.
תחשבו על Notepad, כאשר אנחנו פותחים אותו ולא כותבים Notepad לא עושה שום דבר.
עכשיו נפתח Outlook.com או Gmail.com ולא נכתוב דבר – ברקע עדיין מתרחשים דברים – נכו שמדובר על Ajax וזה לא קשור למשחק אבל הרעיון שיש תהליכים שרצים ברקע ולא רק מחכים לפעולה של המשתמש.
אז איך יוצרים לולאת משחק?, הדוגמא לפניכם הוא הדבר הראשון שהרבה מפתחים חושבים עליו ישר אבל הוא כנראה לא הדבר הכי יעיל:
while (!MyGame.stopped) {
MyGame.update();
}
אחד הדברים המרכזיים שלולאת המשחק מבצעת היא קריאה לפונקציית update, שאחראית על עדכון המשחק – עדכון נתונים, עדכון נקודות הזזת אוביקטים במשחק ועוד).
אם נשתמש ב – While בשביל לקרוא לפעולות update (אנחנו מדברים על JavaScript), הדפדפדן כנראה יחסום את הפונקציה וגם לא נהיה הכי יעילים כי אין לנו אפשרות לשלוט במהירות המשחק או במילים אחרות כל כמה זמן אנחנו רוצים לעדכן את האובייקטים במשחק – FPS – Frames Per Second.
פתרון נוסף שנוכל לבצע הוא שימוש ב – setInterval, ואז בצורה קלה מאוד נוכל להגדיר לולאת שתרוץ על פי קצב ריענון הגיוני.
MyGame.fps = 60;
MyGame.update = function () {
// Move game parts
};
// Start the game loop
MyGame._intervalId = setInterval(Game.update, 1000 / Game.fps);
// To stop the game, use the following:
clearInterval(MyGame._intervalId);
FPS – כדי שהעין שלנו תראה “אנימציה” בעצם צריך להזיז רצף תמונות במהירות מסוימת, אם התמונות יזוז לאט מידי נוכל לראות שזה לא אנימציה. המינימום FPS שצריך על מנת ליצור את התחושה שמדובר באנימציה הוא – 17 FPS.
למרות שיצירת לולאת משחק יכול להיראות פשוט מאוד עדיין אני הולך לדבר על ספריה בשם EASEJS, ספריית EASEJS נותנת תמיכה בעבודה עם Canvas ב – HTML 5 ועוזרת בצורה משמעותית לפיתוח משחקים, והיא מכילה אובייקט בשם – Ticker שישמש אותנו כלולאת המשחק.
ראשית נוריד את - http://www.createjs.com/#!/EaselJS ונפתח דף HTML חדש, נוסיף קישור אל הקבצים שהורדנו.
<script type="text/javascript" src="js/easeljs/geom/Matrix2D.js"></script>
<script type="text/javascript" src="js/easeljs/geom/Rectangle.js"></script>
<script type="text/javascript" src="js/easeljs/events/EventDispatcher.js"></script>
<script type="text/javascript" src="js/easeljs/utils/UID.js"></script>
<script type="text/javascript" src="js/easeljs/utils/Ticker.js"></script>
<script type="text/javascript" src="js/easeljs/utils/SpriteSheetUtils.js"></script>
<script type="text/javascript" src="js/easeljs/display/DisplayObject.js"></script>
<script type="text/javascript" src="js/easeljs/display/Container.js"></script>
<script type="text/javascript" src="js/easeljs/display/Stage.js"></script>
<script type="text/javascript" src="js/easeljs/display/SpriteSheet.js"></script>
<script type="text/javascript" src="js/easeljs/display/BitmapAnimation.js"></script>
<script type="text/javascript" src="js/easeljs/display/Graphics.js"></script>
<script type="text/javascript" src="js/easeljs/display/Bitmap.js"></script>
<script type="text/javascript" src="js/easeljs/display/Text.js"></script>
נוסיף ל – Body את אובייקט ה – Canvas ונקרא לו – gameCanvas
<canvas id="gameCanvas" width="600" height="100"></canvas>
כעת בואו נתחיל לעבוד עם ספריית EASEJS, דבר ראשון שנחנו צריכים הוא ליצור אובייקט חדש מסוג – Stage.
Stage יחזיק את אובייקט ה – Canvas של המשחק וכל פעם שלולאת המשחק תבוצע ה – Stage יעדכן את האובייקטים תחתיו.
stage = new createjs.Stage(id("gameCanvas"));
כעת מה שנשאר לעשות הוא להוסיף תמונה למשחק שלכם, בעזרת שימוש בפונקציית Bitmap אשר מקבל נתיב לתמונה, נוסיף את התמונה לתוך ה – Stage בעזרת קריאה ל – stage.addChild ונעביר אליו את התמונה שלנו.
לאחר מכן נגדיר את קצב הרענון של המשחק על 30 FPS , בעזרת קריאה ל – (createjs.Ticker.setFPS(30.
ודבר אחרון הוא להירשם לאירוע (Event) בשם – tick, פונקציית tick תופעל כל 33.33 מאיות השניה (1000/30).
var stage, image, fpstext;
function init() {
stage = new createjs.Stage(id("gameCanvas"));
image = new createjs.Bitmap("assets/Player.png");
stage.addChild(image);
createjs.Ticker.setFPS(30);
createjs.Ticker.addEventListener("tick", tick);
}
כעת כל מה שנשאר לנו הוא לעדכן את המשחק ברגע שפונקציית tick מבוצעת, בשלב הנוכחי אני מזיז את התמונה 10 פיקסלים ימינה , במידה והתמונה הגיעה ל – 600 פיקסלים (קצה תחום משחק) אני מחזיר את התמונה ל – 0. לאחר שביצענו את השינויים נקרא ל – stage.update על מנת לעדכן את ה – Canvas בהתאם לשינויים שביצענו.
function tick() {
image.x += 10;
if (image.x > 600)
image.x = 0;
stage.update();
}

כניסה לדוגמא
עכשיו נוסיף למשחק שלנו שורה טקסט בעזרת שימוש באובייקט – Text, עוד דוגמא לשימוש הקל בעבודה עם Canvas בעזרת EASEJS.
האובייקט טקסט שנוסיף יציג את מהירות ה – FPS של המשחק, נוסיף את הקוד הבא תחת פונקציית ה - init
fpstext = new createjs.Text("fps:", "20px Arial", "#7a1567");
fpstext.x = 0;
fpstext.y = 20;
stage.addChild(fpstext);
ולסיום תחת פונקציית tick נוסיף את השורה הבאה:
fpstext.text = Math.round(createjs.Ticker.getMeasuredFPS()) + " fps";
וזאת התוצאה שנקבל:

כניסה לדוגמא
אחד השינויים ב – TFS 11 מאפשר לנו להגדיר קבוצות פיתוח והגדרה של סבבי פיתוח עם תאריך התחלה וסיום (Sprint, Iteration וכו).
כאשר מתחילים לתכנן סבב פיתוח נראה חישוב של סה”כ ימי העבודה בסבב הנוכחי, אך סה”כ הימים לא יסתדר לכם כי הוא מחשב שיום ראשון אינו יום עבודה.
זאת שאלה שאני מקבל מהרבה לקוחות – איך אני משנה את תצוגת ה – Backlog שתתאים לימי המנוחה בארץ (שישי ושבת ולא שבת וראשון).
לדוגמא – אם הגדרנו סבב פיתוח מה – 3 לפברואר עד ה – 14 לפברואר – מדובר על 12 ימים סה”כ ו-10 ימי פיתוח אבל אנחנו נראה סה”כ 9 ימי פיתוח. (שני ימי ראשון + יום שבת = שלושה ימי חופש).

על מנת לסגר את TFS 11 שיתמוך בימי החופש בארץ נצטרך להשתמש בפקודת – witadmin שתאפשר לנו לשלוף את קובץ ההגדרות עבור ניהול הפרוייקט.
witadmin exportcommonprocessconfig /collection:http://tfsServer:8080/tfs /f:e:\projectConfig.xml /p:ProjectName
לאחר שהרצנו פקודה זאת ניפתח את הקובץ ונחפש את הטקסט הבא:
<Weekends>
<DayOfWeek>Sunday</DayOfWeek>
<DayOfWeek>Saturday</DayOfWeek>
</Weekends>
</CommonProjectConfiguration>
נשנה את הערך - Sunday ל – Friday, נשמור את הקובץ.
<Weekends>
<DayOfWeek>Friday</DayOfWeek>
<DayOfWeek>Saturday</DayOfWeek>
</Weekends>
</CommonProjectConfiguration>
לבסוף יש להריץ את אותה הפקודה אך במקום export נכתוב import:
witadmin importcommonprocessconfig /collection:http://tfsServer:8080/tfs /f:e:\projectConfig.xml /p:ProjectName
עכשיו אפשר להתחיל לעבוד!

תהנו!
לאחרונה נתקלתי במספר טענות שלא ניתן להריץ בדיקות Coded UI על דפדפנים שונים למעט Internet Explorer, אז רק להבהיר – אכן אפשר!
אתם צריכים את הדברים הבאים על מנת שהתוסף יעבוד ויאפשר לכם להריץ בדיקות Coded UI על דפדפנים שונים:
במידה ואתם עובדים עם Visual Studio 2010 אין צורך לשדרג את השרת, רק להתקין Visual Studio 2012 שיכול לעבוד במקביל עם Visual Studio 2010.
Tools –> Extensions and Updates… –> Online –> Search for = Cross Browser

- לאחר ההתקנה יש להוריד את הדברים הבאים:
אחרי שסיימתם להוריד יש להעתיק את התוכן שלהם לספריה המצויינת מטה, מ – Selenium יש להעתיק את התוכן של net40)
- "C:\Program Files\Common Files\microsoft shared\VSTT\11.0\UITestExtensionPackages" (for 32 bit machines)
- "C:\Program Files (x86)\Common Files\microsoft shared\VSTT\11.0\UITestExtensionPackages" (for 64 bit machines)
אחרי שסיימנו עם ההתקנות כל מה שנשאר הוא להקליט בדיקה בעזרת Coded UI ולשנות את הערך של BrowserWindow.CurrentBrowser לדפדפן שאתם מעוניינים להריץ עליו את הבדיקה.
[TestMethod]
public void Simple_CUIT_For_WebSite()
{
BrowserWindow.CurrentBrowser = "chrome"; //"firefox"
//Coded UI Methods.....
}
סיימתם, עכשיו בדיקות Coded UI יכולות לרוץ על FireFox ו – Google Chrome
תהנו!
There are many improvements in TFS API for 2012 version, in my previous post I’ve talked about TFS API Part 48 – WorkItemControl And Bypass Work Item Rules that allow work item creation with bypass rule mode in WorkItemStore that allow you to create and change any field value without applying the WorkItem template rules.
in this post I’ll show how to delete Team Project using TFS API, be aware Delete Team Project is unrecoverable!

Download Demo Project
Step 1: Connect TFS and Getting Team Projects
Create WPF Application, and add the basic code for connecting TFS and collecting all Team Projects under that collection.
private TfsTeamProjectCollection _tfs;
private WorkItemStore _store;
private void btnConnect_Click(object sender, RoutedEventArgs e)
{
var tpp = new TeamProjectPicker(TeamProjectPickerMode.NoProject, false);
tpp.ShowDialog();
if (tpp.SelectedTeamProjectCollection != null)
{
_tfs = tpp.SelectedTeamProjectCollection;
_store = new WorkItemStore(_tfs);
GetTeamProjects();
}
}
private void GetTeamProjects()
{
projectsList.ItemsSource = _store.Projects;
}
Step 2: Using TeamProjectDeleter
To delete Team Project we need to use a new API called – TeamProjectDeleter, you can find this dll here - C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\ReferenceAssemblies\v4.5\Microsoft.TeamFoundation.DeleteTeamProject.dll..
First create new TeamProjectDeleter object and pass the TfsCollection, Team Project name and force property.
You can also specify if you want to delete the Team Project WSS and finally calling the delete method to perform the Team Project delete operation.
private void btnDelete_Click(object sender, RoutedEventArgs e)
{
if (projectsList.SelectedItem == null) return;
var project = (Project)projectsList.SelectedItem;
var force = chxForce.IsChecked == true ? true : false;
try
{
var deleter = new TeamProjectDeleter(_tfs, project.Name, force);
deleter.StatusEvent += deleter_StatusEvent;
if (deleter.HasWssToDelete)
deleter.ExcludeWss = chxExcludeWss.IsChecked == true ? true : false;
var result = MessageBox.Show(string.Format("You are about to delete \"{0}\" project, are you sure you want to continue?",
project.Name), "Delete Team Project", MessageBoxButton.OKCancel, MessageBoxImage.Warning);
if (result == MessageBoxResult.OK)
{
var t = new Thread(new ThreadStart(deleter.Delete));
t.Start();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
void deleter_StatusEvent(object sender, TeamProjectDeleter.StatusEventArgs e)
{
Execute ex = delegate()
{
txtLog.Text = e.message + Environment.NewLine + txtLog.Text;
};
this.Dispatcher.Invoke(ex);
}
Download Demo Project
It’s been a while since I wrote about TFS API, not because there is no more about it is because I worked on Windows Store for the past couple of months so I didn’t had the time.
More About TFS API
So today post will be about two changes in TFS 2012, the first one is WorkItemControl based on WPF that replaced the WorkItemFormControl based on WinForms, the second will be about WorkItemStore bypass rule – Allowing you to save and change work item values without obeying the work item rules.

Download Demo Project
Step 1: Connect TFS
First create WPF application using Visual Studio 2012 and connect TFS using TeamProjectPicker API.
private void btnConnect_Click(object sender, RoutedEventArgs e)
{
var pick = new TeamProjectPicker(TeamProjectPickerMode.NoProject, false);
pick.AcceptButtonText = "I want this Team Project!";
pick.ShowDialog();
if (pick.SelectedTeamProjectCollection != null)
{
_tfs = pick.SelectedTeamProjectCollection;
//WorkItemStoreFlags - Enum used to determine behavior of work item store object
_store = new WorkItemStore(_tfs, WorkItemStoreFlags.BypassRules);
btnShowWits.IsEnabled = true;
}
else
{
MessageBox.Show("Please select Team Project");
}
}
Step 2: Query Work Items
No just for demo purpose I using the Store object to query all items in the collection.
private void btnShowWits_Click(object sender, RoutedEventArgs e)
{
var workItems = _store.Query("SELECT [System.Id] FROM WorkItems");
if (workItems.Count > 0)
{
btnShowWit.IsEnabled = true;
workItemsList.ItemsSource = workItems;
}
}
Step 3: Bypass WorkItemStore Rules
The interesting part in TFS 2012 that you can create the WorkItemStore object and specify if you want to work in BypassRule mode.
The Bypass Rule allow you to modify the work item fields without any restrictions, for example you can change “Created Date”, or “Created By” – that normally are blocked for changes.
private void chxBypassRules_Checked(object sender, RoutedEventArgs e)
{
if (_tfs == null) return;
_store = new WorkItemStore(_tfs, WorkItemStoreFlags.BypassRules);
}
private void chxBypassRules_Unchecked(object sender, RoutedEventArgs e)
{
if (_tfs == null) return;
_store = new WorkItemStore(_tfs, WorkItemStoreFlags.None);
}
Step 4: Show Work Item Control
After we specify the WorkItemStore mode we want to work with and have selected a Work Item from the list we want to display him outside Visual Studio using WorkItemControl.
The WorkItemControl needs the Item property set to the work item you want to display (or new work item), then create new Window object and set the WorkItemControl as the new Windows Content.
private void btnShowWit_Click(object sender, RoutedEventArgs e)
{
if (workItemsList.SelectedItem == null) return;
_witControl = new WorkItemControl();
_witControl.Item = (WorkItem)workItemsList.SelectedItem;
_container = new Window();
_container.Content = _witControl;
_container.Show();
btnShowErrors.IsEnabled = true;
}
private void btnShowErrors_Click(object sender, RoutedEventArgs e)
{
_witControl.ShowAllFieldErrors("Error Title", 99);
}
Download Demo Project
Enjoy
When customers ask me how they can run Windows Store app from Desktop usually the answer is – You Can’t, but if you really want there is a way to do that.
The reason I usually answer is You Can’t is because – In order to run Windows Store app from Desktop you need to install Windows App Certification Kit, this pack contains “microsoft.windows.softwarelogo.appxlauncher.exe” file that can run a Windows Store app by his application model id.
So if you plan on publishing your app you can’t assume the ACK is installed on the client machine.
Again, if you really want… Let me show you.

Download Demo Project
Step 1: Getting Started
First create a WPF project and add the following reference:
“C:\Program Files (x86)\Windows Kits\8.0\App Certification Kit\microsoft.windows.softwarelogo.shared.dll”
Step 2: Getting Windows App List
The reason you need to add reference to “Microsoft.Windows.Softwarelogo.Shared.dll” so we can receive the program inventory xml file, this file contains the complete list of all installed Windows Store apps.
Under my PC here is the file location - "C:\\Users\\Shai\\AppData\\Local\\Microsoft\\AppCertKit\\programinventory_e25bb752-e7cf-4fb2-8194-874ba9b91c7b.xml"
As I said this file contains all Windows Store app installed on you machine, each Program element under that file will show all the information regarding that specific app.

So, how do you get the file location?
It’s very simple, using GlobaldataAccessor method from softwarelogo.shared.dll you can get the Program Inventory Location string.
string itemValue = GlobalDataAccessor.GetItemValue("ProgramInventoryLocation", "PRE_PROCESS");
Once you got this file all you need is parsing the XML and create a collection on Apps.
I’ve create a ProductInfo class that will represent each Program in the file, as you can see from the code below I simply taking the attributes and elements from the Program element.
public class ProductInfo : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private string _Status;
public string Status
{
get { return _Status; }
set
{
if (value != this._Status)
{
this._Status = value;
NotifyPropertyChanged("Status");
}
}
}
public string LogoUrl { get; set; }
public string ProductName { get; set; }
public string ProductVendor { get; set; }
public string ProductLanguage { get; set; }
public string ProductVersion { get; set; }
public string RootDirPath { get; set; }
public string Id { get; set; }
public string PackageFullName { get; set; }
public ProductInfo(XmlNode xNode)
{
var attrib = xNode.Attributes["Name"];
if (attrib != null && !string.IsNullOrEmpty(attrib.Value)) ProductName = attrib.Value;
attrib = xNode.Attributes["Version"];
if (attrib != null && !string.IsNullOrEmpty(attrib.Value)) ProductVersion = attrib.Value;
attrib = xNode.Attributes["Language"];
if (attrib != null && !string.IsNullOrEmpty(attrib.Value)) ProductLanguage = attrib.Value;
attrib = xNode.Attributes["Publisher"];
if (attrib != null && !string.IsNullOrEmpty(attrib.Value)) ProductVendor = attrib.Value;
attrib = xNode.Attributes["RootDirPath"];
if (attrib != null && !string.IsNullOrEmpty(attrib.Value)) RootDirPath = attrib.Value;
attrib = xNode.Attributes["Id"];
if (attrib != null && !string.IsNullOrEmpty(attrib.Value)) Id = attrib.Value;
var node = xNode.SelectSingleNode("/Log/ProgramList/Program[@Id='" + this.Id + "']/Indicators/PackageManifestIndicator/Properties/Logo");
if (node != null && !string.IsNullOrEmpty(node.InnerText))
{
var imgUrl = System.IO.Path.Combine(RootDirPath, node.InnerText);
if (File.Exists(imgUrl))
LogoUrl = imgUrl;
}
node = xNode.SelectSingleNode("/Log/ProgramList/Program[@Id='" + this.Id + "']/Indicators/PackageManifestIndicator/PackageManifest");
if (node != null && !string.IsNullOrEmpty(node.Attributes["PackageFullName"].InnerText))
{
PackageFullName = node.Attributes["PackageFullName"].InnerText;
}
}
}
And now, let’s connect the two part together, the first thing is getting the ProgramInventorylocation and after that just load that Xml file and parse it to objects with the information you want.
private void BuildAppsList()
{
string itemValue = GlobalDataAccessor.GetItemValue("ProgramInventoryLocation", "PRE_PROCESS");
XmlNodeList list = null;
var reportDoc = new XmlDocument();
reportDoc.Load(itemValue);
ProductList = new ObservableCollection<ProductInfo>();
list = reportDoc.GetElementsByTagName("Program");
if (list.Count < 1)
{
throw new XmlException();
}
foreach (XmlNode node in list)
{
ProductInfo item = new ProductInfo(node);
ProductList.Add(item);
}
dbTable.ItemsSource = ProductList;
}
Step 3: Get App User Model Id
Now after you got all Windows Store app installed on your machine it’s time to run them. In order to run a Windows Store app you’ll need to obtain the AppUserModelId, the reason you need to AppUserModelId is because the appxlauncher.exe needs this value in order to launch the application. (Package Name is not enough).
After completing Step 2 we got the App Package Full Name, we need to use this value to find the AppUserModelId from registry.
HKEY_CURRENT_USER\Software\Classes\ActivatableClasses\Package\**PackageFullName**\Server

So let’s add the following method, this method receive a packageFullName string and perform a search in the registry for the AppUserModelId.
public static string GetAppUserModelId(string packageFullName)
{
string str = string.Empty;
using (RegistryKey key = Registry.CurrentUser.CreateSubKey(string.Format(@"SOFTWARE\Classes\ActivatableClasses\Package\{0}\Server\",
packageFullName)))
{
if (key == null) return str;
var appKeys = from k in key.GetSubKeyNames()
where !k.StartsWith("BackgroundTransferHost")
select k;
foreach (var appKey in appKeys)
{
using (RegistryKey serverKey = key.OpenSubKey(appKey))
{
if (serverKey.GetValue("AppUserModelId") != null)
{
str = serverKey.GetValue("AppUserModelId").ToString();
serverKey.Close();
break;
}
}
}
}
return str;
}
Step 4: Running Windows Store App
After we have the AppUserModelId string for a specific Windows Store app we can run it.
You can test it by opening command line and write the following:
C:\Program Files (x86)\Windows Kits\8.0\App Certification Kit\Microsoft.Windows.SoftwareLogo.AppxLauncher.exe “AppUserModelId”
Start App Button
private void StartApp_Click(object sender, RoutedEventArgs e)
{
var product = ((System.Windows.Controls.Button)sender).Tag as ProductInfo;
var appUserModelId = Helpers.GetAppUserModelId(product.PackageFullName);
var exec = @"C:\Program Files (x86)\Windows Kits\8.0\App Certification Kit\Microsoft.Windows.SoftwareLogo.AppxLauncher.exe";
if (!File.Exists(exec))
{
System.Windows.MessageBox.Show("Please install Windows App Certification Kit for Windows RT");
}
var processInfo = new ProcessStartInfo()
{
Arguments = appUserModelId,
UseShellExecute = false,
CreateNoWindow = true,
FileName = exec
};
Process.Start(processInfo);
}
Step 5: Get Application Status
The last thing you might want is to know the App execution state, in order to do that you need to use IPackageDebugSettings - Enables debugger developers control over the lifecycle of a Windows Store app, such as when it is suspended or resumed. (http://msdn.microsoft.com/en-us/library/hh438393(v=vs.85).aspx)
for that create a PackageStatushelper class with the following code:
public class PackageStatusHelper
{
[ComImport, Guid("B1AEC16F-2383-4852-B0E9-8F0B1DC66B4D")]
public class PackageDebugSettings
{
}
public enum PACKAGE_EXECUTION_STATE
{
PES_UNKNOWN,
PES_RUNNING,
PES_SUSPENDING,
PES_SUSPENDED,
PES_TERMINATED
}
[ComImport, Guid("F27C3930-8029-4AD1-94E3-3DBA417810C1"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IPackageDebugSettings
{
int EnableDebugging([MarshalAs(UnmanagedType.LPWStr)] string packageFullName, [MarshalAs(UnmanagedType.LPWStr)]
string debuggerCommandLine, IntPtr environment);
int DisableDebugging([MarshalAs(UnmanagedType.LPWStr)] string packageFullName);
int Suspend([MarshalAs(UnmanagedType.LPWStr)] string packageFullName);
int Resume([MarshalAs(UnmanagedType.LPWStr)] string packageFullName);
int TerminateAllProcesses([MarshalAs(UnmanagedType.LPWStr)] string packageFullName);
int SetTargetSessionId(int sessionId);
int EnumerageBackgroundTasks([MarshalAs(UnmanagedType.LPWStr)] string packageFullName,
out uint taskCount, out int intPtr, [Out] string[] array);
int ActivateBackgroundTask(IntPtr something);
int StartServicing([MarshalAs(UnmanagedType.LPWStr)] string packageFullName);
int StopServicing([MarshalAs(UnmanagedType.LPWStr)] string packageFullName);
int StartSessionRedirection([MarshalAs(UnmanagedType.LPWStr)] string packageFullName, uint sessionId);
int StopSessionRedirection([MarshalAs(UnmanagedType.LPWStr)] string packageFullName);
int GetPackageExecutionState([MarshalAs(UnmanagedType.LPWStr)] string packageFullName,
out PACKAGE_EXECUTION_STATE packageExecutionState);
int RegisterForPackageStateChanges([MarshalAs(UnmanagedType.LPWStr)] string packageFullName,
IntPtr pPackageExecutionStateChangeNotification, out uint pdwCookie);
int UnregisterForPackageStateChanges(uint dwCookie);
}
public static PACKAGE_EXECUTION_STATE GetPackageExecutionState(string packageFullName)
{
PACKAGE_EXECUTION_STATE packageExecutionState = PACKAGE_EXECUTION_STATE.PES_UNKNOWN;
PackageDebugSettings settings = new PackageDebugSettings();
IPackageDebugSettings settings2 = (IPackageDebugSettings)settings;
try
{
if (settings2.GetPackageExecutionState(packageFullName, out packageExecutionState) != 0)
{
System.Windows.MessageBox.Show("Failed to get package execution state.", "GetPackageExecutionState");
}
}
catch (Exception ex)
{
System.Windows.MessageBox.Show(ex.Message, "GetPackageExecutionState");
}
return packageExecutionState;
}
}
And from the application you can just call the GetPackageExecutionState passing the Package Full Name
private void GetAppStatus_Click(object sender, RoutedEventArgs e)
{
var btn = (System.Windows.Controls.Button)sender;
var product = btn.Tag as ProductInfo;
var status = PackageStatusHelper.GetPackageExecutionState(product.PackageFullName);
switch (status)
{
case PackageStatusHelper.PACKAGE_EXECUTION_STATE.PES_RUNNING:
btn.Foreground = new SolidColorBrush(Colors.Green);
btn.Content = "Running";
break;
case PackageStatusHelper.PACKAGE_EXECUTION_STATE.PES_SUSPENDED:
btn.Foreground = new SolidColorBrush(Colors.Orange);
btn.Content = "Suspended";
break;
case PackageStatusHelper.PACKAGE_EXECUTION_STATE.PES_TERMINATED:
btn.Foreground = new SolidColorBrush(Colors.Red);
btn.Content = "Terminated";
break;
default:
btn.Foreground = new SolidColorBrush(Colors.Gray);
btn.Content = "Unkown";
break;
}
}
Download Demo Project
Enjoy
More Posts
Next page »