Monday, January 26, 2009

WPF & The Data Context

I love me some WPF, though I have to admit that when I first started, I did things wrong. I held onto my old-fangled way of doing things as much as possible, and only hesitantly took steps out into the deep waters where the big fish play.

Which means it wasn't until I started slapping my head up against the datagrid that I first really looked at the Data Context object.

Afterwards, I thoroughly smacked my forehead repeatedly. Imagine it, thousands of lines of code, and a good bit of it can be culled if I had the time or energy to go retrofit it all with Data Contexts.

Now, for the uninitiated, a Data Context is just as it says: an object which holds the data that a WPF form uses as a display source for its UI elements.

So, looking at the eComic application; every CBR file can potentially have a xml fragment/document within it which contains details about that CBR file. This would contain information such as number of pages, page descriptions, artists, writers, series title, issue title, chapter number, volume number, etc. All the important meta data, that's truly unimportant to reading an actual issue of a comic.

So, what we have is this standard kind of programming structure: UI - Business Logic - Data Store. These three things are supposed to work together, while remaining within their own domains and never quite touching. Now, the way I had always done this in the past is that either the UI was aware of the Logic layer or the other way around.

In my mind, they had to be in order to talk to one another. It just made sense.

Enter the joys of WPF.

What happens here is that I have my UI and Data Store as usual. Then I have my business logic, and within it is this Data Context object which holds the data from the data store that I am currently interested in.

Taking eComic, let's keep it simple, and only deal with Series Title and Issue Title.

What happens is that I'll create a class, which exposes properties for each of those elements. I can either have the class populate itself, or populate it elsewhere, that's fundamentally irrelevant to the discussion. All we need is the class itself.

The next step is that when we're building our UI in XAML, we need to utilize the Binding command.

So, if have a Series Title textbox that I want to display in the UI, I'd have this code:

<Textbox Name="SeriesTitle" Margin="5,5,5,5" Text="{Binding SeriesTitle}" />

Now, here's where it gets real fun. All I have to do to get the SeriesTitle from my Data Context to that UI is just assign it to that SeriesTitle's parent container's Data Context. Any changes to the textbox will instantly be promulgated back to the Data Context so I never have to manually check the textbox or any other UI element. All I have to look at is the Data Context. This is accomplished via the PropertyChanged event, which is brought to us courtesy of the INotifyPropertyChanged interface found in the System.ComponentModel Namespace.

So, I'm using the Data Context, and now I have to determine if I need to save changes to the data store based on user input or not.

It is important here that I not waste CPU cycles in saving data that hasn't changed. So, this is where the PropertyChanged event really comes into play.

First off, since we were smart, we implemented access to the PropertyChanged event via a OnPropertyChanged method which takes in the name of the property being changed.

Once the event has been risen, then we would set an IsDirty flag to true. Then, we can check that flag to determine if we need to attempt to save the data or not.

This Data Context example can be viewed here:


   1:  Public Class eComicDataContext

   2:         Implements INotifyPropertyChanged

   3:   

   4:  Private _isDirty As Boolean

   5:  Private _series As String

   6:  Private _issue As String

   7:   

   8:  Public Sub New()

   9:         SeriesTitle = ""

  10:         IssueTitle = ""

  11:         IsDirty = False

  12:  End Sub

  13:   

  14:  Public Property SeriesTitle() As String

  15:         Get

  16:                Return _series

  17:         End Get

  18:         Set(ByVal value As String)

  19:                _series = value

  20:                OnPropertyChanged("SeriesTitle")

  21:         End Set

  22:  End Property

  23:   

  24:  Public Property IssueTitle() As String

  25:         Get

  26:                Return _series

  27:         End Get

  28:         Set(ByVal value As String)

  29:                _series = value

  30:                OnPropertyChanged("IssueTitle")

  31:         End Set

  32:  End Property

  33:   

  34:  Public Property IsDirty() As Boolean

  35:         Get

  36:                Return _isDirty

  37:         End Get

  38:         Private Set(ByVal value As Boolean)

  39:                _isDirty = value

  40:         End Set

  41:  End Property

  42:   

  43:  Public Sub ResetDirtyBit()

  44:         Me.IsDirty = False

  45:  End Sub

  46:   

  47:  Public Sub OnPropertyChanged(ByVal propertyName As String)

  48:         RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))

  49:         IsDirty = True

  50:  End Sub

  51:   

  52:  Public Event PropertyChanged(ByVal sender As Object, ByVal e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged

  53:  End Class

Wednesday, January 21, 2009

eComic breaks 100!

I stopped by CodePlex earlier and discovered a happy fact: the latest version of eComic (the WPF version) has over a hundred downloads now. Insert an appropriately scary version of the happy dance; maybe the one from the op/ed of The Melancholy of Suzumiya Haruhi.

In truth, I'm quite happy, as this means that the application is being utilized. While not every one of those downloads is of the executable (61 of them), the rest is of the source code; so even if folks aren't using the application as an image viewer, maybe they're using it to help them learn more about WPF.

Which I'm also happy over.

Regardless, I do have an update to eComic in the works, but I'm still working on the new functionality that I'm trying to implement into it. Mainly, this will be creating the CBZ archives (CBZ only, I'm not springing for a RAR license to allow creation of CBR files).

Other functionality I'm working on are zooming, and filters for the image display, and either a slider control or a numeric selection control for navigation between pages.

The final functionality I'm implementing here is to make this a bit more thread friendly, in how it operates. Currently, there's a slight pause when you first open up an archive, and it's a slightly longer one when the archive is slightly larger (example: whole volumes of a manga take nearly a minute, while a single chapter between 20 & 30 seconds). This lag, where the UI is unresponsive, and I fear close to failure, is unacceptable; and I want it gone!

After all, I don't want Mr. Bigglesworth to get upset over UI lag.

Good night, talk about Bad works

It takes a bit to shock me in regards to how a website works. Usually, if it consistently shows up, and returns data every time, then you're pretty far ahead of the curve.

And I like playing with new websites as I find out about them. I also like playing with new functionality of existing websites.

Today, a discussion on Pipl, the people-based search engine, came across my RSS feeds from LifeHacker. LifeHacker usually has good information, on it, and quite often I find myself quite pleased with the things that I learn from their blog.

So, I followed the link, wanting to check out Pipl's new search system for Usernames, Email & Phone Numbers. Not that big of a deal, I have a few email addresses--I figured I could search on them. For a control, I first searched for my real name in their standard search. I got back the usual smattering of 'Stephen Wrighton's: these include Mark S. Wrighton, chemist & Chancelor and a Facebook profile that also is Stephen Wrighton.

Satisified with that result, I clicked the Username option and entered my standard forum handle, "Kidan." Which returned references to Adam Kidan. Which, ever since he burst into infamy a few years ago, tends to be the standard for searches on Kidan.

Next I try the Email search, and boy did I hit the jackpot on that one.


I mean come on. I enter an email address, and get a results set that has absolutely NOTHING to do with the address entered?

I could understand if it didn't find anything. I could understand if it took a while to return results. What I can't understand is getting the wrong results.

My only guess is that this is do to AJAX and too many requests. Basically, the server is getting confused on the tokens that it's passing back and forth. Searches are apparently given a search ID or some type of encoded reference for the system to "remember" who gets what. I assume that what's happening here is that when too many requests come in at the same time, then two, or more, clients are getting the exact same encoded key. There's other possibilities for this, but they mean that Pipl and the developers behind this new search algorithm just don't care.

Frankly though, this is just a pathetic outing, especially for a search engine. I can handle my search engine telling me that a request timed out. I can handle a search engine coming back, saying that it didn't find any results. I can even handle results that aren't what I was expecting when I searched for a term (such as entering Stephen Wrighton, and getting results for Mark S. Wrighton or Steven Wright).

What I can't handle, and what destroys any hope of credibility for a search engine is when it comes back having absolutely no basis whatsoever on the search parameter I entered.

Tuesday, January 20, 2009

VS2008 Trick

My boss and I participated in an online training session regarding ASP.NET 3.5 hosted by Microsoft today.

I enjoyed it immensely as it formalized some ideas I had about functionality in 3.5, and told me about a couple of neat tricks for VS2008.

The first is the CSS tools. They are lightyears beyond what existed in previous versions of Visual Studio; and provides meaningful dialogs to utilize the styles.

The second revolves around Intellisense. See, when you're typing in the code editor, Visual Studio will provide useful options based upon the letters & words that you've typed on a given line. This is useful 95% of the time, and irritating the other 5%.

Now, that 5% irritation comes in two flavors. The first is where the Intellisense selects something for you, and it wasn't what you wanted. The second is when Intellisense is hiding the line you're trying to read as you write code.

For example, I'll write a line 1, which involves using a variable. Then I'll go a line above it and initialize said variable. Well, it used to be that I had to escape out of the Intellisense to read the line below.

In Visual Studio 2008, all one has to do is hold down the CTRL key, and the Intellisense window becomes about 98% transparent.

When we heard about this, we insisted that the guy giving the presentation stop and tell us the command to make that happen. Out of all the tips and tricks I've learned about Visual Studio 2008 I think that is my favorite.

Followed quickly by the ability to create side-by-side views of two different code files.

Monday, January 12, 2009

Comparing Nullable(of T) Values

Okay, I spent a good half hour earlier today slamming my head against the wall, trying to figure something out. Why my checkboxes in my tri-state tree were not getting set to the correct state based on user input.

What this is, is each checkbox on the tree has three possible checked states: true, false and indeterminate.

I had some rules going into this:

  1. A checkbox's state is the summation of its children's state (if all children are TRUE, then it is TRUE).
  2. A checkbox is indeterminate when any of its children are not in the same state as itself.
  3. If a Checkbox's Parent is set to TRUE or FALSE, then the Checkbox shall be set to the same
Pretty straightforward if you ask me, and the state was being controlled by a nullable boolean (which is declared DIM _isChecked as Nullable(of Boolean) in VB or Boolean? _isChecked in C#).

Now, I'm using VB.Net for this, and when I only had a single set of children until you got to the lowest level, it all worked perfectly. I was happy and smiling. Well, today I shoved in extra children for every level that could have them, and got something... new for my troubles: only the first child was affecting the parent nodes, thus breaking rule 2, while modifying UP the tree.

Modifying down, still worked like a charm.

So, I'm off grumbling, and toss in a Breakpoint on the comparison operation. I mean, I see in the code where this value is supposed to be changed, and so that proverbial key to the kingdom is where I need to be looking.

I start up the application, get to the point in it where I use the tree, and uncheck something. the break point fires, and the promptly skips over the code, confused, I let it cycle back around as it verify's the parent's state.

Once I'm at the checkpoint again, I look to see what the result of the comparison operation is.

Only to discover, nothing.

Which means that the comparison did not fire, because their is no equilavencies between the two opposing states.

Frowning, I considered the implications of this, when suddenly I realized what was happening. A nullable boolean, has three possible values: True, False and NOTHING. The problem exists because NOTHING cannot be processed through a comparison operation such as "=" or, as I was using, "<>."

So, what was this poor programmer to do?

What I had forgotten, was the nullable objects have a "HasValue" property which returns a boolean based on if the variable is NOTHING or not.

Which means that, I had some fun coding to look forward to. Let's look at this, I had two values to check, both of them Nullable(of Booleans), which means I have this potential set of values:

Possible States
Value 1Value 2What I need to do
NNBoth Nullable Do Nothing
NTDifferent, Change
NFDifferent, Change
TNDifferent, Change
TTBoth True, Do Nothing
TFDifferent, Change
FNDifferent, Change
FTDifferent, Change
FFBoth False, Do Nothing

Where F=False, T=True, and N=Nothing. Those are all the potential states that I had to check for. Luckily, I had a few shortcuts for my logic:
  1. If they're both nothing, I don't have to do anything
  2. If one is nothing and the other Has a value, I HAVE to change
With those two logical operations in place (thanks to my handle state chart) I knew I only had to check for differences when both of my items had a value. Which in turns means that I ended up with an IF statement, which reads like:

One of these days, Blogger will support Code formatting, and I just won't know what to do. But I digress.

Now, if Microsoft had put a bit of FORETHOUGHT into their Nullable(of T) objects in VB.Net they would have provided inherent comparison operations between two instances of the Nullable(of T) objects. But that may be asking for a bit much I fear.

The Expanded Universe (.com!)

I'm a geek, and as such I have multiple and myriad, and often conflicting, interests. One such interest, as evidenced by the sheer number of blogs I have out there, is writing. Yet, above that, I love reading.

Which is why I enjoyed the Ficlets.com website so much. I mean here was something which allowed for numerous stories, and thousands of potential sequels, and prequels to those stories.

Then AOL decided to close it down. Talk about a bummer.

So, since I am a geek, I decided to rebuild the website from the ground up (which is where TheExpandedUniverse.com comes in). I don't have all the functionality which ficlets.com has, at least yet, but that's okay, as I have the fundamental aspects: stories can be written, sequels & prequels to said stories can be written, and people can read and vote for them.

Talk about fun :D.

Well, I found it fun. Even the coding of the website was fun. I learned a host of new things, including LINQ to SQL and how to utilize Markdown and OpenID in a .NET web application.

So, it's there, it's running; now I just get to go back in and add in the missing functionality.

Thursday, January 8, 2009

Fun with OpenId.Net

As I said last night, I'm slapping together something new, but this time it is a ASP.Net website (yeah, go me!). I also stated that I utilized two new technologies (for me at least) in the building of this website; after yesterday's love fest over Linq, I figured it was time to talk about OpenId.

I use OpenId, and I try to use it everywhere I can. Stack Overflow is probably the most obvious, and often, of my usages of it. So, while building this new thing of mine, I figured I would make use of it.

After all, most folks HAVE an OpenId even if they're not aware of that particular fact. Google, Blogger, AOL and LiveJournal are all OpenId providers.

The main reason I wanted to use OpenId is because I just didn't want the hassles of keeping track of usernames, passwords and the like. It's messy, and just not something I want to be doing. OpenId allows me the best of both worlds. I can hide things from unauthorized and unauthenticated users while at the same time, not have to worry about tracking people's passwords.

And let's be honest, so many folks use the same password across multiple sites, that that's a degree of security for myself that I'm more than happy to take.

Now, comes the ubiquitous issues in regards to how I'm consuming an OpenId provider. The answer to that is i'm using DotNetOpenID. This implementation has a Relying Partner, and a Provider examples; which means that you can turn any .NET website into an OpenID Provider.

Me, I opted out of that particular hassle, again, because I don't want any of that nonsense relating to keeping up with who is who.

What is better is that alongside of Linq, I can easily generate new User objects for my datastore (which allows for custom Display Names, signatures, etc.,) in a nearly transparent fashion for the users. I love that all they have to do is log in, and then they are good to go.

Wednesday, January 7, 2009

LINQ to SQL

Oh My Stars and Garters! I think I may be in love. I was working on a personal project last night, and decided on two things:

  1. I would use OpenID as the login provider
  2. I would use Linq to Sql as the Data Access Layer
I'll talk about my experiences with OpenID later, for now, I need to gush a bit about the Linq stuff.

First though, I need to admit something: I was hesitant about adopting Linq. I mean seriously, look at the stuff. You do some fancy hand waving and all of a sudden, you have a data layer? It just seemed so... unnatural.

And I was right, it fundamentally is some fancy hand waving, and then you have a datalayer. It is unnatural, but it is also awesome!

So, what happens is that I build myself my data structures in SQL Server. I then use the diagram tool to make sure that all those fun Primary & Foreign Key and associated relationships are there. At that time, I drag and drop those tables onto the Linq designer surface. My computer freezes for a moment as it does some crunching and then I have data access stuff.

I'm being quite literal here; and it's fast. Fast to use, and fast to develop against.

Consider, I have a Customer object as part of my DataContext. The steps I would take, to save my changes to this are thus:
  1. Instantiate an instance of the Data Context
  2. Check to see if the Customer exists in the Data Context
  3. If yes, return that, if no return nothing
  4. If nothing, instantiate an new instance of a Customer, and add to Customers collection
  5. Make changes to the properties of Customer
  6. Update the Data Context
And sure, those are fundamentally, the exact same steps I would take with any data access layer. But the power is in the code. Consider, to do these steps, I would end up writing probably a hundred lines of code, and a good 50 or so lines of SQL. I mean, I would need to instantiate connections and commands, and just do all that stuff manually.

For Linq, it's this:
Dim db as DataContext
Dim Cust As Customer
Cust = (Select d From db.Customers Where d.Id = MyIdVariable ).SingleOrDefault
If Cust Is Nothing Then
Cust = New Customer
db.Customers.InsertOnSubmit(Cust)
End If
Cust.CustomerName = txt.Text
db.SubmitChanges
That is it.

What's happening there is I'm instantiating the datacontext, grabbing either the Customer object I want, or it returns Nothing. Checking for nothing, and if so, instantiating a new instance of the Customer object, and adding it to the DataContext, and then changing values via properties.

It's clean and straightforward, and utterly awesome.

Tuesday, January 6, 2009

WPF Datagrid Items Refresh

So, I have this WPF datagrid, and it's quite happy in the way it works. With a single exception. Some background, I'm not doing the base behavior for editing/adding, but rather have a separate control outside the grid which is responsible for that behavior (as it is also the control responsible for adding the initial element).

But I was having a problem, and it was with the Grid's UI.

My changes and additions were not making their way into the UI display. No matter how many times I invalidated the User Interface it just would not get displayed. And this was not just for Updates, but also for Inserts and Deletes.

VERY much annoying.

But, I wouldn't be here if I didn't have a solution.

As stated, my initial try was to use the Datagrid.InvalidateUI command, as that's basically what I was trying to do: repaint the grid. What I wasn't doing with that was telling the Datagrid to look at the underlying datasource and refresh the UI from that.

The magic command? Datagrid.Items.Refresh()

Yeah, I did feel a bit silly after finding that out. Of course I blame it on the fact that I've spent too much time in the ASP.NET arena where I can force a data-rebinding by calling the DataBind method of the object. Oh well, I solved the problem, so I'm all sorts of happy.

Friday, January 2, 2009

Tickle Me Elmo

I'm demented. I know this about myself. I find thing funny that leave most folks either scratching their heads in confusion or slightly scared; it's an either or type of thing.

So, today at work we got into this discussion on demented toys, both from our own childhoods and from our child's POV. Inevitably, this discussion then turned to automated toys. The prime example from our childhood was of course Teddy Ruxpn. The example for my kids would have to be that Tickle Me Elmo.

Now, I have two opinions on these types of toys. And by these types, I mean toys which you push a button and they do things, as opposed to you doing things to the toy. My opinion about these, is that they are designed for 1 (or both) of 2 reasons:

  1. To generate schizophrenia in our children, as their toys are talking to them
  2. To stop our kids from developing an imagination, as their toys play by themselves
A reasonable interpretation of facts, especially since there's a huge number of folks out there calling these toys "possessed," and at least one person which described the automatic Elmo toys thusly (see the description line at this link):
Good [G]od, the perfect gift for the child of your worst enemy.
Now, as I stated above, I'm demented, and I remembered from the days when my little brother had a Teddy Ruxpin, that you could make that evil thing say (or sing) whatever you wanted. After all, it was just a standard cassette tape that went into its back. Personally, I enjoyed playing Poison through the Teddy Ruxpin, but that's just me.

Now, what does this have to do with my programming blog?

Glad you asked, and if you didn't, then shame on you.

During this discussion, one of my coworkers brought up the "Death Threat Elmo." Which, as you may, or may not know, is a "Know My Name" Elmo which when its batteries were changed started saying such fun phrases as "Kill James."

Well, I was amused.

Anyways, I thought of the two Tickle Me Elmos which my boys had in their rooms gathering dust. Because, like every other automatic toy they've ever owned, my boys played with the thing for a few hours, and then went back to their "dumb" toys; they make me proud.

But I'm digressing.

So, here are these two Elmo toys, and I now have this overwhelming urge to make them say fun phrases such as:
  • Don't touch me there! It's my special place
  • Mommy says I just fell down the stairs.
  • Don't daddy!
  • It's dark in the cupboard
Yes, I am demented, and now, I would not give said toys back to my children after flashing the rom.

Which brings me to my point: does anyone know anything about these roms? Has anyone flashed them, etc. I tried searching Google, but apparently, the Tickle Me Elmo dolls just aren't good fodder for folks to hack their on-board ROM. I'd rather not start this little hack from scratch (I'm lazy that way).

Blog Widget by LinkWithin