State Street Gang
.NET, straight up

WiX 3.0 Hates Subfolders in VS 2008 Projects

May 19, 2008 16:07 by will

First time using the WiX install project in VS 2008 for me, and its been interesting.  I like it over all, but I do miss the automation of the standard Visual Studio installer project.

I did run into an aggravating issue that I managed to figure out a solution for.  I present it here for your edification.

E_FAIL == Epic Fail?

After working hard at getting my WiX project to successfully install a simple website, I was feeling pretty good about it.  But the next time I loaded up Visual Studio and opened my solution, I got the following error:

Error HRESULT E_FAIL has been returned from a call to a COM component.

That was helpful.  After Ok-ing the dialog away, I saw that my WiX project was grayed out and marked unavailable.  Super.  I tried a number of different things:

More...

Tags:
Categories: Tips | WPF
Actions: E-mail | Permalink | Comments (3) | Comment RSSRSS comment feed

Regex and WPF

May 13, 2008 09:41 by Will

Regex... Love it or Leave Programming

I love regular expressions, but I may be one of the few developers who doesn't have a hate/hate relationship with them.  Some guy once said,

Some people, when confronted with a problem, think “I know, I’ll use regular expressions.” Now they have two problems.

Personally, I think that guy is...  Well, just look at his homepage and you'll figure it out. 

My answer to people who hold this opinion (whether they are joking or super-cereal) is that regex is the language of parsing text.  There are other ways to parse text, but they suck in comparison.  Stop being lazy and learn it. 

Still not in agreement?  Let me put it another way,

Some people, when confronted with relational data, think "I know, I'll use SQL."  Now they have two problems.

If you have the requirement to deal with relational data in your application, you'd better know SQL.  SQL is the language of parsing relational data.  Yes, there are other ways to parse and handle relational data.  There is nothing to say that you can't scan through data tables in a sequential manner using procedural code, but it would suck in comparison. 

That's not to say Regex or SQL is easy.  But good things rarely are easy, and powerful things never are.  With the right tools you can make them easier. 

More...

Tags:
Categories: WPF | Regex
Actions: E-mail | Permalink | Comments (8) | Comment RSSRSS comment feed

Scroll Selected Text Into View in WPF

May 2, 2008 17:08 by Will

(Illness and work has been kicking my ass for the past couple weeks; I'm posting a quick how-to so my parents don't worry.  I'll be finishing up the crypto series next week, I swear!)

I've been doing lots of work in WPF over the last few weeks, getting familiar with xaml for a major project at work (xaml templating and XPS documents, cool stuff).  Getting to re-know standard windows forms controls in their new WPF flavors has been interesting.  Every once in awhile you come on an issue that takes a while to figure out.

One of those I just encountered was getting the WPF version of the TextBox to scroll a particular chunk of text into view.  Here's the scenario:

  • Text is pasted into a multiline TextBox without wrapping that overflows the available visual space
  • Both horizontal and vertical scrollbars appear
  • In code, a word is selected.  That word happens to be outside the visible area of the TextBox.

In this situation, the question becomes how to scroll this selected text into view via code so the user can see the selected text.

The standard methods don't work.  The obvious methods for scrolling (I'll skip them for brevity's sake) only scroll the TextBox vertically, or take a value that cannot easily and reliably be determined from the available information. 

The solution is to use the BringIntoView method.  The problem is that this method is designed to communicate to an owning ScrollView in order to tell it to scroll.  If you call this on the TextBox and the TextBox is using an internal ScrollView, the method fails to work.

You must turn off your TextBox' scrolling and place the TextBox into a ScrollView.  Here's the xaml:

<Border
  DockPanel.Dock="Top"
  BorderBrush="Gray"
  BorderThickness="1">
  <ScrollViewer
   VerticalScrollBarVisibility="Auto"
   HorizontalScrollBarVisibility="Auto">
    <TextBox
     BorderThickness="0"
     Name="_inputString"
     AcceptsReturn="True"
     TextWrapping="NoWrap"
     GotMouseCapture="TextBox_GotFocus"
     Text="{Binding Path=InputString, 
        UpdateSourceTrigger=PropertyChanged}">
    </TextBox>
  </ScrollViewer>
</Border>

Notice the Border; the ScrollView doesn't display a border, so you have to create one and turn off the TextBox' so it doesn't look weird.  Here's the codebehind:

_inputString.Select(m.Index, m.Length);
Rect start = _inputString
  .GetRectFromCharacterIndex(m.Index, true);
Rect end = _inputString
  .GetRectFromCharacterIndex
    (m.Index + m.Length, true);
_inputString.BringIntoView(Rect.Union(start, end));
_inputString.Focus();

The m object in this case is actually a Regex Match object and _inputString is the TextBox; the code is from a databound wpf regex tool I'm working on.  I can select text using the start index of the text and the length of the selection.

The BringIntoView method takes a Rect struct.  The GetRectFromCharacterIndex method returns a Rect that covers an individual character of the text; we want to bring the whole selection into view so we need the union of the Rect from the first and last characters of the selection.

Its a little sloppy.  I haven't done much testing on this; guaranteed that if the first character Rect is on line 278 and the last is on line 279 something odd might happen, but it does work.

Update

Adding this to the scrolling code allows you to scroll to the first half of the text if the selection contains a newline:

if (_inputString.SelectedText.Contains(Environment.NewLine))
{
    string selected = _inputString.SelectedText;
    int length = selected.IndexOf(Environment.NewLine);
    end = _inputString.GetRectFromCharacterIndex
        (m.Index + length, true);
}
else
{
    end = _inputString.GetRectFromCharacterIndex
        (m.Index + m.Length, true);
}
kick it on DotNetKicks.com
Tags:
Categories: Tips | WPF
Actions: E-mail | Permalink | Comments (0) | Comment RSSRSS comment feed

Creating an XPS document in memory via the DOM

March 14, 2008 12:09 by Will

Most of the examples out there covering the creation of XPS documents use the file system as a backing store.  This isn't optimal; in a server situation its downright stupid.  So, how do you create an XPS document in memory?

I've created a sample project (VS 2005) with the code in this article.  Jump down to the bottom of the post to download it and follow along.

A word (or many) about Packages

XPS documents use the Open Packaging Conventions, which are a shared description of how content can be organized within a package.  A package, in this case, is a collection of other content types that can be addressed as a single object.  Content can be accessed within a package via URIs, loaded on demand rather than all at once.  An advantage of this can be seen when accessing a document over a network. 

Office 2007 documents adhere to these conventions.  For example, a word document contains not only the text of the document but the fonts, images, edit history, etc.  All of these parts are different, but they are combined together within the package to create the whole document.

XPS documents are the same.  The in-memory representation of an XPS document works closely with its Package to manage its content.  Packages, as mentioned before, are collections of parts. These parts may be local, or they may be located on another server.  Because of this, loading of package parts can be slow.  To speed up loading of package parts, the PackageStore is used to manage references to these parts and perform local caching. 

So, when creating XPS documents in memory, what we are actually doing is saving parts of the XPS document to a package managed by the PackageStore.  It is due to the flexibility and the power of the design that our job is a bit more complex than just new-ing up an XPS document and adding children to it.

Enough, how about some code?

The first steps to creating an XPS document in memory are to create an XpsDocument and a MemoryStream.  The XpsDocument object controls how parts are added to the package, and the package stores these parts in the MemoryStream.  Remember, the MemoryStream must be disposed when you are done with it, and you are done with it when you are done with the XPS document; either because you are discarding it or because you have written it to whatever backing store you are using (disk, database, etc).

XpsDocument doc;
ms = new MemoryStream(); 

Next, we open a Package on the MemoryStream.  We must tell the Package that we are creating a new document on this stream, and that we wish to be able to read and write to the document.  I wish these'd be called StreamMode and StreamAccess, but since streams came from the file system originally, these enums bear the marks of their heritage.  In addition, we create a PackageStore to manage our Package.  This uses a URI to reference our XPS document, so we create one using the "pack:" URI scheme, since the document is located in a local Package.  The same URI marks our XPS document

Package p = Package.Open(ms, FileMode.Create, FileAccess.ReadWrite);
Uri DocumentUri = new Uri("pack://document.xps");
PackageStore.AddPackage(DocumentUri, p);
doc = new XpsDocument(p, 
	CompressionOption.NotCompressed, 
	DocumentUri.AbsoluteUri);

Creating our DOM in memory is the next step.  Its pretty simple to understand, for the most part.

XPS documents contain one or more FixedDocuments within a "fixed document sequence," which is an ordered collection of individual FixedDocuments.  Each FixedDocument contains one or more pages, represented by PageContent objects.  Each PageContent can have one single child; since XPS documents are designed for fixed layouts, the most obvious type to use as a child is the FixedPage.  For some reason, you cannot add a FixedPage directly to a PageContent; you must first explicitly cast the content page as IAddChild.  I'm not sure exactly why this is.  If anybody has a clue, a comment would be nice!

FixedDocument fd = new FixedDocument();
PageContent pc = new PageContent();
fd.Pages.Add(pc);
FixedPage fp = new FixedPage();
((IAddChild)pc).AddChild(fp);
TextBlock tb = new TextBlock();
tb.Text = "Page one";
fp.Children.Add(tb);

Once the FixedDocument is constructed in memory it can be serialized to the package's stream via the XpsDocumentWriter.

XpsDocumentWriter dw =
	XpsDocument.CreateXpsDocumentWriter(doc);
dw.Write(fd);

And that's it.  The only thing to worry about is our MemoryStream object.  Once its disposed, our PackageStore cannot access the parts within the XPS document package.  This means that as long as you wish to manipulate the XPS document you've just created (including viewing it), you must keep the stream open.  Once you are done with the XPS document (e.g., its being discarded or it has been saved to disk) you can dispose of the stream.

The code for this post is included in a working sample project here:


Tags:
Categories: WPF | XAML | XPS
Actions: E-mail | Permalink | Comments (4) | Comment RSSRSS comment feed

A Quick Introduction to XPS

March 4, 2008 08:37 by will

XPS, or the XML Paper Specification, is a (relatively) new document format developed by Microsoft.  XPS is analogous to PDF; both are fixed document formats designed to be platform-independent.  Being fixed, the layout of the document is set at design time and, hypothetically, should remain the same no matter what device produces the document.  While PDFs are based on PostScript, an ancient and crusty language used to plot mammoths and gazelle on cave walls, XPS documents are described using XAML, a superset of the hyper-modern and streamlined (that's why everything is surrounded with < >'s--to keep down on wind resistance) XML.

The primary benefit of describing XPS documents in XAML is that XAML maps directly to objects, their properties and events.  This means that there is a built in document object model (DOM) that can be used to create, load, parse, modify and save XPS documents in memory at runtime.

One of the biggest problems facing developers when trying to learn how to use XPS documents is that the current documentation on MSDN about XPS documents is utter crap.  Hopefully, it will get better.  But until then...

Up next, a quick overview of the DOM, and creating XPS documents in memory. 


Tags:
Categories: WPF | XAML | XPS
Actions: E-mail | Permalink | Comments (0) | Comment RSSRSS comment feed