Your page has to continuously poll the controller at a fixed time interval and display message on the basis of percent complete.
Sample Controller
public class BatchRunController {
public boolean batchProcessed;
public BatchRunController(){
batchProcessed = false;
}
public String getMessage(){
if(batchProcessed){
Double itemsProcessed;
Double totalItems ;
for(AsyncApexJob a : [select TotalJobItems, Status, NumberOfErrors, MethodName, JobType, JobItemsProcessed, Id,
CreatedDate, CreatedById, CompletedDate, ApexClassId, ApexClass.Name From AsyncApexJob where ApexClass.Name = 'ACPBoxInvoiceDataBatch' order by CreatedDate desc limit 1]){
itemsProcessed = a.JobItemsProcessed;
totalItems = a.TotalJobItems;
}
//Determine the percent complete based on the number of batches complete and set message
if(totalItems == 0){
//A little check here as we don't want to divide by 0.
return 'Batch Job 0% Complete';
}else{
return 'Batch Job ' + String.valueof(((itemsProcessed / totalItems) * 100.0).intValue())+'% Complete.';
}
} else {
return 'Batch Job not yet executed !!';
}
}
public PageReference process() {
ACPBoxInvoiceDataBatch batch = new ACPBoxInvoiceDataBatch();
Database.executeBatch(batch);
batchProcessed = true;
return null;
}
}
Sample VF page
<apex:page controller="BatchRunController">
<apex:form >
<apex:pageBlock >
<apex:actionPoller rerender="msg" interval="5"/>
<apex:commandButton action="{!process}" value="EXECUTE" rerender="msg"/>
<Br/><Br/>
<apex:outputLabel value="{!Message}" id="msg" style="text-wight:bold;"/>
</apex:pageBlock>
</apex:form>
</apex:page>
Initial Display

Display after clicking Execute

After this display, the VF page keeps on polling and displaying appropriate percentages every 5 seconds, and it shows 100% after execution is complete. [Note - Polling will still continue]