Welcome to MSDN Blogs Sign in | Join | Help

Good WPF Book

I get lots of questions about books I recommend for learning WPF.  Adam Nathan's Windows Presentation Foundation Unleashed (WPF) has been around for a while, but I still think it's the best WPF book available.  I have my copy on my Kindle

Managing Cultures in a Global Application

I have been doing a lot of work on globalized applications lately and I have learned a lot. I thought I'd try to share some of what I've learned with everyone. One of the first issues we ran into was managing formatting of data for different scenarios. Educating developers on which culture to use when is challenging and you end up not having a unified formatting. Also, if this formatting ever changes, you would need to then go find all the places which need to be changed.  This could be daunting because you don't know the original intent of the developer.  Did the original developer choose "en-US" because she wanted to format it for a developer, or because that was the culture of the database?  It's hard to tell, so we want to avoid getting ourselves into this situation.

We'll start with a simple pattern and word towards a more advanced one.  They all should work, you just end up with some tradeoffs between simplicity and maintainability. In the simple example, we create a class with properties which map to an intent.

public static class Cultures
{
public static CultureInfo UserFormatCulture { get { return Thread.CurrentThread.CurrentCulture; } }
public static CultureInfo UserLanguageCulture { get { return Thread.CurrentThread.CurrentUICulture; } }
public static CultureInfo DatabaseCulture { get { return CultureInfo.InvariantCulture; }}
public static CultureInfo DirectoryCulture { get { return CultureInfo.GetCulture("en-US"); }}
public static CultureInfo DeveloperCulture { get { return CultureInfo.GetCulture("en-US"); }}
}

The UserFormatCulture is the culture used to format numbers, dates, etcetera for display to the user, or to parse information entered by the user. The UserLanguageCulture is used to look up resources in resource files. These two properties simply map to the built-in properties on the current thread used for the same purpose. The DatabaseCulture and DirectoryCulture provide the culture used to read and store data in external systems. This may be en-US or InvariantCulture depending on the system you are using. This was one of the biggest areas our developers made mistakes before we started using this pattern. We had data stored in many cultures and when we read data out of the directory server we'd get parse errors when dates weren't stored in dd/mm/yyyy format of the user's culture. Using this pattern makes it easy for even junior developers to use the right culture, and if you ever need to change, you have a centralized place to make the change.

A more robust implementation could store this information into a config file rather than hard code it in each property. While this still leaves the properties themselves hardcoded, it's a little more flexible because the values can be changed.  Still, you might have to make a change to a core component if you decide that you need 

The next step would be to get rid of the class all together and use something more akin to a service locator. 

var devCulture = ServiceLocator.Resolve<CultureInfo>("DeveloperCulture"); 

A Culture is kind of a service, right?  It provides services for formatting, so I think we're in the clear calling it a service.  It would be nice if it implemented some interface that we could use, but it works.  The service locator model gives us a little more flexibility and allows us to add new intents without changing our core framework.  We can also build some rules into the service locator about fallback cultures, if a requested intent is not configured.

I tend to lean towards Unity as my container, factory, service locator of choice, but you could use whatever you wanted.  In Unity, I could register some well known types like the DeveloperCulture at startup, but we'd need a static factory extension to support getting the UserFormatCulture and UserLanguageCulture. That's because we want to get what the CultureInfo.CurrentUICulture is right now, not what it was during application startup.

Right now I've only implemented the simple solution in the toolkit, but I'll be moving towards the more dynamic solution when I get more time.

Global Toolkit is now in CodePlex

I've been doing a lot of globalization work lately, so to capture some of my best practices around globalization, I've created a CodePlex project. Matt Dotson's Global Toolkit (http://www.codeplex.com/GlobalToolkit) is where I'll be posting code which can be reused in your global applications.

 I've started to implement some stuff, but it is far from done, so take a look and tell me what you think.  I've focused on a currency class so far that supports currency conversion, and currency enumeration.  I've tried to make the toolkit extensible so that you can plug it into your own architecture. 

Presentation Model Pattern in WPF (Part 2)

In part one we looked at the concept of a presentation model. In this post we are going to going to start putting together some crude code to scaffold out our presentation model base classes.  This article should give you the basic idea how things are related, and what purpose they serve.  There are really 3 core classes I use to provide the foundation for my implementation of a Presentation Model Pattern: (1) PresentationModel, (2) ObservableModelCollection, and (3) ObservableModel.  If you think about the data on your screen, you are probably going to have a root data object (your presentation model), but you are also going to have to represent items in a data grid, charting data, etc.  All these items require that you have a collection (actually an ObservableModelCollection) of “sub-models” (ObservableModel). 

We’ll tackle ObservableModel first since it’s the root of our implementation.  ObservableModel provides a base class which actual models can inherit to automatically get notification of changes to the data in the model.  ObservableModel does this by supporting the INotifyPropertyChanged interface which WPF knows to listen to for the PropertyChanged event. 

This is the same interface that DependencyObject implements, so some might ask “Why don’t we build ObservableModel on DependencyObject?”  Well, it turns out I thought about this.  My first attempt at a PM implementation was based off of DependencyObject (DO from now on), but I quickly found out that some of the more advanced services I wanted to offer were difficult to implement because DO abstracted so much away from me.  One of the best examples of this was Undo/Redo.  To implement this, I really wanted to control how property values were stored, but DO hides this from inheritors. I didn’t want to completely hijack DO, so I eventually resolved to create my own set of classes. 

Implementing INotifyPropertyChanged requires that we raise an event whenever a property changes on an object inherited from OM, but how do we know when a property changes?  We don’t really want our inheritors having to raise the event explicitly in every property setter, so we’ll do the same thing DO does, we’ll implement GetValue and SetValue methods.

That’s about all we need right now. While there are a lot of interfaces we will eventually add to our ObservableModel class, we’ll just implement INotifyPropertyChanged for now.

Now that we have the basics down, let’s create PresentationModel.  Our PresentationModel class needs the same features as ObservableModel, fortunately we thought about this ahead of time, and we are just going to inherit PresentationModel from ObservableModel.  We need to add a few more things to PresentationModel though.  First off, we want to be able to initialize our PM with data.  We could do that in the constructor, but there is a better option: ISupportsInitialize.  While there are two methods to control initialization on this interface, we are going to abstract those from our inheritors, and create an abstract method called Initialize().  This is where our inheritors are going to begin getting data for their screen. 

We also need one more thing: CommandBindings.  In order for our PM to accept commands from the View, we need to have some way for the View to figure out which commands we support and send us those commands.  To do this, we’ll create a CommandBindings property which will be filled up by our inheritors.  The View will add this to the Page’s CommandBindings property so commands are automatically forwarded to us from WPF.

Now we have the basics setup.  For good measure we are also going to create ObservableModelCollection.  Why can’t we just use ObservableCollection?  Well for one, there are a bunch of UI Controls (Infragistics in particular) which don’t do a good job of listening to events on sub-models, so we want our ObservableModelCollection to listen to those events and forward some of them up.  Secondly, we are going to need this when we eventually implement Undo/Redo.  ObservableCollection is a good start though, so we’ll use this as our base class and add the event forwarding.

That’s all for now, in part 3 we’ll create a model/view pair which uses these base classes.

Posted by mattdotson | 2 Comments
Filed under: , , ,

My Toolbox

One thing I always find myself doing is showing people the little indisposable tools in my toolbox.  Here's a list of some I couldn't live without.

  1. Reflector (http://www.aisto.com/roeder/dotnet/) - If you only get one tool, this is the one to choose.  Decompile any .NET code.  I can't even count the number of times I've been able to figure out why I'm getting some random exception back from a system function with this tool.  It also faster to use than the .NET documentation, so I use it to lookup API's.  Do I need to call Dispose and then Close, or does Dispose call Close, now I know.

  2. Rhino mocks (http://ayende.com/projects/rhino-mocks.aspx) - I can't imagine unit testing without rhino.

  3. Snoop (http://www.blois.us/Snoop/) - Do you develop WPF apps. This visual tree tool will help you figure out how to restyle almost anything.
  4. Wireshark (http://www.wireshark.org/) - Nothing beats this network sniffer
  5. TFS Sidekick (http://attrice.info/cm/tfs/) - All those things you wish TFS did, sidekick does.
  6. Stylus Studio (http://www.stylusstudio.com/) - hands down the best XML tool on the planet.
  7. The Regulator (http://tools.osherove.com/CoolTools/Regulator/tabid/185/Default.aspx) I love regular expressions, but they can a nightmare to debug.  This tool makes it regex trial and error fast and easy.
  8. Ghost Doc (http://www.roland-weigelt.de/ghostdoc/) - I like writing code, I don't like writing comments. I like ghost doc.
  9. Sandcastle (http://blogs.msdn.com/sandcastle/) - Again, I like writing code, I don't like writing docs.  Make professional looking documentation from your code.
  10. Process Monitor (http://technet.microsoft.com/en-us/sysinternals/bb896645.aspx) What the heck is locking that file? ProcMon knows.

Of course it goes without saying that Visual Studio is the tool that I use most.  But there are a bunch of things in Visual Studio that take beginners some time to figure out. If you don't know what these are, go figure them out now!!

  1. The immediate window - I wish it was more robust, but what it is now is pretty powerful.
  2. Auto format/ auto indent
  3. Refactoring
  4. Code snippets
  5. Automatic "using" statements
  6. Conditional breakpoints
  7. Profiler
  8. Code analysis
  9. Move to last position (CTRL -)
  10. Incremental Search
  11. Find and Replace with Regex
  12. Flagging threads in the thread window

These are just a few things I use to get my job done.  If you don't know what any of these tools or VS features are, I encourage you to try them out.

Posted by mattdotson | 2 Comments

Presentation Model Pattern in WPF

Choosing a design pattern for the user interface of your application involves weighing trade-offs between testability, complexity, developer tool support, and capabilities.  There are two very prominent camps in user interface patterns:  Forms & Controls and Model-View-Something (Controller, Presenter, ViewModel, etc.).  Martin Fowlers’ article GUI Architectures (http://www.martinfowler.com/eaaDev/uiArchs.html ) provides a detailed history and comparison of some of these approaches. 

Many applications use Forms & Controls pattern for their user interface.  This is partially because it is the default pattern Visual Studio uses to create an application and more likely because it is a very simple pattern with a low barrier of entry for developers.  Though Forms & Controls approach may be simpler to implement for small applications which can get away with limited or no unit testing, larger and more complex applications are increasingly difficult to maintain using this approach.  This inherent lack of testability of a Forms & Controls pattern make is a poor choice for implementing larger applications.

To solve this problem of testability, software architects have turned to more advanced user interface patterns such as Model-View-Controller, Model-View-Presenter (a.k.a. Supervising Controller and Passive View), Presentation Model, and other variants of these patterns.  The goal of these patterns is creating user interface classes which can be easily tested, and minimizing the amount of code which resides in the view (a notoriously difficult part of the application to test in an automated fashion).  Though these patterns are technology independent, the underlying GUI framework lends itself to certain patterns.  Windows Forms technology meshed particularly well with the Model-View-Presenter pattern, while implementing a Presentation Model pattern in Windows Forms required masses of synchronization code.  Windows Presentation Foundation, on the other hand, fits very well with the Presentation Model because of its advanced data binding technology which eliminates the need to write masses of synchronization code between the view and presentation model. 

Each screen is implemented as a View and Presentation Model pair.  Each class has distinct responsibilities.  The view is responsible for how data is presented on the screen, and the presentation model is responsible for what gets presented on the screen and the actions which can be performed on that data. 

The view consists of both the XAML and the code-behind, and displays the data it gets from the presentation model.  Most views will be implemented mostly in XAML with a little supporting C#.  Layout and user interaction are the primary responsibilities of the view.  The view interprets user actions and forwards them to the presentation model as commands. 

Commands are used to invoke behavior.  The command pattern decouples the presentation model from the actual input event which invoked some functionality.  Commands capture the intent of the user, not necessarily the actual mechanics of how they indicated that intent.  For example, the presentation model doesn’t need to know if the user clicked the save button, pressed CTRL+S, or selected save from a menu option.  All the presentation model needs to concern itself with is that the data should be saved.  The command object also provides a good mechanism for the presentation model to indicate if an action is enabled through the CanExecute callback of the command.

While the PresentationModel receives communication from the View via Commands, if it needs to notify the View of something, it should use events.  Events allow us to define the means of communication on the presentation model itself.  While some events should be built into the presentation model base class (notifying when properties changed), others are screen specific and should be in the screen’s presentation model.  There is no separate interface implemented by the View as in MVP.  Having the presentation model provide events simplifies unit test code, because you don’t need to create dummy views.

We’ll cover more in part 2.

Posted by mattdotson | 4 Comments
Filed under: ,

Real World GridView is now on CodePlex

Yup.  I heard you (eventually).  You wanted easier access to the source code of Real World GridView.  Well, now you have it.  CodePlex is Microsoft's new shared source development platform, and it should be much easier to use.  Give it a try, here's the link: http://www.codeplex.com/ASPNetRealWorldContr.  I even checked-in some bug fixes you've been asking for to entice you to try the new site!

Posted by mattdotson | 14 Comments

Multi-Process Synchronization with Named Events

Since I am in between real projects right now, I have had some time to work on some side projects.  As I was working on some threading in one of these projects, I noticed that there was a new class in the System.Threading namespace that had not been there in .NET 1.1 ...  EventWaitHandle.  I was curious to see what this new class was.

Background Info:

Windows exposes several types of synchronization primitives (Mutexes, Events, etc.), but in .NET 1.1 we were limited in our use of them.  Sure, we had AutoResetEvent and ManualResetEvent, but the framework did not expose the "named" version of these.  In case you were wondering, the "named" version allows us to share an event between two (or more) processes.  The name makes it easy for multiple processes to say "give me 'EventA' "!!  So before 2.0, we had pretty good support for synchronization within a process, but not very good support across processes.  Yes, in .NET 1.1, we could get to this functionality using PInvoke, but it could get complicated quickly because you can attach security descriptors to an event.  And anyone who has tried to PInvoke to set some ACLs can tell you that security descriptor is a beast that you don't want to be playing with in C#.  Fortunately this has been remedied in the 2.0 framework, and the Access Control Lists are now 1st class .NET objects.  I am going to go out on a limb and speculate that this is why named events were not included in the first version of the framework.

Back on Track:

"Great!" you say, but what the heck does this have to do with EventWaitHandle?  Well, EventWaitHandle is .NET 2.0's implementation of events -- both named and unnamed.  You can see that ManualResetEvent and AutoResetEvent now inherit from EventWaitHandle.

So, let's get started and see what we can create with this.  In this example, WorkerA does some work, which notifies WorkerB who begins some work, which notifies WorkerC to finish up some work.  You can see that we are creating a simple pipeline.  (A real pipeline would keep working, but ours just exits after one iteration).

Let's start by providing some tools for these events to get access to the events.

public static class NamedEvents

{

    public static EventWaitHandle OpenOrCreate(string name, bool initialState, EventResetMode mode)

    {

        EventWaitHandle ewh = null;

        try

        {

            ewh = EventWaitHandle.OpenExisting(name);

        }

        catch (WaitHandleCannotBeOpenedException)

        {

            //Handle does not exist, create it.

            ewh = new EventWaitHandle(initialState, mode, name);

        }

 

        return ewh;

    }

 

    public static EventWaitHandle OpenOrWait(string name)

    {

        EventWaitHandle ewh = null;

 

        while (null == ewh)

        {

            try

            {

                ewh = EventWaitHandle.OpenExisting(name);

            }

            catch (WaitHandleCannotBeOpenedException)

            {

                Thread.Sleep(50);

            }

        }

 

        return ewh;

    }

}

We've provided the tools to create an event if it doesn't already exist or open it if it does, and to open an existing event or wait for it to be created.  Unfortunately there is not a good way to "test" if an event exists, so we just have to catch WaitHandleCannotBeOpenedException.  Here is our code for WorkerB, though the other workers are almost identical except that they set and wait on different events.

static void Main(string[] args)

{

    EventWaitHandle completedA = NamedEvents.OpenOrWait("CompletedA");

    EventWaitHandle completedB = NamedEvents.OpenOrCreate("CompletedB", false, EventResetMode.ManualReset);

    EventWaitHandle pipelineDone = NamedEvents.OpenOrWait("PipelineDone");

 

    Console.WriteLine("{0} Initialized", Process.GetCurrentProcess().ProcessName);

 

    completedA.WaitOne();

 

    for (int i = 0; i < 10; i++)

    {

        Thread.Sleep(250);

        Console.WriteLine("{0} Working: {1:hh-mm-ss.ffff}", Process.GetCurrentProcess().ProcessName, DateTime.UtcNow);

    }

 

    Console.WriteLine("{0} Done", Process.GetCurrentProcess().ProcessName);

 

    completedB.Set();

 

    //wait until the whole pipeline is done.

    pipelineDone.WaitOne();

 

    completedB.Close();

 

    Console.WriteLine("{0} Exiting", Process.GetCurrentProcess().ProcessName);

 

}

In the output, we can see that B waits for A to finish, and C waits for B to finish.

Now, you know how to use named events!!

Posted by mattdotson | 9 Comments

Attachment(s): BlogThreads.zip

Real World GridView: Excel-like Frozen Headers for ASP.NET 2.0

Many of us are familiar with frozen cells in Excel, but it is typically quite difficult to implement something like that HTML.  In this “Real World GridViews”, we investigate adding this functionality to GridView to make frozen headers easy to reuse across pages.   Though I am only going to go over freezing headers here, once you get the idea, it is a short step to freeze columns (right or left) and footers. 

 

 

This is part 3 of “Real World GridViews”, and if you missed part 1 or 2 then you have some reading to do J Here is a link to Part 2: http://blogs.msdn.com/mattdotson/articles/541795.aspx

 

I’m starting with a refresher course before we delve into the details.  Since a web control’s primary purpose is to spit out HTML, it’s pretty darn important to understand exactly what HTML a web control spits out, in relation to what HTML you want it to render.  Really GridView just renders a HTML table, surrounded by a DIV, but it is important to understand that. 

 

<div>

    <table cellspacing="0" rules="all" border="1" id="SampleGrid" style="border-collapse:collapse;">

        <tr>

            <th scope="col">Header</th>

            ...

        </tr>

        <tr>

            <td>Data</td>

            ...
        </tr>
    </table>
</div>

 

Notice that all the properties you set on the GridView (id, borders, etc) end up getting set on the TABLE element.  That is going to be of particular interest to us later in this article.  Now we are ready to get started!!

 

This work is based on the brilliant algorithm of Brett Merkey, and if you want to really understand how it works, visit his web site (http://web.tampabay.rr.com/bmerkey/examples/locked-column-csv.html).  To summarize his work, he has devised a way to use CSS expressions to create the illusion of “relative fixed positioning”.  His algorithm is fixing the position of a TH or TD relative to the surrounding DIV.  In case you’ve never heard of a CSS expression, it is just some JavaScript in the CSS style which reevaluates whenever the page changes (i.e. someone scrolls, or a dependant element changes).  IE does some black-magic to figure out if a style bound to an expression needs to be reevaluated, and seems to err on the side of forcing reevaluation if it’s not sure if something changed. That said, be warned that if you have a lot of elements with CSS expressions applied to them (over a thousand on my machine), CSS expression can use up a LOT of client side CPU, so be judicious in their use.  Secondly, IE is the only browser which supports CSS expressions, so this is an IE only solution.

 

I have made some performance optimizations to Brett’s original code, but conceptually it’s the same thing.  Brett uses getElementById which can get VERY slow with large tables, though he does include a note about using “parentNode.parentNode.parentNode.parentNode.scrollTop” which is a little better.  After some extensive testing, I’ve found an even faster way: “this.offsetParent.scrollTop”.  The key to the outer div being the offset parent is that it’s positioning is set to “relative”, which seems odd because that is the default, but it has to be set! 

 

We are going to make this control completely self contained, so you will not have to deploy a separate CSS with it.  The first thing we need to do is register the styles.  In ASP.NET 1.1, this would have been difficult, but fortunately ASP.NET 2.0 has made this significantly easier.  First we are going to create a class for our style which inherits from “Style”.

 

private class FrozenTopStyle : Style

{

    protected override void FillStyleAttributes(CssStyleCollection attributes, IUrlResolutionService urlResolver)

    {

        base.FillStyleAttributes(attributes, urlResolver);

 

        attributes[HtmlTextWriterStyle.Top] = "expression(this.offsetParent.scrollTop)";

        attributes[HtmlTextWriterStyle.Position] = "relative";

        attributes[HtmlTextWriterStyle.ZIndex] = "2";

    }

}

 

And now that we’ve done that, we can register it with the page.  I’ve added a simple check to make sure that we only register the class once when we have multiple grids on the same page.

 

protected override void OnPreRender(EventArgs e)

{

    base.OnPreRender(e);

 

    if (this.FreezeHeader && !this.Page.Items.Contains(FrozenGridView.FrozenTopCssClass))

    {

        this.Page.Items[FrozenGridView.FrozenTopCssClass] = "Registered";

        this.Page.Header.StyleSheet.CreateStyleRule(new FrozenTopStyle(), null, "." + FrozenGridView.FrozenTopCssClass);

    }

}

 

 Now that we have our styles registered, it’s time to apply those styles to our header.  As you’ve seen in the other articles, we do this after data binding.  The FreezeCells() function just loops through the cells in a header and applies the CSS class to each cell.  It’s interesting to note that this code combines the existing style and our style.  Many people don’t realize that you can assign more than one CSS class to an element just by separating them by a space (i.e. class=“style1 style2” applies both style1 and style2).

 

private void FreezeCells()

{

    if (this.FreezeHeader)

    {

        foreach (DataControlFieldHeaderCell th in this.HeaderRow.Cells)

        {

            th.CssClass = FrozenGridView.FrozenTopCssClass + " " + th.CssClass;

        }

    }

}

 

This is where we run into a challenge.  We have applied the styles to all the header cells, and now we need to apply some styles to the DIV surrounding the GridView’s table.  Unfortunately GridView does not supply us with a way to easily modify the surrounding DIV, and in some cases it doesn’t even display the DIV!!  Because of this, we are forced to override Render().  Using my favorite tool, .NET Reflector, I was able to figure out what GridView’s implementation of Render did, and just add the stuff I needed. 

 

Naturally, you are asking, “what do we need to add”?  Well, we want this thing to scroll, so we need to set the overflow style to something appropriate.  We actually let the page tell us what type of scrolling it wants with our Scrolling property, so we need to translate a ScrollBars variable into the appropriate CSS style.  We’ve created two private properties which help us out.  Here we’ll just look at OverflowX, because OverflowY is very similar:

 

private string OverflowX

{

    get

    {

        if (this.Scrolling == ScrollBars.Horizontal || this.Scrolling == ScrollBars.Both)

        {

            return "scroll";

        }

        else if (this.Scrolling == ScrollBars.Auto)

        {

            return "auto";

        }

        else

        {

            return "visible";

        }

    }

}

 

Secondly, scrolling only happens when the DIV is smaller than the TABLE, so we need to be able to set the Height and Width of the DIV separately from the dimensions of the TABLE.  As we saw in the refresher course at the beginning of this article, GridView’s implementation only decorates the TABLE tag, and does nothing to the DIV.  In order to get the behavior we want, we are going to have to change that.  I’ve overridden the GridView’s default implementation of the Height and Width to set the DIV’s dimensions and added ScrollHeight and ScrollWidth to set the dimensions of the TABLE.

 

public Unit ScrollHeight

{

    get

    {

        return base.Height;

    }

    set

    {

        base.Height = value;

    }

}

 

public override Unit Height

{

    get

    {

        object val = this.ViewState["DivHeight"];

        if (null == val)

        {

            return Unit.Empty;

        }

 

        return (Unit)val;

    }

    set

    {

        this.ViewState["DivHeight"] = value;

    }

}

 

Finally we need to write all these styles to the DIV.  We do that in our override of the Render() function.

 

writer.AddAttribute(HtmlTextWriterAttribute.Id, String.Format(CultureInfo.InvariantCulture, "__gv{0}__div", clientID), true);

writer.AddStyleAttribute(HtmlTextWriterStyle.OverflowX, this.OverflowX);

writer.AddStyleAttribute(HtmlTextWriterStyle.OverflowY, this.OverflowY);

if (!this.Width.IsEmpty)

{

    writer.AddStyleAttribute(HtmlTextWriterStyle.Width, this.Width.ToString(CultureInfo.InvariantCulture));

}

if (!this.Height.IsEmpty)

{

    writer.AddStyleAttribute(HtmlTextWriterStyle.Height, this.Height.ToString(CultureInfo.InvariantCulture));

}

writer.AddStyleAttribute(HtmlTextWriterStyle.Position, "relative");

writer.AddStyleAttribute(HtmlTextWriterStyle.BorderColor, "Black");

writer.AddStyleAttribute(HtmlTextWriterStyle.BorderWidth, "3");

writer.AddStyleAttribute(HtmlTextWriterStyle.BorderStyle, "solid");

writer.RenderBeginTag(HtmlTextWriterTag.Div);

 

That’s it! We now have a GridView which will provide frozen column headers and scrolling just by setting a few properties from the page!!  You can see from the series of articles that most of the time GridView is kind to inheritors, however there are those few cases (like our render function) where simply extending the base implementation just won’t cut it.  When you find yourself in one of these situations, don’t panic, make sure you have .NET Reflector in your toolbox.

 

I’ve posted the code for all three articles @ http://www.codeplex.com/ASPNetRealWorldContr .  You can actually see what I am working on next if you look around!!

Posted by mattdotson | 57 Comments
Filed under:

Finally ... Part 2 of Real World GridViews

GridView is a great control, and out of the box it does a lot of wonderful stuff, but I haven't worked on a project yet where it did everything I needed.  "Real World GridView" is an attempt to share some of the common customizations I end up doing to GridView on a regular basis.

I finally got around to posting the second article in the series.  This article deals with tweaking GridView's inner table to add custom rows and secondly with grouping data in a GridView.
http://blogs.msdn.com/mattdotson/articles/541795.aspx

In case you missed the first article about BulkEditing, you can read it here:
http://blogs.msdn.com/mattdotson/articles/490868.aspx

Posted by mattdotson | 0 Comments
Filed under:

Lambda Expressions: C# has a Lisp??

Until I started looking at C# 3.0, I had never heard of lambda expressions.  A quick glance at Wikipedia tells me that they seem to pre-date computers, and languages like Lisp and Ruby seem to be based on them. But what the heck are lambda expressions??  The short answer is: They are just shorthand anonymous methods.

It took me a little while to get my head wrapped around they syntax (especially the "classical" syntax on wikipedia), but now I get it!!  In the simplest possible example, here is a function which adds one to the parameter:

Func<int, int> increment = A => A + 1;
Console.WriteLine(increment(5)); // outputs 6

Basically, we are defining a function "increment" as its parameter A, plus one.  Ok, that's not so bad, and I know a lot of places I could use a quick little function definition to parse some input or test some conditions.  Given that, I thought I would try something a little harder ... factorial.

Now with factorial, I ran into some immediate problems.  I don't think that lambda expressions are designed to be recursive.  I couldn't find an equivalent to "this" for the expression, and if I reference the function itself, I get an error telling me that I am using an undefined variable.  After some tinkering, I found a way around this.  Lambda expressions can be passed as parameters to other lambda expressions.  So what if I just passed my factorial expression a reference to itself.

Func<long, object, long> f = (A, B) => A == 0 ? 1 : (A*((Func<long,object,long>)B)(A-1,B));
Func<long, long
> factorial = A => f(A, f);

Console.WriteLine(factorial(25).ToString("#,###"));
// outputs 7,034,535,277,573,963,776

And so that's what I did.  I had to pass the function reference as an object because I could not really specify its type.  f's definition would look like this if I tried:

Func<long, Func<long, Func<long, ... and so on ... , long>, long>, long>

The "f" expression does the real factorial work, "factorial" just gets the first iteration going.  Are lambda expressions going to change the way we program??  I don't know.  What do you think?

Lisp and similar languages (which are) seem to be making a comeback, and I had no clue that Orbitz.com is written in Lisp until I read it on wikipedia.  I don't really know anything about Lisp, but it got me thinking that maybe Lisp could be a first class .NET language now that lambda expressions will be baked into the CLR 3.0.  I'll have to look into this further.

Posted by mattdotson | 6 Comments

Using SynchronizationContexts.

SynchronizationContext is new in .NET 2.0, and I haven't found too much written about it yet.  It is used by classes like BackgroundWorker and some other built-in classes, and I wanted to show how you can use it in your code as well.  I came across this while working on my WinCast project where I needed to handle events coming from the RSS synchronization engine on a background thread.  I wanted to handle these events (like a new feed added) by adding new UI elements.  If you've done any work in Windows programming, you are probably aware that accessing the UI from a thread that didn't create the UI is a big no no.  In .NET 1.1, we could use Invoke on a control to get something executed on the UI thread, but that wasn't the greatest solution.  .NET 2.0 brings us SynchoronizationContext which allows us to execute a section of code in the UI context (the thread that created the UI).  Also, it allows us to specify if the background thread will block waiting for the UI thread (using Send) or will not block (using Post).  You can get the UI context from the WindowsFormsSynchronizationContext.Current property.  Here is an admittedly dumb example of a lastUpdatedTime being updated on a background thread, and the UI being properly updated on the UI thread to show the new time in a textbox.

public partial class UsingContextForm : Form
{
   
SynchronizationContext uiContext;
   
System.Timers.Timer backgroundTimer;
   
DateTime lastUpdated = DateTime.Now;

   public UsingContextForm()
   
{
      
// save our ui context for use later
      
uiContext = WindowsFormsSynchronizationContext.Current;

      
InitializeComponent();
   
      
// start a timer on a background thread 
      
backgroundTimer = new System.Timers.Timer(1000.0);
      
backgroundTimer.Elapsed += new System.Timers.ElapsedEventHandler(backgroundTimer_Elapsed);
      
backgroundTimer.Start();
   
}

   void backgroundTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
   
{
      
//This executes on a background thread
      
lastUpdated = DateTime.Now;

      
//Tell the ui thread to execute some code for us.
      
uiContext.Send(new SendOrPostCallback
            
delegate(object state) 
            
{
               
//This executes on the ui thread
               
textBox1.Text = lastUpdated.ToLongTimeString();
            

         
), null);
   
}
}

Posted by mattdotson | 9 Comments
Filed under:

WinCast: A podcast receiver built-on the Windows RSS Foundation.

I saw the Windows RSS API was published the other day and so I just had to create something with it. What better than a podcast receiver!! I’ve gotten into podcasts since I bought myself a Zen MicroPhoto for x-mas. My previous client, Doppler, which I chose because it’s .NET, was using > 300MB of memory, so this seemed like a pretty good project. A couple days ago I began WinCast. It’s still in a rough state, but I’m using it for my podcasting client now, and have been fairly happy (except for the file handles bug below).

About WinCast
WinCast runs in the system tray (incase you were wondering where it went when you started it). It is a very simple UI … after all this is a proof of concept. I left adding feeds to IE. Just browse to the RSS of a feed, and add a subscription to it using IE. When you add the subscription, make sure you save it in the WinCast feed folder and you select it to download enclosures automatically.

About the RSS API The RSS API is a COM based API, which was somewhat disappointing, but oh well. I created a RCW and was on my way. Because it’s COM and not .NET, the code is not as pretty. For instance, you have to call getEnum() on the collections to loop through them, but once you figure it out, it’s not so bad. I am pleased that the object model is very simple, and easy to use. For the most part, I was able to do most of the basic tasks with the API.

I did run into a lot of bugs that prevented me from doing things I wanted to. Here are some of the major ones:
(1) Feed folder and feed events do not seem to work, which is why I had to implement polling of the RSS store.
(2) After looping through many feeds, my process runs out of files handles!! It seems like the API does not dispose of the files it opens. I looked for a way to ensure the files are closed, but I couldn’t figure anything out.
(3) Downloading seems to fail for a few subscriptions
(4) File naming of enclosures doesn’t work so well. KPCC’s Air Talk from NPR names all their shows Hour 1 and Hour 2 for each day. Well, the RSS service, overwrites the previous day’s Hour 1.mp3 with the new day.
(5) I wish there was an identifier for feeds, but for now I ended up making my own.

If you are interested in seeing the source or using WinCast, I posted it on GotDotNet. http://www.gotdotnet.com/workspaces/workspace.aspx?id=ec0ae6b9-f5cd-4e93-898b-197d5d0aa503
Posted by mattdotson | 2 Comments

WPF: Expander Dropdown

Do you ever get sick of hunting through a dropdown that has hundreds of items in no particular order.  I do!!  I've seen far too many LOB apps that feel the need to have hundreds items in a dropdown.  So this week, I created the expander dropdown to at least provide some organization to those massive dropdowns ... I have some spare time since it doesn't look like I'm going to find an XBOX 360 until next summer [:)].  I saw some of Namita's samples and that really sparked the idea, so thanks to her.

Here is the unexpanded view:

And the expanded view:

And, for all of you still with the Sept CTP like me ... I'll try embedding the sample as a web browser application. Let me know how this works.

I removed the inline web browser app -- MD

And of course, the source:

http://mattdot.members.winisp.net/Blog/ExpanderDropdown/WpfBlogSamples.zip

Posted by mattdotson | 8 Comments

Fluid Dynamics with WPF

This week I thought I would test the waters with 3D in WPF.  After seeing this video on Channel 9.  I thought ... "Wow, that's a cool water effect".  Well, I waited and waited for the code to be posted, but to no avail.  So I've started my own little project to attempt this.  Eventually I'll map real windows onto this mesh, but this is just the first run.  Yeah, yeah ... I know mac did it first ... I wonder how many lines of code it took them?

This first step is just to get the water effect.  I had to turn to fluid dynamics to figure out how to do this.  Mathematics for 3D Game Programming had a lot of useful information for simulating the water.  I would highly recommend this book to understand 3D principles.  Here is a screen shot of the first draft:

I learned a lot from this first draft.  Most importantly, it is about a thousand times faster not to recreate the mesh every frame.  This site had some great information on morphing the mesh, as well as some great 3D samples (not all that pretty ... but solid fundamentals).  ScreenSpaceLine3D's are pretty cool for creating a wireframe.  The code I included in MeshBuilder should allow you to easily create wireframes of your 3D meshes.  Unfortunately performance goes to $#!% when I turn on wireframe, so if anyone has some performance optimizations, let me know!

Additionally, I would have liked to use databinding for the camera sliders, but I had difficulty binding 3 sliders to one property on the camera.  I found the MultiBinding class today, I'm going to see if this will let me solve that problem.

 

And the code:


<Window x:Class="LearnAvalonCS.FluidWindow" xmlns="http://schemas.microsoft.com/winfx/avalon/2005"

xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005" Title="LearnAvalonCS" Loaded="WindowLoaded">

   <DockPanel>
      
<
StackPanel Width="200" Orientation="Vertical" DockPanel.Dock="Right">
         
<
StackPanel.Background>
            
<
LinearGradientBrush>
               
<
LinearGradientBrush.GradientStops>
                  
<
GradientStop Color="AntiqueWhite" Offset="0.0"/>
                  
<
GradientStop Color="Silver" Offset="0.3"/>
                  
<
GradientStop Color="Silver" Offset="0.7"/>
                  
<
GradientStop Color="AntiqueWhite" Offset="1.0"/>
               
</
LinearGradientBrush.GradientStops>
            
</
LinearGradientBrush>
         
</
StackPanel.Background>
         
         
<
Button Click="RestartFluid">Restart</Button>
         
<
Button Click="StopFluid">Stop</Button>
         
<
GroupBox Header="Camera">
            
<
StackPanel>
               
<
Slider x:Name="CameraX" Value="0" Maximum="3" Minimum="-3" Slider.TickPlacement="None"></Slider>
               
<
Slider x:Name="CameraY" Value="0" Maximum="3" Minimum="-3" Slider.TickPlacement="None"></Slider>
               
<
Slider x:Name="CameraZ" Value="3" Maximum="10" Minimum="0" Slider.TickPlacement="None"></Slider>
            
</
StackPanel>
         
</
GroupBox>

         
<
CheckBox x:Name="ShowWireframe">
            
<
TextBlock>Show Wireframe</TextBlock>
         
</
CheckBox>

         
<
GroupBox Header="Wave Parameters">
         
   <
StackPanel>
            
   <
TextBlock>Viscosity</TextBlock>
               
<
Slider x:Name="Viscosity" Maximum="1.0" Minimum="0.01" Value="0.2"></Slider>
               
<
TextBlock>Time Interval</TextBlock>
               
<
Slider x:Name="TimeInterval" Maximum="1.0" Minimum="0.01" Value="0.3"></Slider>
               
<
TextBlock>Wave Velocity</TextBlock>
               
<
Slider x:Name="WaveVelocity" Maximum="1.0" Minimum="0.01" Value="0.7"></Slider>
               
<
TextBlock>Distance between verts</TextBlock>
               
<
Slider x:Name="VertDistance" Maximum="1.0" Minimum="0.01" Value="0.5"></Slider>
            
</
StackPanel>
         
</
GroupBox>
      
</
StackPanel>
      
<
Viewport3D Name="myViewport">
         
<
ModelVisual3D>
            
<
ModelVisual3D.Content>
               
<
Model3DGroup>
                  
<
DirectionalLight Color="#FFFFFFFF" Direction="-3,-4,-5" />
                  
<
Model3DGroup x:Name="myModelGroup"/>
               
</
Model3DGroup>
            
</
ModelVisual3D.Content>
         
</
ModelVisual3D>
         
<
Viewport3D.Camera>
            
<
PerspectiveCamera x:Name="myCamera" FarPlaneDistance="20" LookAtPoint="0,0,0" Up="0,0,1" Position="2,0,3" NearPlaneDistance="1" FieldOfView="45"/>
         
</
Viewport3D.Camera>
      
</
Viewport3D>
   
</
DockPanel>
</
Window>


using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Media.Media3D;

namespace LearnAvalonCS
{

/// <summary>
/// Interaction logic for FluidWindow.xaml
/// </summary>

public partial class FluidWindow : Window
{
   
private FluidDynamics fluid;
   
private Model3DGroup wireframe;

   public FluidWindow()
   
{
      
InitializeComponent();
   
}

   // To use Loaded event put Loaded="WindowLoaded" attribute in root element of .xaml file.
   
private void WindowLoaded(object sender, RoutedEventArgs e) 
   
{
      
CameraX.Value = myCamera.Position.X;
      
CameraY.Value = myCamera.Position.Y;
      
CameraZ.Value = myCamera.Position.Z;

      CameraX.ValueChanged += new RoutedPropertyChangedEventHandler<double>(CameraPositionChanged);
      
CameraY.ValueChanged +=
new RoutedPropertyChangedEventHandler<double>(CameraPositionChanged);
      
CameraZ.ValueChanged +=
new RoutedPropertyChangedEventHandler<double>(CameraPositionChanged);

      SetupScene();
   
}

   private void SetupScene()
   
{
      
myModelGroup.Children.Clear();
      
if (null != fluid)
         
fluid.Stop();

      fluid = new FluidDynamics(50, 50, VertDistance.Value, TimeInterval.Value, WaveVelocity.Value, Viscosity.Value);
      
fluid.ModelChanged +=
new EventHandler(fluid_ModelChanged);

      MaterialGroup materials = new MaterialGroup();
      
Brush b = Brushes.DarkBlue.Copy();
      
b.Opacity = 60;
      
b.Freeze();
      
materials.Children.Add(
new DiffuseMaterial(b));
      
materials.Children.Add(
new SpecularMaterial(b, 0.05));
      
      
GeometryModel3D model = new GeometryModel3D(fluid.Mesh, materials);
      
wireframe =
MeshBuilder.CreateWireframe(fluid.Mesh);

      if (ShowWireframe.IsChecked)
      
{
         
myModelGroup.Children.Add(wireframe);
      
}

      //add the mesh
      
myModelGroup.Children.Add(model);
      
myModelGroup.Transform =
new TranslateTransform3D(new Vector3D(-1, -1, 0));
      
fluid.Start();
      
fluid.Disturb();
   }

   void fluid_ModelChanged(object sender, EventArgs e)
   
{
      
if (ShowWireframe.IsChecked)
      
{
         
MeshBuilder.UpdateWireframe(wireframe, fluid.Mesh);
      
}
   
}

   void CameraPositionChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
   
{
      
myCamera.Position =
new Point3D(CameraX.Value, CameraY.Value, CameraZ.Value);
   
}

   private void RestartFluid(object sender, RoutedEventArgs e)
   
{
      
SetupScene();
   
}

   private void StopFluid(object sender, RoutedEventArgs e)
   
{
      
fluid.Stop();
   
}

}
}


using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Media.Media3D;
using System.Windows;
using System.Windows.Media;
using System.Windows.Threading;
using System.Diagnostics;

namespace LearnAvalonCS
{

public class FluidDynamics
{
   
private DispatcherTimer timer;
   
private MeshGeometry3D mesh;
   
private Point3D[][] buffer = new Point3D[2][];
   
private uint frameCount = 0;

   int width;
   
int height;

   private double k1, k2, k3;

   public event EventHandler ModelChanged;

   public FluidDynamics(int n, int m, double d, double t, double c, double mu)
   
{
      
this.mesh = MeshBuilder.BuildPlane(2.0/(double)n, n, m) as MeshGeometry3D;
      
this.width = n;
      
this.height = m;

      Debug.Assert(n * m == mesh.Positions.Count);

      buffer[0] = new Point3D[mesh.Positions.Count];
      
buffer[1] =
new Point3D[mesh.Positions.Count];

      double f1 = c * c * t * t / (d * d);
      
double f2 = 1.0 / (mu * t + 2.0);
      
k1 = (4.0 - 8.0 * f1) * f2;
      
k2 = (mu * t - 2) * f2;
      
k3 = 2.0 * f1 * f2;
      
mesh.Positions.CopyTo(buffer[0], 0);
      
mesh.Positions.CopyTo(buffer[1], 0);
   
}

   public MeshGeometry3D Mesh
   
{
      
get
      
{
         
return this.mesh;
      
}
   
}

   private void OnModelChanged(EventArgs e)
   
{
      
if (this.ModelChanged != null)
      
{
   
      this.ModelChanged(this, e);
      
}
   
}

   public void Start()
   
{
      
timer =
new DispatcherTimer(DispatcherPriority.Render);
      
timer.Interval =
TimeSpan.FromMilliseconds(200.0);
      
timer.Tick +=
new EventHandler(DrawGeometry);
      
timer.Start();
   
}

   public void Stop()
   
{
      
if (null != timer)
         
timer.Stop();
   
}

   public void Disturb()
   
{
      
double factor = (this.mesh.Bounds.SizeX + this.mesh.Bounds.SizeY)/4;
      
double secondary = factor * 0.85;
      
double tercary = secondary * 0.65;
      
int pi = this.buffer[0].Length/2 + width/2;
      
this.buffer[0][pi].Z = factor;
      
this.buffer[1][pi].Z = factor;
      
this.buffer[0][pi+1].Z = secondary;
      
this.buffer[1][pi + 1].Z = secondary;
      
this.buffer[0][pi - 1].Z = secondary;
      
this.buffer[1][pi - 1].Z = secondary;
      
this.buffer[0][pi + this.height].Z = secondary;
      
this.buffer[1][pi + this.height].Z = secondary;
      
this.buffer[0][pi + this.height + 1].Z = tercary ;
      
this.buffer[1][pi + this.height + 1].Z = tercary;
      
this.buffer[0][pi + this.height - 1].Z = tercary;
      
this.buffer[1][pi + this.height - 1].Z = tercary;
      
this.buffer[0][pi - this.height].Z = secondary;
      
this.buffer[1][pi - this.height].Z = secondary;
      
this.buffer[0][pi - this.height + 1].Z = tercary;
      
this.buffer[1][pi - this.height + 1].Z = tercary;
      
this.buffer[0][pi - this.height - 1].Z = tercary;
      
this.buffer[1][pi - this.height - 1].Z = tercary;
   
}

   void DrawGeometry(object sender, EventArgs e)
   
{
      
Random rand = new Random();
      
Point3D[] currentBuffer = buffer[1 - (frameCount % 2)];
      
Point3D[] previousBuffer = buffer[frameCount % 2];

      //stop the timer while we draw
      
timer.Stop();
      
for (int j = 1; j < this.height - 1; j++)
      
{
         
int p = j * height;
         
for (int i = 1; i < this.width - 1; i++)
         
{
            
previousBuffer[p + i].Z = k1 * currentBuffer[p + i].Z + k2 * previousBuffer[p + i].Z
                  
+ k3 * (currentBuffer[p + i + 1].Z + currentBuffer[p + i - 1].Z
                  
+ currentBuffer[p + i + height].Z + currentBuffer[p + i - height].Z);
         
}
      
}

      for (int alpha = 0; alpha < previousBuffer.Length; alpha++)
      
{
         
this.mesh.Positions[alpha] = previousBuffer[alpha];
      
}

      //Compute new normals.
      
for (int j = 1; j < height - 1; j++)
      
{
         
int offset = j*width;
         
for (int i = 1; i < width - 1; i++)
         
{
            
Vector3D normal = new Vector3D(previousBuffer[offset + i - 1].Z - previousBuffer[offset + i - 1].Z,
                  
previousBuffer[offset + i - width].Z - previousBuffer[offset + i + width].Z,
                  
this.mesh.Normals[offset + i].Z);
            
this.mesh.Normals[offset + i] = normal;
         
}
      
}

      frameCount++;

      //restart the timer.
      
timer.Start();

      OnModelChanged(EventArgs.Empty);
   }
}

public static class MeshBuilder
{
   
public static Geometry3D BuildPlane(double w, int m, int n)
   
{
      
MeshGeometry3D mesh = new MeshGeometry3D();
      
int pointOffset = 0;
      
for (int j = 0; j < m; j++)   
      
{
         
double x = ((double)j) * w;   
         
for (int k = 0; k < n; k++, pointOffset++)
         
{
            
mesh.Positions.Add(
new Point3D(x, ((double)k) * w, 0.0));
            
mesh.Normals.Add(
new Vector3D(0.0, 0.0, 2.0*w));
            
if(0 == j || 0 == k)
               
continue;

            mesh.TriangleIndices.Add(pointOffset - n - 1);
            
mesh.TriangleIndices.Add(pointOffset - 1);
            
mesh.TriangleIndices.Add(pointOffset - n);
            
mesh.TriangleIndices.Add(pointOffset);
            
mesh.TriangleIndices.Add(pointOffset - n);
            
mesh.TriangleIndices.Add(pointOffset - 1);
         
}
      
}

      mesh.TextureCoordinates.Add(new Point(0.0, w * (double)n));
      
mesh.TextureCoordinates.Add(
new Point(w * (double)m, w * (double)n));
      
mesh.TextureCoordinates.Add(
new Point(0.0, 0.0));
      
mesh.TextureCoordinates.Add(
new Point(w * (double)m, 0.0));

      return mesh;
   }

   public static void UpdateWireframe(Model3DGroup wireframe, MeshGeometry3D mesh)
   
{
      
for(int i = 0; i < wireframe.Children.Count; i++)
      
{
         
ScreenSpaceLines3D wire = wireframe.Children[i] as ScreenSpaceLines3D;
         
int a = mesh.TriangleIndices[i*3];
         
int b = mesh.TriangleIndices[i*3 + 1];
         
int c = mesh.TriangleIndices[i*3 + 2];
         
Point3D pa = mesh.Positions[a];
         
Point3D pb = mesh.Positions[b];
         
Point3D pc = mesh.Positions[c];

         wire.Points[0] = pa;
         
wire.Points[1] = pb;
         
wire.Points[2] = pc;
         
wire.Points[3] = pa;
      
}
   
}

   public static Model3DGroup CreateWireframe(MeshGeometry3D mesh)
   
{
      
Model3DGroup group = new Model3DGroup();
      
for (int i = 0; i < mesh.TriangleIndices.Count; i += 3)
      
{
         
int a = mesh.TriangleIndices[i];
         
int b = mesh.TriangleIndices[i + 1];
         
int c = mesh.TriangleIndices[i + 2];

         
Point3D pa = mesh.Positions[a];
         
Point3D pb = mesh.Positions[b];
         
Point3D pc = mesh.Positions[c];

         ScreenSpaceLines3D wireframe = new ScreenSpaceLines3D();
         
wireframe.Color =
Colors.AliceBlue;
         
wireframe.Points.Add(pa);
         
wireframe.Points.Add(pb);
         
wireframe.Points.Add(pc);
         
wireframe.Points.Add(pa);
         
group.Children.Add(wireframe);
      
}
      
return group;
   
}
}
}

Posted by mattdotson | 4 Comments
Filed under:
More Posts Next page »
 
Page view tracker