July 25, 2012

ASP.Net: Wait for postback response


We have a ASP.Net application where some postbacks might take some time to be handled by the server. When the user triggers a postback, we want the client to be forced to wait until the postback is handled by the server.


My Problem

I want this mechanism to kick in on every page of the site, though some pages do not realy require it.

Problem solved?

Obviously you end up in the master page, but as in animal farm: "All pages are equal, but some pages are more equal then others". I want certain pages to handle the postback slightly different from the default...

Problem Solved!

The solution contains 3 elements: html, css and a little javascript. It's the javascript that helped me out.

html:
     <div id="mainContent">  
       <div class="loadinggs" id="loadingoverlay">  
         <div class="loadingmsg">  
           <img src="../../Images/ajax-loader.gif" align="middle" alt="busy" />  
           Bezig met verwerken...  
         </div>  
       </div>  
       <asp:ContentPlaceHolder ID="MainContent" runat="server" />  
     </div>  
The place of the code depends on what part you want to block. The example code 'blocks' the main content, leaving the menu (in another placeholder) accessible.

CSS:
 .loadinggs  
 {  
   position:fixed;  
   width:100%;  
   height:100%;  
   background:rgba(255, 255, 255, 0.5);  
   z-index:1000000000000;  
   display:none;  
   }  
 .loadingmsg  
 {  
   position:absolute;  
   top:30%;  
   left:50%;  
   background:#fff;  
   border:2px solid #196DC1;  
   padding:5px;  
 }  

HTML & CSS by Gio! (thanks again!)
The CSS is primarily IE9 (as by my requirement), but you can easily tweak it to support other browsers aswell.

Javascript

 <%--  
     Initializing the Page PageRequestManager, handling 4 key events:  
     1 - BeginRequest - Start of a postback  
     2 - Endrequest  - Server response on postback event  
     3 - PageLoading - ... (self explainatory I guess)  
     4 - PageLoaded  -  
     By default on each page a animated loading img is shown. But certain pages have additional functionality.  
     In appl_init we check if additional functions were provided for each event. If not the default behaviour is applied.  
 --%>  
 <script type="text/javascript">  
   var pgRegMgr;  
   // add an event handler to 'pageload'  
   Sys.Application.add_init(appl_init);  
   /*  
   The PageRequestManager contains postback information.  
   When this page initializes, we can add handlers to this manager to catch events.  
   NOTE: This javascript code need te be placed AFTER the </body> tag !!!!  
   */  
   function appl_init() {  
     // if we do not have the PageRequestManager instance, get it AND add the handler(s)  
     if (pgRegMgr == null) {  
       if (Sys != null) {  
         if (Sys.WebForms != null) {  
           pgRegMgr = Sys.WebForms.PageRequestManager.getInstance();  
           // check for specific event handlers, if not found use the default  
           if (window.pageBeginRequest) { pgRegMgr.add_beginRequest(pageBeginRequest); } else { pgRegMgr.add_beginRequest(BeginHandler); }  
           if (window.pageEndRequest) { pgRegMgr.add_endRequest(pageEndRequest); } else { pgRegMgr.add_endRequest(EndHandler); }  
           if (window.pageLoading) { pgRegMgr.add_pageLoading(pageLoading); } else { pgRegMgr.add_pageLoading(BeginHandler); }  
           if (window.pageLoaded) { pgRegMgr.add_pageLoaded(pageLoaded); } else { pgRegMgr.add_pageLoading(EndHandler); }  
         }  
       }  
     }  
   }  
   /*  
   Default pageBeginRequest and pageLoading behaviour.  
   */  
   function BeginHandler() {  
     document.getElementById('loadingoverlay').style.display = 'block';  
   }  
   /*  
   Default pageEndRequest and pageLoaded behaviour.  
   */  
   function EndHandler() {  
     document.getElementById('loadingoverlay').style.display = 'none';  
   }  
 </script>  
 <asp:ContentPlaceHolder ID="AfterBody_AddedHandlers" runat="server" />  

Now on each postback of a control the content part of the screen is 'locked' until the server responds. The script supports the usage of  a specific handler on the content page hosted by the master. Just add the handlers in the <asp:Content> tag and do enclose them with <script></script> tags. And -if applicable- don't forget to hide the loading message...

 <asp:Content ID="Content2" ContentPlaceHolderID="AfterBody_AddedHandlers" runat="Server">  
   <script type="text/javascript">  
     /*  
     When a postback was handled by the server (Code behind was executed...)  
     we want to check if a specific control was causing the postback...  
     */  
     function pageEndRequest() {  
       //do specific page stuff...  
     }  
     /*  
     Catch the pageLoaded event.  
     */  
     function pageLoaded() {  
      //eg. call a specific function like a body onload  
       UpdateTotals();  
       document.getElementById('loadingoverlay').style.display = 'none';  
     }  
   </script>  
 </asp:Content>  

July 23, 2012

Sync Outlook with Android!

Looking for free software to synchronize your outlook data with your Android device?
After searching the web with google again and again I found a solution. They say the best things in life are free, but your should really consider a donation to these developers!

MyPhoneExplorer



This is what you are looking for! Fully functional, not a trial version. And do donate, it's worth it!


July 20, 2012

Setup and Deployment: A Windows service


So I made myself a nice Windows Service in Visual Studio 2010 for some background work. As numerous sites point out, I did not forget to add a "project installer" -which seems to be required- by right clicking on the Service Design view and selecting "add installer" from the popup. Set some properties and we're good to go. That was the easy part as it turned out.

My Problem


Now it needed to be installed on client machines so I added a new project to my solution: Other Project Types -> Setup and Deployment -> Visual Studio Installer. "Been there, done that" I would think...

I added the "Primary output from myService (active)" to the file system. Just as I did countless times, for countless other apps. Organized the install information like name, company, version and so on and so forth... The usual stuff really. When build, indeed the project generates the ".msi" and "setup.exe" as expected.

Problem solved?


Right click "setup.exe" from my release folder of the setup project. "Run as administrator" to be absolutly sure the User Account Control doesn't bother me, and install! And -as expected- install ended with no problems. So cool, creating my own service... No let's see if this works...

[anxious on]
Start button -> services.msc -> here's my list of services...[anxious off]

Huh? Where is it? It did install, it did! Look at the control panel -> Add remove software -> It's right there!

Problem solved!


When installing a service -it turns out- we need some extra settings in the setup project.

Right click the setup project and select "Custom actions" from the popup menu. Add the primary output from your service to Install, Commit, Rollback and uninstall.

Finally rebuild your setup -don't forget to uninstall your previous attempt- and install your new build. Now run "services.msc" and...
"There it is!" (to qoute the great Charlie Harper :)

Off course starting the service might still be a dissapointment, depending on your coding skills. But atleast it got installed!




July 13, 2012

Generated objects, property exists? (or not!)


The ORM of choice of my employer is Telerik Open Access. It supports both forward and reverse mapping. When doing SCRUM development, it gives us the flexibility to quickly add some database columns, tables, foreign key relations etc. in the database and then generate the business objects in our data layer.



My problem

In a particular project the customer opted for record tracking. Who -and when- created (e.g.) a person in the database, and who (when) had it changed.
The catch though:
  • it's not required for all tables. 
  • The creation / modification user is only known in the software (no database trigger solution).

Problem solved?

One part of the solution was to edit the Telerik Open Access class generation templates. In those templates we added a (default!) constructor. In the constructor we set the creator and creation date properties. Adding / implementing INotifyPropertyChanged handled the modification properties. The generation of the business classes was no problem, but compiling the project was another story...
Not every business class had the creation properties, so we faced "some" compiler errors.

Problem solved!

Refelection saved the day. Instead of setting the property straight forward in the constructor:
 this._createUser="new creator";  
it's 'decorated' with an if:
 if(!this.GetType().GetProperty("CreateUser") == null)  
 {  
   this.GetType().GetProperty("CreateUser").SetValue(this,"new creator",null);  
 }  

Now we can (re-)generate the class model without the headache of modifying 70+ constructors for objects without tracking (or the need to add 40+ constructers  when not generated).

July 12, 2012

Linked tables in SQL Server 2008 (R2)

Usually Google is my friend, but looking for SQL Linked tables gave me some frustration. Admittedly, I searched for the wrong thing...

My problem

Consider 2 identical databases on 1 (SQL 2008) server. The data is -off course- different, however some tables need to be 'in sync'. My initial thought was to make database one the lead database, storing the data. In database two I delete that particular table and create a linked table to database one. Just like one would do in MS Access...

Problem solved?

Hold that thought: "Just like in access"... Short version of the story: there are no linked tables in SQL Server!

Problem solved!

It turns out in SQL Server it's not "linked tables", it's Synonyms!!! So when I did a search on Synonyms Google showed a wealth of information.


Some things I stumbled into when replacing the tables with synonyms:
  • First remove all foreign keys related to tables which are being replaced by synonyms
  • Then remove the primary keys of tables to be replaced
  • Synonyms can not have FK constraints!
So  linking tables in SQL turns out to be as easy as:
 DROP TABLE [dbo].[myTable]  
 CREATE SYNONYM [dbo].[myTable] FOR [TheOtherDatabase].[dbo].[myTable]