Hi
In preperation for my chalk and talk session at the Microsoft Launch 05 event next week I stated using my i-mate JasJar for CF 2.0 development and was initially extremely frustrated when I got the following error:
“Installation error. Stop all applications and process, maximize available storage space, and run install again. Suport inf 4.”
That is until I found this post which helped me solve it:
http://groups.google.co.uk/group/microsoft.public.dotnet.framework.compactframework/browse_thread/thread/2132b052a370c220/e73203a525e50d3b?lnk=st&q=deploying+CF+2.0+to+JasJar&rnum=1&hl=en#e73203a525e50d3b
Basically, the JasJar comes with three programs already installed. One of these must be using a beta of CF 2.0 which causes the Visual Studio auto deployment of CF 2.0 proper to fail. Once I removed all three programs everything worked beautifully.
If you're having the same problem I hope this help you avoid the hours and hours of wasted time I spent.
J.
OK, so today over lunch Mike Taulty and Benjamin Mitchell gave me a hard time about being behind on both reading and writing blogs. I tried to deflect this by pointing out Benjamin's choice of the pink PDC t-shirt created questions around his sexuality however his point was valid - I have been so busy with Sentient and Mobile GPS software for sports people that I hadn't kept up.
So, this afternoon I've downloaded the latest version of SharpReader and started adding blogs to read. By accident I found a fantastic SharpReader feature - you can drag and drop RSS urls directly into SharpReader's Subscribed Feeds list. Extremely cool and saves me loads of time messing around. I thought I'd share!
J.
OK, I'm here sitting in my room in the Renaissance Hollywood Hotel.
Yesterday was spent getting here. A very early start to catch the 6.35 flight from Heathrow to Amsterdam to join the Microsoft PDC Jumbo to LA. The PDC Jumbo was due to leave Amsterdam at 11.15 however security look a little longer than expected so Benjamin, Clemence and I found time for a quick pre-flight beer! Actually Benjamin wimped out and took a coffee complaining about already feeling a little rough after the night before.
The flight was nearly 12 hours but passed relatively quickly with the help of my new PSP and Ridge Racer - it rocks! I also completed reading Richard Branson's Autobiography (seemed polite to read his book since he's paying my wages currently).
Having fun already bumping into people I haven't seen for a while. It was great to bump into Benjamin during check-in at Amsterdam. He only lives about a mile from me but we never seem to find time to catch-up. He also managed to get us both bulk head seats which was nice! Also, managed to grab a quick chat with both James Pratt and Steve Swartz already which is cool.
Today is pre-conf day which I wasn't planning to attend but I did pop along to the Conference Centre to collect my conference pass and goodie bag. The goodie bag is OK but not great. First of all it's not a rucksack which is a shame and it's just stuff full of the usual marketing guff and magazines. The three cool things are the Channel 9 foam toy, the Universal Studio Party invite and the PDC t-shirt (so long as you didnt get a girlie pink one!)
I did think I might attend the pre-conf afternoon session but wasnt prepared the full-day $300 for just the afternoon but was informed I couldn't pay $150 for afternoon because the "computer says No" !!! (Even Microsoft have this problem).
Another thing which made me laugh was the instructions from the hotel to the conference included a streetmap which was clearly taken from Google Maps! Steve is going to kill someone if he sees it.
It appears that Windows Workflow Foundation (WWF) is the worse kept secret from the conference already. Supposedly not announced until after Bill's keynote however it's already being discussed in the sessions and the WinFX developer reference poster included in the goodie bag details the System.Workflow namespace!
Tonight is bowling night with UK DPE so I'm looking forward to hooking up with the UK DPE people and friends. http://mtaulty.com/FlexWiki/preview.aspx
Tomorrow I'm going to attend pre-conf to find out all about:
PRE08 Adding Integration, Analysis and Reporting Functionality to Your Application through SQL Server 2005 Business Intelligence
Speaker(s): Bill Baker, Jason Carlson, Lukasz Pawlowski, Alex Payne
More rambling tomorrow.
J.
You've got to love those people at Microsoft.
They've kindly invited me to attend PDC in LA next week as their guest. Something about a "key influencer" :-)
So, next week I return to blogging possibly for one week only due to the madnesses of my regular work life juggling Sentient and Sportsdo activities but at least for next week I plan to geek out totally, get some geeky R&R and get blogging.
Yippee, LA here I come.
J.
I am currently working with a client who is transitioning from VB.net to C# and as part of my preparation for a conversion workshop I found this very handy resource:
http://www.harding.edu/USER/fmccown/WWW/vbnet_csharp_comparison.html
Thank you to Frank McCown for making this available.
It is also interesting to see he has prepared a Java to C# comparision for those of you who have seen the light and are coming over to join the C# fun!
Jonathan
It's been a couple of week since I attended the Indigo SDR event in Seattle and my head is still buzzing with excitement about it.
The SDR was the first time I've gone deep on what the Indigo team have been doing and I'm impressed, infact very impressed. The balance between capabilities, extensibility and simplicity seems to be spot on.
The bit I love the most about Indigo is the worlds simplest sample code will still provide you with a secure, reliable and potentially authenticated web service call with only a few lines of code. Security is on by default so you have to explictly turn it off rather than the other way round.
I will post some Indigo sample code when I next get a few minutes spare.
Indigo rocks! Sadly waiting for it to be launched doesnt. :-(
Jonathan
Over the past couple of months I have been focusing my time on creating a new business division within Sentient called Sportsdo. Our new Sportsdo division creates software for sports people which runs on SmartPhone and PocketPC.
www.Sportsdo.info
We have been through a very steep learning curve with the .NET Compact Framework and are now participating in the beta program for the next Windows Mobile platform release (Magneto).
The current suite of applications we have developed include Rundo, Skido, Snowdo, Bikedo and Cycledo. We have many others in the pipeline too including Golfdo, Trackdo, Saildo, etc. etc.
We will make some Magneto code example available as soon as our NDA allows.
In our System.Threading talk we cover the impact of threading in an ASP.NET application and specifically how damaging to your applications scaleability it can be to call external systems with unpredictable performance characteristics. (eg. SQL-Server, WebServices, COM component, java, etc).
The problem here is you will be "hogging" your ASP.NET thread for the duration of the transaction. You may not feel this is a problem because you clearly can't return the page until this external transaction has completed so you may as well wait. You might even turn this into an async call so you can do something else in parallel however you still end up ultimately waiting for the remote system to complete before you can complete your page construction and return the page to the caller.
So why is this so bad?
Well the problem is you have a finite number of threads available in the Thread Pool to process page requests so if you are "hogging" them in your pages while you wait for an external system to respond you will be blocking other page requests.
To demonstrate this I knocked up two aspx pages; one which did nothing and another which spun up a new thread which waited for 30 seconds before completing.
When I ran my nothing page alone using ACT over 5 mins I got the following results:
Response Code: 200 - The request completed successfully. Count: 141,575 Percent (%): 100.00
When I ran my nothing page and my badly threaded page at the same time using ACT over 5 mins I got the following results (I started my thread page first which allowed it to grab all the available threads!):
Response Code: 503 - The service is temporarily overloaded. Count: 45,169 Percent (%): 99.92
Response Code: 500 - The server encountered an unexpected condition that prevented it from fulfilling the request. Count: 36 Percent (%): 0.08
So as you can see from the results above my badly threaded page completely blocked all activity on the web server for 5 whole minutes!
It is also worth noting that the processor utilisation during this period was almost zero so the web server was genuinely busy doing nothing!
I have included the source code for my badly threaded page below. In my example I use "raw" threads however remember the same applies to async calls to SQL Server, Web Services, etc. I think this is particularly important when using 3rd party Web Services because your partners behaviour will have a direct impact on yours. You need to protect yourself from a scenario where your partner's response times drops through the floor or they become unavailable. Remember this effect can be chained so they might be busy waiting on someone else!
So finally what is the solution? Well Fritz Onion has written a couple of really good articles on this both for ASP.NET 1.1 and ASP.NET 2.0 so there is no point in my repeating his excellent advice.
These articles can be found at:
Use Threads and Build Asynchronous Handlers in Your Server-Side Web Code (ASP.NET 1.1)
Async Pages in ASP.NET 2.0 - some results
The moral of this story; Be careful what code you write - I might just kill your web server!
The source code for my busy page is below.
public class WebForm1 : System.Web.UI.Page
{
Thread _t;
DateTime _start;
protected System.Web.UI.WebControls.Label Label1;
private void Page_Load(object sender, System.EventArgs e)
{
_start = DateTime.Now;
_t = new Thread(new ThreadStart(MyThread));
_t.Start();
}
private void MyThread()
{
string setting = ConfigurationSettings.AppSettings["ThreadWait"];
int wait = int.Parse(setting);
Thread.Sleep(wait);
}
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender (e);
_t.Join();
Label1.Text = "Started - " + _start.ToLongTimeString()+"
";
Label1.Text += "Finished - " + DateTime.Now.ToLongTimeString();
}
#region Web Form Designer generated code
override protected void OnInit(EventArgs e)
{
//
// CODEGEN: This call is required by the ASP.NET Web Form Designer.
//
InitializeComponent();
base.OnInit(e);
}
///
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
///
private void InitializeComponent()
{
this.Load += new System.EventHandler(this.Page_Load);
}
#endregion
}
A friend of mine recently commented on the apparent stupidity of having a Milk Helpline listed on your regular bottle of semi-skimmed. To his surprise I told him this was a Maven Trap.
The obvious next question is "What is a Maven Trap?" To answer this properly I decided to buy him a copy of Malcolm Gladwell's excellent book The Tipping Point which is where I first came across the term.
To my surprise the chapter which talks about Maven Traps has been deleted from the current paperback edition of The Tipping Point so I contacted Malcolm Gladwell to find out why. He wasn't aware the chapter had been removed either but was kind enough to give me permission to re-publish it on my blog so here it is:
Finding the Mavens
Malcolm Gladwell, The Tipping Point
Whenever I look at an unopened bar of Ivory bath soap, I flip it over and burst out laughing. In the midst of all the production information, there is a line that says: “Questions? Comments? Call 1-800-395-9960.” Who on earth could ever have a question about Ivory soap? In fact, who on earth would ever have a question about Ivory soap so important that they felt compelled to call the company right away? The answer, of course, is that while most of us would never dial that number, a very small percentage of profoundly weird people may well feel compelled from time to time to call in with a question. These are people who feel passionate about soap. They are the soap Mavens, and if you are in the soap business you had better treat those soap Mavens well because they are the ones whom all their friends turn to for advice about soap.
The Ivory soap 800 number is what I call a Maven trap – a way of efficiently figuring out who the Mavens are in a particular world – and how to set Maven traps is one of the central problems facing the modern market-place. For the better part of a century, we defined influence in this country in the form of status. The most important influence in making up our minds, we were told, was the people who made the most money and who had the moist education and who lived in the choicest neighbourhoods. The virtual of this notion was that these kinds of people were easy to find: in face, and entire industry in the marketing world was created around the convenient delivery of long lists of people who had graduate degrees, made lots of money, and lived in nice neighbourhoods. But Connectors, Mavens, and Salesmen are a little different. They are distinguished not by worldly status and achievement, but by the particular standing they have among their friends. People look up to them not out of envy, but out of love, which is why these kinds of personalities have the power to break though the rising tide of isolation and immunity. But love is a very difficult thing to track. How on earth do you find these kinds of people?
This is a question that I’ve been asked again and again over the last year, and there is no easy answer. Connectors, I think, are the sorts of people who don’t need to be found. They make it their business to find you. But Mavens are a little harder, which is whey it is so important, I think, to come up with strategies for finding Mavens – Maven traps.
Consider the experiences of Lexus. In 1990, just after Lexus frst introduced its line of luxury cars in the United States, the company realized that it had two minor problems with its LS400 line that required a recall. This situation was, by any measure, an awkward one. Lexus had decide, from the beginning, to build its reputation around quality workmanship and reliability. And now, within little more than a year of the brand’s launch, the company was being forced to admit to problems with its flagship. So Lexus decided to make a special effort. Most recalls are handled by making an announcement to the press and mailing a notification letter to owners. Lexus, instead, call each owner individually on the telephone the day the recall was announced. When the owners picked up their cars at the dealership after the work was completed, each car had been washed and the tank filled with gas. If an owner lived more than a hundred miles from a dealership, the dealer sent a mechanic to his or her home. In one instance, a technician flew from Los Angeles to Anchorage to make the necessary repairs.
Was it necessary to go to such lengths? You could argue that Lexus overreacted. The problems with the car were relatively minor. And the number of cars involved in the recall – so soon after Lexus had entered the marketplace- was small. Lexus would seem to have had many opportunities to correct the damage. The key fact, though, was not the number of people affected by the recall but the kind of people affected by the recall. Who, after all, are the people willing to take a chance and buy a brand-new luxury model? Car Mavens. There may have been only a few thousand Lexus owners at that point, but they were car experts, people whose friends ask them for advice about cars. Lexus realized that it had a captive audience of Mavens and that if they went the extra mile they could kick-start a word-of-mouth epidemic about the quality of their customer service – and that’s just what happened. The company emerged form what could have been a disaster with a reputation for customer service that continues to this day. One automotive publication later called it “the perfect recall.”
This is the perfect Maven trap – using the recognition that sometimes a specific time or place or situation happens to bring together a perfect Maven audience.
I would also strongly recommend Malcolm's new book Blink.
We are next running our very popular "New Technology Seminar" on Wednesday 27th April.
http://www.sentient.co.uk/newtechnologyseminar.aspx
Microsoft has kindly sponsered this event which means we can offer FREE entry for this time only.
In the seminar we will be showing you how you can use the following technologies to improve your business effectiveness:
Blogging - weblogs, blogs, rss, ezine replacement, subscription revenue
Bluetooth - bluejacking, tracking, prescence
Google - SEO, AdWords, AdSense
Skype - VoIP, SkypeOut, SkypeAPI
Firefox - What makes this new browser so special?
Instant Messenger - MSN Messenger, Video conferencing
Wireless - WiFi, Hotspots, Roaming.
Mobile Devices - SmartPhone, Tablet PC, Pocket PC (PDA)
Location Data - MapPoint, GPS, GIS Mapping
Website Security - Avoiding SQL Injection, Cross-site Scripting
Viruses and Spyware - Prevention is better than cure
I am also constantly looking for new technology which can help us be more effective. Please add your recommendations to this blog.
Thanks
Jonathan
VBUG (Manchester) in association with INETA have asked me to talk about System.Threading at their meeting on Wednesday 9th March.
For more information please follow this link:
http://www.vbug.net/events/default.asp?region=Manchester (too be listed soon)
Hope to see you there.
Jonathan
PS. When I originally posted this the topic was SharePoint so if you are seeing cached/aggregated version of this that explains the disparity.
VBUG (Essex) in association with INETA have asked me to talk about System.Threading at their meeting on Monday 4th April.
For more information please follow this link:
http://www.vbug.net/events/default.asp
Hope to see you there.
Jonathan
VBUG (London) in association with INETA have asked me to talk about SharePoint at their meeting on Wednesday 15th March.
For more information please follow this link:
http://www.vbug.net/events/default.asp?region=London
Hope to see you there.
Jonathan
During my System.Threading talk last night one of the questions from the audience asked what the IsBackground property on the Thread classes was used for.
The intellisense for this property says "Gets or Sets a value indicating whether or not the tread is a background thread." which to be honest I think we could have guessed! What this description lacks is any information about why you would want to set it.
It's always embarrassing when you can't immediately answer a question from the audience but in this case I was stumped so I promised to investigate the answer and here it is.
The IsBackground property allows you to tell the runtime if the thread is a foreground (UI) thread or a background thread. By default all threads are created as foreground threads unless you change this property.
So why do we care?
Well, the .NET runtime does something special when the last foreground thread terminates - it aborts all the background threads and terminates the application. (We know Thread.Abort is bad however if the application is being shutdown anyway that badness has a very limited duration.)
This can be nicely illustrated by creating a WinForms application which launches a background thread which runs forever. By default when you run your application it will run forever unless you explicitly abort the background thread or the application. This can be seen by running your simple application, closing the Form1 window and noticing the application is still running.
If however you set IsBackground to true after creating your background thread:
Thread t = new Thread(new ThreadStart(BackgroundThread));
t.IsBackground = true;
t.Start();
You will notice that when you close the only window (Form1) the application will terminate.
I will certainly be updating our System.Threading presentation to explain this behaviour more clearly in the future. A great question and something we should all be doing.
VBUG (Coventry) in association with INETA have asked me to talk about System.Threading at their meeting on Tuesday 15th February.
For more information please follow this link:
http://www.vbug.net/events/default.asp?region=Leicester
Hope to see you there.
Jonathan
VBUG (Horsham) in association with INETA have asked me to talk about SharePoint at their meeting on Thursday 10th February.
For more information please follow this link:
http://www.vbug.net/events/default.asp?region=Portsmouth
Hope to see you there.
Jonathan
I've seen many, many requests on the Microsoft newsgroups asking how you can
insert a picture into a Word document without saving it to the file system
first.
This example application described in this blog illustrates both methods for
inserting a picture firstly using the Word object model
(InlineShapes.AddPicture) and secondly using Word's XML support (InsertXML).
The Word object model code is very straight forward:
private
void
buttonInsertNormal_Click(object
sender, System.EventArgs e)
{
object
oMissing = Type.Missing;
object
oCollapseEnd = Word.WdCollapseDirection.wdCollapseEnd;
_wordApp.ActiveDocument.Content.Collapse(ref
oCollapseEnd);
string
filename = @"c:\temp\WordMLImage.bmp";
try
{
pictureBox1.Image.Save(filename);
_wordApp.ActiveWindow.Selection.Range.InlineShapes.AddPicture(filename,
ref
oMissing,
ref
oMissing,
ref
oMissing);
}
finally
{
File.Delete(filename);
}
}
The only problem with this code is the requirement to write the image to the
file system so Word can reload it. There are many circumstances where you
would prefer to avoid touching the file system or maybe your application does
not have write permission.
Fortunately there is an alternative approach using Word's XML support.
This approach requires us to constructing a WordML document fragment
representing the image to be inserted and then calling Word's InsertXML
function to place the image in the document.
The code to do this is as follows:
private
void
buttonInsertWordML_Click(object
sender, System.EventArgs e)
{
object
oMissing = Type.Missing;
object
oCollapseEnd = Word.WdCollapseDirection.wdCollapseEnd;
_wordApp.ActiveDocument.Content.Collapse(ref
oCollapseEnd);
try
{
PictWriter pw =
new
PictWriter(pictureBox1.Image, "Example Image", "Example Image");
_wordApp.ActiveWindow.Selection.Range.InsertXML(pw.ToString(),
ref
oMissing);
}
catch
(Exception ex)
{
// do
something sensible here.
}
}
You will notice the use of a custom class called Sentient.WordML.PictWriter
which handles the task of converting the image into the WordML document
fragment.
The complete class source is included in the zip download at the end of the
article however the core elements are the following two methods.
WriteDoc handles writing the WordML document wrapper. InsertXML expects a
complete WordML document including full namespace references and style
definitions if you are using styles (we are not).
WritePict handles writing the actual image data into the XML stream as Base64.
///<summary>
/// Write the whole WordML document
///</summary>
///<param
name="wtr">The XmlTextWriter to write
to</param>
protected
void
WriteDoc(XmlTextWriter wtr)
{
// start <xml>
tag
wtr.WriteStartDocument();
// add processing
instructions
wtr.WriteProcessingInstruction("mso-application",
"progid=\"Word.Document\"");
// start
<wordDocument> tag
wtr.WriteStartElement("w", "wordDocument", WordMLNS);
// write
namespaces
foreach(string
prefix
in
_namespaces.AllKeys)
{
wtr.WriteAttributeString("xmlns", prefix,
null, _namespaces[prefix]);
}
// start <body>
tag
wtr.WriteStartElement("body", WordMLNS);
// call WritePict to
add our image to the Xml stream.
WritePict(wtr);
// end <body>
tag
wtr.WriteEndElement();
// end
<wordDocument> tag
wtr.WriteEndElement();
// end <xml>
tag
wtr.WriteEndDocument();
}
///<summary>
/// Write the Pict WordML element
///</summary>
///<param
name="wtr">The XmlTextWriter to write
to</param>
protected
void
WritePict(XmlTextWriter wtr)
{
if(_data==null)
{
return;
}
// start <pict>
tag
wtr.WriteStartElement("pict", WordMLNS);
// start
<binData> tag
wtr.WriteStartElement("binData", WordMLNS);
wtr.WriteAttributeString("name", WordMLNS,
string.Format("wordml://{0}{1}", _name,
_extension));
// write the image as
Base64
wtr.WriteBase64(_data, 0, _data.Length);
// end
<binData> tag
wtr.WriteEndElement();
// start
<shape> tag which describes the shape containing the image
wtr.WriteStartElement("shape", VMLNS);
wtr.WriteAttributeString("id", "_x0000_" + _name);
wtr.WriteAttributeString("style",
string.Format("width:{0}px;height:{1}px",
_width, _height));
// start
<imagedata> tag which links to the <binData> above.
wtr.WriteStartElement("imagedata", VMLNS);
wtr.WriteAttributeString("src",
string.Format("wordml://{0}{1}", _name,
_extension));
wtr.WriteAttributeString("title", OfficeNS, _title);
// end
<imagedata> tag
wtr.WriteEndElement();
// end <shape>
tag
wtr.WriteEndElement();
// end <pict>
tag
wtr.WriteEndElement();
}
As you can see the InsertXML approach requires a little more code however does
achieve our objective of not touching the file system.
The only drawback I've found with the InsertXML approach is you dont get the
image autosizing functionality which means if you want the image to be reduced
or enlarged in size you'll have to do this yourself in code either by resizing
the image before inserting it or using the Word object model to manipulate the
InlineShape properties after it has been inserted.
A major bonus is that using InsertXML is considerably faster than writing
the image to the file system and then calling InlineShape.Add so you get a
considerable performance improvement especially if you are working with large
images.
The source for this experimentation can be downloaded from the following page on
the Sentient website:
http://www.sentient.co.uk/WordMLInsertPicture.aspx
If you know of a better way to do this please add a comment to this blog.
In a recent project we had a scenario where we had to find all the paragraphs in
a particular style eg. Heading 1, Heading 2, etc. to build a custom table of
contents.
My initial thought was to iterate over the document paragraphs looking for the
particular style we were interested in.
The code looked like this:
private
void
FindStyleUsingParagraphIteration(Word.Document doc,
string
style)
{
foreach
(Word.Paragraph p
in
doc.Paragraphs)
{
Word.Style s =
(Word.Style)p.Range.get_Style();
if
(s.NameLocal == style)
{
string
text = p.Range.Text;
int
page = (int)
p.Range.get_Information(Word.WdInformation.wdActiveEndPageNumber);
Single vert = (Single)
p.Range.get_Information(Word.WdInformation.wdVerticalPositionRelativeToPage);
Message +=
string.Format("
Style: {0}, text: {1}, page: {2}, vert: {3}",
s.NameLocal, p.Range.Text, page, vert.ToString());
}
}
}
This worked perfectly however it was a little slow so I
looked for a better approach.
My second attempt was to use Word's built in Find functionality to find
paragraphs in a particular style.
The code looked like this:
private
void
FindStyleUsingFind(Word.Document doc,
string
style)
{
object
oMissing = Type.Missing;
object oTrue =
true;
object
oStyle = style;
Word.Range r = doc.Content;
bool found =
true;
while
(found)
{
try
{
r.Find.ClearFormatting();
r.Find.set_Style(ref
oStyle);
found = r.Find.Execute(ref
oMissing,
ref
oMissing,
ref
oMissing,
ref
oMissing,
ref
oMissing,
ref
oMissing,
ref
oMissing,ref
oMissing,
ref
oTrue,
ref
oMissing,
ref
oMissing,
ref
oMissing,
ref
oMissing,
ref
oMissing,
ref
oMissing);
}
catch
{
found =
false;
}
if
(found)
{
string
text = r.Text;
int
page = (int)
r.get_Information(Word.WdInformation.wdActiveEndPageNumber);
Single vert = (Single)
r.get_Information(Word.WdInformation.wdVerticalPositionRelativeToPage);
Message +=
string.Format("
Style: {0}, text: {1}, page: {2}, vert: {3}",
oStyle.ToString(), r.Text, page, vert.ToString());
}
}
}
The performance of this approach was much better
however it resulted in the Word status bar flickering as it performed the Finds
so I started looking for a third option.
My third attempt was to use Word's built in XML support and write an xpath
against the WordML schema.
The code looks like this:
private void FindStyleUsingXPath(Word.Document doc, string style)
{
try
{
string schema =
"xmlns:w=\"http://schemas.microsoft.com/office/word/2003/wordml\"";
string xPath = string.Format("//w:p[descendant::w:pStyle[@w:val='{0}']]/w:r/w:t",
style);
Word.XMLNodes nodes
= doc.SelectNodes(xPath,schema,false);
if (nodes != null)
{
foreach (Word.XMLNode node in nodes)
{
int page = (int)
node.Range.get_Information(Word.WdInformation.wdActiveEndPageNumber);
Single vPos = (Single)
node.Range.get_Information(Word.WdInformation.wdVerticalPositionRelativeToPage);
string text =
node.Text;
Message += string.Format("
Page (0), vert (1), (2)\n", page, vPos, text);
}
}
}
catch(Exception ex)
{
Message +=
ex.Message;
}
}
Sadly this is where it all went wrong. The code ran
fine (without any exceptions being thrown) however it returned no
results!
The first thing I noted was that it took longer to return no results
than my Find attempt took to do the job properly so this isn't a good approach
in terms of performance anyway however it was strange that it didn't return
any results. After some investigate I found that the wordml namespace was
not registered in the doc.XMLSchemaReferences collection so I thought that maybe
I should add it manually using the following code:
object oNamespaceURI
= "http://schemas.microsoft.com/office/word/2003/wordml";
object oAlias =
"w";
object oFileLocation
= @"C:\Program Files\Microsoft Office 2003 Developer Resources\Microsoft Office
2003 XML Reference Schemas\WordprocessingML Schemas\w10.xsd";
string schema = string.Format("xmlns:{0}=\"{1}\"",oNamespaceURI,
oAlias);
doc.XMLSchemaReferences.Add(ref oNamespaceURI, ref oAlias, ref oFileLocation, false);
Unfortunately this code generates the following exception "This schema
cannot be used because it attempts to declare a namespace reserved by Word." so
that isn't the answer.
Finally in frustration I emailed a buddy of mine on the Office team who confirmed
definatively that SelectNodes(...) would only work custom schemas and
not WordML. So there you have it, don't waste your time like me trying to do
this until at least the next version. (I have submitted a feature
request).
For those of you who are interested in the relative performance here are the stats for my test
document:
Using Paragraph
iteration: 10.1445872 seconds
Using Find:
1.9928656 seconds
Using XPath: 4.9771568
seconds (failed)
My conclusion was to use the Find.Execute method and put up with the status
bar flickering!
The source for this experimentation can be downloaded from the following page
on the Sentient website:
http://www.sentient.co.uk/wordFindStyle.aspx
If you know of a better way to do this please add a comment to this blog.
I am often asked how to intercept the postback when an <asp:Button/> is
clicked. The answer is not immediately obvious but once you know the
trick it is very straight forward.
What makes this tricky is that an <asp:Button/> renders as <input
type="submit" ... /> which means that the page will submit automatically
when the button is clicked.
This appears to not give you an opportunity to intercept the click however
the first little bit of magic you need to know is that if the button has an
"onclick" event that will fire before the page is submitted and the
second little bit of magic to know is that the boolean return value from your
"onclick" event determines if the page submission continues (return
true) or not (return false).
So, this sample code will ask you to confirm the postback and provide you and
option to cancel the submittion:
WebForm1.aspx
<%@ Page language="c#"
Codebehind="WebForm1.aspx.cs" AutoEventWireup="false"
Inherits="ButtonClick.WebForm1" %>
<!DOCTYPE
HTML
PUBLIC
"-//W3C//DTD HTML 4.0 Transitional//EN"
>
<HTML>
<HEAD>
<title>WebForm1</title>
<meta
name="GENERATOR"
Content="Microsoft
Visual Studio .NET 7.1">
<meta
name="CODE_LANGUAGE"
Content="C#">
<meta
name="vs_defaultClientScript"
content="JavaScript">
<meta
name="vs_targetSchema"
content="http://schemas.microsoft.com/intellisense/ie5">
</HEAD>
<body>
<form
id="Form1"
method="post"
runat="server">
<P>Click
the button to trigger the confirmButton() javascript function.</P>
<P><asp:Button
id="ButtonSubmit"
runat="server"
Text="Submit"></asp:Button></P>
<P><asp:Label
id="LabelStatus"
runat="server">Status:
</asp:Label></P>
</form>
</body>
</HTML>
WebForm1.aspx.cs
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
namespace ButtonClick
{
public
class WebForm1 : System.Web.UI.Page
{
protected System.Web.UI.WebControls.Button ButtonSubmit;
protected System.Web.UI.WebControls.Label LabelStatus;
private void Page_Load(object
sender, System.EventArgs e)
{
string jsConfirm = @"<script language='javascript'>
function confirmButton()
{
var agree=confirm('Are you sure you wish to continue?');
if (agree)
return true;
else
return false;
}
</script>";
Page.RegisterClientScriptBlock("confirm",jsConfirm);
ButtonSubmit.Attributes.Add("onclick","return confirmButton();");
}
#region Web Form Designer generated code
override protected
void OnInit(EventArgs e)
{
//
// CODEGEN: This call is required by the ASP.NET Web Form Designer.
//
InitializeComponent();
base.OnInit(e);
}
/// <summary>
/// Required method for Designer support -
do not modify
/// the contents of this method with the
code editor.
/// </summary>
private void InitializeComponent()
{
this.ButtonSubmit.Click += new System.EventHandler(this.ButtonSubmit_Click);
this.Load += new System.EventHandler(this.Page_Load);
}
#endregion
private void ButtonSubmit_Click(object
sender, System.EventArgs e)
{
LabelStatus.Text = "Status: ASP Button (Server click)";
}
}
}
Easy when you know how!
A colleague of mine, Nick Francis, has just blogged about the importance of backing up your Reporting Services security key.
http://www.sentient.co.uk/blogs/nickfr/2.aspx
Without this important step you are in real trouble if someone changes the security credentials used by Report Services in your environment.
As Nick says, "Do it now!"
More Posts
Next page »