tag:blogger.com,1999:blog-189077072024-03-13T17:51:14.263-07:00Jack Stephens' Geek BrochureA Trek through .NET Programming, Life, our current Universe, etc.Jack Stephenshttp://www.blogger.com/profile/04996728441112030246noreply@blogger.comBlogger121125tag:blogger.com,1999:blog-18907707.post-2301368155594955362019-05-03T18:25:00.000-07:002019-05-03T18:30:08.728-07:00Vanilla JavaScript for the Client Side of my JWT Server App<p>This is the Vanilla JavaScript client side code to accompany <a href="https://brochure.jrcs3.com/2019/04/jwt-on-net-core-22-and-little-else.html">JWT on .NET Core 2.2 and Little Else</a>. I have the sourcecode on <a href="https://github.com/jrcs3/SimpleJwt4Core22/tree/SimpleJsClient">my Github account as a branch of the other project</a>.</p><h2>Logging in (as far as it goes)</h2><p>To log in, I need to make a request to my services with the data gathered from my “login” form. The request is a POST and the in sent to the server as JSON (“content-Type:application/json” in the head. When I get a response back from the server, XMLHttpRequest calls getJwtProcessResponse():</p><!-- HTML generated using hilite.me --><div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><pre style="margin: 0; line-height: 125%">function getJwt() {
showResults(<span style="color: #0000FF">"working ...."</span>);
<span style="color: #008800; font-style: italic">// Assemble data to log in, should match schema of MakeTokenViewModel</span>
<span style="color: #000080; font-weight: bold">const</span> userName = document.getElementById(<span style="color: #0000FF">"txtUserName"</span>).<span style="color: #000080; font-weight: bold">value</span>;
<span style="color: #000080; font-weight: bold">const</span> role = document.getElementById(<span style="color: #0000FF">"txtRole"</span>).<span style="color: #000080; font-weight: bold">value</span>;
<span style="color: #000080; font-weight: bold">const</span> id = document.getElementById(<span style="color: #0000FF">"txtId"</span>).<span style="color: #000080; font-weight: bold">value</span>;
<span style="color: #000080; font-weight: bold">const</span> fail = document.getElementById(<span style="color: #0000FF">"chkFail"</span>).<span style="color: #000080; font-weight: bold">checked</span>;
<span style="color: #000080; font-weight: bold">const</span> message =
<span style="color: #a61717; background-color: #e3d2d2">`</span>{<span style="color: #0000FF">"UserName"</span>:<span style="color: #0000FF">"${userName}"</span>,<span style="color: #0000FF">"Role"</span>: <span style="color: #0000FF">"${role}"</span>,<span style="color: #0000FF">"Id"</span>: <span style="color: #a61717; background-color: #e3d2d2">$</span>{id},<span style="color: #0000FF">"Fail"</span>:<span style="color: #a61717; background-color: #e3d2d2">$</span>{fail}}<span style="color: #a61717; background-color: #e3d2d2">`</span>;
<span style="color: #008800; font-style: italic">// Make the request</span>
<span style="color: #000080; font-weight: bold">const</span> xhttp = <span style="color: #000080; font-weight: bold">new</span> XMLHttpRequest();
xhttp.onreadystatechange = getJwtProcessResponse;
xhttp.open(<span style="color: #0000FF">"Post"</span>, <span style="color: #a61717; background-color: #e3d2d2">`$</span>{BASE_URL}jwt/maketoken<span style="color: #a61717; background-color: #e3d2d2">`</span>, <span style="color: #000080; font-weight: bold">true</span>);
xhttp.setRequestHeader(<span style="color: #0000FF">"content-Type"</span>, <span style="color: #0000FF">"application/json"</span>);
xhttp.send(message);
}
</pre></div><p>When I get a response back and it’s ready: if the status is 200, login succeeded, and I store the JWT in Local Storage with writeJwt() (since I’m not sending anything other than the JWT, I don’t have to parse it out of this.responseText). Otherwise, alert the user of the failure.</p><!-- HTML generated using hilite.me --><div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><pre style="margin: 0; line-height: 125%"><span style="color: #008800; font-style: italic">// Get and process the request</span>
function getJwtProcessResponse() {
<span style="color: #000080; font-weight: bold">if</span> (<span style="color: #000080; font-weight: bold">this</span>.readyState == <span style="color: #0000FF">4</span>) {
<span style="color: #000080; font-weight: bold">if</span> (<span style="color: #000080; font-weight: bold">this</span>.status == <span style="color: #0000FF">200</span>) {
writeJwt(<span style="color: #000080; font-weight: bold">this</span>.responseText);
showResults(<span style="color: #0000FF">"token written to localStorage"</span>);
}
<span style="color: #000080; font-weight: bold">else</span> {
alert(<span style="color: #a61717; background-color: #e3d2d2">`$</span>{<span style="color: #000080; font-weight: bold">this</span>.status}<span style="color: #a61717; background-color: #e3d2d2">\</span>n <span style="color: #a61717; background-color: #e3d2d2">$</span>{<span style="color: #000080; font-weight: bold">this</span>.responseText}<span style="color: #a61717; background-color: #e3d2d2">`</span>);
showResults(<span style="color: #0000FF">"Failed!"</span>);
}
}
}
</pre></div><h2>Making a call to the Service</h2><p>Here I am going to make a simple GET request with the JWT in Local Storage (if available) </p><!-- HTML generated using hilite.me --><div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><pre style="margin: 0; line-height: 125%"><span style="color: #008800; font-style: italic">// Make a call to the end point specified by urlExtension</span>
<span style="color: #008800; font-style: italic">// (each endpoint has a different permission, for demo purpose)</span>
function makeCall(urlExtension) {
showResults(<span style="color: #0000FF">"working ...."</span>);
<span style="color: #000080; font-weight: bold">const</span> jwt = readJwt();
<span style="color: #000080; font-weight: bold">const</span> xhttp = <span style="color: #000080; font-weight: bold">new</span> XMLHttpRequest();
xhttp.onreadystatechange = makeCallProcessResponse;
xhttp.open(<span style="color: #0000FF">"Get"</span>, <span style="color: #a61717; background-color: #e3d2d2">`$</span>{BASE_URL}values<span style="color: #a61717; background-color: #e3d2d2">$</span>{urlExtension}<span style="color: #a61717; background-color: #e3d2d2">`</span>, <span style="color: #000080; font-weight: bold">true</span>);
<span style="color: #008800; font-style: italic">// We want to test with the user not logged in</span>
<span style="color: #000080; font-weight: bold">if</span> (!!jwt) {
xhttp.setRequestHeader(<span style="color: #0000FF">"Authorization"</span>, <span style="color: #a61717; background-color: #e3d2d2">`</span> Bearer <span style="color: #a61717; background-color: #e3d2d2">$</span>{jwt}<span style="color: #a61717; background-color: #e3d2d2">`</span>);
}
xhttp.send();
}
</pre></div><p>When I get a response back and it’s ready: if the status is 200, I am authorized to and call showResults() to show what I got back. Otherwise the user is alerted about the failure</p><!-- HTML generated using hilite.me --><div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><pre style="margin: 0; line-height: 125%">function makeCallProcessResponse() {
<span style="color: #000080; font-weight: bold">if</span> (<span style="color: #000080; font-weight: bold">this</span>.readyState == <span style="color: #0000FF">4</span>) {
<span style="color: #000080; font-weight: bold">if</span> (<span style="color: #000080; font-weight: bold">this</span>.status == <span style="color: #0000FF">200</span>) {
showResults(<span style="color: #000080; font-weight: bold">this</span>.responseText);
}
<span style="color: #000080; font-weight: bold">else</span> {
alert(<span style="color: #a61717; background-color: #e3d2d2">`$</span>{<span style="color: #000080; font-weight: bold">this</span>.status}<span style="color: #a61717; background-color: #e3d2d2">\</span>n <span style="color: #a61717; background-color: #e3d2d2">$</span>{<span style="color: #000080; font-weight: bold">this</span>.responseText}<span style="color: #a61717; background-color: #e3d2d2">`</span>);
showResults(<span style="color: #0000FF">"Failed!"</span>);
}
}
}
</pre></div><h2>Logging out</h2><p>You really don’t log off JWT, they expire. To simulate logging out of a site, you make Local Storage forget:</p><!-- HTML generated using hilite.me --><div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><pre style="margin: 0; line-height: 125%">function deleteJwt() {
clearJwt();
}
</pre></div><h2>Reading JWT “payload”</h2><p>You can embed data in middle part of the JWT (the Payload) as unencrypted Base64 encoded JSON (Yes, I stole it from StackOverflow).</p><!-- HTML generated using hilite.me --><div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><pre style="margin: 0; line-height: 125%"><span style="color: #008800; font-style: italic">//see https://stackoverflow.com/a/38552302/3819</span>
function parseJwt(token) {
<span style="color: #000080; font-weight: bold">const</span> base64Url = token.split(<span style="color: #800080">'.'</span>)[<span style="color: #0000FF">1</span>];
<span style="color: #000080; font-weight: bold">const</span> base64 = base64Url.replace(/-/g, <span style="color: #800080">'+'</span>).replace(/_/g, <span style="color: #800080">'/'</span>);
<span style="color: #000080; font-weight: bold">return</span> JSON.parse(window.atob(base64));
}
</pre></div><h2>The localStorage functions</h2><p>These are the functions that actually read and write the JWT to localStorage:</p><!-- HTML generated using hilite.me --><div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><pre style="margin: 0; line-height: 125%">function writeJwt(jwt) {
<span style="color: #000080; font-weight: bold">if</span> (<span style="color: #000080; font-weight: bold">typeof</span> Storage !== <span style="color: #0000FF">"undefined"</span>) {
localStorage.setItem(<span style="color: #0000FF">"jwt"</span>, jwt);
} <span style="color: #000080; font-weight: bold">else</span> {
showResults(<span style="color: #0000FF">"Sorry, your browser does not support Web Storage..."</span>)
}
}
function readJwt() {
<span style="color: #000080; font-weight: bold">if</span> (<span style="color: #000080; font-weight: bold">typeof</span> Storage !== <span style="color: #0000FF">"undefined"</span>) {
<span style="color: #000080; font-weight: bold">return</span> localStorage.getItem(<span style="color: #0000FF">"jwt"</span>);
} <span style="color: #000080; font-weight: bold">else</span> {
showResults(<span style="color: #0000FF">"Sorry, your browser does not support Web Storage..."</span>);
}
<span style="color: #000080; font-weight: bold">return</span> <span style="color: #0000FF">""</span>;
}
function clearJwt() {
<span style="color: #000080; font-weight: bold">if</span> (<span style="color: #000080; font-weight: bold">typeof</span> Storage !== <span style="color: #0000FF">"undefined"</span>) {
localStorage.removeItem(<span style="color: #0000FF">"jwt"</span>);
} <span style="color: #000080; font-weight: bold">else</span> {
showResults(<span style="color: #0000FF">"Sorry, your browser does not support Web Storage..."</span>);
}
}
</pre></div><h2>The Whole Page</h2><p>And here is the whole page (JS, CSS and HTML all in one):</p><!-- HTML generated using hilite.me --><div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><pre style="margin: 0; line-height: 125%"><span style="color: #008080"><!DOCTYPE html></span>
<span style="color: #000080; font-weight: bold"><html></span>
<span style="color: #000080; font-weight: bold"><head></span>
<span style="color: #000080; font-weight: bold"><style></span>
<span style="color: #000080; font-weight: bold">body</span> {
<span style="color: #000080; font-weight: bold">margin</span>: <span style="color: #0000FF">25px</span>;
}
<span style="color: #000080; font-weight: bold">label</span> {
<span style="color: #000080; font-weight: bold">width</span>: <span style="color: #0000FF">80px</span>;
<span style="color: #000080; font-weight: bold">display</span>: <span style="color: #000080; font-weight: bold">inline</span>-<span style="color: #000080; font-weight: bold">block</span>;
<span style="color: #000080; font-weight: bold">margin</span>: <span style="color: #0000FF">2px</span>;
}
<span style="color: #000080; font-weight: bold">input</span> {
<span style="color: #000080; font-weight: bold">margin</span>: <span style="color: #0000FF">2px</span>;
}
<span style="color: #000080; font-weight: bold">button</span> {
<span style="color: #000080; font-weight: bold">width</span>: <span style="color: #0000FF">150px</span>;
<span style="color: #000080; font-weight: bold">margin</span>: <span style="color: #0000FF">2px</span>;
}
#result {
<span style="color: #000080; font-weight: bold">width</span>: <span style="color: #0000FF">100</span>%;
<span style="color: #000080; font-weight: bold">height</span>: <span style="color: #0000FF">150px</span>;
}
.grid-container {
<span style="color: #000080; font-weight: bold">width</span>: <span style="color: #0000FF">100</span>%;
<span style="color: #000080; font-weight: bold">display</span>: grid;
grid-gap: <span style="color: #0000FF">10px</span>;
grid-template-columns: <span style="color: #0000FF">1</span>fr <span style="color: #0000FF">1</span>fr <span style="color: #0000FF">1</span>fr;
}
.grid-container {
<span style="color: #000080; font-weight: bold">display</span>: <span style="color: #000080; font-weight: bold">inline</span>-grid;
}
<span style="color: #000080; font-weight: bold"></style></span>
<span style="color: #000080; font-weight: bold"></head></span>
<span style="color: #000080; font-weight: bold"><body></span>
<span style="color: #000080; font-weight: bold"><h2></span>JWT Client<span style="color: #000080; font-weight: bold"></h2></span>
<span style="color: #000080; font-weight: bold"><div</span> <span style="color: #FF0000">id=</span><span style="color: #0000FF">"theGrid"</span><span style="color: #FF0000">class=</span><span style="color: #0000FF">"grid-container"</span><span style="color: #000080; font-weight: bold">></span>
<span style="color: #000080; font-weight: bold"><div</span> <span style="color: #FF0000">id=</span><span style="color: #0000FF">"data"</span><span style="color: #FF0000">class=</span><span style="color: #0000FF">"grid-item"</span> <span style="color: #000080; font-weight: bold">></span>
<span style="color: #000080; font-weight: bold"><h3></span>Data for JWT<span style="color: #000080; font-weight: bold"></h3></span>
<span style="color: #000080; font-weight: bold"><p></span>This is the data used to create the JWT. The Server recognizes 2 roles: "admin" and "super".<span style="color: #000080; font-weight: bold"></p></span>
<span style="color: #000080; font-weight: bold"><p></span>(We can simulate a log in failure by checking "Fail")<span style="color: #000080; font-weight: bold"></p></span>
<span style="color: #000080; font-weight: bold"><label</span> <span style="color: #FF0000">for=</span><span style="color: #0000FF">"txtUserName"</span><span style="color: #000080; font-weight: bold">></span>User Name <span style="color: #000080; font-weight: bold"></label><input</span> <span style="color: #FF0000">type=</span><span style="color: #0000FF">"text"</span> <span style="color: #FF0000">id=</span><span style="color: #0000FF">"txtUserName"</span> <span style="color: #FF0000">value=</span><span style="color: #0000FF">"johns"</span> <span style="color: #000080; font-weight: bold">/><br</span> <span style="color: #000080; font-weight: bold">/></span>
<span style="color: #000080; font-weight: bold"><label</span> <span style="color: #FF0000">for=</span><span style="color: #0000FF">"txtRole"</span><span style="color: #000080; font-weight: bold">></span>Role<span style="color: #000080; font-weight: bold"></label><input</span> <span style="color: #FF0000">type=</span><span style="color: #0000FF">"text"</span> <span style="color: #FF0000">id=</span><span style="color: #0000FF">"txtRole"</span> <span style="color: #FF0000">value=</span><span style="color: #0000FF">"admin"</span> <span style="color: #000080; font-weight: bold">/><br</span> <span style="color: #000080; font-weight: bold">/></span>
<span style="color: #000080; font-weight: bold"><label</span> <span style="color: #FF0000">for=</span><span style="color: #0000FF">"txtId"</span><span style="color: #000080; font-weight: bold">></span>Id<span style="color: #000080; font-weight: bold"></label><input</span> <span style="color: #FF0000">type=</span><span style="color: #0000FF">"number"</span> <span style="color: #FF0000">id=</span><span style="color: #0000FF">"txtId"</span> <span style="color: #FF0000">value=</span><span style="color: #0000FF">"42"</span> <span style="color: #000080; font-weight: bold">/><br</span> <span style="color: #000080; font-weight: bold">/></span>
<span style="color: #000080; font-weight: bold"><label</span> <span style="color: #FF0000">for=</span><span style="color: #0000FF">"chkFail"</span><span style="color: #000080; font-weight: bold">></span>Fail<span style="color: #000080; font-weight: bold"></label><input</span> <span style="color: #FF0000">type=</span><span style="color: #0000FF">"checkbox"</span> <span style="color: #FF0000">id=</span><span style="color: #0000FF">"chkFail"</span> <span style="color: #FF0000">value=</span><span style="color: #0000FF">"false"</span> <span style="color: #000080; font-weight: bold">/><br</span> <span style="color: #000080; font-weight: bold">/></span>
<span style="color: #000080; font-weight: bold"></div></span>
<span style="color: #000080; font-weight: bold"><div</span> <span style="color: #FF0000">id=</span><span style="color: #0000FF">"access"</span> <span style="color: #FF0000">class=</span><span style="color: #0000FF">"grid-item"</span><span style="color: #000080; font-weight: bold">></span>
<span style="color: #000080; font-weight: bold"><h3></span>Make JWT<span style="color: #000080; font-weight: bold"></h3></span>
<span style="color: #000080; font-weight: bold"><p></span>Here we get the JWT from the "server", store and retrieve the JWT.<span style="color: #000080; font-weight: bold"></p></span>
<span style="color: #000080; font-weight: bold"><p></span>The JWT is stored in Local Storage.<span style="color: #000080; font-weight: bold"></p></span>
<span style="color: #000080; font-weight: bold"><button</span> <span style="color: #FF0000">type=</span><span style="color: #0000FF">"button"</span> <span style="color: #FF0000">onclick=</span><span style="color: #0000FF">"getJwt()"</span><span style="color: #000080; font-weight: bold">></span>Get Token<span style="color: #000080; font-weight: bold"></button><br</span> <span style="color: #000080; font-weight: bold">/></span>
<span style="color: #000080; font-weight: bold"><button</span> <span style="color: #FF0000">type=</span><span style="color: #0000FF">"button"</span> <span style="color: #FF0000">onclick=</span><span style="color: #0000FF">"showJwt()"</span><span style="color: #000080; font-weight: bold">></span>Show Token<span style="color: #000080; font-weight: bold"></button><br</span> <span style="color: #000080; font-weight: bold">/></span>
<span style="color: #000080; font-weight: bold"><button</span> <span style="color: #FF0000">type=</span><span style="color: #0000FF">"button"</span> <span style="color: #FF0000">onclick=</span><span style="color: #0000FF">"decode()"</span><span style="color: #000080; font-weight: bold">></span>Decode<span style="color: #000080; font-weight: bold"></button><br</span> <span style="color: #000080; font-weight: bold">/></span>
<span style="color: #000080; font-weight: bold"><button</span> <span style="color: #FF0000">type=</span><span style="color: #0000FF">"button"</span> <span style="color: #FF0000">onclick=</span><span style="color: #0000FF">"deleteJwt()"</span><span style="color: #000080; font-weight: bold">></span>Clear Token<span style="color: #000080; font-weight: bold"></button><br</span> <span style="color: #000080; font-weight: bold">/></span>
<span style="color: #000080; font-weight: bold"></div></span>
<span style="color: #000080; font-weight: bold"><div</span> <span style="color: #FF0000">id=</span><span style="color: #0000FF">"use"</span> <span style="color: #FF0000">class=</span><span style="color: #0000FF">"grid-item"</span><span style="color: #000080; font-weight: bold">></span>
<span style="color: #000080; font-weight: bold"><h3></span>Use JWT<span style="color: #000080; font-weight: bold"></h3></span>
<span style="color: #000080; font-weight: bold"><p></span>Here we use the JWT we stored in Local Storage and make calls to
various endpoints which have different permissions on the server<span style="color: #000080; font-weight: bold"></p></span>
<span style="color: #000080; font-weight: bold"><p></span>The Server decodes the claims in the JWT and returns them as JSON object<span style="color: #000080; font-weight: bold"></p></span>
<span style="color: #000080; font-weight: bold"><button</span> <span style="color: #FF0000">type=</span><span style="color: #0000FF">"button"</span> <span style="color: #FF0000">onclick=</span><span style="color: #0000FF">"makeCall('')"</span><span style="color: #000080; font-weight: bold">></span>Call "/"<span style="color: #000080; font-weight: bold"></button><br</span> <span style="color: #000080; font-weight: bold">/></span>
<span style="color: #000080; font-weight: bold"><button</span> <span style="color: #FF0000">type=</span><span style="color: #0000FF">"button"</span> <span style="color: #FF0000">onclick=</span><span style="color: #0000FF">"makeCall('/admin')"</span><span style="color: #000080; font-weight: bold">></span>Call "/admin"<span style="color: #000080; font-weight: bold"></button><br</span> <span style="color: #000080; font-weight: bold">/></span>
<span style="color: #000080; font-weight: bold"><button</span> <span style="color: #FF0000">type=</span><span style="color: #0000FF">"button"</span> <span style="color: #FF0000">onclick=</span><span style="color: #0000FF">"makeCall('/super')"</span><span style="color: #000080; font-weight: bold">></span>Call "/super"<span style="color: #000080; font-weight: bold"></button><br</span> <span style="color: #000080; font-weight: bold">/></span>
<span style="color: #000080; font-weight: bold"><button</span> <span style="color: #FF0000">type=</span><span style="color: #0000FF">"button"</span> <span style="color: #FF0000">onclick=</span><span style="color: #0000FF">"makeCall('/either')"</span><span style="color: #000080; font-weight: bold">></span>Call "/either"<span style="color: #000080; font-weight: bold"></button><br</span> <span style="color: #000080; font-weight: bold">/></span>
<span style="color: #000080; font-weight: bold"><button</span> <span style="color: #FF0000">type=</span><span style="color: #0000FF">"button"</span> <span style="color: #FF0000">onclick=</span><span style="color: #0000FF">"makeCall('/open')"</span><span style="color: #000080; font-weight: bold">></span>Call "/open"<span style="color: #000080; font-weight: bold"></button><br</span> <span style="color: #000080; font-weight: bold">/></span>
<span style="color: #000080; font-weight: bold"></div></span>
<span style="color: #000080; font-weight: bold"></div></span>
<span style="color: #000080; font-weight: bold"><div</span> <span style="color: #FF0000">id=</span><span style="color: #0000FF">"results"</span><span style="color: #000080; font-weight: bold">></span>
<span style="color: #000080; font-weight: bold"><label</span> <span style="color: #FF0000">for=</span><span style="color: #0000FF">"result"</span><span style="color: #000080; font-weight: bold">></span>Results<span style="color: #000080; font-weight: bold"></label><br</span> <span style="color: #000080; font-weight: bold">/></span>
<span style="color: #000080; font-weight: bold"><textarea</span> <span style="color: #FF0000">id=</span><span style="color: #0000FF">"result"</span><span style="color: #000080; font-weight: bold">></textarea></span>
<span style="color: #000080; font-weight: bold"></div></span>
<span style="color: #000080; font-weight: bold"><script></span>
<span style="color: #000080; font-weight: bold">const</span> BASE_URL = <span style="color: #0000FF">"/api/"</span>;
<span style="color: #000080; font-weight: bold">function</span> getJwt() {
showResults(<span style="color: #0000FF">"working ...."</span>);
<span style="color: #008800; font-style: italic">// Assemble data to log in, should match schema of MakeTokenViewModel</span>
<span style="color: #000080; font-weight: bold">const</span> userName = document.getElementById(<span style="color: #0000FF">"txtUserName"</span>).value;
<span style="color: #000080; font-weight: bold">const</span> role = document.getElementById(<span style="color: #0000FF">"txtRole"</span>).value;
<span style="color: #000080; font-weight: bold">const</span> id = document.getElementById(<span style="color: #0000FF">"txtId"</span>).value;
<span style="color: #000080; font-weight: bold">const</span> fail = document.getElementById(<span style="color: #0000FF">"chkFail"</span>).checked;
<span style="color: #000080; font-weight: bold">const</span> message = <span style="color: #a61717; background-color: #e3d2d2">`</span>{<span style="color: #0000FF">"UserName"</span>:<span style="color: #0000FF">"${userName}"</span>,<span style="color: #0000FF">"Role"</span>: <span style="color: #0000FF">"${role}"</span>,<span style="color: #0000FF">"Id"</span>: ${id},<span style="color: #0000FF">"Fail"</span>:${fail}}<span style="color: #a61717; background-color: #e3d2d2">`</span>;
<span style="color: #008800; font-style: italic">// Make the request</span>
<span style="color: #000080; font-weight: bold">const</span> xhttp = <span style="color: #000080; font-weight: bold">new</span> XMLHttpRequest();
xhttp.onreadystatechange = getJwtProcessResponse;
xhttp.open(<span style="color: #0000FF">"Post"</span>, <span style="color: #a61717; background-color: #e3d2d2">`</span>${BASE_URL}jwt/maketoken<span style="color: #a61717; background-color: #e3d2d2">`</span>, <span style="color: #000080; font-weight: bold">true</span>);
xhttp.setRequestHeader(<span style="color: #0000FF">"content-Type"</span>, <span style="color: #0000FF">"application/json"</span>);
xhttp.send(message);
}
<span style="color: #008800; font-style: italic">// Get and process the request</span>
<span style="color: #000080; font-weight: bold">function</span> getJwtProcessResponse() {
<span style="color: #000080; font-weight: bold">if</span> (<span style="color: #000080; font-weight: bold">this</span>.readyState == <span style="color: #0000FF">4</span>) {
<span style="color: #000080; font-weight: bold">if</span> (<span style="color: #000080; font-weight: bold">this</span>.status == <span style="color: #0000FF">200</span>) {
writeJwt(<span style="color: #000080; font-weight: bold">this</span>.responseText);
showResults(<span style="color: #0000FF">"token written to localStorage"</span>);
}
<span style="color: #000080; font-weight: bold">else</span> {
alert(<span style="color: #a61717; background-color: #e3d2d2">`</span>${<span style="color: #000080; font-weight: bold">this</span>.status}<span style="color: #a61717; background-color: #e3d2d2">\</span>n ${<span style="color: #000080; font-weight: bold">this</span>.responseText}<span style="color: #a61717; background-color: #e3d2d2">`</span>);
showResults(<span style="color: #0000FF">"Failed!"</span>);
}
}
}
<span style="color: #008800; font-style: italic">// Make a call to the end point specified by urlExtension</span>
<span style="color: #008800; font-style: italic">// (each endpoint has a different permission, for demo purpose)</span>
<span style="color: #000080; font-weight: bold">function</span> makeCall(urlExtension) {
showResults(<span style="color: #0000FF">"working ...."</span>);
<span style="color: #000080; font-weight: bold">const</span> jwt = readJwt();
<span style="color: #000080; font-weight: bold">const</span> xhttp = <span style="color: #000080; font-weight: bold">new</span> XMLHttpRequest();
xhttp.onreadystatechange = makeCallProcessResponse;
xhttp.open(<span style="color: #0000FF">"Get"</span>, <span style="color: #a61717; background-color: #e3d2d2">`</span>${BASE_URL}values${urlExtension}<span style="color: #a61717; background-color: #e3d2d2">`</span>, <span style="color: #000080; font-weight: bold">true</span>);
<span style="color: #008800; font-style: italic">// We want to test with the user not logged in</span>
<span style="color: #000080; font-weight: bold">if</span> (!!jwt) {
xhttp.setRequestHeader(<span style="color: #0000FF">"Authorization"</span>, <span style="color: #a61717; background-color: #e3d2d2">`</span> Bearer ${jwt}<span style="color: #a61717; background-color: #e3d2d2">`</span>);
}
xhttp.send();
}
<span style="color: #000080; font-weight: bold">function</span> makeCallProcessResponse() {
<span style="color: #000080; font-weight: bold">if</span> (<span style="color: #000080; font-weight: bold">this</span>.readyState == <span style="color: #0000FF">4</span>) {
<span style="color: #000080; font-weight: bold">if</span> (<span style="color: #000080; font-weight: bold">this</span>.status == <span style="color: #0000FF">200</span>) {
showResults(<span style="color: #000080; font-weight: bold">this</span>.responseText);
}
<span style="color: #000080; font-weight: bold">else</span> {
alert(<span style="color: #a61717; background-color: #e3d2d2">`</span>${<span style="color: #000080; font-weight: bold">this</span>.status}<span style="color: #a61717; background-color: #e3d2d2">\</span>n ${<span style="color: #000080; font-weight: bold">this</span>.responseText}<span style="color: #a61717; background-color: #e3d2d2">`</span>);
showResults(<span style="color: #0000FF">"Failed!"</span>);
}
}
}
<span style="color: #000080; font-weight: bold">function</span> showJwt() {
<span style="color: #000080; font-weight: bold">const</span> jwt = readJwt();
<span style="color: #000080; font-weight: bold">if</span> (jwt === <span style="color: #000080; font-weight: bold">null</span>) {
showResults(<span style="color: #0000FF">"no token to display"</span>);
<span style="color: #000080; font-weight: bold">return</span>;
}
showResults(jwt);
}
<span style="color: #000080; font-weight: bold">function</span> deleteJwt() {
clearJwt();
}
<span style="color: #000080; font-weight: bold">function</span> writeJwt(jwt) {
<span style="color: #000080; font-weight: bold">if</span> (<span style="color: #000080; font-weight: bold">typeof</span> Storage !== <span style="color: #0000FF">"undefined"</span>) {
localStorage.setItem(<span style="color: #0000FF">"jwt"</span>, jwt);
} <span style="color: #000080; font-weight: bold">else</span> {
showResults(<span style="color: #0000FF">"Sorry, your browser does not support Web Storage..."</span>)
}
}
<span style="color: #000080; font-weight: bold">function</span> readJwt() {
<span style="color: #000080; font-weight: bold">if</span> (<span style="color: #000080; font-weight: bold">typeof</span> Storage !== <span style="color: #0000FF">"undefined"</span>) {
<span style="color: #000080; font-weight: bold">return</span> localStorage.getItem(<span style="color: #0000FF">"jwt"</span>);
} <span style="color: #000080; font-weight: bold">else</span> {
showResults(<span style="color: #0000FF">"Sorry, your browser does not support Web Storage..."</span>);
}
<span style="color: #000080; font-weight: bold">return</span> <span style="color: #0000FF">""</span>;
}
<span style="color: #000080; font-weight: bold">function</span> clearJwt() {
<span style="color: #000080; font-weight: bold">if</span> (<span style="color: #000080; font-weight: bold">typeof</span> Storage !== <span style="color: #0000FF">"undefined"</span>) {
localStorage.removeItem(<span style="color: #0000FF">"jwt"</span>);
} <span style="color: #000080; font-weight: bold">else</span> {
showResults(<span style="color: #0000FF">"Sorry, your browser does not support Web Storage..."</span>);
}
}
<span style="color: #000080; font-weight: bold">function</span> decode() {
<span style="color: #000080; font-weight: bold">const</span> jwt = readJwt();
<span style="color: #000080; font-weight: bold">if</span> (jwt == <span style="color: #000080; font-weight: bold">null</span>) {
showResults(<span style="color: #0000FF">"no token to decode"</span>);
<span style="color: #000080; font-weight: bold">return</span>;
}
<span style="color: #000080; font-weight: bold">const</span> parsed = parseJwt(jwt);
showResults(JSON.stringify(parsed, <span style="color: #000080; font-weight: bold">null</span>, <span style="color: #0000FF">2</span>));
}
<span style="color: #008800; font-style: italic">//see https://stackoverflow.com/a/38552302/3819</span>
<span style="color: #000080; font-weight: bold">function</span> parseJwt(token) {
<span style="color: #000080; font-weight: bold">const</span> base64Url = token.split(<span style="color: #0000FF">'.'</span>)[<span style="color: #0000FF">1</span>];
<span style="color: #000080; font-weight: bold">const</span> base64 = base64Url.replace(<span style="color: #0000FF">/-/g</span>, <span style="color: #0000FF">'+'</span>).replace(<span style="color: #0000FF">/_/g</span>, <span style="color: #0000FF">'/'</span>);
<span style="color: #000080; font-weight: bold">return</span> JSON.parse(window.atob(base64));
};
<span style="color: #000080; font-weight: bold">function</span> showResults(results) {
document.getElementById(<span style="color: #0000FF">"result"</span>).value = results;
}
<span style="color: #000080; font-weight: bold"></script></span>
<span style="color: #000080; font-weight: bold"></body></span>
<span style="color: #000080; font-weight: bold"></html></span>
</pre></div>Jack Stephenshttp://www.blogger.com/profile/04996728441112030246noreply@blogger.com0tag:blogger.com,1999:blog-18907707.post-39390468841608649472019-04-13T13:19:00.000-07:002019-04-13T13:43:39.089-07:00JWT on .NET Core 2.2 and Little Else<p>I wanted to see how to implement JSON Web Tokens (JWT) in C#/ASP.NET Core 2.2 without the chaos of anything else, like passwords, a database or anything else. I have a complete example project <a href="https://github.com/jrcs3/SimpleJwt4Core22">on my GitHub</a>. If you clone my respository, you can skip downn to <a href="#discussion">Discussion</a>.<br />
</p><h1>Solution</h1><p>I created a project that I called "SimpleJwt4Core22" that was configured not to use SSL and use port 55477 (so the URL would be "http://localhost:55477")<br />
</p><ol><div>
<li><p>Add the following configuration to <strong><a href="https://github.com/jrcs3/SimpleJwt4Core22/blob/master/SimpleJwt4Core22/appsettings.json">appsettings.json</a></strong>:</p><!-- HTML generated using hilite.me --><div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><table><tr><td><pre style="margin: 0; line-height: 125%"></pre></td><td><pre style="margin: 0; line-height: 125%"><span style="color: #0000FF">"Jwt"</span><span style="color: #a61717; background-color: #e3d2d2">:</span> {
<span style="color: #000080; font-weight: bold">"Site"</span>: <span style="color: #0000FF">"http://jrcs3.com"</span>,
<span style="color: #000080; font-weight: bold">"SigningKey"</span>: <span style="color: #0000FF">"Not a safe place to put a security key, eh?"</span>,
<span style="color: #000080; font-weight: bold">"ExperyInMinutes"</span>: <span style="color: #0000FF">"600"</span>
}<span style="color: #a61717; background-color: #e3d2d2">,</span>
</pre></td></tr>
</table></div></div></li>
<li><p>Ensure that <strong>IConfiguration</strong> is injected into <strong><a href="https://github.com/jrcs3/SimpleJwt4Core22/blob/master/SimpleJwt4Core22/Startup.cs">Startup.cs</a></strong>, the constructor and private field should look like this:</p><!-- HTML generated using hilite.me --><div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><table><tr><td><pre style="margin: 0; line-height: 125%"></pre></td><td><pre style="margin: 0; line-height: 125%"><span style="color: #000080; font-weight: bold">public</span> Startup(IConfiguration configuration)
{
Configuration = configuration;
}
<span style="color: #000080; font-weight: bold">public</span> IConfiguration Configuration { <span style="color: #000080; font-weight: bold">get</span>; }
</pre></td></tr>
</table></div>(depending on which template you used to create the project, this code may already be present.)<br />
</li>
<li><p>Configure site to use JWT for its AuthenticationScheme in Startup’s <strong>ConfigureServices()</strong> method.</p><!-- HTML generated using hilite.me --><div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><table><tr><td><pre style="margin: 0; line-height: 125%"></pre></td><td><pre style="margin: 0; line-height: 125%"><span style="color: #008800; font-style: italic">// JWT Begin Insert</span>
services.AddAuthentication(option =>
{
option.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
option.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
option.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.SaveToken = <span style="color: #000080; font-weight: bold">true</span>;
options.RequireHttpsMetadata = <span style="color: #000080; font-weight: bold">true</span>;
options.TokenValidationParameters = <span style="color: #000080; font-weight: bold">new</span> TokenValidationParameters()
{
ValidateIssuer = <span style="color: #000080; font-weight: bold">true</span>,
ValidateAudience = <span style="color: #000080; font-weight: bold">true</span>,
ValidAudience = Configuration[<span style="color: #0000FF">"Jwt:Site"</span>],
ValidIssuer = Configuration[<span style="color: #0000FF">"Jwt:Site"</span>],
IssuerSigningKey = <span style="color: #000080; font-weight: bold">new</span> SymmetricSecurityKey(
Encoding.UTF8.GetBytes(Configuration[<span style="color: #0000FF">"Jwt:SigningKey"</span>]))
};
});
<span style="color: #008800; font-style: italic">// JWT End Insert</span>
</pre></td></tr>
</table></div></li>
<li><p>Ensure that MVC is configured at the end of Startup’s <strong>ConfigureServices()</strong> method.</p><!-- HTML generated using hilite.me --><div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><table><tr><td><pre style="margin: 0; line-height: 125%"></pre></td><td><pre style="margin: 0; line-height: 125%">services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
</pre></td></tr>
</table></div><p>(depending on which template you used to create the project, this code may already be present.)</p></li>
<li><p>Add the following line to Startup’s Configure() method (just below the IsDevelopment() block)</p><!-- HTML generated using hilite.me --><div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><table><tr><td><pre style="margin: 0; line-height: 125%"></pre></td><td><pre style="margin: 0; line-height: 125%"><span style="color: #008800; font-style: italic">// JWT Begin Insert</span>
app.UseAuthentication();
<span style="color: #008800; font-style: italic">// JWT End Insert</span>
</pre></td></tr>
</table></div></li>
<li><p>Ensure that MVC is enabled in Startup’s Configure() method, you should see this line:</p><!-- HTML generated using hilite.me --><div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><table><tr><td><pre style="margin: 0; line-height: 125%"></pre></td><td><pre style="margin: 0; line-height: 125%">app.UseMvc();
</pre></td></tr>
</table></div>(depending on which template you used to create the project, this code may already be present.)<br />
</li>
<li><p>Create a Controller and a ViewModel. I called the Controller "JwtController" and put it in the Controllers directory (create it if you need to) and I called ViewModel "MakeTokenViewModel"</p><p>So here’s the code for the <strong><a href="https://github.com/jrcs3/SimpleJwt4Core22/blob/master/SimpleJwt4Core22/ViewModels/MakeTokenViewModel.cs">/ViewModels/MakeTokenViewModel.cs</a></strong>:</p><!-- HTML generated using hilite.me --><div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><table><tr><td><pre style="margin: 0; line-height: 125%"></pre></td><td><pre style="margin: 0; line-height: 125%"><span style="color: #000080; font-weight: bold">using</span> System.ComponentModel.DataAnnotations;
<span style="color: #000080; font-weight: bold">namespace</span> SimpleJwt4Core22.ViewModels
{
<span style="color: #000080; font-weight: bold">public</span> <span style="color: #000080; font-weight: bold">class</span> MakeTokenViewModel
{
<span style="color: #FF0000"> [Required]</span>
<span style="color: #000080; font-weight: bold">public</span> <span style="color: #000080; font-weight: bold">int</span> Id { <span style="color: #000080; font-weight: bold">get</span>; <span style="color: #000080; font-weight: bold">set</span>; }
<span style="color: #FF0000"> [Required]</span>
<span style="color: #000080; font-weight: bold">public</span> <span style="color: #000080; font-weight: bold">string</span> UserName { <span style="color: #000080; font-weight: bold">get</span>; <span style="color: #000080; font-weight: bold">set</span>; }
<span style="color: #FF0000"> [Required]</span>
<span style="color: #000080; font-weight: bold">public</span> <span style="color: #000080; font-weight: bold">string</span> Role { <span style="color: #000080; font-weight: bold">get</span>; <span style="color: #000080; font-weight: bold">set</span>; }
<span style="color: #008800; font-style: italic">// To simulate login or other failure:</span>
<span style="color: #000080; font-weight: bold">public</span> <span style="color: #000080; font-weight: bold">bool</span> Fail { <span style="color: #000080; font-weight: bold">get</span>; <span style="color: #000080; font-weight: bold">set</span>; }
}
}
</pre></td></tr>
</table></div><p>And <strong><a href="https://github.com/jrcs3/SimpleJwt4Core22/blob/master/SimpleJwt4Core22/Controllers/JwtController.cs">/Controllers/JwtController.cs</a></strong>:</p><a name="JwtController" ></a><br />
<!-- HTML generated using hilite.me --><div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><table><tr><td><pre style="margin: 0; line-height: 125%"></pre></td><td><pre style="margin: 0; line-height: 125%"><span style="color: #000080; font-weight: bold">using</span> Microsoft.AspNetCore.Mvc;
<span style="color: #000080; font-weight: bold">using</span> Microsoft.Extensions.Configuration;
<span style="color: #000080; font-weight: bold">using</span> Microsoft.IdentityModel.Tokens;
<span style="color: #000080; font-weight: bold">using</span> SimpleJwt4Core22.ViewModels;
<span style="color: #000080; font-weight: bold">using</span> System;
<span style="color: #000080; font-weight: bold">using</span> System.IdentityModel.Tokens.Jwt;
<span style="color: #000080; font-weight: bold">using</span> System.Security.Claims;
<span style="color: #000080; font-weight: bold">using</span> System.Text;
<span style="color: #000080; font-weight: bold">namespace</span> SimpleJwt4Core22.Controllers
{
<span style="color: #FF0000"> [Route("api/[controller]</span><span style="color: #0000FF">")]</span>
<span style="color: #FF0000"> [ApiController]</span>
<span style="color: #000080; font-weight: bold">public</span> <span style="color: #000080; font-weight: bold">class</span> JwtController : ControllerBase
{
<span style="color: #000080; font-weight: bold">private</span> <span style="color: #000080; font-weight: bold">readonly</span> IConfiguration _configuration;
<span style="color: #000080; font-weight: bold">public</span> JwtController(IConfiguration configuration)
{
_configuration = configuration;
}
<span style="color: #FF0000"> [Route("maketoken")]</span>
<span style="color: #FF0000"> [HttpPost]</span>
<span style="color: #000080; font-weight: bold">public</span> ActionResult Login([FromBody] MakeTokenViewModel model)
{
<span style="color: #008800; font-style: italic">// Simulate login or other failure:</span>
<span style="color: #000080; font-weight: bold">if</span> (model.Fail)
{
<span style="color: #000080; font-weight: bold">return</span> BadRequest(<span style="color: #0000FF">"JWT Creation Failure"</span>);
}
<span style="color: #000080; font-weight: bold">var</span> claim = <span style="color: #000080; font-weight: bold">new</span>[]
{
<span style="color: #000080; font-weight: bold">new</span> Claim(<span style="color: #0000FF">"name"</span>, model.UserName),
<span style="color: #000080; font-weight: bold">new</span> Claim(<span style="color: #0000FF">"id"</span>, model.Id.ToString()),
<span style="color: #000080; font-weight: bold">new</span> Claim(<span style="color: #0000FF">"role"</span>, model.Role)
};
<span style="color: #000080; font-weight: bold">var</span> signingKey = <span style="color: #000080; font-weight: bold">new</span> SymmetricSecurityKey(
Encoding.UTF8.GetBytes(_configuration[<span style="color: #0000FF">"Jwt:SigningKey"</span>]));
<span style="color: #000080; font-weight: bold">int</span> experyInMinutes = Convert.ToInt32(_configuration[<span style="color: #0000FF">"Jwt:ExperyInMinutes"</span>]);
<span style="color: #000080; font-weight: bold">var</span> token = <span style="color: #000080; font-weight: bold">new</span> JwtSecurityToken(
issuer: _configuration[<span style="color: #0000FF">"Jwt:Site"</span>],
audience: _configuration[<span style="color: #0000FF">"Jwt:Site"</span>],
expires: DateTime.UtcNow.AddMinutes(experyInMinutes),
signingCredentials: <span style="color: #000080; font-weight: bold">new</span> SigningCredentials(
signingKey, SecurityAlgorithms.HmacSha256),
claims: claim
);
<span style="color: #000080; font-weight: bold">return</span> Ok(<span style="color: #000080; font-weight: bold">new</span> JwtSecurityTokenHandler().WriteToken(token));
}
}
}
</pre></td></tr>
</table></div><p>At his point you should be able to POST to the endpoint http://localhost:55477/api/jwt/maketoken with Postman, Fiddler or the following the curl command:</p><!-- HTML generated using hilite.me --><div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><table><tr><td><pre style="margin: 0; line-height: 125%"></pre></td><td><pre style="margin: 0; line-height: 125%">curl -X POST -H <span style="color: #0000FF">"Content-Type: application/json"</span> <span style="color: #0000FF">\</span>
-d <span style="color: #0000FF">'{"UserName": "dvader","Role": "admin","Id": 42}'</span> <span style="color: #0000FF">\</span>
http://localhost:55477/api/jwt/maketoken
</pre></td></tr>
</table></div><p>I like to give curl command because they are short and sweet. On Windows 10 you can run curl on Windows Subsystem for Linux or Git Bash or a number of other shells. I added the line continuation character ('\') for readability. (In a future post I will consume this endpoint in Javascript</p></li>
<li><p>To use the JWT, we need some endpoint that uses it. I created <strong><a href="https://github.com/jrcs3/SimpleJwt4Core22/blob/master/SimpleJwt4Core22/Controllers/ValuesController.cs">/Controllers/ValuesController.cs</a></strong>, with endpoints that provides with different endpoints that require different roles (admin & super):</p><a name="ValuesController"></a><br />
<!-- HTML generated using hilite.me --><div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><table><tr><td><pre style="margin: 0; line-height: 125%"></pre></td><td><pre style="margin: 0; line-height: 125%"><span style="color: #000080; font-weight: bold">using</span> Microsoft.AspNetCore.Authorization;
<span style="color: #000080; font-weight: bold">using</span> Microsoft.AspNetCore.Mvc;
<span style="color: #000080; font-weight: bold">using</span> Newtonsoft.Json;
<span style="color: #000080; font-weight: bold">using</span> System.Collections.Generic;
<span style="color: #000080; font-weight: bold">using</span> System.Security.Claims;
<span style="color: #000080; font-weight: bold">namespace</span> SimpleJwt4Core22.Controllers
{
<span style="color: #FF0000"> [Authorize]</span>
<span style="color: #FF0000"> [Route("api/[controller]</span><span style="color: #0000FF">")]</span>
<span style="color: #FF0000"> [ApiController]</span>
<span style="color: #000080; font-weight: bold">public</span> <span style="color: #000080; font-weight: bold">class</span> ValuesController : ControllerBase
{
<span style="color: #000080; font-weight: bold">private</span> <span style="color: #000080; font-weight: bold">readonly</span> JsonSerializerSettings _serializerSettings;
<span style="color: #000080; font-weight: bold">public</span> ValuesController()
{
_serializerSettings = <span style="color: #000080; font-weight: bold">new</span> JsonSerializerSettings
{
Formatting = Formatting.Indented
};
}
<span style="color: #008800; font-style: italic">// GET api/values</span>
<span style="color: #FF0000"> [HttpGet]</span>
<span style="color: #000080; font-weight: bold">public</span> IActionResult Get()
{
<span style="color: #000080; font-weight: bold">return</span> handleRequest();
}
<span style="color: #008800; font-style: italic">// All of these endpoints do the same thing except for the </span>
<span style="color: #008800; font-style: italic">// authorized roles</span>
<span style="color: #FF0000"> [Route("admin")]</span>
<span style="color: #FF0000"> [Authorize(Roles = "admin")]</span>
<span style="color: #FF0000"> [HttpGet]</span>
<span style="color: #000080; font-weight: bold">public</span> IActionResult GetAdmin()
{
<span style="color: #000080; font-weight: bold">return</span> handleRequest();
}
<span style="color: #FF0000"> [Route("super")]</span>
<span style="color: #FF0000"> [Authorize(Roles = "super")]</span>
<span style="color: #FF0000"> [HttpGet]</span>
<span style="color: #000080; font-weight: bold">public</span> IActionResult GetSuper()
{
<span style="color: #000080; font-weight: bold">return</span> handleRequest();
}
<span style="color: #FF0000"> [Route("either")]</span>
<span style="color: #FF0000"> [Authorize(Roles = "super,admin")]</span>
<span style="color: #FF0000"> [HttpGet]</span>
<span style="color: #000080; font-weight: bold">public</span> IActionResult GetBoth()
{
<span style="color: #000080; font-weight: bold">return</span> handleRequest();
}
<span style="color: #FF0000"> [Route("open")]</span>
<span style="color: #FF0000"> [AllowAnonymous]</span>
<span style="color: #FF0000"> [HttpGet]</span>
<span style="color: #000080; font-weight: bold">public</span> IActionResult GetOpen()
{
<span style="color: #000080; font-weight: bold">return</span> handleRequest();
}
<span style="color: #000080; font-weight: bold">private</span> IActionResult handleRequest()
{
<span style="color: #008800; font-style: italic">// Read the claims that I wrote in JwtController:</span>
<span style="color: #000080; font-weight: bold">var</span> claims = ((ClaimsIdentity)User.Identity).Claims;
<span style="color: #000080; font-weight: bold">var</span> id = getClaimByType(claims, <span style="color: #0000FF">"id"</span>);
<span style="color: #000080; font-weight: bold">var</span> name = getClaimByType(claims, <span style="color: #0000FF">"name"</span>);
<span style="color: #008800; font-style: italic">// In JwtController, I created a claim for "role" </span>
<span style="color: #008800; font-style: italic">// so I would expect this to have a value:</span>
<span style="color: #000080; font-weight: bold">var</span> role = getClaimByType(claims, <span style="color: #0000FF">"role"</span>);
<span style="color: #008800; font-style: italic">// however, by magic, this one has the value:</span>
<span style="color: #000080; font-weight: bold">var</span> msRole = getClaimByType(claims,
<span style="color: #0000FF">"http://schemas.microsoft.com/ws/2008/06/identity/claims/role"</span>);
<span style="color: #000080; font-weight: bold">var</span> response = <span style="color: #000080; font-weight: bold">new</span>
{
id,
name,
role,
msRole
};
<span style="color: #008800; font-style: italic">// Send the claims back in the Response:</span>
<span style="color: #000080; font-weight: bold">var</span> json = JsonConvert.SerializeObject(response, _serializerSettings);
<span style="color: #000080; font-weight: bold">return</span> <span style="color: #000080; font-weight: bold">new</span> OkObjectResult(json);
}
<span style="color: #000080; font-weight: bold">public</span> <span style="color: #000080; font-weight: bold">static</span> <span style="color: #000080; font-weight: bold">string</span> getClaimByType(IEnumerable<Claim> jwt, <span style="color: #000080; font-weight: bold">string</span> typeKey)
{
<span style="color: #000080; font-weight: bold">foreach</span> (<span style="color: #000080; font-weight: bold">var</span> claim <span style="color: #000080; font-weight: bold">in</span> jwt)
{
<span style="color: #000080; font-weight: bold">if</span> (claim.Type == typeKey)
{
<span style="color: #000080; font-weight: bold">return</span> claim.Value;
}
}
<span style="color: #000080; font-weight: bold">return</span> <span style="color: #000080; font-weight: bold">string</span>.Empty;
}
}
}
</pre></td></tr>
</table></div><p>Again we are ready to make a GET to the endpoint http://localhost:55477/api/values (or one if it's sub routes) with Postman, Fiddler or the following the curl command (this time we need to send the JWT:</p><!-- HTML generated using hilite.me --><div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><table><tr><td><pre style="margin: 0; line-height: 125%"></pre></td><td><pre style="margin: 0; line-height: 125%">jwt=<span style="color: #000080; font-weight: bold">$(</span> <span style="color: #0000FF">\</span>
curl -X POST -H <span style="color: #0000FF">"Content-Type: application/json"</span> <span style="color: #0000FF">\</span>
-d <span style="color: #0000FF">'{"UserName": "ckent","Role": "super","Id": 42,"fail":false }'</span> <span style="color: #0000FF">\</span>
http://localhost:55477/api/jwt/maketoken <span style="color: #0000FF">\</span>
<span style="color: #000080; font-weight: bold">)</span>
curl -X GET -H <span style="color: #0000FF">"Authorization: Bearer $jwt"</span> http://localhost:55477/api/values/super
</pre></td></tr>
</table></div><br />
</li>
</ol><a name="discussion" ></a><br />
<h1>Discussion</h1><p>The code in Startup.cs configures and enables Authentication and sets it to the JwtBearer scheme. I put some configuration data in appsettings.json.</p><p>Yes, this is a rather stilted example. It does not validate a password and allows the user to "log in" with any role. I have one endpoint to obtain a JWT, and several others to simulate getting data with different required roles (and one that doesn’t even require you to be logged in at all). </p><h2>Logging In/Getting your JWT</h2><p>To make a JWT you need to create an IEnumerable of Claims that you wish to make:<br />
</p><!-- HTML generated using hilite.me --><div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><table><tr><td><pre style="margin: 0; line-height: 125%"></pre></td><td><pre style="margin: 0; line-height: 125%"><span style="color: #000080; font-weight: bold">var</span> claim = <span style="color: #000080; font-weight: bold">new</span>[]
{
<span style="color: #000080; font-weight: bold">new</span> Claim(<span style="color: #0000FF">"name"</span>, model.UserName),
<span style="color: #000080; font-weight: bold">new</span> Claim(<span style="color: #0000FF">"id"</span>, model.Id.ToString()),
<span style="color: #000080; font-weight: bold">new</span> Claim(<span style="color: #0000FF">"role"</span>, model.Role)
};
</pre></td></tr>
</table></div><p>Then you construct a new JwtSecurityToken with the claims, signing key, expery time, etc to make your JWT: <br />
</p><!-- HTML generated using hilite.me --><div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><table><tr><td><pre style="margin: 0; line-height: 125%"></pre></td><td><pre style="margin: 0; line-height: 125%"><span style="color: #000080; font-weight: bold">var</span> signingKey = <span style="color: #000080; font-weight: bold">new</span> SymmetricSecurityKey(Encoding.UTF8.GetBytes(
_configuration[<span style="color: #0000FF">"Jwt:SigningKey"</span>]));
<span style="color: #000080; font-weight: bold">int</span> experyInMinutes = Convert.ToInt32(_configuration[<span style="color: #0000FF">"Jwt:ExperyInMinutes"</span>]);
<span style="color: #000080; font-weight: bold">string</span> site = _configuration[<span style="color: #0000FF">"Jwt:Site"</span>];
<span style="color: #000080; font-weight: bold">var</span> token = <span style="color: #000080; font-weight: bold">new</span> JwtSecurityToken(
issuer: site,
audience: site,
expires: DateTime.UtcNow.AddMinutes(experyInMinutes),
signingCredentials: <span style="color: #000080; font-weight: bold">new</span> SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256),
claims: claim
);
</pre></td></tr>
</table></div><p>Return the JWT. Refer to <strong><a href="#JwtController">JwtController</a>.Login()</strong> for the complete example.<br />
</p><h2>Securing your endpoints</h2><p>The endpoints in <strong><a href="#ValuesController">ValuesController</a></strong> are secured with attributes. I applied the AuthorizeAttribute to the class, so you must be logged in to hit any endpoint in the class, unless the AllowAnonymousAttribute has been applied to the endpoint's method. You can further restrict access to an endpoint by applying an AuthorizeAttribute Roles set.<br />
</p><p>Here are all the endpoints:<style>
table.endpointTable {
width: 100%
}
table.endpointTable th, table.endpointTable td {
border: 1px solid black;
margin: 2px;
}
table.endpointTable th:nth-child(1) {
width: 120px;
}
table.endpointTable th:nth-child(2) {
width: 230px;
}
.endpointTable th, .endpointTable td { padding: 3px 6px; } /* cellpadding */
</style><br />
<table class="endpointTable"><tr> <th>Endpoint (URL)</th> <th> Attribute </th> <th>Notes</th> </tr>
<tr> <td>/api/values</td> <td></td> <td>Must be logged in (have a JWT) with ANY role</td> </tr>
<tr> <td>/api/values/admin</td> <td>[Authorize(Roles = "admin")]</td> <td>Must be logged with the role "admin"</td> </tr>
<tr> <td>/api/values/super</td> <td>[Authorize(Roles = "super")]</td> <td>Must be logged with the role "super"</td> </tr>
<tr> <td>/api/values/either</td> <td>[Authorize(Roles = "admin,super")]</td> <td>Must be logged with either role "admin" or "super"</td> </tr>
<tr> <td>/api/values/open</td> <td>[AllowAnonymous]</td> <td>DOES NOT need to be logged in (doesn't need JWT)</td> </tr>
</table></p><br />
Jack Stephenshttp://www.blogger.com/profile/04996728441112030246noreply@blogger.com0tag:blogger.com,1999:blog-18907707.post-78819741761844873592018-07-23T17:06:00.000-07:002018-07-26T10:15:20.105-07:00AngularJS vs Angular: index.html and boostrapping<p>This is part of a series that <a href="https://brochure.jrcs3.com/2018/07/angularjs-vs-angular-introduction.html">compares AngularJS (the old Angular) to Angular</a>. <br />
</p><p>For the first comparison, I’m going to look at index.html and how the apps are set up or bootstrapped.<br />
</p><h3>AngularJS</h3><!-- HTML generated using hilite.me --><div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><table><tr><td><pre style="margin: 0; line-height: 125%"> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28</pre></td><td><pre style="margin: 0; line-height: 125%"><span style="color: #0000ff"><!DOCTYPE html></span>
<html ng-app=<span style="color: #a31515">"mainModule"</span>>
<head>
<title>TasksA1</title>
<script src=<span style="color: #a31515">"https://code.angularjs.org/1.7.0/angular.min.js"</span>></script>
<script src=<span style="color: #a31515">"https://code.angularjs.org/1.7.0/angular-resource.min.js"</span>></script>
<script src=<span style="color: #a31515">"https://code.angularjs.org/1.7.0/angular-route.js"</span>></script>
<script src=<span style="color: #a31515">"app.js"</span>></script>
<script src=<span style="color: #a31515">"app-routing.module.js"</span>></script>
<script src=<span style="color: #a31515">"services/todo.service.js"</span>></script>
<script src=<span style="color: #a31515">"pages/home/home.component.js"</span>></script>
<script src=<span style="color: #a31515">"pages/to-do-list/to-do-list.component.js"</span>></script>
<script src=<span style="color: #a31515">"pages/to-do-item/to-do-item.component.js"</span>></script>
<span style="color: #008000"><!-- Angular CLR project somehow adds Boostrap into the file --></span>
<link rel=<span style="color: #a31515">"stylesheet"</span>
href=<span style="color: #a31515">"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"</span>
integrity=<span style="color: #a31515">"sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"</span>
crossorigin=<span style="color: #a31515">"anonymous"</span>>
<link rel=<span style="color: #a31515">"stylesheet"</span> type=<span style="color: #a31515">"text/css"</span> href=<span style="color: #a31515">"style.css"</span>>
</head>
<body>
<h1>ToDos using AngularJS (1)</h1>
<a href=<span style="color: #a31515">"#!/toDoList"</span>>ToDos</a>
|
<a href=<span style="color: #a31515">"#!/toDoAdd"</span>>New ToDo</a>
<div ng-view></div>
</body>
</html>
</pre></td></tr>
</table></div><h5><a href="https://github.com/jrcs3/tasks-a1/blob/master/app/index.html">index.html</a></h5><p>On line 2, there is the ng-app directive, telling AngularJS to do its magic. Lines 5-13 there are script tags, first Angulars then mine. Then on line 26 we have a div with a ng-view directive, that's where all the magic happens.<br />
</p><h3>Angluar</h3><!-- HTML generated using hilite.me --><div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><table><tr><td><pre style="margin: 0; line-height: 125%"> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18</pre></td><td><pre style="margin: 0; line-height: 125%"><span style="color: #0000ff"><!doctype html></span>
<html lang=<span style="color: #a31515">"en"</span>>
<head>
<meta charset=<span style="color: #a31515">"utf-8"</span>>
<title>TasksA6Ts</title>
<base href=<span style="color: #a31515">"/"</span>>
<meta name=<span style="color: #a31515">"viewport"</span> content=<span style="color: #a31515">"width=device-width, initial-scale=1"</span>>
<link rel=<span style="color: #a31515">"icon"</span> type=<span style="color: #a31515">"image/x-icon"</span> href=<span style="color: #a31515">"favicon.ico"</span>>
</head>
<body>
<h1>ToDos using Angular /w TypeScript</h1>
<a href=<span style="color: #a31515">"/toDoList"</span>>ToDos</a>
|
<a href=<span style="color: #a31515">"/toDoAdd"</span>>New ToDo</a>
<app-root><span style="border: 1px solid #FF0000"><</span>/app-root>
</body>
</html>
</pre></td></tr>
</table></div><h5><a href="https://github.com/jrcs3/tasks-a6-ts/blob/master/src/index.html">index.html</a></h5><p>The app-root tag replaces the div with the ng-view directive. There is no ng-app directive or script tags. When I did a view source. But how does it know which files to insert; I think it gets them from app.module.ts (the files are reduced to ES5 and dumped into a single file).<br />
</p><!-- HTML generated using hilite.me --><div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><table><tr><td><pre style="margin: 0; line-height: 125%"> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29</pre></td><td><pre style="margin: 0; line-height: 125%"><span style="color: #0000ff">import</span> { ReactiveFormsModule , FormsModule } from <span style="color: #a31515">'@angular/forms'</span>;
<span style="color: #0000ff">import</span> { BrowserModule } from <span style="color: #a31515">'@angular/platform-browser'</span>;
<span style="color: #0000ff">import</span> { NgModule } from <span style="color: #a31515">'@angular/core'</span>;
<span style="color: #0000ff">import</span> { AppComponent } from <span style="color: #a31515">'./app.component'</span>;
<span style="color: #0000ff">import</span> { ToDoItemComponent } from <span style="color: #a31515">'./pages/to-do-item/to-do-item.component'</span>;
<span style="color: #0000ff">import</span> { ToDoListComponent } from <span style="color: #a31515">'./pages/to-do-list/to-do-list.component'</span>;
<span style="color: #0000ff">import</span> { HttpClientModule } from <span style="color: #a31515">'@angular/common/http'</span>;
<span style="color: #0000ff">import</span> { AppRoutingModule } from <span style="color: #a31515">'./app-routing.module'</span>;
<span style="color: #0000ff">import</span> { HomeComponent } from <span style="color: #a31515">'./pages/home/home.component'</span>;
<span style="border: 1px solid #FF0000">@</span>NgModule({
declarations: [
AppComponent,
ToDoItemComponent,
ToDoListComponent,
HomeComponent
],
imports: [
BrowserModule,
AppRoutingModule,
ReactiveFormsModule,
FormsModule,
HttpClientModule
],
providers: [],
bootstrap: [AppComponent]
})
<span style="color: #0000ff">export</span> <span style="color: #0000ff">class</span> AppModule { }
</pre></td></tr>
</table></div><h5><a href="https://github.com/jrcs3/tasks-a6-ts/blob/master/src/app/app.module.ts">app.module.ts</a></h5><p>These are the files in the actual project, but an Angular app needs to be built. I built the app with the command <strong>ng build</strong> and when I look at the build results and this is the generated index.html:<br />
</p><!-- HTML generated using hilite.me --><div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><table><tr><td><pre style="margin: 0; line-height: 125%"> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23</pre></td><td><pre style="margin: 0; line-height: 125%"><span style="color: #0000ff"><!doctype html></span>
<html lang=<span style="color: #a31515">"en"</span>>
<head>
<meta charset=<span style="color: #a31515">"utf-8"</span>>
<title>TasksA6Ts</title>
<base href=<span style="color: #a31515">"/"</span>>
<meta name=<span style="color: #a31515">"viewport"</span> content=<span style="color: #a31515">"width=device-width, initial-scale=1"</span>>
<link rel=<span style="color: #a31515">"icon"</span> type=<span style="color: #a31515">"image/x-icon"</span> href=<span style="color: #a31515">"favicon.ico"</span>>
</head>
<body>
<h1>ToDos using Angular /w TypeScript</h1>
<a href=<span style="color: #a31515">"/toDoList"</span>>ToDos</a>
|
<a href=<span style="color: #a31515">"/toDoAdd"</span>>New ToDo</a>
<app-root><span style="border: 1px solid #FF0000"><</span>/app-root>
<script type=<span style="color: #a31515">"text/javascript"</span> src=<span style="color: #a31515">"runtime.js"</span>></script>
<script type=<span style="color: #a31515">"text/javascript"</span> src=<span style="color: #a31515">"polyfills.js"</span>></script>
<script type=<span style="color: #a31515">"text/javascript"</span> src=<span style="color: #a31515">"styles.js"</span>></script>
<script type=<span style="color: #a31515">"text/javascript"</span> src=<span style="color: #a31515">"vendor.js"</span>></script>
<script type=<span style="color: #a31515">"text/javascript"</span> src=<span style="color: #a31515">"main.js"</span>></script>
</body>
</html>
</pre></td></tr>
</table></div><h5>Generated index.html</h5><p>All of the TypeScript files mentioned in app.module.ts are transpiled into ES5 into main.js<br />
</p><div><h2>About the series AngularJS vs Angular</h2><ul><li><a href="https://brochure.jrcs3.com/2018/07/angularjs-vs-angular-introduction.html">The Introduction</a></li>
<li><a href="https://brochure.jrcs3.com/2018/07/angularjs-vs-angular-indexhtml-and.html">index.html and boostrapping</a></li>
</ul></div><br />
Jack Stephenshttp://www.blogger.com/profile/04996728441112030246noreply@blogger.com1tag:blogger.com,1999:blog-18907707.post-45942291225127595512018-07-23T16:21:00.000-07:002018-07-23T17:12:04.774-07:00AngularJS vs Angular: The Introduction<p>As I try to maintain my status as a “Full Stack Developer” I have been looking at Angular and AngularJS. So, what gives? Angular is an open source web application platform that Google released in 2016 to replace AngularJS (then called Angular). AngularJS is Google’s original Angular, released in 2010. Angular and AngularJS are completely different.<br />
</p><p>In this experiment I wrote a simple list/detail site in both <a href="https://github.com/jrcs3/tasks-a1">AngularJS (in JavaScript ES5)</a> and <a href="https://github.com/jrcs3/tasks-a6-ts">Angular (in TypeScript)</a>. I created a <a href="https://github.com/jrcs3/todos">simple REST endpoint</a> with <a href="https://www.npmjs.com/package/json-server">JSON-Server</a>.<br />
</p><h2>Methodology</h2><h3>AngularJS</h3><p>I took a project I wrote a coupe of years ago and simplified it and then refactored it to match the files structure of the Angular app (foo.ts became foo.js).<br />
</p><h3>Angular</h3><p>I created the app, the components and services using the Angular CLI (version 6). I put all of the REST calls in a “services” to match the architecture I used in the AngularJS app; most of the samples I found online called the http methods directly from the components.<br />
</p><h2>The App</h2><p>The Task List is the “Hello World” of databinding, so I went with this. I dropped the “People” table so we only have 1 table: “Todos”. Both apps do the same thing using the same structure as far as I could push it.<br />
</p><div><h2>About the series AngularJS vs Angular</h2><ul><li><a href="https://brochure.jrcs3.com/2018/07/angularjs-vs-angular-introduction.html">The Introduction</a></li>
<li><a href="https://brochure.jrcs3.com/2018/07/angularjs-vs-angular-indexhtml-and.html">index.html and boostrapping</a></li>
</ul></div>Jack Stephenshttp://www.blogger.com/profile/04996728441112030246noreply@blogger.com4tag:blogger.com,1999:blog-18907707.post-64575312852063086162018-06-28T16:11:00.000-07:002018-06-28T16:15:18.862-07:00ACID Cheat Sheet<p>This on is for Database Transactions</p><ul><li><b>Atomicity</b> – The transaction is all or nothing. Succeeds or fails as a unit. <br />If the bank adds $100 to my account, then the $100 must come from your account.</li>
<li><b>Consistency</b> – Outside of the transaction, we can only see the begin or end state of the transaction. <br />We will never see the half of the transaction where I get the $100 and you haven’t given up your $100. (too bad).</li>
<li><b>Isolation</b> – The transaction is hidden from the outside world until it is committed. <br />I imagine that during a transaction there are two universes. In the transaction’s universe, the values change as you work though the steps. In the other universe, the values remain as they were before.</li>
<li><b>Durability</b> – Once the transaction is committed, it is on the record forever. <br />If they decide that I shouldn’t have gotten the $100 from you after the transaction was committed, they can create a new transaction where they take the $100 away from me, but they can’t reverse the committed transaction. You can’t rewrite history.</li>
</ul>Jack Stephenshttp://www.blogger.com/profile/04996728441112030246noreply@blogger.com0tag:blogger.com,1999:blog-18907707.post-39729838091911441082018-05-29T19:37:00.000-07:002018-05-29T20:11:01.865-07:004 Pillars of Object Oriented Programming Cheat SheetAnother cheat sheet for interviewing. <br />
I think of the 4 pillars consisting of 2 coins. Abstraction / Encapsulation and Inheritance / Polymorphism. So here they are:<br />
<ul><li><strong>Abstraction</strong> – Look at the thing we are modeling: What is it? What are its characteristics? What does it do? Make the object as simple as possible and don't include unnecessary functionality. Data Hiding for simplification.</li>
<li><strong>Encapsulation</strong> – Create a black box around the how. You interact with an object through public properties (setters and getters) and methods. Data Hiding for security.</li>
</ul>Both Abstraction and Encapsulation are about all about hiding stuff from us. Protecting us from unnecessary complexity. Abstraction takes place when we fill out our CRC cards when we are designing our classes and Encapsulation takes place when we actually write the code.<br />
<br />
<ul><li><strong>Inheritance</strong> – Reuse code! A child class inherits from a parent, from a grandparent, from great grandparent, and so on .. I think of examples of class hierarchies, like Animal, Dog, Pitbull, etc.</li>
<li><strong>Polymorphism</strong> – From "many forms": A child can be substituted for the parent. If you cast a Pitbull as a Dog, you can call the Dog.Bark() method and the Pitbull.Bark() will be run (if it has been overridden). Also, you can have a collection of Dogs, and issue the Bark() command on each of them without caring which kind of Dog is barking (Pitbull or Corgi).</li>
</ul>I think of Inheritance and Polymorphism as cause and effect. Because a Pitbull is derived from Dog (Inheritance), the Pitbull can bark like a Dog (Polymorphism).<br />
<br />
Jack Stephenshttp://www.blogger.com/profile/04996728441112030246noreply@blogger.com1tag:blogger.com,1999:blog-18907707.post-40293192015040480142018-05-25T17:52:00.001-07:002018-05-25T17:55:41.252-07:00S.O.L.I.D. Principals Cheat Sheet<br />
I am between gigs, so I have been given opportunities to discus the SOLID Principals along with others of the classics of Object Orientation and basic Computer Science.<br />
<br />
The SOLID Principals are somehow connected to <a href="https://en.wikipedia.org/wiki/Robert_C._Martin">Robert C. “Uncle Bob” Martin</a> and his 2000 paper <a href="https://fi.ort.edu.uy/innovaportal/file/2032/1/design_principles.pdf">Design Principles and Design Patterns</a>. The word “Solid” appears nowhere in the document. Well anyway, the <a href="https://en.wikipedia.org/wiki/SOLID">SOLID Principals</a> are:<br />
<br />
<ul><li><a href="https://en.wikipedia.org/wiki/Single_responsibility_principle"><strong>Single Responsibility Principle</strong></a> – A class should do one thing and do it well. No more epic methods or “manager” classed that do everything.<br />
</li>
<li><a href="https://en.wikipedia.org/wiki/Open/closed_principle"><strong>Open/Closed Principle</strong></a> – Open for extension but closed for modification. When you publish an API, others are depending on you. Don’t change the way that a method works. Someone may even be depending on a bug in your code!</li>
<li><a href="https://en.wikipedia.org/wiki/Liskov_substitution_principle"><strong>Liskov Substitution Principle</strong></a> – You should be able to substitute a child for a parent and everything work. Named after <a href="https://en.wikipedia.org/wiki/Barbara_Liskov">Barbra Liskov</a> of MIT; learning this kind of detail makes it easier for me to remember.<br />
</li>
<li><a href="https://en.wikipedia.org/wiki/Interface_segregation_principle"><strong>Interface Segregation Principle</strong></a> – More smaller interfaces! Each interface should do 1 thing. Avoid creating a monster interface that does everything. (this is an extension of the Single Responsibility Principal, right?)<br />
</li>
<li><a href="https://en.wikipedia.org/wiki/Dependency_inversion_principle"><strong>Dependency Inversion</strong></a> – Don’t create an object directly. Use an abstraction to create it. Whether it is the Abstract Factory pattern, an IOC container, simply pass in the object in, or whatever.<br />
</li>
</ul>Jack Stephenshttp://www.blogger.com/profile/04996728441112030246noreply@blogger.com0tag:blogger.com,1999:blog-18907707.post-14718692666608879722017-08-02T22:05:00.001-07:002018-05-29T20:06:29.807-07:00CSS Popup SampleHere's some code I wrote for something I'm doing for work.<br />
This code will show and hide a popup<br />
<!-- HTML generated using hilite.me --><div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><pre style="margin: 0; line-height: 125%"><html>
<head>
<style>
#pop {
<span style="color: #0000ff">height</span>: 200px;
<span style="color: #0000ff">width</span>: 200px;
<span style="color: #0000ff">position</span>: <span style="color: #0000ff">fixed</span>;
<span style="color: #0000ff">bottom</span>: 50%;
<span style="color: #0000ff">right</span>: 50%;
<span style="color: #0000ff">border</span>: 2px <span style="color: #0000ff">solid</span>;
<span style="color: #0000ff">padding</span>: 10px;
<span style="color: #0000ff">background</span>: white;
<span style="color: #0000ff">display</span>: <span style="color: #0000ff">none</span>;
}
#buttonsOnPop {
<span style="color: #0000ff">position</span>: <span style="color: #0000ff">absolute</span>;
<span style="color: #0000ff">right</span>: 0;
<span style="color: #0000ff">bottom</span>: 0;
<span style="color: #0000ff">margin</span>: 5px;
}
#popTitle {
<span style="color: #0000ff">vertical-align</span>: <span style="color: #0000ff">top</span>;
<span style="color: #0000ff">text-align</span>: <span style="color: #0000ff">center</span>;
<span style="color: #0000ff">width</span>: 100%;
<span style="color: #0000ff">background-color</span>: limegreen;
}
</style>
<script>
<span style="color: #0000ff">function</span> closePopYes() {
alert(<span style="color: #a31515">"yes"</span>);
}
<span style="color: #0000ff">function</span> closePopNo() {
document.getElementById(<span style="color: #a31515">"pop"</span>).style.display=<span style="color: #a31515">'none'</span>;
}
<span style="color: #0000ff">function</span> showPop() {
<span style="color: #0000ff">var</span> text = <span style="color: #a31515">"<p>To be or not to be</p><p>Revente</p>"</span>;
<span style="color: #0000ff">var</span> popContent = document.getElementById(<span style="color: #a31515">"popContent"</span>);
popContent.innerHTML = text;
document.getElementById(<span style="color: #a31515">"pop"</span>).style.display=<span style="color: #a31515">'block'</span>;
}
</script>
</head>
<body>
<div id=<span style="color: #a31515">"pop"</span>>
<div id=<span style="color: #a31515">"popTitle"</span>>Pop Window</div>
<div id=<span style="color: #a31515">"popContent"</span>>
</div>
<div id=<span style="color: #a31515">"buttonsOnPop"</span>>
<button id=<span style="color: #a31515">"yes"</span> onclick=<span style="color: #a31515">"closePopYes();"</span>>Yes</button>
<button id=<span style="color: #a31515">"no"</span> onclick=<span style="color: #a31515">"closePopNo();"</span>>No</button>
</div>
</div>
<button id=<span style="color: #a31515">"showPop"</span> onclick=<span style="color: #a31515">"showPop()"</span>>Show Pop</button>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur vitae iaculis enim.
Vivamus a tellus sit amet tortor mattis auctor at eget purus. Fusce condimentum semper
varius. Integer ultricies enim et ipsum scelerisque, dignissim ultricies metus molestie.
Quisque rutrum, sem id vehicula malesuada, orci purus malesuada dolor, id vehicula nulla
nisi sed dui. Donec at eros vestibulum, euismod mi quis, rhoncus justo. Curabitur quis
metus sit amet erat efficitur pellentesque. Nunc dui massa, efficitur ac dapibus ut,
interdum vel metus. Nunc mattis eleifend nisl, id rhoncus erat cursus et. Nullam semper,
risus sed feugiat elementum, turpis lacus egestas nisi, quis bibendum lorem turpis eu
purus. Etiam nec lorem tristique, condimentum ligula ac, mattis erat. Proin aliquet
quis lectus eu hendrerit. Curabitur vitae laoreet nulla. Pellentesque imperdiet odio id
urna imperdiet bibendum eget non erat.
</p>
</body>
</html>
</pre></div>Jack Stephenshttp://www.blogger.com/profile/04996728441112030246noreply@blogger.com0tag:blogger.com,1999:blog-18907707.post-61888524765436712932016-06-30T06:20:00.001-07:002016-06-30T06:20:04.799-07:00VS Item Template for ASP.NET Identity 2.0<p>ASP.NET Identity does many cool things; a lot more than the old Membership Provider did. I did a project lately that needed to do what the Membership Provider did, Users and Roles. It turns out that the ASP.NET Identity supports Membership, but it is poorly documented. <p>I looked high and low on the Internet for good examples of ASP.NET Identity doing simple Membership without any extra fluff (my client didn’t want any of it), but did not find any! Since I could not find a good any, I decided to make one and put it on <a href="https://github.com/jrcs3/MvcFormsUsersAndRoles">GitHub</a>. <p>I also built a <a href="https://github.com/jrcs3/MvcFormsUsersAndRoles/releases">Visual Studio Item Template that will add most of what you need add and edit roles and users</a>. <h2>The Template</h2> <p>I released my template on my Github account here. It only works with ASP.NET MVC projects created in Visual Studio 2015. It is too stupid to validate that you are using To use my template: <ol> <li>Download <b>FormsAuth.zip</b> from the latest release. <li>Copy it <b>to <My Documents>\Visual Studio 2015\Templates\Item Templates\Visual C#</b> <li>Open <b>Developer Command Prompt for VS2015</b> as administrator <li>Run <b>devenv /installvstemplates</b> <li>Open Visual Studio 2015 (don’t use an already opened copy, the template won’t be there) <li>Create a new project (you can open an existing project as long as it is an ASP.NET MVC project created in VS 2015 or upgraded to ASP.NET Identity 2, VS 2013 projects won’t work, then skip to step 9) <li>Choose <b>ASP.NET Web Application</b>, name it and click <b>OK</b> <li>Select the template <b>MVC</b> <li><b>Project</b> => <b>Add New Item</b> (or Ctrl+Shift+A) <li>On the left, select <b>Visual C#</b> then select <b>Forms Auth Role and User CRUD for MVC Project using ASP.NET Identity </b>(better name?) <li>Follow the instructions in the read me file.</li></ol> <h3>The things I make you do</h3> <p>I wanted to be safe and not touch any of your code, so I am asking you to do it for me. <h3>Create ApplicationRoleManager</h3> <p>The default Visual Studio ASP.NET MVC project is aware of Users but not Roles. You will need to tell it to care about Roels by adding an ApplicationRoleManager. So go to App_Start > Startup.Auth.cs and change the Startup.ConfigureAuth to look like this: </p> <p><font color="#0000ff">public</font><font color="#000000"> </font><font color="#0000ff">partial</font><font color="#000000"> </font><font color="#0000ff">class</font><font color="#000000"> </font><font color="#2b91af">Startup <br></font><font color="#000000">{ <br> </font><font color="#0000ff">public</font><font color="#000000"> </font><font color="#0000ff">void</font><font color="#000000"> ConfigureAuth(</font><font color="#2b91af">IAppBuilder</font><font color="#000000"> app) <br> { <br> app.CreatePerOwinContext(</font><font color="#2b91af">ApplicationDbContext</font><font color="#000000">.Create); <br> </font><font color="#008000">// Add this line: <br></font><font color="#000000"> app.CreatePerOwinContext<</font><font color="#2b91af">ApplicationRoleManager</font><font color="#000000">>(</font><font color="#2b91af">ApplicationRoleManager</font><font color="#000000">.Create); <br> app.CreatePerOwinContext<</font><font color="#2b91af">ApplicationUserManager</font><font color="#000000">>(</font><font color="#2b91af">ApplicationUserManager</font><font color="#000000">.Create); <br> app.CreatePerOwinContext<</font><font color="#2b91af">ApplicationSignInManager</font><font color="#000000">>(</font><font color="#2b91af">ApplicationSignInManager</font><font color="#000000">.Create);</font> </p> <h3>Add links to navigate to User and Roles pages</h3> <p>OK, you have Users and Roles and forms to edit Users and Roles; but how do you get to them? If you are using the default ASP.NET MVC layout, make the main menu in <strong>_Layout.cshtml</strong> look like this: </p> <p><font color="#0000ff"><</font><font color="#800000">div</font><font color="#000000"> </font><font color="#ff0000">class</font><font color="#0000ff">="navbar-collapse collapse"> <br></font><font color="#000000"> </font><font color="#0000ff"><</font><font color="#800000">ul</font><font color="#000000"> </font><font color="#ff0000">class</font><font color="#0000ff">="nav navbar-nav"> <br></font><font color="#000000"> </font><font color="#0000ff"><</font><font color="#800000">li</font><font color="#0000ff">></font><font color="#000000">@Html.ActionLink(</font><font color="#a31515">"Home"</font><font color="#000000">, </font><font color="#a31515">"Index"</font><font color="#000000">, </font><font color="#a31515">"Home"</font><font color="#000000">)</font><font color="#0000ff"></</font><font color="#800000">li</font><font color="#0000ff">> <br></font><font color="#000000"> </font><font color="#0000ff"><</font><font color="#800000">li</font><font color="#0000ff">></font><font color="#000000">@Html.ActionLink(</font><font color="#a31515">"About"</font><font color="#000000">, </font><font color="#a31515">"About"</font><font color="#000000">, </font><font color="#a31515">"Home"</font><font color="#000000">)</font><font color="#0000ff"></</font><font color="#800000">li</font><font color="#0000ff">> <br></font><font color="#000000"> </font><font color="#0000ff"><</font><font color="#800000">li</font><font color="#0000ff">></font><font color="#000000">@Html.ActionLink(</font><font color="#a31515">"Contact"</font><font color="#000000">, </font><font color="#a31515">"Contact"</font><font color="#000000">, </font><font color="#a31515">"Home"</font><font color="#000000">)</font><font color="#0000ff"></</font><font color="#800000">li</font><font color="#0000ff">> <br><br></font><font color="#000000"> @</font><font color="#0000ff">if</font><font color="#000000"> (Request.IsAuthenticated && User.IsInRole(</font><font color="#a31515">"Admin"</font><font color="#000000">)) { <br> </font><font color="#0000ff"><</font><font color="#800000">li</font><font color="#0000ff">></font><font color="#000000">@Html.ActionLink(</font><font color="#a31515">"RolesAdmin"</font><font color="#000000">, </font><font color="#a31515">"Index"</font><font color="#000000">, </font><font color="#a31515">"RolesAdmin"</font><font color="#000000">)</font><font color="#0000ff"></</font><font color="#800000">li</font><font color="#0000ff">> <br></font><font color="#000000"> </font><font color="#0000ff"><</font><font color="#800000">li</font><font color="#0000ff">></font><font color="#000000">@Html.ActionLink(</font><font color="#a31515">"UsersAdmin"</font><font color="#000000">, </font><font color="#a31515">"Index"</font><font color="#000000">, </font><font color="#a31515">"UsersAdmin"</font><font color="#000000">)</font><font color="#0000ff"></</font><font color="#800000">li</font><font color="#0000ff">> <br></font><font color="#000000"> } <br> </font><font color="#0000ff"></</font><font color="#800000">ul</font><font color="#0000ff">> <br></font><font color="#000000"> @Html.Partial(</font><font color="#a31515">"_LoginPartial"</font><font color="#000000">) <br></font><font color="#0000ff"></</font><font color="#800000">div</font><font color="#0000ff">> <br></p></font> <h3>Create “Seed” User</h3> <p>OK, you have all this wonderful User and Role functionality. The trouble is that you need a user who is in the role “Admin” to add and edit users. I added a special form that will allow you to register one and only one “seed” user who will be a member of “Admin”; if it sees that the role “Admin” already exists, it will send you to an error page. Go to <a href="http://yourdomain/seeduser/register">http://yourdomain/seeduser/register</a> and create a your new very first Admin user! After you create your seed user, you can delete SeedUserController and the SeedUser Views (files in /Views/SeedUsers). <h3>Other things to do</h3> <p>If you don’t want to expose any of your site to the outside, make all controllers require authorization with the [Authorize] attribute. <p>Delete Register page and routes (Including my Register Seed User). In the AccountController, delete (or comment out) both Register methods. Jack Stephenshttp://www.blogger.com/profile/04996728441112030246noreply@blogger.com0tag:blogger.com,1999:blog-18907707.post-1130827959026731822016-05-15T09:53:00.001-07:002016-05-15T09:53:40.511-07:00Notes on Roles with ASP.NET Identity on MVC<p>How do you handle authentication and authorization (or “security”) on a small scale ASP.NET MVC site? The other day I talked to someone who was experiencing problems with setting up security. He set up a MVC project in Visual Studio and couldn’t get it do what he wanted it to do. <h4>Membership Provider</h4> <p>Back in the day there was the Membership Provider, which was easy to use but isn’t hardened enough for these modern times. But was cool, you could set up users and roles with the ASP.NET Web Configuration Tool, you could control access to Controller methods with the Authorize attribute, so if I wanted Admins to access AdminOnly, I could do this: </p><pre style="font-family: consolas"><span style="background: white; color: black"> [</span><span style="background: white; color: #2b91af">Authorize</span><span style="background: white; color: black">(Roles = </span><span style="background: white; color: #a31515">"Admin"</span><span style="background: white; color: black">)]
</span><span style="background: white; color: blue">public </span><span style="background: white; color: #2b91af">ActionResult </span><span style="background: white; color: black">AdminOnly()
{
</span><span style="background: white; color: blue">return </span><span style="background: white; color: black">View();
}</span></pre>
<p>The guy talked to above probably wanted something that behaves like the Membership Provider.
<h4>ASP.NET Identity</h4>
<p>The Membership Provider has been replaced by ASP.NET Identity, which supports OAuth, Two-factor authentication and other coolness. My problem: there isn’t any obvious support for roles. All of the web examples deal with the new cool (send email to confirm, login using Facebook, etc.)
<p>I created a MVC site on Visual Studio 2015 Community and poking around ASP.NET Identity, I noticed that many of the parts that support roles
<h5>AspNetRoles & AspNetUserRoles Tables</h5>
<p>I created a new row in AspNetRoles named “Admin” (id = 1) and added a record in AspNetUserRoles to like 1 of my users in AspNetUsers to the role “Admin”.
<h4>Decorate Controller method with proper security</h4>
<p>I added this method to my new AccessController (I don’t use ActionResult because I’m basically lazy.): </p><pre style="font-family: consolas"><span style="background: white; color: black"> [</span><span style="background: white; color: #2b91af">Authorize</span><span style="background: white; color: black">(Roles = </span><span style="background: white; color: #a31515">"Admin"</span><span style="background: white; color: black">)]
</span><span style="background: white; color: blue">public string </span><span style="background: white; color: black">AdminOnly()
{
</span><span style="background: white; color: green">// I don?t use ActionResult because I?m basically lazy.
//Otherwise I?d have to create a View.
</span><span style="background: white; color: blue">return </span><span style="background: white; color: #a31515">"Welcome, you are allowed here because you are an Admin!"</span><span style="background: white; color: black">;
}
</span></pre>
<p>1. Without logging on I go to http://localhost:58625/Access/AdminOnly and I get the Login page. Good.
<p>2. Logged in as an administrator and I get to the page. Good.
<p>3. Logged in as a non-administrator and I get the Login page. Weird.
<h4>Redirecting to Unauthorized page</h4>
<p>OK, you don’t want to go to the Login page when you are logged in and are going to places where you don’t belong. ASP.NET Identity appears to redirect to Login whenever you go to somewhere you don’t belong. It makes the site feel incompetent. So I changed the Login() method in AccountController to: </p><pre style="font-family: consolas"><span style="background: white; color: black"> [</span><span style="background: white; color: #2b91af">AllowAnonymous</span><span style="background: white; color: black">]
</span><span style="background: white; color: blue">public </span><span style="background: white; color: #2b91af">ActionResult </span><span style="background: white; color: black">Login(</span><span style="background: white; color: blue">string </span><span style="background: white; color: black">returnUrl)
{
</span><span style="background: white; color: blue">if </span><span style="background: white; color: black">(User != </span><span style="background: white; color: blue">null </span><span style="background: white; color: black">&& User.Identity != </span><span style="background: white; color: blue">null </span><span style="background: white; color: black">&& User.Identity.IsAuthenticated)
{
</span><span style="background: white; color: blue">return </span><span style="background: white; color: black">RedirectToAction(</span><span style="background: white; color: #a31515">"Unauthorized"</span><span style="background: white; color: black">, </span><span style="background: white; color: #a31515">"Account"</span><span style="background: white; color: black">);
}
ViewBag.ReturnUrl = returnUrl;
</span><span style="background: white; color: blue">return </span><span style="background: white; color: black">View();
} </span></pre>
<p>And added </p><pre style="font-family: consolas"><span style="background: white; color: black"> [</span><span style="background: white; color: #2b91af">AllowAnonymous</span><span style="background: white; color: black">]
</span><span style="background: white; color: blue">public </span><span style="background: white; color: #2b91af">ActionResult </span><span style="background: white; color: black">Unauthorized()
{
</span><span style="background: white; color: blue">return </span><span style="background: white; color: black">View();
} </span></pre>
<p>And added the appropriate view.
<h4>What about administering my users?</h4>
<p>Right now I’m doing it in SQL Server. I’m looking into alternatives. More about that later.</p>Jack Stephenshttp://www.blogger.com/profile/04996728441112030246noreply@blogger.com0tag:blogger.com,1999:blog-18907707.post-78311886027888536722016-03-22T16:12:00.001-07:002016-05-09T17:05:39.814-07:00Future: Audio Games with Amazon Alexa (or Siri)<p>The other day I attended an AWS user group where a Solutions Architect for Alexa Voice Service spoke about developing for Alexa. She showed off her Amazon Echo and a couple of skills that she had written for Alexa and urged us all to write our own skills with various AWS services. </p><p>I’m not a cloud expert, so much of the details went straight over my head. </p><p>She also talked about how voice UI and Alexa are new and will end up everywhere, even cars. We could build an Alexa client on a Raspberry Pi today!</p><h2>Eliza</h2><p>Could Alexa run <a href="https://en.wikipedia.org/wiki/ELIZA">Eliza</a>? When I was in college I learned about Eliza, a program that can respond as a <a href="http://www.goodtherapy.org/learn-about-therapy/types/person-centered">Rogerian psychotherapist</a> (that target was chosen because the natural language processing could be really lame and it would still seam real). After college I bought a 286 computer and found a GWBasic version of Eliza. I played Eliza more like a game, sometimes her responses would make me laugh until it hurts! </p><p>The thing that made Eliza so much fun wasn’t the quality of the Natural Language Processing. She could change me to you and you to me and spit the sentence right back to you, track if she had “heard” certain references to your parents and “say” profound things like “I understand”.</p><!--
<p><br />
<a href="https://lh3.googleusercontent.com/-TDLqkSROfpI/VvHRRmGy54I/AAAAAAAAGHg/EgZfRg73MTs/s1600-h/Eliza3.png"><br />
<img title="Eliza" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="Eliza" src="https://lh3.googleusercontent.com/-TDLqkSROfpI/VvHRRmGy54I/AAAAAAAAGHg/EgZfRg73MTs/s1600-h/Eliza3.png?imgmax=800" width="644" height="417"></a><br />
</p>--><p>I image that the text processing to be wired up as “YOU” and speech synthesis wired to “ELIZA”. I used an <a href="http://www.masswerk.at/elizabot/eliza.html">online version of Eliza</a>.</p><h2>Interactive Fiction</h2><p>Back in the ancient times, in some computer games, you would type commands and the computer would present you with paragraphs of text describing where you are. Why can’t Alexa read the paragraphs to me? I looked around the internet and found a place where I can play <a href="http://textadventures.co.uk/games/view/5zyoqrsugeopel3ffhz_vq/zork">Zork</a> online.</p><!--
<p><br />
.<a href="https://lh3.googleusercontent.com/-to70FdDcNfA/VvHRS-sgkTI/AAAAAAAAGHo/l8k_gNBx-U4/s1600-h/ZORK6.png"><img title="ZORK" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="ZORK" src="https://lh3.googleusercontent.com/-Az9_RTS721A/VvHRTbjoxWI/AAAAAAAAGHs/VBK28vikf08/ZORK_thumb4.png?imgmax=800" width="644" height="287"></a></p>--><br />
<p>Wow! How exciting. Unlike Eliza, Zork has a <a href="http://zork.wikia.com/wiki/Command_List">very limited vocabulary</a>. Looking at the command list I thought I found a way out:</p><!--
<p><br />
<a href="https://lh3.googleusercontent.com/-ZP5vZf1jsz4/VvHRT6vYMFI/AAAAAAAAGHw/zW0P7RMF0Io/s1600-h/Hadies3.png"><img title="Hadies" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="Hadies" src="https://lh3.googleusercontent.com/-rgpb1AlkyXM/VvHRUp4iquI/AAAAAAAAGH0/ABrprE8d8fU/Hadies_thumb1.png?imgmax=800" width="644" height="282"></a></p>--><br />
<p>Anyway I was able to wonder around as a ghost. These games where great for the time, a time when 64KB was a huge machine. </p><h2>Dungeons & Dragons’ Dungeon Master</h2><p>Eliza is too stupid and classic interactive fiction commands are too primitive. I think the D&D Dungeon Master is a good character for Alixa (or Siri, or Cortana) to play. I’ve listened to <a href="http://www.earwolf.com/show/nerd-poker/">Nerd Poker</a>, it is one of many <a href="https://www.reddit.com/r/DnD/comments/23y3d6/what_are_some_of_your_favorite_dnd_podcasts/">podcasts where people play D&D</a>, they are audio only, so we, as the audience, experience the game without maps.</p><p><strong>YOU</strong>: Alexa, Dungeon Master<br />
<strong>COMPUTER</strong>: Welcome to Dungeon Master<br />
<strong>YOU</strong>: Resume Goldmine<br />
<strong>COMPUTER</strong>: You are in a timber braced earthen tunnel lit only by the torch you are holding. Up ahead you see a heavy wood door guarded by an orc.<br />
<strong>YOU</strong>: I take out my sword and approach the door.<br />
<strong>COMPUTER</strong>: The orc comes toward you displaying his battle axe.<br />
<strong>YOU</strong>: I attack the orc with my sword</p><p>The DM could support higher level commands, In Zork, if I wanted to return to the white house, I would have to the tell the game how to get there (if I can remember). In D&D, I can tell the DM where I want to return to and she would role some dice and tell me if I get back there without any excitement (if she isn’t too uptight); if there is any, she would guide me through any necessary exciting events.</p><p><strong>YOU</strong>: I would like to return to the Armory<br />
<strong>COMPUTER</strong>: On the way you meet an orc in the Great Hall.<br />
<strong>YOU</strong>: I take out my sword.</p><h2>Future Improvements</h2><p>Having Alexa’s native voice lead you through a Dungeon would be exciting in the beginning, playing with your ears and all, but after a while, it would get old. What does a future audio games should like? I don’t know, but I would look at other things audio. There is old radio and podcasts, I like the sound of the <a href="http://blacklist.wolfpop.com/">Black List Table Reads</a> and its Ear Movies could serve a template for the sound design of future audio games.</p><h2>Cars</h2><p>With audio games it will be possible to play an audio game while you run down bicycles and mow down pedestrians on your way into the ditch as you conquer dragons and orcs! There is a dark side to new technology!</p>Jack Stephenshttp://www.blogger.com/profile/04996728441112030246noreply@blogger.com0tag:blogger.com,1999:blog-18907707.post-29049967198957394942014-01-22T21:23:00.001-08:002014-01-22T21:23:48.260-08:00I am a Social Media Introvert<p>This weekend I listened to <a href="http://www.hanselminutes.com/406/discourse-and-the-art-of-discussion-with-jeff-atwood">Hanselminutes #406, Discourse and The Art of Discussion with Jeff Atwood.</a> Well, anyway, Jeff and Scott talked about what social network ninja they were. How their online families are closer than their “real” families. <p>Since I have taken up Improv, I am less shy than I was before. I’ve learned to act extroverted, I can socialize with non-geeks, I can do scary things like speak in front of people. Blah, blah, blah. <p>However, I haven’t learned how to be social ON LINE. I have < 50 Facebook friends, most of them either family or from the Spokane Improv community. I haven’t tweeted for over a year, perhaps I will tweet a link to this post. I haven’t written to this blog in half a year. (I did work as a vendor at Microsoft on a secret project, so I was scared to write about tech, but the <a href="http://www.microsoft.com/en-us/projectsiena/default.aspx">Siena beta</a> has been out for a month, so why haven’t I written about what’s in the beta?) <p>So, what is the social media equivalent to improv? Is there something that I can do to get out of my social media like improv got me out of my real life shell? Some great blogger, I can’t remember who, said that you should go forth and write your blog about whatever is on your mind, so I will go forth and blog about not blogging, and here it is.</p> Jack Stephenshttp://www.blogger.com/profile/04996728441112030246noreply@blogger.com1tag:blogger.com,1999:blog-18907707.post-75999943401484442162013-09-28T20:50:00.001-07:002013-09-28T20:50:44.653-07:00The Bing Challenge<p>I took the <a href="http://www.bingiton.com/">Bing Challenge</a> and <a href="http://www.bing.com/">Bing</a> … didn’t win. <a href="http://www.google.com/">Google</a> beat them 4 to 1. <p>Bing has a prettier results page, but it got some things wrong. I searched for “<a href="http://www.bing.com/search?q=Improv+Spokane">Improv Spokane</a>” and I got a beautiful map of major Home Improvement stores in Spokane Washington. I know the improv scene in Spokane and I would have expected to see the <a href="http://www.bluedoortheatre.com/">Blue Door Theatre</a> in the top 3 of that search. It was #1 with <a href="https://www.google.com/#q=Improv+Spokane">Google</a> (and <a href="http://search.yahoo.com/search?p=Spokane+Improv">Yahoo!</a>), but below the fold on Bing. Bing did do a better job on “<a href="http://www.bing.com/search?q=Improv+Seattle">Improv Seattle</a>”. <p>On the other 4 searches, Bing did an acceptable job, I did vote for Google for all but “<a href="http://www.bing.com/search?q=Gracie+Allen">Gracie Allen</a>”, but I wouldn’t have been disappointed with any of the 3 other results. To me it feels as if Bing is focusing on the most popular results and going down from there. With its ten years or so head start, Google has built a giant pile of data and logic. <p>Since I’m a contractor at Microsoft right now, I actually use Bing a fair amount: 90% at work and 25% at home. Even before I got my current job I used Bing 20% of the time. Bing is slowly getting better and that’s a good thing. We need more than Google in the popular search space.</p> Jack Stephenshttp://www.blogger.com/profile/04996728441112030246noreply@blogger.com0tag:blogger.com,1999:blog-18907707.post-53780278902723856192013-08-31T20:31:00.001-07:002013-08-31T20:31:44.009-07:00Humorous Speech<p>Last week I participated in a humorous speech contest at Toastmasters. I didn’t win, I was second of three. <p>I can be funny as part of a normal speech, I can get one or two laugh lines I to a 5 to 7 minute speech. Since the purpose of the speech is to be funny, I kinda freak out. <p>In a humorous speech the audience is expecting me to be funny, so I can’t use the element of surprise. In a normal speech, a failed joke doesn’t necessarily sink the speech. <a name="_GoBack"></a> <p>In a humorous speech the audience is expecting higher joke density. My usual Toastmaster speech has two laugh lines and perhaps an unintentional joke hidden within. So I need to triple my joke count without them sounding forced. <p>There is a big difference between the fellow who is funny and the comedian; any time the civilian is funny it is a bonus, any time the pro fails to be funny it is a failure. </p> Jack Stephenshttp://www.blogger.com/profile/04996728441112030246noreply@blogger.com1tag:blogger.com,1999:blog-18907707.post-70443114143851970572013-07-31T20:50:00.001-07:002013-07-31T21:09:47.130-07:00Take Improv after you move<p>At the beginning of June I moved from Spokane to Bellevue to work at Microsoft. I signed up for <a href="http://www.unexpectedproductions.org/school">improv class with Unexpected Productions</a> even before I left Spokane. This was a brilliant move! The quarter ended last night, which makes me sad. I have already signed up for the 200 level class that starts in September. <p>Since then I have gone to <a href="http://matadorseattle.com/location/redmond/">the Matador</a> (a Mexican themed bar in downtown Redmond) after a couple of my classes, to a couple of shows with some classmates, found a Toastmasters club (again with a couple of classmates who already belong). <p>Pre-improv Jack would still be hanging out alone in his apartment two months out after moving to a new city. My previous improv experience has given me some better social skills than I had before; improv class creates openness in the students. <p>If I ever move again I will look for improv classes in my new community. I will start at the beginning (even if I become a player at <a href="http://www.unexpectedproductions.org/">UP</a>) and move up with my class. My objective isn’t necessarily to become a great improviser, but to have the opportunity to play and become more skilled at dealing with people (as opposed to technology). <p>I know that both readers of this blog are probably saying “enough of this improv stuff already!” This stuff has worked for me and I am excited about the results. I don’t know that pre-improv would have gotten this job. It has given me the courage to get up in front of people in non-improv situations; I have spoken at a half dozen code camps and I am working the Toastmasters program. <p>Even if I’m a really bad improviser, it’s worth it.</p>Jack Stephenshttp://www.blogger.com/profile/04996728441112030246noreply@blogger.com0tag:blogger.com,1999:blog-18907707.post-47819651320062247682013-06-26T21:26:00.001-07:002013-06-26T21:26:15.383-07:00Back at Microsoft<p>I took another contract job at Microsoft; back for the first time in almost 13 years. I started at the beginning of June. I’m excited to work on cutting edge technology with smart people (try to be the dumbest person in the room if you can). <p>What am I working on? I can’t say.</p> Jack Stephenshttp://www.blogger.com/profile/04996728441112030246noreply@blogger.com0tag:blogger.com,1999:blog-18907707.post-37952651408392119482013-05-09T22:24:00.001-07:002013-06-26T21:28:36.670-07:00NPR Teenage Diaries Revisited on Tourette’s<p>On NPR’s All Things Considered, they did a story on <a href="http://apps.npr.org/teenage-diaries/#josh">Josh Cutler and his experience with Tourette’s syndrome</a>. It got me to think about, my own experience,so I wrote this letter to Josh through NPR’s comment page.</p><blockquote><p>Josh Cutler, <p><a name="_GoBack"></a>I don’t know what you will say about having Tourette’s at 50, however I can tell you about my experience at 49. <p>In my generation, we weren’t all that aware of Tourette’s, I was just a troubled kid in Special Education who wasn’t supposed to go to college (but I did graduate from Central Washington University in 5 years). I wasn’t diagnosed until after I turned 40. <p>Now I am a computer programmer. I’ve worked for many small companies and one really large one. I think I do solitary work because I don’t trust my behavior. I admire you for having the guts to teach; I’m sad that it didn’t go well. I wish the world would be more supportive of people like us. I relate to children, I’m scared of adult’s reaction to me. <p>I totally relate with your jujitsu class; over the last few years I’ve been taking this Improv class over and over again; I believe that keeps me sane. I walk over a mile to work everyday day. The motion of the scene work and walking all over town helps me keep my ticks in check. I think the Improv has been good to me because it allows me to play with behavior and just be childlike among other grown-ups. <p>Most of my ticks are in my face shoulder, leg neck and elsewhere in my body. At times the energy behind the twitch can become overwhelming; sometimes I will shake all over for an hour at night. I’ve twitched to the point of pain. I don’t cuss, I have trouble saying the F-word in the locker room where I’m supposed to have a foul mouth. I do have a problem with “angry” outbursts. <p>I too had my bad Tourette’s event with long term consequences. My incident happened at Microsoft in 2000. I had been “normal” for over a year and then I burst. So, based in a moderate episode I am banned from ever working there again, and as far as I can tell, it is still in force. [NOTE: it isn't still in force, see my <a href="http://brochure.jrcs3.com/2013/06/back-at-microsoft.html">next post</a>] <p>The curse of Tourette’s is that it leads to bazaar behavior. When I’ve behaved badly it is scary because it is different, not because it is necessarily dangerous. <p>Thank you, Josh, for sharing your life with us. It means a lot to me. You’ve given me the opportunity to put things into words. <p>Best of luck <p>Jack Stephens <p>Spokane</p></blockquote><p>I think there is a point where it is good to put it out there.</p>Jack Stephenshttp://www.blogger.com/profile/04996728441112030246noreply@blogger.com0tag:blogger.com,1999:blog-18907707.post-86591522731999662982013-04-14T15:20:00.001-07:002013-04-15T19:26:51.762-07:00Compare two DataSets for testing<p>There is a client that has several statements that are rendered as Crystal Reports; we bind the Crystal Reposts to ADO.NET DataSets. The DataSets are loaded from stored procedures in the usual way. Right now we are printing several of these statements and some poor clerk has to compare those values with an earlier correct one. <p>My idea is to store the DataSets of known good statements and then I can automatically compare them with a new DataSet loaded from the stored procedure. So, over the weekend I wrote these classes to do the comparisons: <h2>The Code</h2><pre class="code"><span style="color: blue">using </span>System;
<span style="color: blue">using </span>System.Collections.Generic;
<span style="color: blue">using </span>System.Data;
<span style="color: blue">using </span>System.Linq;
<span style="color: blue">namespace </span>CompareDS
{
<span style="color: gray">/// <summary>
/// </span><span style="color: green">Compare the contents of DataSets or DataTables
</span><span style="color: gray">/// </summary>
</span><span style="color: blue">public class </span><span style="color: #2b91af">CompareDataSet
</span>{
<span style="color: gray">/// <summary>
/// </span><span style="color: green">Compare the content of two DataSets
</span><span style="color: gray">/// </summary>
/// <param name="fromDS"></span><span style="color: green">The DataSet considered to be correct</span><span style="color: gray"></param>
/// <param name="toDS"></span><span style="color: green">The DataSet to be tested</span><span style="color: gray"></param>
/// <returns></span><span style="color: green">A list of Inconsistencies</span><span style="color: gray"></returns>
</span><span style="color: blue">public static </span><span style="color: #2b91af">List</span><<span style="color: #2b91af">CompareDifference</span>> DoCompareDataSet(
<span style="color: #2b91af">DataSet </span>fromDS, <span style="color: #2b91af">DataSet </span>toDS)
{
<span style="color: blue">var </span>rVal = <span style="color: blue">new </span><span style="color: #2b91af">List</span><<span style="color: #2b91af">CompareDifference</span>>();
<span style="color: green">// Make sure that the table count match
</span><span style="color: blue">if </span>(fromDS.Tables.Count != toDS.Tables.Count)
{
rVal.Add(<span style="color: blue">new </span><span style="color: #2b91af">CompareDifference
</span>{
Message = <span style="color: blue">string</span>.Format(<span style="color: #a31515">"Non maching # of tables: {0} != {1}"</span>,
fromDS.Tables.Count, toDS.Tables.Count)
});
}
<span style="color: green">// Process the smaller # of tables
</span><span style="color: blue">int </span>minColCount = <span style="color: #2b91af">Math</span>.Min(fromDS.Tables.Count, toDS.Tables.Count);
<span style="color: blue">for </span>(<span style="color: blue">int </span>i = 0; i < minColCount; ++i)
{
<span style="color: #2b91af">DataTable </span>fromTable = fromDS.Tables[i];
<span style="color: #2b91af">DataTable </span>toTable = toDS.Tables[i];
<span style="color: blue">string </span>tableName = fromTable.TableName;
rVal.AddRange(DoCompareTable(fromTable, toTable, tableName, i));
}
<span style="color: blue">return </span>rVal;
}
<span style="color: gray">/// <summary>
/// </span><span style="color: green">Compare the content of two DataTables
</span><span style="color: gray">/// </summary>
/// <param name="fromTable"></span><span style="color: green">The DataTable considered to be correct</span><span style="color: gray"></param>
/// <param name="toTable"></span><span style="color: green">The DataTable to be tested</span><span style="color: gray"></param>
/// <param name="tableName"></span><span style="color: green">The Name of the table to be compared</span><span style="color: gray"></param>
/// <param name="tableNumber"></span><span style="color: green">The zero based table number</span><span style="color: gray"></param>
/// <returns></span><span style="color: green">A list of Inconsistencies</span><span style="color: gray"></returns>
</span><span style="color: blue">public static </span><span style="color: #2b91af">List</span><<span style="color: #2b91af">CompareDifference</span>> DoCompareTable(<span style="color: #2b91af">DataTable </span>fromTable,
<span style="color: #2b91af">DataTable </span>toTable, <span style="color: blue">string </span>tableName = <span style="color: #a31515">"Table"</span>, <span style="color: blue">int </span>tableNumber = 0)
{
<span style="color: blue">var </span>rVal = <span style="color: blue">new </span><span style="color: #2b91af">List</span><<span style="color: #2b91af">CompareDifference</span>>();
<span style="color: green">// Number of Rows should match
</span><span style="color: blue">if </span>(fromTable.Rows.Count != toTable.Rows.Count)
{
rVal.Add(<span style="color: blue">new </span><span style="color: #2b91af">CompareDifference
</span>{
TableName = tableName,
TableNumber = tableNumber,
Message = <span style="color: blue">string</span>.Format(<span style="color: #a31515">"Non maching # of rows: {0} != {1}"</span>,
fromTable.Rows.Count, toTable.Rows.Count)
});
}
<span style="color: green">// Create new CompareDataTable for this table
</span><span style="color: blue">var </span>compTables = <span style="color: blue">new </span><span style="color: #2b91af">CompareDataTable</span> (fromTable, toTable,
tableName, tableNumber);
<span style="color: blue">int </span>minColCount = <span style="color: #2b91af">Math</span>.Min(fromTable.Rows.Count, toTable.Rows.Count);
<span style="color: blue">for </span>(<span style="color: blue">int </span>i = 0; i < minColCount; ++i)
{
rVal.AddRange(compTables.CompareRow(
fromTable.Rows[i], toTable.Rows[i], i));
}
<span style="color: blue">return </span>rVal;
}
}
<span style="color: gray">/// <summary>
/// </span><span style="color: green">Compares a DateTable row by row.
</span><span style="color: gray">/// </span><span style="color: green">I use a seperate non-static class so I don't have to re-aquire
</span><span style="color: gray">/// </span><span style="color: green">the table's Columns (Overkill, I know)
</span><span style="color: gray">/// </summary>
</span><span style="color: blue">public class </span><span style="color: #2b91af">CompareDataTable
</span>{
<span style="color: blue">private </span><span style="color: #2b91af">DataTable </span>_fromTable;
<span style="color: blue">private </span><span style="color: #2b91af">DataTable </span>_toTable;
<span style="color: blue">private </span><span style="color: #2b91af">DataColumnCollection </span>_fromColumns;
<span style="color: blue">private </span><span style="color: #2b91af">DataColumnCollection </span>_toColumns;
<span style="color: blue">private string </span>_tableName;
<span style="color: blue">public int </span>_tableNumber;
<span style="color: gray">/// <summary>
/// </span><span style="color: green">Constructor: I use this class to avoid re-aquiring Columns
</span><span style="color: gray">/// </span><span style="color: green">for each row.
</span><span style="color: gray">/// </summary>
/// <param name="fromTable"></span><span style="color: green">The DataTable considered to be correct</span><span style="color: gray"></param>
/// <param name="toTable"></span><span style="color: green">The DataTable to be tested</span><span style="color: gray"></param>
/// <param name="tableName"></span><span style="color: green">The Name of the table to be compared</span><span style="color: gray"></param>
/// <param name="tableNumber"></span><span style="color: green">The zero based table number</span><span style="color: gray"></param>
</span><span style="color: blue">public </span>CompareDataTable(<span style="color: #2b91af">DataTable </span>fromTable, <span style="color: #2b91af">DataTable </span>toTable,
<span style="color: blue">string </span>tableName, <span style="color: blue">int </span>tableNumber)
{
_fromTable = fromTable;
_toTable = toTable;
_tableName = tableName;
_tableNumber = tableNumber;
<span style="color: green">// Remember the Table Columns
</span>_fromColumns = _fromTable.Columns;
_toColumns = _toTable.Columns;
}
<span style="color: gray">/// <summary>
/// </span><span style="color: green">Compare the content of two DataRows
</span><span style="color: gray">/// </summary>
/// <param name="fromRow"></span><span style="color: green">The DataRow considered to be correct</span><span style="color: gray"></param>
/// <param name="toRow"></span><span style="color: green">The DataRow to be tested</span><span style="color: gray"></param>
/// <param name="rowNumber"></span><span style="color: green">The zero based number of the row</span><span style="color: gray"></param>
/// <returns></span><span style="color: green">A list of Inconsistencies</span><span style="color: gray"></returns>
</span><span style="color: blue">public </span><span style="color: #2b91af">List</span><<span style="color: #2b91af">CompareDifference</span>> CompareRow(<span style="color: #2b91af">DataRow </span>fromRow,
<span style="color: #2b91af">DataRow </span>toRow, <span style="color: blue">int </span>rowNumber)
{
<span style="color: blue">var </span>rVal = <span style="color: blue">new </span><span style="color: #2b91af">List</span><<span style="color: #2b91af">CompareDifference</span>>();
<span style="color: blue">int </span>minColCount = <span style="color: #2b91af">Math</span>.Min(fromRow.ItemArray.Count(),
toRow.ItemArray.Count());
<span style="color: blue">for </span>(<span style="color: blue">int </span>i = 0; i < minColCount; ++i)
{
<span style="color: blue">object </span>fromCell = fromRow.ItemArray[i];
<span style="color: blue">object </span>toCell = toRow.ItemArray[i];
<span style="color: blue">string </span>fromColName = _fromColumns[i].ColumnName;
<span style="color: blue">string </span>toColName = _toColumns[i].ColumnName;
<span style="color: green">// Column names must match
</span><span style="color: blue">if </span>(fromColName != toColName)
{
rVal.Add(<span style="color: blue">new </span><span style="color: #2b91af">CompareDifference
</span>{
TableName = _tableName,
TableNumber = _tableNumber,
RowNumber = rowNumber,
ColumnName = fromColName,
ColumnNumber = i,
Message = <span style="color: blue">string</span>.Format(
<span style="color: #a31515">"Non matching column names \"{0}\" != \"{1}\""</span>,
fromColName, toColName)
});
}
<span style="color: green">// Type must match
</span><span style="color: blue">if </span>(fromCell.GetType() != toCell.GetType())
{
rVal.Add(<span style="color: blue">new </span><span style="color: #2b91af">CompareDifference
</span>{
TableName = _tableName,
TableNumber = _tableNumber,
RowNumber = rowNumber,
ColumnName = fromColName,
ColumnNumber = i,
ExpectedValue = fromCell.ToString(),
ActualValue = toCell.ToString(),
Message = <span style="color: blue">string</span>.Format(
<span style="color: #a31515">"No matching types \"{0}\" != \"{1}\""</span>,
fromCell.GetType(), toCell.GetType())
});
}
<span style="color: green">// And values (string compare for simplicity)
</span><span style="color: blue">else if </span>(fromCell.ToString() != toCell.ToString())
{
rVal.Add(<span style="color: blue">new </span><span style="color: #2b91af">CompareDifference
</span>{
TableName = _tableName,
TableNumber = _tableNumber,
RowNumber = rowNumber,
ColumnName = fromColName,
ColumnNumber = i,
ExpectedValue = fromCell.ToString(),
ActualValue = toCell.ToString(),
Message = <span style="color: blue">string</span>.Format(
<span style="color: #a31515">"No matching values \"{0}\" != \"{1}\""</span>,
fromCell, toCell)
});
}
}
<span style="color: blue">return </span>rVal;
}
}
<span style="color: gray">/// <summary>
/// </span><span style="color: green">Simple class to store error messages
</span><span style="color: gray">/// </summary>
</span><span style="color: blue">public class </span><span style="color: #2b91af">CompareDifference
</span>{
<span style="color: gray">/// <summary>
/// </span><span style="color: green">The Name of the Table
</span><span style="color: gray">/// </span><span style="color: green">where the inconsistancy exists
</span><span style="color: gray">/// </summary>
</span><span style="color: blue">public int </span>TableNumber { <span style="color: blue">get</span>; <span style="color: blue">set</span>; }
<span style="color: gray">/// <summary>
/// </span><span style="color: green">The zero based number of the Table
</span><span style="color: gray">/// </span><span style="color: green">where the inconsistancy exists
</span><span style="color: gray">/// </summary>
</span><span style="color: blue">public string </span>TableName { <span style="color: blue">get</span>; <span style="color: blue">set</span>; }
<span style="color: gray">/// <summary>
/// </span><span style="color: green">The zero based number of the column
</span><span style="color: gray">/// </span><span style="color: green">where the inconsistancy exists
</span><span style="color: gray">/// </summary>
</span><span style="color: blue">public int</span>? ColumnNumber { <span style="color: blue">get</span>; <span style="color: blue">set</span>; }
<span style="color: gray">/// <summary>
/// </span><span style="color: green">The name of the column
</span><span style="color: gray">/// </span><span style="color: green">where the inconsistancy exists
</span><span style="color: gray">/// </summary>
</span><span style="color: blue">public string </span>ColumnName { <span style="color: blue">get</span>; <span style="color: blue">set</span>; }
<span style="color: gray">/// <summary>
/// </span><span style="color: green">The number of the row
</span><span style="color: gray">/// </span><span style="color: green">where the inconsistancy exists
</span><span style="color: gray">/// </summary>
</span><span style="color: blue">public int</span>? RowNumber { <span style="color: blue">get</span>; <span style="color: blue">set</span>; }
<span style="color: gray">/// <summary>
/// </span><span style="color: green">The expected value
</span><span style="color: gray">/// </summary>
</span><span style="color: blue">public string </span>ExpectedValue { <span style="color: blue">get</span>; <span style="color: blue">set</span>; }
<span style="color: gray">/// <summary>
/// </span><span style="color: green">The actual value
</span><span style="color: gray">/// </summary>
</span><span style="color: blue">public string </span>ActualValue { <span style="color: blue">get</span>; <span style="color: blue">set</span>; }
<span style="color: gray">/// <summary>
/// </span><span style="color: green">An error message
</span><span style="color: gray">/// </summary>
</span><span style="color: blue">public string </span>Message { <span style="color: blue">get</span>; <span style="color: blue">set</span>; }
}
}
</pre>Jack Stephenshttp://www.blogger.com/profile/04996728441112030246noreply@blogger.com1tag:blogger.com,1999:blog-18907707.post-2893038229865164822013-03-10T16:00:00.001-07:002013-04-22T19:22:19.610-07:00Displaying Status with Live Tiles<h6>Brain Dead Simple Windows RT App Part 4</h6><p>In Windows Phone, Microsoft introduced the idea of Tiles that could provide the use with updates. Your tile is important because it is the one thing that is always there whispering to the user “run me” day and night. There are four types of updates, Local, Scheduled, Periodic and Push. Because this is a basic level intro article, I’m going to ignore Scheduled, Periodic and Push and work on Local (in the wild, you will probably need to use one of the other three). <p>Windows 8 offers a total of 46 tile templates that are enumerated in <a href="http://msdn.microsoft.com/en-US/library/windows/apps/windows.ui.notifications.tiletemplatetype">TileTemplateType</a>. To update a tile you: <ol><li><b>Get the Template</b> with TileUpdateManager.GetTemplateContent</li>
<li><b>Create a notification</b> or a new TileNotification object with a reference to the template you retrieved on Step 1 and set any ExpirationTime</li>
<li><b>Set notification text</b> on the Template XML you retrieved on Step 1</li>
<li><b>Update Live Tile</b> Create a TileUpdater and call its Update with a the TileNotification created in Step 1</li>
</ol><h2>Back to the App</h2><p>I’m lazy, so I don’t even want to open my app to know how excited I am. I can use the apps tile to remind me. Now I only need to open the app to use the more advanced features like changing my excitement level. <p>NOTE: In this app, the settings roam but the Live Tile does not. Since this is a really simple demo, I’m OK with that, I would never ship an app like this.</p><pre class="code"><span style="color: blue">public static void </span><span style="color: black">SetLiveTile(</span><span style=" color: blue">string </span><span style=" color: black">title, </span><span style=" color: blue">string </span><span style=" color: black">body, </span><span style=" color: blue">int</span><span style=" color: black">? seconds = </span><span style=" color: blue">null</span><span style=" color: black">)
{
</span><span style=" color: green">// Get tile template.
</span><span style=" color: blue">var </span><span style=" color: black">tileTemplate = </span><span style=" color: #2b91af">TileTemplateType</span><span style=" color: black">.TileSquareBlock;
</span><span style=" color: #2b91af">XmlDocument </span><span style=" color: black">tileXml = </span><span style=" color: #2b91af">TileUpdateManager</span><span style=" color: black">.GetTemplateContent(tileTemplate);
</span><span style=" color: green">// Create notification.
</span><span style=" color: blue">var </span><span style=" color: black">notification = </span><span style=" color: blue">new </span><span style=" color: #2b91af">TileNotification</span><span style=" color: black">(tileXml);
</span><span style=" color: blue">if </span><span style=" color: black">(seconds.HasValue)
{
notification.ExpirationTime = </span><span style=" color: #2b91af">DateTime</span><span style=" color: black">.Now +
</span><span style=" color: #2b91af">TimeSpan</span><span style=" color: black">.FromSeconds(seconds.Value);
}
</span><span style=" color: green">// Set notification text.
</span><span style=" color: #2b91af">XmlNodeList </span><span style=" color: black">nodes = tileXml.GetElementsByTagName(</span><span style=" color: #a31515">"text"</span><span style=" color: black">);
nodes[0].InnerText = title;
nodes[1].InnerText = body;
</span><span style=" color: green">// Update Live Tile.
</span><span style=" color: blue">var </span><span style=" color: black">upd = </span><span style=" color: #2b91af">TileUpdateManager</span><span style=" color: black">.CreateTileUpdaterForApplication();
upd.Update(notification);
}
</span></pre><br />
<p>And I add the following line to the end of saveSettings()</p><pre class="code"><span style=" color: black">SetLiveTile(</span><span style=" color: blue">string</span><span style=" color: black">.Format(</span><span style=" color: #a31515">"{0}!"</span><span style=" color: black">, _exlamationPointCount), HelloMessage, 3600);</span></pre><br />
<h2>Complete Code</h2><br />
<pre class="code"><span style="color: blue">using </span><span style="color: black">System;
</span><span style="color: blue">using </span><span style="color: black">System.ComponentModel;
</span><span style="color: blue">using </span><span style="color: black">Windows.Data.Xml.Dom;
</span><span style="color: blue">using </span><span style="color: black">Windows.Storage;
</span><span style="color: blue">using </span><span style="color: black">Windows.UI.Notifications;
</span><span style="color: blue">using </span><span style="color: black">Windows.UI.Xaml;
</span><span style="color: blue">using </span><span style="color: black">Windows.UI.Xaml.Controls;
</span><span style="color: blue">using </span><span style="color: black">Windows.UI.Xaml.Navigation;
</span><span style="color: blue">namespace </span><span style="color: black">HelloWindows
{
</span><span style="color: gray">/// <summary>
/// </span><span style="color: green">An empty page that can be used on its own or navigated to within a Frame.
</span><span style="color: gray">/// </summary>
</span><span style="color: blue">public sealed partial class </span><span style="color: #2b91af">MainPage </span><span style="color: black">: </span><span style="color: #2b91af">Page</span><span style="color: black">, </span><span style="color: #2b91af">INotifyPropertyChanged
</span><span style="color: black">{
</span><span style="color: blue">public event </span><span style="color: #2b91af">PropertyChangedEventHandler </span><span style="color: black">PropertyChanged;
</span><span style="color: blue">private const string </span><span style="color: black">BASE_MESSAGE = </span><span style="color: #a31515">"Hello Windows"</span><span style="color: black">;
</span><span style="color: blue">public </span><span style="color: black">MainPage()
{
</span><span style="color: blue">this</span><span style="color: black">.InitializeComponent();
</span><span style="color: green">// Load Settings here:
</span><span style="color: black">loadSettings();
HelloMessage = setMessge(_exlamationPointCount);
</span><span style="color: blue">this</span><span style="color: black">.DataContext = </span><span style="color: blue">this</span><span style="color: black">;
}
</span><span style="color: blue">private void </span><span style="color: black">RaisePropertyChanged(</span><span style="color: blue">string </span><span style="color: black">propertyName)
{
</span><span style="color: blue">if </span><span style="color: black">(PropertyChanged != </span><span style="color: blue">null</span><span style="color: black">)
{
PropertyChanged(</span><span style="color: blue">this</span><span style="color: black">, </span><span style="color: blue">new </span><span style="color: #2b91af">PropertyChangedEventArgs</span><span style="color: black">(propertyName));
}
}
</span><span style="color: blue">public string </span><span style="color: black">HelloMessage { </span><span style="color: blue">get</span><span style="color: black">; </span><span style="color: blue">set</span><span style="color: black">; }
</span><span style="color: gray">/// <summary>
/// </span><span style="color: green">Invoked when this page is about to be displayed in a Frame.
</span><span style="color: gray">/// </summary>
/// <param name="e"></span><span style="color: green">Event data that describes how this page was reached.
</span><span style=" color: gray">/// </span><span style=" color: green">The Parameter property is typically used to configure the page.</span><span style=" color: gray"></param>
</span><span style=" color: blue">protected override void </span><span style=" color: black">OnNavigatedTo(</span><span style=" color: #2b91af">NavigationEventArgs </span><span style=" color: black">e)
{
}
</span><span style=" color: blue">private int </span><span style=" color: black">_exlamationPointCount = 0;
</span><span style=" color: blue">private string </span><span style=" color: black">setMessge(</span><span style=" color: blue">int </span><span style=" color: black">exlamationPointCount)
{
</span><span style=" color: blue">if </span><span style=" color: black">(exlamationPointCount > 0)
{
</span><span style=" color: blue">return </span><span style=" color: black">_baseMessage + </span><span style=" color: blue">new string</span><span style=" color: black">(</span><span style=" color: #a31515">'!'</span><span style=" color: black">, exlamationPointCount);
}
</span><span style=" color: blue">else
</span><span style=" color: black">{
</span><span style=" color: blue">return </span><span style=" color: black">_baseMessage;
}
}
</span><span style=" color: blue">private void </span><span style=" color: black">addPoint_Click(</span><span style=" color: blue">object </span><span style=" color: black">sender, </span><span style=" color: #2b91af">RoutedEventArgs </span><span style=" color: black">e)
{
HelloMessage = setMessge(++_exlamationPointCount);
RaisePropertyChanged(</span><span style=" color: #a31515">"HelloMessage"</span><span style=" color: black">);
RaisePropertyChanged(</span><span style=" color: #a31515">"IsRemovePossible"</span><span style=" color: black">);
</span><span style=" color: green">// This is overkill
</span><span style=" color: black">saveSettings();
}
</span><span style=" color: blue">private void </span><span style=" color: black">removePoint_Click(</span><span style=" color: blue">object </span><span style=" color: black">sender, </span><span style=" color: #2b91af">RoutedEventArgs </span><span style=" color: black">e)
{
</span><span style=" color: blue">if </span><span style=" color: black">(_exlamationPointCount >= 1)
{
HelloMessage = setMessge(--_exlamationPointCount);
RaisePropertyChanged(</span><span style=" color: #a31515">"HelloMessage"</span><span style=" color: black">);
RaisePropertyChanged(</span><span style=" color: #a31515">"IsRemovePossible"</span><span style=" color: black">);
</span><span style=" color: green">// This is overkill
</span><span style=" color: black">saveSettings();
}
}
</span><span style=" color: green">// Make it possible to disable Remove Point button when there
// is nothing left to remove
</span><span style=" color: blue">public bool </span><span style=" color: black">IsRemovePossible
{
</span><span style=" color: blue">get </span><span style=" color: black">{ </span><span style=" color: blue">return </span><span style=" color: black">_exlamationPointCount > 0; }
}
</span><span style=" color: green">// I replace most refrence to BASE_MESSAGE to _baseMessage
</span><span style=" color: blue">private string </span><span style=" color: black">_baseMessage = BASE_MESSAGE;
</span><span style=" color: green">// Ensure that RoamingSettings has been loaded
</span><span style=" color: blue">private static </span><span style=" color: #2b91af">ApplicationDataContainer </span><span style=" color: black">_roamingSettings;
</span><span style=" color: blue">public static </span><span style=" color: #2b91af">ApplicationDataContainer </span><span style=" color: black">RoamingSettings
{
</span><span style=" color: blue">get
</span><span style=" color: black">{
</span><span style=" color: blue">if </span><span style=" color: black">(_roamingSettings == </span><span style=" color: blue">null</span><span style=" color: black">)
{
</span><span style=" color: green">// If I don't want roaming, use
// ApplicationData.Current.LocalSettings;
</span><span style=" color: black">_roamingSettings = </span><span style=" color: #2b91af">ApplicationData</span><span style=" color: black">.Current.RoamingSettings;
}
</span><span style=" color: blue">return </span><span style=" color: black">_roamingSettings;
}
}
</span><span style=" color: green">// I know that saving every time the value is changed,
// in real life I'd save the data in App.OnSuspending()
</span><span style=" color: blue">private void </span><span style=" color: black">saveSettings()
{
</span><span style=" color: green">// I combine both values into a ApplicationDataCompositeValue
// and save them both to "HighPriority"
</span><span style=" color: blue">var </span><span style=" color: black">composite = </span><span style=" color: blue">new </span><span style=" color: #2b91af">ApplicationDataCompositeValue</span><span style=" color: black">();
composite[</span><span style=" color: #a31515">"baseMessage"</span><span style=" color: black">] = _baseMessage;
composite[</span><span style=" color: #a31515">"exlamationPointCount"</span><span style=" color: black">] = _exlamationPointCount;
RoamingSettings.Values[</span><span style=" color: #a31515">"HighPriority"</span><span style=" color: black">] = composite;
SetLiveTile(</span><span style=" color: blue">string</span><span style=" color: black">.Format(</span><span style=" color: #a31515">"{0}!" </span><span style=" color: black">, _exlamationPointCount),
HelloMessage, 3600);
}
</span><span style=" color: green">// To be called from the constructor
</span><span style=" color: blue">private void </span><span style=" color: black">loadSettings()
{
</span><span style=" color: green">// I get "HighPriority" cast it to ApplicationDataCompositeValue
// and extract the values.
</span><span style=" color: blue">var </span><span style=" color: black">composite = (</span><span style=" color: #2b91af">ApplicationDataCompositeValue</span><span style=" color: black">)
RoamingSettings.Values[</span><span style=" color: #a31515">"HighPriority"</span><span style=" color: black">];
</span><span style=" color: blue">if </span><span style=" color: black">(composite != </span><span style=" color: blue">null</span><span style=" color: black">) </span><span style=" color: green">// the first time it will be null
</span><span style=" color: black">{
_baseMessage = (</span><span style=" color: blue">string</span><span style=" color: black">)composite[</span><span style=" color: #a31515">"baseMessage"</span><span style=" color: black">];
_exlamationPointCount = (</span><span style=" color: blue">int</span><span style=" color: black">)composite[</span><span style=" color: #a31515">"exlamationPointCount"</span><span style=" color: black">];
}
}
</span><span style=" color: blue">public static void </span><span style=" color: black">SetLiveTile(</span><span style=" color: blue">string </span><span style=" color: black">title, </span><span style=" color: blue">string </span><span style=" color: black">body, </span><span style=" color: blue">int</span><span style=" color: black">? seconds = </span><span style=" color: blue">null</span><span style="color: black">)
{
</span><span style=" color: green">// Get tile template.
</span><span style=" color: blue">var </span><span style=" color: black">tileTemplate = </span><span style=" color: #2b91af">TileTemplateType</span><span style=" color: black">.TileSquareBlock;
</span><span style=" color: #2b91af">XmlDocument </span><span style=" color: black">tileXml = </span><span style=" color: #2b91af">TileUpdateManager</span><span style=" color: black">.GetTemplateContent(tileTemplate);
</span><span style=" color: green">// Create notification.
</span><span style=" color: blue">var </span><span style=" color: black">notification = </span><span style=" color: blue">new </span><span style=" color: #2b91af">TileNotification</span><span style=" color: black">(tileXml);
</span><span style=" color: blue">if </span><span style=" color: black">(seconds.HasValue)
{
notification.ExpirationTime = </span><span style=" color: #2b91af">DateTime</span><span style=" color: black">.Now +
</span><span style=" color: #2b91af">TimeSpan</span><span style=" color: black">.FromSeconds(seconds.Value);
}
</span><span style=" color: green">// Set notification text.
</span><span style=" color: #2b91af">XmlNodeList </span><span style=" color: black">nodes = tileXml.GetElementsByTagName(</span><span style=" color: #a31515">"text"</span><span style=" color: black">);
nodes[0].InnerText = title;
nodes[1].InnerText = body;
</span><span style=" color: green">// Update Live Tile.
</span><span style=" color: blue">var </span><span style=" color: black">upd = </span><span style=" color: #2b91af">TileUpdateManager</span><span style=" color: black">.CreateTileUpdaterForApplication();
upd.Update(notification);
}
}
}</span></pre><h3>My Brain Dead Simple Windows RT App: The Series</h3><ul><li><a href="/2013/03/brain-dead-simple-windows-rt-app.html">Part 1: Brain Dead Simple Windows RT App</a></li>
<li><a href="/2013/03/using-app-bars-to-hide-chrome.html">Part 2: Using App Bars to Hide Chrome</a></li>
<li><a href="/2013/03/roaming-data-with-applicationdata.html">Part 3: Roaming data with ApplicationData</a></li>
<li><a href="/2013/03/live-tile-title.html">Part 4: Displaying Status with Live Tiles</a></li>
</ul>Jack Stephenshttp://www.blogger.com/profile/04996728441112030246noreply@blogger.com0tag:blogger.com,1999:blog-18907707.post-64793786707895091032013-03-07T05:50:00.001-08:002013-04-22T19:23:40.884-07:00Roaming data with ApplicationData<h5>Brain Dead Simple Windows RT App Part 3</h5><p>In Window 8, you should be able to move between your devices and your apps should follow you. In the Microsoft documentation, there is a lot of talk about some office worker who likes to start some task on her office computer (possibly when her boss’s back is turned) and finish them on the bus with her tablet. The feature that they are pushing with these examples is Roaming. <p>So by the time that Emily (we can call her that) turns off her computer at work, the app needs to save some state data to the cloud, when she opens the same app on her tablet, it goes out and gets the that data from the cloud and uses it to remember where she left off. <p>All the stuff you need to do this is in <font face="Courier New">Windows.Storage.ApplicationData</font>. You have a choice of where you want to save it (<font face="Courier New">Roaming</font> or <font face="Courier New">Local</font>) and how (<font face="Courier New">ApplicationDataContainer</font> or <font face="Courier New">StorageFolder</font>). <p><strong>Roaming</strong> data is first stored locally and then pushed to Microsoft servers on the cloud; there is a limit to how much data can be roamed, you can use <font face="Courier New">RoamingStorageQuota</font> to find out how much (note that if you exceed the quota, it will just fail without any errors at all). <p><strong>Local</strong> stores the data locally so no roaming occurs but you have more space to work with and can be used for things that aren’t appropriate to roam. <p><font face="Courier New">ApplicationDataContainer</font> (used for <font face="Courier New">RoamingSettings</font> & <font face="Courier New">LocalSettings</font>) contains a property called Values which is a dictionary that contains the settings. Each value in the dictionary is handled separately, so one value may be roamed before the other. You can use <font face="Courier New">ApplicationDataCompositeValue</font> to stick values together and they will be treated as a single unit. Oh, yea, there is a special value in the dictionary called <font face="Courier New">“HighPriority”</font> that will be roamed before everything else. <p><font face="Courier New">StorageFolder</font> (used for <font face="Courier New">RoamingFolder</font>, <font face="Courier New">LocalFolder</font> & <font face="Courier New">TemporaryFolder</font>) gives you a handle to a folder in the file system that you can use to read and write files. Files in <font face="Courier New">RoamingFolder</font> will be synced with the magic Microsoft server (if the total size doesn’t exceed <font face="Courier New">RoamingStorageQuota</font>. Files in <font face="Courier New">LocalFolder</font> are not synced but will stay around for a while; the files in <font face="Courier New">TemporaryFolder</font> don’t.</p><h2>Back to the App</h2><p>When I run my Hello app, I can press the Exclamation Point Button 42 times but when I close it down and open it up again, if forgets. So, when I open the app again, there are NO exclamation points and that dampens my enthusiasm; I want to keep my excitement so the exclamation points need to stay. We can solve this problem with Roaming! <p> </p><pre class="code"><span style=" color: green">// I replace most refrence to BASE_MESSAGE to _baseMessage
</span><span style=" color: blue">private string </span><span style=" color: black">_baseMessage = BASE_MESSAGE;
</span><span style=" color: green">// Ensure that RoamingSettings has been loaded
</span><span style=" color: blue">private static </span><span style=" color: #2b91af">ApplicationDataContainer </span><span style=" color: black">_roamingSettings;
</span><span style=" color: blue">public static </span><span style=" color: #2b91af">ApplicationDataContainer </span><span style=" color: black">RoamingSettings
{
</span><span style=" color: blue">get
</span><span style=" color: black">{
</span><span style=" color: blue">if </span><span style=" color: black">(_roamingSettings == </span><span style=" color: blue">null</span><span style=" color: black">)
{
</span><span style=" color: green">//If I don't want roaming, use ApplicationData.Current.LocalSettings;
</span><span style=" color: black">_roamingSettings = </span><span style=" color: #2b91af">ApplicationData</span><span style=" color: black">.Current.RoamingSettings;
}
</span><span style=" color: blue">return </span><span style=" color: black">_roamingSettings;
}
}
</span><span style=" color: green">// I know that saving every time the value is changed,
// in real life I'd save the data in App.OnSuspending()
</span><span style=" color: blue">private void </span><span style=" color: black">saveSettings()
{
</span><span style=" color: green">// I combine both values into a ApplicationDataCompositeValue
// and save them both to "HighPriority"
</span><span style=" color: blue">var </span><span style=" color: black">composite = </span><span style=" color: blue">new </span><span style=" color: #2b91af">ApplicationDataCompositeValue</span><span style=" color: black">();
composite[</span><span style=" color: #a31515">"baseMessage"</span><span style=" color: black">] = _baseMessage;
composite[</span><span style=" color: #a31515">"exlamationPointCount"</span><span style=" color: black">] = _exlamationPointCount;
RoamingSettings.Values[</span><span style=" color: #a31515">"HighPriority"</span><span style=" color: black">] = composite;
}
</span><span style=" color: green">// To be called from the constructor
</span><span style=" color: blue">private void </span><span style=" color: black">loadSettings()
{
</span><span style=" color: green">// I get "HighPriority" cast it to ApplicationDataCompositeValue
// and extract the values.
</span><span style=" color: blue">var </span><span style=" color: black">composite = (</span><span style=" color: #2b91af">ApplicationDataCompositeValue</span><span style=" color: black">)
RoamingSettings.Values[</span><span style=" color: #a31515">"HighPriority"</span><span style=" color: black">];
</span><span style=" color: blue">if </span><span style=" color: black">(composite != </span><span style=" color: blue">null</span><span style=" color: black">) </span><span style=" color: green">// the first time it will be null
</span><span style=" color: black">{
_baseMessage = (</span><span style=" color: blue">string</span><span style=" color: black">)composite[</span><span style=" color: #a31515">"baseMessage"</span><span style=" color: black">];
_exlamationPointCount = (</span><span style=" color: blue">int</span><span style=" color: black">)composite[</span><span style=" color: #a31515">"exlamationPointCount"</span><span style=" color: black">];
}
}
</span></pre><br />
<p>And I change the constructor to look like:</p><pre class="code"><span style=" color: blue">public </span><span style=" color: black">MainPage()
{
</span><span style=" color: blue">this</span><span style=" color: black">.InitializeComponent();
</span><span style=" color: green">// Load Settings here:
</span><span style=" color: black">loadSettings();
HelloMessage = setMessge(_exlamationPointCount);
</span><span style=" color: blue">this</span><span style=" color: black">.DataContext = </span><span style="background: white; color: blue">this</span><span style=" color: black">;
}
</span></pre><br />
<p>And the <font face="Courier New">setMessage() </font>to the field instead of the constant:</p><pre class="code"><span style=" color: blue">private string </span><span style=" color: black">setMessge(</span><span style=" color: blue">int </span><span style=" color: black">exlamationPointCount)
{
</span><span style=" color: blue">if </span><span style=" color: black">(exlamationPointCount > 0)
{
</span><span style=" color: blue">return </span><span style=" color: black">_baseMessage + </span><span style=" color: blue">new string</span><span style=" color: black">(</span><span style=" color: #a31515">'!'</span><span style=" color: black">, exlamationPointCount);
}
</span><span style=" color: blue">else
</span><span style=" color: black">{
</span><span style=" color: blue">return </span><span style=" color: black">_baseMessage;
}
}
</span></pre><br />
<p>Since I haven’t changed the markup, I won’t repost that part (see my <a href="http://brochure.jrcs3.com/2013/03/using-app-bars-to-hide-chrome.html">previous post</a>), but I will repost the code behind (MainPage.xaml.cs):</p><pre class="code"><span style=" color: blue">using </span><span style=" color: black">System.ComponentModel;
</span><span style=" color: blue">using </span><span style=" color: black">Windows.Storage;
</span><span style=" color: blue">using </span><span style=" color: black">Windows.UI.Xaml;
</span><span style=" color: blue">using </span><span style=" color: black">Windows.UI.Xaml.Controls;
</span><span style=" color: blue">using </span><span style=" color: black">Windows.UI.Xaml.Navigation;
</span><span style=" color: blue">namespace </span><span style=" color: black">HelloWindows
{
</span><span style=" color: gray">/// <summary>
/// </span><span style=" color: green">An empty page that can be used on its own or navigated to within a Frame.
</span><span style=" color: gray">/// </summary>
</span><span style=" color: blue">public sealed partial class </span><span style=" color: #2b91af">MainPage </span><span style=" color: black">: </span><span style=" color: #2b91af">Page</span><span style=" color: black">, </span><span style=" color: #2b91af">INotifyPropertyChanged
</span><span style=" color: black">{
</span><span style=" color: blue">public event </span><span style=" color: #2b91af">PropertyChangedEventHandler </span><span style=" color: black">PropertyChanged;
</span><span style=" color: blue">private const string </span><span style=" color: black">BASE_MESSAGE = </span><span style=" color: #a31515">"Hello Windows"</span><span style=" color: black">;
</span><span style=" color: blue">public </span><span style=" color: black">MainPage()
{
</span><span style=" color: blue">this</span><span style=" color: black">.InitializeComponent();
</span><span style=" color: green">// Load Settings here:
</span><span style=" color: black">loadSettings();
HelloMessage = setMessge(_exlamationPointCount);
</span><span style=" color: blue">this</span><span style=" color: black">.DataContext = </span><span style=" color: blue">this</span><span style=" color: black">;
}
</span><span style=" color: blue">private void </span><span style=" color: black">RaisePropertyChanged(</span><span style=" color: blue">string </span><span style=" color: black">propertyName)
{
</span><span style=" color: blue">if </span><span style=" color: black">(PropertyChanged != </span><span style=" color: blue">null</span><span style=" color: black">)
{
PropertyChanged(</span><span style=" color: blue">this</span><span style=" color: black">, </span><span style=" color: blue">new </span><span style=" color: #2b91af">PropertyChangedEventArgs</span><span style=" color: black">(propertyName));
}
}
</span><span style=" color: blue">public string </span><span style=" color: black">HelloMessage { </span><span style=" color: blue">get</span><span style=" color: black">; </span><span style=" color: blue">set</span><span style=" color: black">; }
</span><span style=" color: gray">/// <summary>
/// </span><span style=" color: green">Invoked when this page is about to be displayed in a Frame.
</span><span style=" color: gray">/// </summary>
/// <param name="e"></span><span style=" color: green">Event data that describes how this page was reached.
</span><span style=" color: gray">/// </span><span style=" color: green">The Parameter property is typically used to configure the page.</span><span style=" color: gray"></param>
</span><span style=" color: blue">protected override void </span><span style=" color: black">OnNavigatedTo(</span><span style=" color: #2b91af">NavigationEventArgs </span><span style=" color: black">e)
{
}
</span><span style=" color: blue">private int </span><span style=" color: black">_exlamationPointCount = 0;
</span><span style=" color: blue">private string </span><span style=" color: black">setMessge(</span><span style=" color: blue">int </span><span style=" color: black">exlamationPointCount)
{
</span><span style=" color: blue">if </span><span style=" color: black">(exlamationPointCount > 0)
{
</span><span style=" color: blue">return </span><span style=" color: black">_baseMessage + </span><span style=" color: blue">new string</span><span style=" color: black">(</span><span style=" color: #a31515">'!'</span><span style=" color: black">, exlamationPointCount);
}
</span><span style=" color: blue">else
</span><span style=" color: black">{
</span><span style=" color: blue">return </span><span style=" color: black">_baseMessage;
}
}
</span><span style=" color: blue">private void </span><span style=" color: black">addPoint_Click(</span><span style=" color: blue">object </span><span style=" color: black">sender, </span><span style=" color: #2b91af">RoutedEventArgs </span><span style=" color: black">e)
{
HelloMessage = setMessge(++_exlamationPointCount);
RaisePropertyChanged(</span><span style=" color: #a31515">"HelloMessage"</span><span style=" color: black">);
RaisePropertyChanged(</span><span style=" color: #a31515">"IsRemovePossible"</span><span style=" color: black">);
</span><span style=" color: green">// This is overkill
</span><span style=" color: black">saveSettings();
}
</span><span style=" color: blue">private void </span><span style=" color: black">removePoint_Click(</span><span style=" color: blue">object </span><span style=" color: black">sender, </span><span style=" color: #2b91af">RoutedEventArgs </span><span style=" color: black">e)
{
</span><span style=" color: blue">if </span><span style=" color: black">(_exlamationPointCount >= 1)
{
HelloMessage = setMessge(--_exlamationPointCount);
RaisePropertyChanged(</span><span style=" color: #a31515">"HelloMessage"</span><span style=" color: black">);
RaisePropertyChanged(</span><span style=" color: #a31515">"IsRemovePossible"</span><span style=" color: black">);
</span><span style=" color: green">// This is overkill
</span><span style=" color: black">saveSettings();
}
}
</span><span style=" color: green">// Make it possible to disable Remove Point button when there
// is nothing left to remove
</span><span style=" color: blue">public bool </span><span style=" color: black">IsRemovePossible
{
</span><span style=" color: blue">get </span><span style=" color: black">{ </span><span style=" color: blue">return </span><span style=" color: black">_exlamationPointCount > 0; }
}
</span><span style=" color: green">// I replace most refrence to BASE_MESSAGE to _baseMessage
</span><span style=" color: blue">private string </span><span style=" color: black">_baseMessage = BASE_MESSAGE;
</span><span style=" color: green">// Ensure that RoamingSettings has been loaded
</span><span style=" color: blue">private static </span><span style=" color: #2b91af">ApplicationDataContainer </span><span style=" color: black">_roamingSettings;
</span><span style=" color: blue">public static </span><span style=" color: #2b91af">ApplicationDataContainer </span><span style=" color: black">RoamingSettings
{
</span><span style=" color: blue">get
</span><span style=" color: black">{
</span><span style=" color: blue">if </span><span style=" color: black">(_roamingSettings == </span><span style=" color: blue">null</span><span style=" color: black">)
{
</span><span style=" color: green">// If I don't want roaming, use
// ApplicationData.Current.LocalSettings;
</span><span style=" color: black">_roamingSettings = </span><span style=" color: #2b91af">ApplicationData</span><span style=" color: black">.Current.RoamingSettings;
}
</span><span style=" color: blue">return </span><span style=" color: black">_roamingSettings;
}
}
</span><span style=" color: green">// I know that saving every time the value is changed,
// in real life I'd save the data in App.OnSuspending()
</span><span style=" color: blue">private void </span><span style=" color: black">saveSettings()
{
</span><span style=" color: green">// I combine both values into a ApplicationDataCompositeValue
// and save them both to "HighPriority"
</span><span style=" color: blue">var </span><span style=" color: black">composite = </span><span style=" color: blue">new </span> <span style=" color: #2b91af">ApplicationDataCompositeValue</span><span style=" color: black">();
composite[</span><span style=" color: #a31515">"baseMessage"</span><span style=" color: black">] = _baseMessage;
composite[</span><span style=" color: #a31515">"exlamationPointCount"</span><span style=" color: black">] = _exlamationPointCount;
RoamingSettings.Values[</span><span style=" color: #a31515">"HighPriority"</span><span style=" color: black">] = composite;
}
</span><span style=" color: green">// To be called from the constructor
</span><span style=" color: blue">private void </span><span style=" color: black">loadSettings()
{
</span><span style=" color: green">// I get "HighPriority" cast it to ApplicationDataCompositeValue
// and extract the values.
</span><span style=" color: blue">var </span><span style=" color: black">composite = (</span><span style=" color: #2b91af">ApplicationDataCompositeValue</span><span style=" color: black">)
RoamingSettings.Values[</span><span style=" color: #a31515">"HighPriority"</span><span style=" color: black">];
</span><span style=" color: blue">if </span><span style=" color: black">(composite != </span><span style=" color: blue">null</span> <span style=" color: black">) </span><span style=" color: green">// the first time it will be null
</span><span style=" color: black">{
_baseMessage = (</span><span style=" color: blue">string</span><span style=" color: black">)composite[</span><span style=" color: #a31515">"baseMessage"</span><span style=" color: black">];
_exlamationPointCount = (</span><span style=" color: blue">int</span><span style=" color: black">)composite[</span><span style=" color: #a31515">"exlamationPointCount"</span><span style=" color: black">];
}
}
}
}</span></pre><h3>My Brain Dead Simple Windows RT App: The Series</h3><ul><li><a href="/2013/03/brain-dead-simple-windows-rt-app.html">Part 1: Brain Dead Simple Windows RT App</a></li>
<li><a href="/2013/03/using-app-bars-to-hide-chrome.html">Part 2: Using App Bars to Hide Chrome</a></li>
<li><a href="/2013/03/roaming-data-with-applicationdata.html">Part 3: Roaming data with ApplicationData</a></li>
<li><a href="/2013/03/live-tile-title.html">Part 4: Displaying Status with Live Tiles</a></li>
</ul>Jack Stephenshttp://www.blogger.com/profile/04996728441112030246noreply@blogger.com0tag:blogger.com,1999:blog-18907707.post-28852588192197983842013-03-04T06:00:00.000-08:002013-04-22T19:26:14.080-07:00Using App Bars to Hide Chrome<h5>Brain Dead Simple Windows RT App Part 2</h5><p>In Microsoft’s UX Guidelines for Windows Store apps, they talk about reducing “chrome” and hiding it in well known locations (to anyone who uses Windows 8). A couple of those well known locations are the top and bottom App Bars. Microsoft has very specific <a href="http://msdn.microsoft.com/en-us/library/windows/apps/hh761499.aspx#appbar">recommendations on where commands belong on these things</a>. I will violate those standards and put one button on the top and one on the bottom.</p><p>App Bars are hidden UI elements that can be used to hide commands that are too chromey for the main canvas. They appear when you: <ul><li>swipe from the top or bottom of the screen</li>
<li>right click your mouse</li>
<li>press WindowKey + Z</li>
</ul><h2>The Changes</h2><p>The app fails to express my level of enthusiasm, which can very as time goes on. So, I propose that I add the ability to add and remove exclamation points to the greeting as my mood changes. <p>I will put a button to add an exclamation point to the Top App Bar (in clear violation of the UX guidelines) and a button to remove exclamation point to the Bottom App Bar. </p><pre class="code"><span style=" color: blue"><</span><span style=" color: #a31515">Page.TopAppBar</span><span style=" color: blue">>
<</span><span style=" color: #a31515">AppBar</span><span style=" color: blue">>
<</span><span style=" color: #a31515">StackPanel </span><span style=" color: red">HorizontalAlignment</span><span style=" color: blue">="Right" </span><span style=" color: red">Orientation</span><span style=" color: blue">="Horizontal">
<</span><span style=" color: #a31515">Button </span><span style=" color: red">Style</span><span style=" color: blue">="{</span><span style=" color: #a31515">StaticResource </span><span style=" color: red">AddAppBarButtonStyle</span><span style=" color: blue">}"
</span><span style=" color: red">Click</span><span style=" color: blue">="addPoint_Click" />
</</span><span style=" color: #a31515">StackPanel</span><span style=" color: blue">>
</</span><span style=" color: #a31515">AppBar</span><span style=" color: blue">>
</</span><span style=" color: #a31515">Page.TopAppBar</span><span style=" color: blue">>
<</span><span style=" color: #a31515">Page.BottomAppBar</span><span style=" color: blue">>
<</span><span style=" color: #a31515">AppBar</span><span style=" color: blue">>
<</span><span style=" color: #a31515">StackPanel </span><span style=" color: red">HorizontalAlignment</span><span style=" color: blue">="Right" </span><span style=" color: red">Orientation</span><span style=" color: blue">="Horizontal">
<</span><span style=" color: #a31515">Button </span><span style=" color: red">Style</span><span style=" color: blue">="{</span><span style=" color: #a31515">StaticResource </span><span style=" color: red">RemoveAppBarButtonStyle</span><span style=" color: blue">}"
</span><span style=" color: red">Click</span><span style=" color: blue">="removePoint_Click"
</span><span style=" color: red">IsEnabled</span><span style=" color: blue">="{</span><span style=" color: #a31515">Binding </span><span style=" color: red">Path</span><span style=" color: blue">=IsRemovePossible}" />
</</span><span style=" color: #a31515">StackPanel</span><span style=" color: blue">>
</</span><span style=" color: #a31515">AppBar</span><span style=" color: blue">>
</</span><span style=" color: #a31515">Page.BottomAppBar</span><span style=" color: blue">></span>
</pre><br />
<p>The styles are available in \Common\StandardStyles.xaml, you need to find them and uncomment them or copy them to MainPage.xaml’s Page.Resources (like I did):</p><pre class="code"><span style=" color: green"><!-- These styles along with many other can be found in \Common\StandardStyles.xaml -->
</span><span style=" color: blue"><</span><span style=" color: #a31515">Style </span><span style=" color: red">x</span><span style=" color: blue">:</span><span style=" color: red">Key</span><span style=" color: blue">="AddAppBarButtonStyle" </span><span style=" color: red">TargetType</span><span style=" color: blue">="ButtonBase"
</span><span style=" color: red">BasedOn</span><span style=" color: blue">="{</span><span style=" color: #a31515">StaticResource </span><span style=" color: red">AppBarButtonStyle</span><span style=" color: blue">}">
<</span><span style=" color: #a31515">Setter </span><span style=" color: red">Property</span><span style=" color: blue">="AutomationProperties.AutomationId"
</span><span style=" color: red">Value</span><span style=" color: blue">="AddAppBarButton" />
<</span><span style=" color: #a31515">Setter </span><span style=" color: red">Property</span><span style=" color: blue">="AutomationProperties.Name" </span><span style=" color: red">Value</span><span style=" color: blue">="Add" />
<</span><span style=" color: #a31515">Setter </span><span style=" color: red">Property</span><span style=" color: blue">="Content" </span><span style=" color: red">Value</span><span style=" color: blue">="&#xE109;" />
</</span><span style=" color: #a31515">Style</span><span style=" color: blue">>
<</span><span style=" color: #a31515">Style </span><span style=" color: red">x</span><span style=" color: blue">:</span><span style=" color: red">Key</span><span style=" color: blue">="RemoveAppBarButtonStyle" </span><span style=" color: red">TargetType</span><span style=" color: blue">="ButtonBase"
</span><span style=" color: red">BasedOn</span><span style=" color: blue">="{</span><span style=" color: #a31515">StaticResource </span><span style=" color: red">AppBarButtonStyle</span><span style=" color: blue">}">
<</span><span style=" color: #a31515">Setter </span><span style=" color: red">Property</span><span style=" color: blue">="AutomationProperties.AutomationId"
</span><span style=" color: red">Value</span><span style=" color: blue">="RemoveAppBarButton" />
<</span><span style=" color: #a31515">Setter </span><span style=" color: red">Property</span><span style=" color: blue">="AutomationProperties.Name" </span><span style=" color: red">Value</span><span style=" color: blue">="Remove" />
<</span><span style=" color: #a31515">Setter </span><span style=" color: red">Property</span><span style=" color: blue">="Content" </span><span style=" color: red">Value</span><span style=" color: blue">="&#xE108;" />
</</span><span style=" color: #a31515">Style</span><span style=" color: blue">>
</span></pre><br />
<p>So here’s the additional code (I put it at the bottom of my MainPage.xaml.cs), what is does really isn’t important for this post, it just needs to do something to prove that everything is working.</p><pre class="code"><span style=" color: blue">private int </span><span style=" color: black">_exlamationPointCount = 0;
</span><span style=" color: blue">private static string </span><span style=" color: black">setMessge(</span><span style=" color: blue">int </span><span style=" color: black">exlamationPointCount)
{
</span><span style=" color: blue">if </span><span style=" color: black">(exlamationPointCount > 0)
{
</span><span style=" color: blue">return </span><span style=" color: black">BASE_MESSAGE + </span><span style=" color: blue">new string</span><span style=" color: black">(</span><span style=" color: #a31515">'!'</span><span style=" color: black">, exlamationPointCount);
}
</span><span style=" color: blue">else
</span><span style=" color: black">{
</span><span style=" color: blue">return </span><span style=" color: black">BASE_MESSAGE;
}
}
</span><span style=" color: blue">private void </span><span style=" color: black">addPoint_Click(</span><span style=" color: blue">object </span><span style=" color: black">sender, </span><span style=" color: #2b91af">RoutedEventArgs </span><span style=" color: black">e)
{
HelloMessage = setMessge(++_exlamationPointCount);
RaisePropertyChanged(</span><span style=" color: #a31515">"HelloMessage"</span><span style=" color: black">);
RaisePropertyChanged(</span><span style=" color: #a31515">"IsRemovePossible"</span><span style=" color: black">);
}
</span><span style=" color: blue">private void </span><span style=" color: black">removePoint_Click(</span><span style=" color: blue">object </span><span style=" color: black">sender, </span><span style=" color: #2b91af">RoutedEventArgs </span><span style=" color: black">e)
{
</span><span style=" color: blue">if </span><span style=" color: black">(_exlamationPointCount >= 1)
{
HelloMessage = setMessge(--_exlamationPointCount);
RaisePropertyChanged(</span><span style=" color: #a31515">"HelloMessage"</span><span style=" color: black">);
RaisePropertyChanged(</span><span style=" color: #a31515">"IsRemovePossible"</span><span style=" color: black">);
}
}
</span><span style=" color: green">// Make it possible to disable Remove Point button when there
// is nothing left to remove
</span><span style=" color: blue">public bool </span><span style=" color: black">IsRemovePossible
{
</span><span style=" color: blue">get </span><span style=" color: black">{ </span><span style=" color: blue">return </span><span style=" color: black">_exlamationPointCount > 0; }
}</span></pre><br />
<p>Basically, what I’m doing is keeping track of exclamation point count in _exlamationPointCount. When the buttons are click I increment or decrement the count, make a new message with setMessge() and notify that the properties have been changed.<br />
<h2>Complete MainPage Code Listings</h2><br />
<p>MainPage.xaml</p><pre class="code"><span style=" color: blue"><</span><span style=" color: #a31515">Page
</span><span style=" color: red">x</span><span style=" color: blue">:</span><span style=" color: red">Class</span><span style=" color: blue">="HelloWindows.MainPage"
</span><span style=" color: red">xmlns</span><span style=" color: blue">="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
</span><span style=" color: red">xmlns</span><span style=" color: blue">:</span><span style=" color: red">x</span><span style=" color: blue">="http://schemas.microsoft.com/winfx/2006/xaml"
</span><span style=" color: red">xmlns</span><span style=" color: blue">:</span><span style=" color: red">local</span><span style=" color: blue">="using:HelloWindows"
</span><span style=" color: red">xmlns</span><span style=" color: blue">:</span><span style=" color: red">d</span><span style=" color: blue">="http://schemas.microsoft.com/expression/blend/2008"
</span><span style=" color: red">xmlns</span><span style=" color: blue">:</span><span style=" color: red">mc</span><span style=" color: blue">="http://schemas.openxmlformats.org/markup-compatibility/2006"
</span><span style=" color: red">mc</span><span style=" color: blue">:</span><span style=" color: red">Ignorable</span><span style=" color: blue">="d">
<</span><span style=" color: #a31515">Page.Resources</span><span style=" color: blue">>
</span><span style=" color: green"><!-- These styles along with many other can be found in
\Common\StandardStyles.xaml -->
</span><span style=" color: blue"><</span><span style=" color: #a31515">Style </span><span style=" color: red">x</span><span style=" color: blue">:</span><span style=" color: red">Key</span><span style=" color: blue">="AddAppBarButtonStyle" </span><span style=" color: red">TargetType</span><span style=" color: blue">="ButtonBase"
</span><span style=" color: red">BasedOn</span><span style=" color: blue">="{</span><span style=" color: #a31515">StaticResource </span><span style=" color: red">AppBarButtonStyle</span><span style=" color: blue">}">
<</span><span style=" color: #a31515">Setter </span><span style=" color: red">Property</span><span style=" color: blue">="AutomationProperties.AutomationId"
</span><span style=" color: red">Value</span><span style=" color: blue">="AddAppBarButton" />
<</span><span style=" color: #a31515">Setter </span><span style=" color: red">Property</span><span style=" color: blue">="AutomationProperties.Name" </span><span style=" color: red">Value</span><span style=" color: blue">="Add '!'" />
<</span><span style=" color: #a31515">Setter </span><span style=" color: red">Property</span><span style=" color: blue">="Content" </span><span style=" color: red">Value</span><span style=" color: blue">="&#xE109;" />
</</span><span style=" color: #a31515">Style</span><span style=" color: blue">>
<</span><span style=" color: #a31515">Style </span><span style=" color: red">x</span><span style=" color: blue">:</span><span style=" color: red">Key</span><span style=" color: blue">="RemoveAppBarButtonStyle" </span><span style=" color: red">TargetType</span><span style=" color: blue">="ButtonBase"
</span><span style=" color: red">BasedOn</span><span style=" color: blue">="{</span><span style=" color: #a31515">StaticResource </span><span style=" color: red">AppBarButtonStyle</span><span style=" color: blue">}">
<</span><span style=" color: #a31515">Setter </span><span style=" color: red">Property</span><span style=" color: blue">="AutomationProperties.AutomationId"
</span><span style=" color: red">Value</span><span style=" color: blue">="RemoveAppBarButton" />
<</span><span style=" color: #a31515">Setter </span><span style=" color: red">Property</span><span style=" color: blue">="AutomationProperties.Name" </span><span style=" color: red">Value</span><span style=" color: blue">="Remove '!'" />
<</span><span style=" color: #a31515">Setter </span><span style=" color: red">Property</span><span style=" color: blue">="Content" </span><span style=" color: red">Value</span><span style=" color: blue">="&#xE108;" />
</</span><span style=" color: #a31515">Style</span><span style=" color: blue">>
</</span><span style=" color: #a31515">Page.Resources</span><span style=" color: blue">>
<</span><span style=" color: #a31515">Page.TopAppBar</span><span style=" color: blue">>
<</span><span style=" color: #a31515">AppBar</span><span style=" color: blue">>
<</span><span style=" color: #a31515">StackPanel </span><span style=" color: red">HorizontalAlignment</span><span style=" color: blue">="Right" </span><span style=" color: red">Orientation</span><span style=" color: blue">="Horizontal">
<</span><span style=" color: #a31515">Button </span><span style=" color: red">Style</span><span style=" color: blue">="{</span><span style=" color: #a31515">StaticResource </span><span style=" color: red">AddAppBarButtonStyle</span><span style=" color: blue">}"
</span><span style=" color: red">Click</span><span style=" color: blue">="addPoint_Click" />
</</span><span style=" color: #a31515">StackPanel</span><span style=" color: blue">>
</</span><span style=" color: #a31515">AppBar</span><span style=" color: blue">>
</</span><span style=" color: #a31515">Page.TopAppBar</span><span style=" color: blue">>
<</span><span style=" color: #a31515">Page.BottomAppBar</span><span style=" color: blue">>
<</span><span style=" color: #a31515">AppBar</span><span style=" color: blue">>
<</span><span style=" color: #a31515">StackPanel </span><span style=" color: red">HorizontalAlignment</span><span style=" color: blue">="Right" </span><span style=" color: red">Orientation</span><span style=" color: blue">="Horizontal">
<</span><span style=" color: #a31515">Button </span><span style=" color: red">Style</span><span style=" color: blue">="{</span><span style=" color: #a31515">StaticResource </span><span style=" color: red">RemoveAppBarButtonStyle</span><span style=" color: blue">}"
</span><span style=" color: red">Click</span><span style=" color: blue">="removePoint_Click"
</span><span style=" color: red">IsEnabled</span><span style=" color: blue">="{</span><span style=" color: #a31515">Binding </span><span style=" color: red">Path</span><span style=" color: blue">=IsRemovePossible}" />
</</span><span style=" color: #a31515">StackPanel</span><span style=" color: blue">>
</</span><span style=" color: #a31515">AppBar</span><span style=" color: blue">>
</</span><span style=" color: #a31515">Page.BottomAppBar</span><span style=" color: blue">>
<</span><span style=" color: #a31515">Grid </span><span style=" color: red">Background</span><span style=" color: blue">="{</span><span style=" color: #a31515">StaticResource </span><span style=" color: red">ApplicationPageBackgroundThemeBrush</span><span style=" color: blue">}">
<</span><span style=" color: #a31515">TextBlock </span><span style=" color: red">Text</span><span style=" color: blue">="{</span><span style=" color: #a31515">Binding </span><span style=" color: red">Path</span><span style=" color: blue">=HelloMessage}"
</span><span style=" color: red">Style</span><span style=" color: blue">="{</span><span style=" color: #a31515">StaticResource </span><span style=" color: red">HeaderTextStyle</span><span style=" color: blue">}"
</span><span style=" color: red">HorizontalAlignment</span><span style=" color: blue">="Center" </span><span style=" color: red">VerticalAlignment</span><span style=" color: blue">="Center" />
</</span><span style=" color: #a31515">Grid</span><span style=" color: blue">>
</</span><span style=" color: #a31515">Page</span><span style=" color: blue">>
</span></pre><br />
<p>MainPage.xaml.cs</p><pre class="code"><p><span style=" color: blue">using </span><span style=" color: black">System.ComponentModel;
</span><span style=" color: blue">using </span><span style=" color: black">Windows.UI.Xaml;
</span><span style=" color: blue">using </span><span style=" color: black">Windows.UI.Xaml.Controls;
</span><span style=" color: blue">using </span><span style=" color: black">Windows.UI.Xaml.Navigation;
</span><span style=" color: blue">namespace </span><span style=" color: black">HelloWindows
{
</span><span style=" color: gray">/// <summary>
/// </span><span style=" color: green">An empty page that can be used on its own or navigated to within a Frame.
</span><span style=" color: gray">/// </summary>
</span><span style=" color: blue">public sealed partial class </span><span style=" color: #2b91af">MainPage </span><span style=" color: black">: </span><span style=" color: #2b91af">Page</span><span style=" color: black">, </span><span style=" color: #2b91af">INotifyPropertyChanged
</span><span style=" color: black">{
</span><span style=" color: blue">public event </span><span style=" color: #2b91af">PropertyChangedEventHandler </span><span style=" color: black">PropertyChanged;
</span><span style=" color: blue">private const string </span><span style=" color: black">BASE_MESSAGE = </span> <span style=" color: #a31515">"Hello Windows"</span><span style=" color: black">;
</span><span style=" color: blue">public </span><span style=" color: black">MainPage()
{
</span><span style=" color: blue">this</span><span style=" color: black">.InitializeComponent();
HelloMessage = setMessge(_exlamationPointCount);
</span><span style=" color: blue">this</span><span style=" color: black">.DataContext = </span><span style=" color: blue">this</span><span style=" color: black">;
}
</span><span style=" color: blue">private void </span><span style=" color: black">RaisePropertyChanged(</span><span style=" color: blue">string </span><span style=" color: black">propertyName)
{
</span><span style=" color: blue">if </span><span style=" color: black">(PropertyChanged != </span><span style=" color: blue">null</span><span style=" color: black">)
{
PropertyChanged(</span><span style=" color: blue">this</span><span style=" color: black">, </span><span style=" color: blue">new </span><span style=" color: #2b91af">PropertyChangedEventArgs</span><span style=" color: black">(propertyName));
}
}
</span><span style=" color: blue">public string </span><span style=" color: black">HelloMessage { </span><span style=" color: blue">get</span><span style=" color: black">; </span><span style=" color: blue">set</span> <span style=" color: black">; }
</span><span style=" color: gray">/// <summary>
/// </span><span style=" color: green">Invoked when this page is about to be displayed in a Frame.
</span><span style=" color: gray">/// </summary>
/// <param name="e"></span><span style=" color: green">Event data that describes how this page was reached.
/// </span><span style=" color: green">The Parameter
</span><span style=" color: gray">/// </span><span style=" color: green">property is typically used to configure the page.</span><span style=" color: gray"></param>
</span><span style=" color: blue">protected override void </span><span style=" color: black">OnNavigatedTo(</span><span style=" color: #2b91af">NavigationEventArgs </span><span style=" color: black">e)
{
}
</span><span style=" color: blue">private int </span><span style=" color: black">_exlamationPointCount = 0;
</span><span style=" color: blue">private static string </span><span style=" color: black">setMessge(</span><span style=" color: blue">int </span><span style=" color: black">exlamationPointCount)
{
</span><span style=" color: blue">if </span><span style=" color: black">(exlamationPointCount > 0)
{
</span><span style=" color: blue">return </span><span style=" color: black">BASE_MESSAGE + </span><span style=" color: blue">new string</span><span style=" color: black">(</span><span style=" color: #a31515">'!'</span><span style=" color: black">, exlamationPointCount);
}
</span><span style=" color: blue">else
</span><span style=" color: black">{
</span><span style=" color: blue">return </span><span style=" color: black">BASE_MESSAGE;
}
}
</span><span style=" color: blue">private void </span><span style=" color: black">addPoint_Click(</span><span style=" color: blue">object </span><span style=" color: black">sender, </span><span style=" color: #2b91af">RoutedEventArgs </span><span style=" color: black">e)
{
HelloMessage = setMessge(++_exlamationPointCount);
RaisePropertyChanged(</span><span style=" color: #a31515">"HelloMessage"</span><span style=" color: black">);
RaisePropertyChanged(</span><span style=" color: #a31515">"IsRemovePossible"</span><span style=" color: black">);
}
</span><span style=" color: blue">private void </span><span style=" color: black">removePoint_Click(</span><span style=" color: blue">object </span><span style=" color: black">sender, </span><span style=" color: #2b91af">RoutedEventArgs </span><span style=" color: black">e)
{
</span><span style=" color: blue">if </span><span style=" color: black">(_exlamationPointCount >= 1)
{
HelloMessage = setMessge(--_exlamationPointCount);
RaisePropertyChanged(</span><span style=" color: #a31515">"HelloMessage"</span><span style=" color: black">);
RaisePropertyChanged(</span><span style=" color: #a31515">"IsRemovePossible"</span><span style=" color: black">);
}
}
</span><span style=" color: green">// Make it possible to disable Remove Point button when there
// is nothing left to remove
</span><span style=" color: blue">public bool </span><span style=" color: black">IsRemovePossible
{
</span><span style=" color: blue">get </span><span style=" color: black">{ </span><span style=" color: blue">return </span><span style=" color: black">_exlamationPointCount > 0; }
}
}
}</span></p></pre><h3>My Brain Dead Simple Windows RT App: The Series</h3><ul><li><a href="/2013/03/brain-dead-simple-windows-rt-app.html">Part 1: Brain Dead Simple Windows RT App</a></li>
<li><a href="/2013/03/using-app-bars-to-hide-chrome.html">Part 2: Using App Bars to Hide Chrome</a></li>
<li><a href="/2013/03/roaming-data-with-applicationdata.html">Part 3: Roaming data with ApplicationData</a></li>
<li><a href="/2013/03/live-tile-title.html">Part 4: Displaying Status with Live Tiles</a></li>
</ul>Jack Stephenshttp://www.blogger.com/profile/04996728441112030246noreply@blogger.com0tag:blogger.com,1999:blog-18907707.post-52597077804696163902013-03-03T20:32:00.001-08:002013-04-22T19:27:57.949-07:00Brain Dead Simple Windows RT App<h5>Brain Dead Simple Windows RT App Part 1</h5><p>This article creates a really simple base app that I will use as a starting point for at least 1 other blog post (I’m holding this one until the first one is done). <p>In this series I will site design standards and then violate them. My intention is to justify why you need to learn how to use things like App Bars and Charms. At the same time, I don’t want to spend too many words trying to make everything UX correct. Microsoft has committed thousands of bits to the internet about <a href="http://msdn.microsoft.com/en-us/library/windows/apps/hh465424.aspx">Windows 8 UX standards</a> on MSDN and various blogs. <h2>Build the Brain Dead Simple App</h2><ol><li>Create a new Visual C# Windows Store “Blank App (XAML)”<br />
<li>Call it “HelloWindows”<br />
<li>Open MainPage.xaml<br />
<li>Within the bottom most Grid, add a text block with the Text of “Hello Windows”</li><br />
<br />
<br />
</ol><p>I added a little style and now the grid XAML looks like this:</p><pre class="code"><span style=" color: black"></span><span style=" color: blue"><</span><span style=" color: #a31515">Grid </span><span style=" color: red">Background</span><span style=" color: blue">="{</span><span style=" color: #a31515">StaticResource </span><span style=" color: red">ApplicationPageBackgroundThemeBrush</span><span style=" color: blue">}">
<</span><span style=" color: #a31515">TextBlock </span><span style=" color: red">Text</span><span style=" color: blue">="Hello Windows"
</span> <span style=" color: red">Style</span><span style=" color: blue">="{</span><span style=" color: #a31515">StaticResource </span><span style=" color: red">HeaderTextStyle</span><span style=" color: blue">}"
</span><span style=" color: red">HorizontalAlignment</span><span style=" color: blue">="Center" </span><span style=" color: red">VerticalAlignment</span><span style=" color: blue">="Center" />
</</span><span style=" color: #a31515">Grid</span><span style=" color: blue">>
</span></pre><h2>Add Binding for Future Proofiness</h2><p>I know that I will need to make my app a little more interactive. With this in mind, I’m going to add some really simple binding code and XAML plumbing so I can separate this from the later posts. </p><pre class="code"><span style=" color: black"></span><span style=" color: blue">public sealed partial class </span><span style=" color: #2b91af">MainPage </span><span style=" color: black">: </span><span style=" color: #2b91af">Page</span><span style=" color: black">, </span><span style=" color: #2b91af">INotifyPropertyChanged
</span><span style=" color: black">{
</span><span style=" color: blue">public event </span><span style=" color: #2b91af">PropertyChangedEventHandler </span><span style=" color: black">PropertyChanged;
</span><span style=" color: blue">private const string </span><span style=" color: black">BASE_MESSAGE = </span><span style=" color: #a31515">"Hello Windows"</span><span style=" color: black">;
</span><span style=" color: blue">public </span><span style=" color: black">MainPage()
{
</span><span style=" color: blue">this</span><span style=" color: black">.InitializeComponent();
HelloMessage = setMessge(ExlamationPointCount);
</span><span style=" color: blue">this</span><span style=" color: black">.DataContext = </span><span style=" color: blue">this</span><span style=" color: black">;
}
</span><span style=" color: blue">private void </span><span style=" color: black">RaisePropertyChanged(</span><span style=" color: blue">string </span><span style=" color: black">propertyName)
{
</span><span style=" color: blue">if </span><span style=" color: black">(PropertyChanged != </span><span style=" color: blue">null</span><span style=" color: black">)
{
PropertyChanged(</span><span style=" color: blue">this</span><span style=" color: black">, </span><span style=" color: blue">new </span><span style=" color: #2b91af">PropertyChangedEventArgs</span><span style=" color: black">(propertyName));
}
}
</span></pre>AND XAML <pre class="code"><span style=" color: black"></span><span style=" color: blue"><</span><span style=" color: #a31515">Grid </span><span style=" color: red">Background</span><span style=" color: blue">="{</span><span style=" color: #a31515">StaticResource </span><span style=" color: red">ApplicationPageBackgroundThemeBrush</span><span style=" color: blue">}">
<</span><span style=" color: #a31515">TextBlock </span><span style=" color: red">Text</span><span style=" color: blue">="{</span><span style=" color: #a31515">Binding </span><span style=" color: red">Path</span><span style=" color: blue">=HelloMessage}"
</span> <span style=" color: red">Style</span><span style=" color: blue">="{</span><span style=" color: #a31515">StaticResource </span><span style=" color: red">HeaderTextStyle</span><span style=" color: blue">}" />
</</span><span style=" color: #a31515">Grid</span><span style=" color: blue">>
</span></pre><h3>My Brain Dead Simple Windows RT App: The Series</h3><ul><li><a href="/2013/03/brain-dead-simple-windows-rt-app.html">Part 1: Brain Dead Simple Windows RT App</a></li>
<li><a href="/2013/03/using-app-bars-to-hide-chrome.html">Part 2: Using App Bars to Hide Chrome</a></li>
<li><a href="/2013/03/roaming-data-with-applicationdata.html">Part 3: Roaming data with ApplicationData</a></li>
<li><a href="/2013/03/live-tile-title.html">Part 4: Displaying Status with Live Tiles</a></li>
</ul>Jack Stephenshttp://www.blogger.com/profile/04996728441112030246noreply@blogger.com0tag:blogger.com,1999:blog-18907707.post-86317370948065765602013-02-26T22:41:00.000-08:002013-02-26T22:42:20.745-08:00I want Half View in Windows 8<p>Windows 8 has this mode that they call "Snap View" where an app takes 10% of the screen and another one takes the other 90%. This is cool because it allows me to work with two apps at the same time (sort of). However I sometimes like to compare things where one app isn’t that much more important than the other. For this I would request half screen where each app would get 50%.</p><p>This may be an edge case, but I like to compare the WinRT version of common apps like Kindle and Evernote with their Classic Windows versions. With two monitors I could do this, but if with just my laptop, no way. I would also like to compare two WinRT apps or even compare the data of two different apps.</p>Jack Stephenshttp://www.blogger.com/profile/04996728441112030246noreply@blogger.com0tag:blogger.com,1999:blog-18907707.post-42804358796805449292013-02-11T19:24:00.001-08:002013-02-11T19:24:28.607-08:00My New Surface RT<p>This weekend I finally got my Windows RT tablet. It is a 64 GB Surface. I am using it right now to write this very blog entry. <h4>ARM vs Intel</h4> <p>Originally, I was going to hold out for a Surface Pro (and I nearly made it), but I began to think about: 1) I need to be able to test my stuff on an ARM device. 2) I need to think in a Metro way (since I don’t work for the Empire, I can still use that term), I need to find a Metro app to solve whatever problem I need to solve. 3) Most of the desktop stuff I need to do require more screen area; 10 inches is not enough space for Visual Studio. 4) Besides, I’m a cheapskate. <p>I am running Windows 8 on my elderly HP laptop. I plan on getting a new laptop with touch much later this year; it’ll probably be cheaper than this Surface RT. <h4>Office</h4> <p>I know that Window 8 is somewhat experimental. Still, I’m disappointed that there is no Metro, er, Modern App version of Office. I know that would be a big task and that Microsoft will eventually get around to it, it would be cool to see how such a big and important app migrated to the Modern World. Is there an Office worthy app out there that belongs on the Tablet.</p> Jack Stephenshttp://www.blogger.com/profile/04996728441112030246noreply@blogger.com0tag:blogger.com,1999:blog-18907707.post-68599762860991319142013-01-29T00:49:00.001-08:002013-01-31T21:38:00.573-08:00WinRT CS vs JS: Grids<p>C# XAML</p><p>In XAML, a grid is pretty straight forward. Within the Grid tag, you define the rows and column in <font face="Courier New">RowDefinitions</font> and <font face="Courier New">ColumnDefinitions</font> tags. Below the definitions, you place the controls that will reside in the Grid, you assign them to cells and control cell spans with Row. attributes. </p><pre class="code"><span style="background: white; color: blue"><</span><span style="background: white; color: #a31515">Grid </span><span style="background: white; color: red">Background</span><span style="background: white; color: blue">="{</span><span style="background: white; color: #a31515">StaticResource </span><span style="background: white; color: red">ApplicationPageBackgroundThemeBrush</span><span style="background: white; color: blue">}">
<</span><span style="background: white; color: #a31515">Grid.RowDefinitions</span><span style="background: white; color: blue">>
<</span><span style="background: white; color: #a31515">RowDefinition </span><span style="background: white; color: red">Height</span><span style="background: white; color: blue">="1*"/>
<</span><span style="background: white; color: #a31515">RowDefinition </span><span style="background: white; color: red">Height</span><span style="background: white; color: blue">="1*"/>
<</span><span style="background: white; color: #a31515">RowDefinition </span><span style="background: white; color: red">Height</span><span style="background: white; color: blue">="1*"/>
</</span><span style="background: white; color: #a31515">Grid.RowDefinitions</span><span style="background: white; color: blue">>
<</span><span style="background: white; color: #a31515">Grid.ColumnDefinitions</span><span style="background: white; color: blue">>
<</span><span style="background: white; color: #a31515">ColumnDefinition </span><span style="background: white; color: red">Width</span><span style="background: white; color: blue">="1*"/>
<</span><span style="background: white; color: #a31515">ColumnDefinition </span><span style="background: white; color: red">Width</span><span style="background: white; color: blue">="1*"/>
<</span><span style="background: white; color: #a31515">ColumnDefinition </span><span style="background: white; color: red">Width</span><span style="background: white; color: blue">="1*"/>
</</span><span style="background: white; color: #a31515">Grid.ColumnDefinitions</span><span style="background: white; color: blue">>
<</span><span style="background: white; color: #a31515">TextBlock </span><span style="background: white; color: red">Grid.Row</span><span style="background: white; color: blue">="0" </span><span style="background: white; color: red">Grid.Column</span><span style="background: white; color: blue">="0" </span><span style="background: white; color: red">Grid.ColumnSpan</span><span style="background: white; color: blue">="3"></span><span style="background: white; color: black">Title</span><span style="background: white; color: blue"></</span><span style="background: white; color: #a31515">TextBlock</span><span style="background: white; color: blue">>
<</span><span style="background: white; color: #a31515">TextBlock </span><span style="background: white; color: red">Grid.Row</span><span style="background: white; color: blue">="1" </span><span style="background: white; color: red">Grid.Column</span><span style="background: white; color: blue">="0"></span><span style="background: white; color: black">2, 1</span><span style="background: white; color: blue"></</span><span style="background: white; color: #a31515">TextBlock</span><span style="background: white; color: blue">>
<</span><span style="background: white; color: #a31515">TextBlock </span><span style="background: white; color: red">Grid.Row</span><span style="background: white; color: blue">="1" </span><span style="background: white; color: red">Grid.Column</span><span style="background: white; color: blue">="1"></span><span style="background: white; color: black">2, 2</span><span style="background: white; color: blue"></</span><span style="background: white; color: #a31515">TextBlock</span><span style="background: white; color: blue">>
<</span><span style="background: white; color: #a31515">TextBlock </span><span style="background: white; color: red">Grid.Row</span><span style="background: white; color: blue">="1" </span><span style="background: white; color: red">Grid.Column</span><span style="background: white; color: blue">="2"></span><span style="background: white; color: black">2, 3</span><span style="background: white; color: blue"></</span><span style="background: white; color: #a31515">TextBlock</span><span style="background: white; color: blue">>
<</span><span style="background: white; color: #a31515">TextBlock </span><span style="background: white; color: red">Grid.Row</span><span style="background: white; color: blue">="2" </span><span style="background: white; color: red">Grid.Column</span><span style="background: white; color: blue">="0"></span><span style="background: white; color: black">3, 1</span><span style="background: white; color: blue"></</span><span style="background: white; color: #a31515">TextBlock</span><span style="background: white; color: blue">>
<</span><span style="background: white; color: #a31515">TextBlock </span><span style="background: white; color: red">Grid.Row</span><span style="background: white; color: blue">="2" </span><span style="background: white; color: red">Grid.Column</span><span style="background: white; color: blue">="1"></span><span style="background: white; color: black">3, 2</span><span style="background: white; color: blue"></</span><span style="background: white; color: #a31515">TextBlock</span><span style="background: white; color: blue">>
<</span><span style="background: white; color: #a31515">TextBlock </span><span style="background: white; color: red">Grid.Row</span><span style="background: white; color: blue">="2" </span><span style="background: white; color: red">Grid.Column</span><span style="background: white; color: blue">="2"></span><span style="background: white; color: black">3, 3</span><span style="background: white; color: blue"></</span><span style="background: white; color: #a31515">TextBlock</span><span style="background: white; color: blue">>
</</span><span style="background: white; color: #a31515">Grid</span><span style="background: white; color: blue">>
</span></pre><br />
<p>This is straight forward; the WinJS examples will attempt to match this layout. <br />
<h2>JavaScript HTML</h2><br />
<p>In WinJS, a grid is achieved using an <a href="http://msdn.microsoft.com/en-us/library/ie/hh673533%28v=vs.85%29.aspx">IE 10 specific version</a> of the <a href="http://www.w3.org/TR/2012/WD-css3-grid-layout-20120322/">CSS Grid Layout</a>. Since this is CSS, you can select HTML elements using id or class selectors, you can even use inline styles. In this example, I will mix in all three. Basically you: <br />
<p>Create a style for the grid, in that style you need to tell it to display as a grid with a <font face="Courier New">display</font> attribute with the value <font face="Courier New">-ms-grid</font>. <br />
<p>Define the columns with a <font face="Courier New">-ms-grid-columns</font> property (“1fr” means 1 fraction value, behaves like 1* in XAML, see table below). <br />
<p>Define the rows with a<font face="Courier New"> -ms-grid-rows</font> property. <br />
<p>You can assign an HTML element to a row with a <font face="Courier New">-ms-grid-row</font> and to a column with a<font face="Courier New"> -ms-grid-column</font>. I have been playing with the idea of creating a class for each row and column/ <br />
<p>With this in mind, I created the following CSS (put in css/default.css, if you want to do this yourself):</p><pre class="code"><span style="background: white; color: maroon">#MainGrid </span><span style="background: white; color: black">{
</span><span style="background: white; color: red">display</span><span style="background: white; color: black">: </span><span style="background: white; color: blue">-ms-grid</span><span style="background: white; color: black">;
</span><span style="background: white; color: red">-ms-grid-columns</span><span style="background: white; color: black">: </span><span style="background: white; color: blue">1fr 1fr 1fr</span><span style="background: white; color: black">;
</span><span style="background: white; color: red">-ms-grid-rows</span><span style="background: white; color: black">: </span><span style="background: white; color: blue">1fr 1fr 1fr</span><span style="background: white; color: black">;
</span><span style="background: white; color: red">height</span><span style="background: white; color: black">: </span><span style="background: white; color: blue">100%</span><span style="background: white; color: black">; </span><span style="background: white; color: #006400">/* make it big, so we can see it */
</span><span style="background: white; color: black">}
</span><span style="background: white; color: maroon">.Col01 </span><span style="background: white; color: black">{
</span><span style="background: white; color: red">-ms-grid-column</span><span style="background: white; color: black">: </span><span style="background: white; color: blue">1</span><span style="background: white; color: black">;
}
</span><span style="background: white; color: maroon">.Col02 </span><span style="background: white; color: black">{
</span><span style="background: white; color: red">-ms-grid-column</span><span style="background: white; color: black">: </span><span style="background: white; color: blue">2</span><span style="background: white; color: black">;
}
</span><span style="background: white; color: maroon">.Col03 </span><span style="background: white; color: black">{
</span><span style="background: white; color: red">-ms-grid-column</span><span style="background: white; color: black">: </span><span style="background: white; color: blue">3</span><span style="background: white; color: black">;
}
</span><span style="background: white; color: maroon">.Row01 </span><span style="background: white; color: black">{
</span><span style="background: white; color: red">-ms-grid-row</span><span style="background: white; color: black">: </span><span style="background: white; color: blue">1</span><span style="background: white; color: black">;
}
</span><span style="background: white; color: maroon">.Row02 </span><span style="background: white; color: black">{
</span><span style="background: white; color: red">-ms-grid-row</span><span style="background: white; color: black">: </span><span style="background: white; color: blue">2</span><span style="background: white; color: black">;
}
</span><span style="background: white; color: maroon">.Row03 </span><span style="background: white; color: black">{
</span><span style="background: white; color: red">-ms-grid-row</span><span style="background: white; color: black">: </span><span style="background: white; color: blue">3</span><span style="background: white; color: black">;
}
</span></pre><br />
<p>OK, so you have some styles, but you can’t see styles. You need some HTML markup to receive the styles. <br />
<p>At this early stage in my WInJS development, it makes sense to me to style the main grid using an ID selector, so I’m calling the main grid div “MainGrid”. Each grid is going to be unique. <br />
<p>For row and column assignment I am using the ColXX and RowXX classes. To me is as easy to read as XAML’s Grid.Row=”0” and you can avoid creating ids for every cell in every cell in your application. <br />
<p>And I’m using the inline style on the first row to force it to span the 3 columns. I was debating using a class called “ColSpan03” but this gave me the opportunity to demonstrate an inline style. <br />
<p>With all this in mind, here’s the HTML for my grid (put in the body of default.html, if you are playing along at home):</p><pre class="code"><span style="background: white; color: blue"><</span><span style="background: white; color: maroon">div </span><span style="background: white; color: red">id</span><span style="background: white; color: blue">="MainGrid" >
<</span><span style="background: white; color: maroon">div </span><span style="background: white; color: red">class</span><span style="background: white; color: blue">="Col01 Row01" </span><span style="background: white; color: red">style</span><span style="background: white; color: blue">="</span><span style="background: white; color: red">-ms-grid-column-span</span><span style="background: white; color: black">: </span><span style="background: white; color: blue">3"></span><span style="background: white; color: black">Title</span><span style="background: white; color: blue"></</span><span style="background: white; color: maroon">div</span><span style="background: white; color: blue">>
<</span><span style="background: white; color: maroon">div </span><span style="background: white; color: red">class</span><span style="background: white; color: blue">="Col01 Row02"></span><span style="background: white; color: black">2, 1</span><span style="background: white; color: blue"></</span><span style="background: white; color: maroon">div</span><span style="background: white; color: blue">>
<</span><span style="background: white; color: maroon">div </span><span style="background: white; color: red">class</span><span style="background: white; color: blue">="Col02 Row02"></span><span style="background: white; color: black">2, 2</span><span style="background: white; color: blue"></</span><span style="background: white; color: maroon">div</span><span style="background: white; color: blue">>
<</span><span style="background: white; color: maroon">div </span><span style="background: white; color: red">class</span><span style="background: white; color: blue">="Col03 Row02"></span><span style="background: white; color: black">2, 3</span><span style="background: white; color: blue"></</span><span style="background: white; color: maroon">div</span><span style="background: white; color: blue">>
<</span><span style="background: white; color: maroon">div </span><span style="background: white; color: red">class</span><span style="background: white; color: blue">="Col01 Row03"></span><span style="background: white; color: black">3, 1</span><span style="background: white; color: blue"></</span><span style="background: white; color: maroon">div</span><span style="background: white; color: blue">>
<</span><span style="background: white; color: maroon">div </span><span style="background: white; color: red">class</span><span style="background: white; color: blue">="Col02 Row03"></span><span style="background: white; color: black">3, 2</span><span style="background: white; color: blue"></</span><span style="background: white; color: maroon">div</span><span style="background: white; color: blue">>
<</span><span style="background: white; color: maroon">div </span><span style="background: white; color: red">class</span><span style="background: white; color: blue">="Col03 Row03"></span><span style="background: white; color: black">3, 3</span><span style="background: white; color: blue"></</span><span style="background: white; color: maroon">div</span><span style="background: white; color: blue">>
</</span><span style="background: white; color: maroon">div</span><span style="background: white; color: blue">></span></pre><br />
<h2>Comparing units in Row and Column Definitions</h2><br />
<p>Here is a list of the various units that can be used to define the sized of rows and columns.</p><br />
<table width="650" border="1"><tbody>
<tr> <td valign="top" width="150"><strong>Purpose</strong></td> <td valign="top" width="150"><strong>XAML</strong></td> <td valign="top" width="150"><strong>WinJS</strong></td> <td valign="top" width="200"><strong>Notes</strong></td></tr>
<tr> <td valign="top" width="150">Fractional Units</td> <td valign="top" width="150">* so 1 fractional unit would be “1*”, 2 would be 2* and so on</td> <td valign="top" width="150">fr so 1 fractional unit would be “1fr”, 2 would be “2fr” and so on</td> <td valign="top" width="200"> </td></tr>
<tr> <td valign="top" width="150">Auto Sizing</td> <td valign="top" width="150">“auto”</td> <td valign="top" width="150">“auto”</td> <td valign="top" width="200">Although they are the same keyword, they behave slightly differently, <br />
WinJS is more willing to wrap text and XAML is more likely to scroll</td></tr>
<tr> <td valign="top" width="150">Percent</td> <td valign="top" width="150">Not supported</td> <td valign="top" width="150">% so fifty percent would be “50%”</td> <td valign="top" width="225">For XAML, you could use Fractional Units that add up to 100</td></tr>
<tr> <td valign="top" width="150">Pixels</td> <td valign="top" width="150">Just the number, so 20 pixels would be “20”</td> <td valign="top" width="150">px, so 20 pixels would be 20px</td> <td valign="top" width="200"> </td></tr>
<tr> <td valign="top" width="150">Inches</td> <td valign="top" width="150">Not supported</td> <td valign="top" width="150">in</td> <td valign="top" width="200"> </td></tr>
<tr> <td valign="top" width="150">Centimeters</td> <td valign="top" width="150">Not supported</td> <td valign="top" width="150">cm</td> <td valign="top" width="200"> </td></tr>
<tr> <td valign="top" width="150">Millimeters</td> <td valign="top" width="150">Not supported</td> <td valign="top" width="150">mm</td> <td valign="top" width="200"> </td></tr>
<tr> <td valign="top" width="150">Points</td> <td valign="top" width="150">Not supported</td> <td valign="top" width="150">pt</td> <td valign="top" width="200"> </td></tr>
</tbody></table>Jack Stephenshttp://www.blogger.com/profile/04996728441112030246noreply@blogger.com0