Thursday, April 14, 2011

As I discussed in my last post, we created a TimeSpinner control based on the Extended WPF Toolkit’s ButtonSpinner.
Now, the toolkit has a DateTimeUpDown control that will display a date or time and allow the user to click the spinners for each time part, but I needed a little more. I needed the raw text to be editable and I wanted the spinners to just modify the minutes portion. I also wanted to have special parsing based on the number of characters entered.
4 chars:
Parse as military time.
3 chars:
Parse as modified military time, but only assume waking hours.
So 933 becomes 9:33 AM
345 becomes 3:45 PM
We put the cut-off at 7. Anything before 7 assumes PM anything 7 or after assumes AM.
2 chars:
Assuming shortened military time.
12 = 12:00 PM
15 = 3:00 PM
1 char:
Parses as shortened military time, but assumes waking hours like 3 chars.
1 = 1:00 PM
8 = 8:00 AM
The TimeSpinnerControl exposes one bindable property, Time. So it would be consumed like:
1: <runtime:TimeSpinnerControl Time="{Binding Path=ReturnTime}" 2: x:Name="spinnerReturnTime"/>
So, here is what we did:
TimeSpinnerControl.xaml:
1: <UserControl x:Class="ParaPlan.Controls.TimeSpinnerControl"
2: xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4: xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
5: xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
6: xmlns:wpftoolkit="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit.Extended"
7: mc:Ignorable="d">
8: <wpftoolkit:ButtonSpinner Spin="Time_Spin" IsTabStop="False">
9: <TextBox Name="textTime"
10: MinWidth="60"
11: Margin="0"
12: MouseDoubleClick="textTimeDoubleClick"
13: Keyboard.GotKeyboardFocus="textTimeGotKeyboardFocus"
14: MouseEnter="textTimeMouseEnter"
15: Keyboard.LostKeyboardFocus="textTimeLostKeyboardFocus"/>
16: </wpftoolkit:ButtonSpinner>
17: </UserControl>
TimeSpinnerControl.xaml.cs:
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Text;
5: using System.Windows;
6: using System.Windows.Controls;
7: using System.Windows.Data;
8: using System.Windows.Documents;
9: using System.Windows.Input;
10: using System.Windows.Media;
11: using System.Windows.Media.Imaging;
12: using System.Windows.Navigation;
13: using System.Windows.Shapes;
14: using ParaPlan.Extensions;
15: using System.Globalization;
16:
17: namespace ParaPlan.Controls
18: { 19: /// <summary>
20: /// Interaction logic for TimeSpinnerControl.xaml
21: /// </summary>
22: public partial class TimeSpinnerControl : UserControl
23: { 24: public TimeSpinnerControl()
25: { 26: InitializeComponent();
27: }
28:
29: #region Time Property
30:
31: /// <summary>
32: /// Time Dependency Property
33: /// </summary>
34: public static readonly DependencyProperty TimeProperty =
35: DependencyProperty.Register("Time", typeof(DateTime), typeof(TimeSpinnerControl), 36: new FrameworkPropertyMetadata(DateTime.MinValue,
37: FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
38: new PropertyChangedCallback(OnTimeChanged)));
39:
40: /// <summary>
41: /// Gets or sets the Time property.
42: /// </summary>
43: public DateTime Time
44: { 45: get { return (DateTime)GetValue(TimeProperty); } 46: set { SetValue(TimeProperty, value); } 47: }
48:
49: /// <summary>
50: /// Handles changes to the Time property.
51: /// </summary>
52: private static void OnTimeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
53: { 54: ((TimeSpinnerControl)d).OnTimeChanged(e);
55: }
56:
57: /// <summary>
58: /// Provides derived classes an opportunity to handle changes to the Time property.
59: /// </summary>
60: protected virtual void OnTimeChanged(DependencyPropertyChangedEventArgs e)
61: { 62: DateTime value = (DateTime)e.NewValue;
63: if (value.IsMinValue())
64: { 65: textTime.Text = "";
66: return;
67: }
68: textTime.Text = value.ToShortTimeString();
69: }
70:
71: #endregion
72:
73: #region Event Handlers
74: /// <summary>
75: /// Handles when the user clicks a spinner
76: /// </summary>
77: /// <param name="sender"></param>
78: /// <param name="e"></param>
79: private void Time_Spin(object sender, Microsoft.Windows.Controls.SpinEventArgs e)
80: { 81: if (e.Direction == Microsoft.Windows.Controls.SpinDirection.Increase)
82: { 83: this.Time = this.Time.AddMinutesRoundUp(5);
84: }
85: else
86: { 87: this.Time = this.Time.AddMinutesRoundUp(-5);
88: }
89:
90: }
91:
92: /// <summary>
93: /// Handles when the user double clicks in the textbox
94: /// </summary>
95: /// <param name="sender"></param>
96: /// <param name="e"></param>
97: private void textTimeDoubleClick(object sender, MouseButtonEventArgs e)
98: { 99: this.textTime.SelectAll();
100: }
101:
102: /// <summary>
103: /// Handles when the user tabs in the control
104: /// </summary>
105: /// <param name="sender"></param>
106: /// <param name="e"></param>
107: private void textTimeGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
108: { 109: this.textTime.SelectAll();
110: }
111:
112: /// <summary>
113: /// Handles when the user enters the control by mouse
114: /// </summary>
115: /// <param name="sender"></param>
116: /// <param name="e"></param>
117: private void textTimeMouseEnter(object sender, MouseEventArgs e)
118: { 119: this.textTime.SelectAll();
120: }
121:
122: /// <summary>
123: /// Handles when the control loses focus. Now we want to parse what
124: /// the user entered
125: /// </summary>
126: /// <param name="sender"></param>
127: /// <param name="e"></param>
128: private void textTimeLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
129: { 130: this.Time = ParseText(this.textTime.Text);
131: }
132: #endregion
133:
134: private DateTime ParseText(string value)
135: { 136: //empty...move on
137: if (string.IsNullOrWhiteSpace(value))
138: { 139: return DateTime.MinValue;
140: }
141:
142: //parsed as valid date
143: DateTime date = DateTime.MinValue;
144: DateTime.TryParse(value, out date);
145: if (!date.IsMinValue())
146: { 147: return date;
148: }
149:
150: string input = value;
151: int first = int.Parse(value.Substring(0, 1));
152: switch (value.Length)
153: { 154: case 3:
155: input = "0" + value;
156: if (first > 0 && first < 7)
157: { 158: input = (first + 12).ToString() + value.Substring(1, 2);
159: }
160: break;
161: case 2:
162: input = value + "00";
163: break;
164: case 1:
165: input = "0" + value + "00";
166: if (first > 0 && first < 7)
167: { 168: input = (first + 12).ToString() + "00";
169: }
170: break;
171: default:
172: break;
173: }
174: DateTime.TryParseExact(input, "HHmm", null, DateTimeStyles.None, out date);
175: return date;
176: }
177:
178:
179: }
180: }
DateTime Extension Method:
1: /// <summary>
2: /// Adds or subtracts a value to a date time rounding it to
3: /// multiples of the entered value
4: /// </summary>
5: /// <param name="dt">this</param>
6: /// <param name="value">Amount to add or subtract</param>
7: /// <returns>Adjusted DateTime</returns>
8: /// <history>
9: /// [Tim Hibbard] 04/08/2011 Created
10: /// </history>
11: public static DateTime AddMinutesRoundUp(this DateTime dt, int value)
12: { 13: int delta = dt.Minute % value;
14: int subtractDeltaFrom = 0;
15: //since value to add is negative, we want to round down
16: //to nearest delta value
17: if (value < 0)
18: { 19: //but only if delta is more than 0. If delta is 0,
20: //then we are already rounded down
21: if (delta > 0)
22: { 23: subtractDeltaFrom = Math.Abs(value);
24: }
25:
26: }
27: return dt.AddMinutes(subtractDeltaFrom - delta).AddMinutes(value);
28: }
Tuesday, April 12, 2011

Our software will automatically generate pick up and drop off times based on distance of trip, how many other people are on the bus, how long it takes to drop them off, etc. We display these times in our custom TimeSpinner control based on WPF Extended Toolkit’s button spinner.
Since the computer is generating the time, they are often not human-friendly. 6:00 AM is a lot easier to remember than 6:03 AM. So we give the users the option to modify these times. Using the spinners, they can adjust the time 5 minutes up or 5 minutes down. However, 6:08 is still difficult to remember, so what we really need is to be able to round up (or down) to the nearest 5 minutes, then adjust in 5 minute increments after that.
So 6:03 AM to 6:05 AM to 6:10 AM to 6:15 AM and likewise, round down from 6:03 AM to 6:00 AM to 5:55 AM to 5:50 AM.
We used an extension method to accomplish this. Note that it accepts increments other than 5 minutes, that’s just what we use.
1: /// <summary>
2: /// Adds or subtracts a value to a date time rounding it to
3: /// multiples of the entered value
4: /// </summary>
5: /// <param name="dt">this</param>
6: /// <param name="value">Amount to add or subtract</param>
7: /// <returns>Adjusted DateTime</returns>
8: public static DateTime AddMinutesRoundUp(this DateTime dt, int value)
9: {
10: int delta = dt.Minute % value;
11: int subtractDeltaFrom = 0;
12: //if value to add is negative, we want to round down
13: //to nearest delta value
14: if (value < 0)
15: {
16: //but only if delta is more than 0. If delta is 0,
17: //then we are already rounded down
18: if (delta > 0)
19: {
20: subtractDeltaFrom = Math.Abs(value);
21: }
22:
23: }
24: return dt.AddMinutes(subtractDeltaFrom - delta).AddMinutes(value);
25: }
Friday, April 08, 2011

Sometimes it handy to force the user’s attention to a specific screen. We can do this with a dialog, but sometimes the user doesn’t know that window is on top. To draw the user’s eye to the window, I like to place a backdrop between the rest of the application and the dialog window. I use the following code to make that happen.
1: public bool? ShowDialogWithBackdrop(Window win)
2: {
3: //create backgroup
4: Window backgroundWindow = new Window();
5: backgroundWindow.Top = 0;
6: backgroundWindow.Left = 0;
7: backgroundWindow.Height = SystemParameters.WorkArea.Height;
8: backgroundWindow.Width = SystemParameters.WorkArea.Width;
9: backgroundWindow.AllowsTransparency = true;
10: backgroundWindow.Background = Brushes.Transparent;
11: backgroundWindow.WindowStyle = WindowStyle.None;
12: backgroundWindow.ShowInTaskbar = false;
13: Rectangle rect = new Rectangle() { Fill = Brushes.Silver, Opacity = 0.5 };
14: backgroundWindow.Content = rect;
15: backgroundWindow.Show();
16: //set the owner so if the user clicks on the backdrop,
17: //the window will flash
18: win.Owner = backgroundWindow;
19: bool? rv = win.ShowDialog();
20: //dialog has returned. Close backdrop.
21: backgroundWindow.Close();
22: return rv;
23: }
Monday, June 21, 2010
The most common use for Preprocessor Directives is to intelligently group your code using the #region … #endregion tag (sidenote: Ctrl + M, M is a great keyboard shortcut to expand and contract your regions). Preprocessor Directives can also be used to tell the compiler which code should or should not be compiled based on the configuration environment. Wrapping code in an #if DEBUG … #endif block will only execute if the environment is in debug mode.
This proved to be a handy way to debug a situation this morning in a multi-threaded scenario where breakpoints and Console.WriteLine were not giving me enough information. I added a new tab with initial visibility as collapsed, set it to visible in an #if DEBUG … #endif block, and wrote events to it also in a debug block.
For situations where you want code to execute only in a production environment, use #if !DEBUG … #endif.
View MSDN’s page on Preprocessor Directives for more information.
Sunday, June 20, 2010
On Jan 16, 2011, we are expecting a new addition to our family. Ben is going to learn words like “share” and “don’t put that in the baby’s nose”. We expect he’ll do great!

Thursday, June 17, 2010
There is a sales technique where the strategy is to get the customer to say “No deal” as soon as possible. The idea being that by establishing terms that your customer is not comfortable with with, the sooner you can figure out what they will be willing to agree to. The same principal can be applied to code design. Instead of nested if…then statements, a code block should quickly eliminate the cases it is not equipped to handle and just focus on what it is meant to handle.
This is code that will quickly become unmaintainable as requirements change:
private void SaveClient(Client c)
{
if (c != null)
{
if (c.BirthDate != DateTime.MinValue)
{
foreach (Sale s in c.Sales)
{
if (s.IsProcessed)
{
SaveSaleToDatabase(s);
}
}
SaveClientToDatabase(c);
}
}
}
If an additional requirement comes along that requires the Client to have Manager approval or for a Sale to be under $20K, this code will get messy and unreadable.
A better way to meet the same requirements would be:
private void SaveClient(Client c)
{
if (c == null)
{
return;
}
if (c.BirthDate == DateTime.MinValue)
{
return;
}
foreach (Sale s in c.Sales)
{
if (!s.IsProcessed)
{
continue;
}
SaveSaleToDatabase(s);
}
SaveClientToDatabase(c);
}
This technique moves on quickly when it finds something it doesn’t like. This makes it much easier to add a Manager approval constraint. We would just insert the new requirement before the action takes place.
Tuesday, June 08, 2010
A lot of reports work on data from last month. It is a nice touch to have these dates pre-populated for your users. Using extension methods, the code can look cleaner too.
Extension Methods:
public static class DateHelper
{
public static DateTime FirstOfTheMonth(this DateTime dt)
{
return new DateTime(dt.Year, dt.Month, 1);
}
public static DateTime LastOfTheMonth(this DateTime dt)
{
return dt.FirstOfTheMonth().AddMonths(1).AddDays(-1);
}
}
Consuming Code:
void Prepopulate()
{
startDateBox.CurrentlySelectedDate = DateTime.Now.AddMonths(-1).FirstOfTheMonth();
endDateBox.CurrentlySelectedDate = DateTime.Now.AddMonths(-1).LastOfTheMonth();
}
Monday, June 07, 2010
I would like to blog more about the problems I encounter on a daily basis. I find that taking 10 minutes or so to write a simple solution to my problems helps me retain that information.
I always forget the specific syntax to declaring variables in T-SQL.
declare @startdate datetime;
declare @enddate datetime;
set @startdate = '04/01/2010';
set @enddate = '04/30/2010';
select count(id) from triphistory where tripdate between @startdate and @enddate
Saturday, March 20, 2010
We recently added OAuth to MentionNotifier so that users can quickly view and edit their subscriptions without needed an additional login. This is enabled by default for new users, but existing users will need to do the following steps to associate their subscriptions with OAuth:
1) Go to http://software.engraph.com/ManageMentionNotifier
2) Click “Sign in with Twitter”
3) Verify that your twittername and email are correct
4) Click "Associate with OAuth"
This will also allow you to reply to notification emails and MentionNotifier will tweet on your behalf. This is made possible by @sidePop written by @ferventcoder
Note that the reply by email is new and buggy, so make sure that what was tweeted is correct and as expected.
If you run into any issues, sent me a reply to @timhibbard.
You can also join the MentionNotifier fan page on facebook, or follow @MentionNotifier on twitter.
Friday, January 22, 2010
If you want to specify that a virtual server starts up automatically when the host server starts up:
1) On host server, browse to “C:\Users\Public\Documents”
2) Right click “Shared Virtual Machines”, select Properties
3) Browse to security tab, add yourself as having full control over this folder
4) Repeat steps 2-3 for “Shared Virtual Networks”
5) In Virtual Server web interface, shut down the virtual machine you wish to auto-start
6) Navigate to Edit Configuration –> General Properties
7) Check “Run virtual machine under the following user account”
8) Enter the credentials from step 3
9) Select desired startup action and delay, then start virtual machine.
Now when your host machine reboots, your virtual machines will start up automatically. Step 4 is very important as my wasted morning can attest to.
Wednesday, December 30, 2009
EnGraph is looking for an ASP.NET developer to join our team. We are a small company in Lenexa, KS that creates .NET applications for Paratransit agencies.
A good candidate would be very comfortable with ASP.NET, Forms Authentication and JavaScript. A huge bonus would be knowledge of IIS, Google Maps API, AJAXPro, ActiveReports, WPF and SQL.
We are accepting resumes immediately and look to hire as soon as January 18th. We would consider contract or full-time and we would require office attendance.
Please send your resume to timhibbard@engraph.com
Friday, October 02, 2009
Last weekend I ran the Shoreline Shuffle trail 5K. It was my first trail race and also my first first place finish. As a bonus, I also broke 20 minutes, which I didn’t think was possible for me on a trail run. I’ve posted a full race report (with way too many pics) on my running blog.
Wednesday, September 23, 2009
TwitApps was a great tool, it would email you when somebody @mentioned you on twitter. But, alas, they are shutting down and leaving what I think is a huge hole.
So I built my own service that does the exact same thing.
To use the service, visit MentionNotifier, and subscribe to your @mentions. To get notified of updates, follow @MentionNotifier or @timhibbard on twitter.
Here are some of the FAQ’s:
Q: What is this?
A: It is a service to notify you by email when somebody has mentioned you in a tweet.
Q: Who do the emails come from?
A: MentionNotifier@gmail.com
Q: How quickly will I be notified of a new tweet?
A: Currently, it checks every minute.
Q: How does it work?
A: It uses the search API of twitter to check for your twitter username.
Q: Isn't that just a glorified RSS reader?
A: Yes.
Q: Will it work with hashtags?
A: Yes. Put in the search string you want to be notified on (#KUbball) and the email address to send new tweets to.
Q: Will I receive multiple emails if I get multiple mentions?
A: It will group the mentions into a single email each checking cycle.
Q: Will it work if I get a mention from somebody that protects their tweets?
A: No, because protected tweets are not part of the public timeline.
Friday, July 31, 2009
I use Gmail for my personal email, and as an archive for my EnGraph email, but haven’t been able to use it as my main email client as it appends a “On Behalf of” to messages sent from Gmail using a non-gmail address.
Google has recently released an update to Gmail allowing you to use an SMTP server of your choosing, which removes the “On Behalf Of”.
Now I can email my clients from Gmail and they don’t know the difference!
Friday, July 17, 2009
Pre-approval letter in hand, let the house shopping begin!10:10 AM Apr 28th
Apparently it's impossible to look at a house without the realtor begging to be your buyer's agent. Back off people!1:36 PM Apr 30th
Finally going live with software that I've been working on for 2+ years!3:59 PM Apr 30th
First install and we run into a proxy brick wall...smooth4:43 PM Apr 30th
Done with an exhausting day of house hunting. Found a lot that we liked. One that we LOVED, in shawnee of all places.6:40 PM May 2nd
We still aren't sure if we could actually leave lawrence though. So much coolness in this town.6:41 PM May 2nd
Found another very cool house in shawnee. Love this area, didn't even really knew it existed before this weekend.3:01 PM May 3rd
My fav website these days: http://land.jocogov.org/ - amazing how some people are asking 2-3% above appraised value. Crack smokers8:59 AM May 6th
We just had a realtor tell us she wouldn't accept our pre-approval letter from BofA. Umm, seriously?5:47 PM May 6th
Officially submitted an offer on the shawnee house. Many knots in stomach.1:31 PM May 9th
Ugh...their counter offer sucks and they probably aren't going to like our counter offer...preparing ourselves to walk away from this one9:12 PM May 9th
Counter counter offer submitted. This is starting to get fun. Bring it on!12:13 AM May 10th
Still in negotiations, only battling over closing and possession dates now. Everything else is ironed out.4:32 PM May 10th
Our counter offer was rejected over...get this...$172 of rent we asked for because they wanted to live in the home for 4 days past closing7:55 AM May 12th
@ericjgruber That's how we feel. If this was such a big deal, what else would they have been stupid about during the rest of the process8:09 AM May 12th
Couldn't have said it better myself - @chibb's take on our offer being rejected - http://bit.ly/6m1Wt8:12 AM May 12th
http://twitpic.com/51v8d - The camry is rocking the gowagon.com sticker. Thanks guys. Unrelated: Sorry @chibb :)2:49 PM May 12th
Ben and @chibb landed safely in Chicago and Ben was a champ during the flight!10:09 AM May 14th
I don't know if "trained" is the word I'm looking for, but my wife is out of town and I still put the toilet seat down10:12 PM May 14th
Ugh...I've been up all night with the Buffalo Wild Wings Flu. No trail run for me this morning.6:58 AM May 16th
I slept 13 hours and feel a lot better. Still a little light headed and queasy and down about 8 lbs from avg weight. Never eating BWW again.7:27 AM May 17th
Excited that @chibb and Ben come home today. Trying to get the house cleaned up before I go pick them up from the airport8:08 AM May 17th
Choking down oatmeal with no milk. Apparently I have the bug where you feel fine on Sunday, but kicks you in the junk on monday.2:57 PM May 18th
Great bday dinner with @chibb, Ben, @kylejarcher, Allie and Sophs - http://bkite.com/07W4y7:20 PM May 28th
Ben is growing up. Got rid of the infant car seat today and installed the @Britax1:46 PM May 30th
Just placed an offer on a great place in Lenexa8:21 PM May 30th
Our offer was accepted!!10:42 AM May 31st
My favorite part of our new house - Pic: http://bkite.com/084g211:41 AM Jun 1st
Buying a warranty actually paid off for me. Got a new TomTom 930 today cause 910 that I bought 3 years ago has been crapping out.2:31 PM Jun 6th
We hit a few snags today during the inspection and getting insurance. Hopefully all will be resolved quickly.8:37 PM Jun 8th
Heading to a WPF conference in Chicago via Pella, IA and Madison. Ben's first really big road trip. #fb7:12 AM Jun 11th
Round 2 of negotiations (post-inspection) are done!8:14 AM Jun 11th
Getting ready to check in for my classes. Gonna be a great day of WPF learning. - http://bkite.com/08qvX7:58 AM Jun 12th
Awesome trail run tonight with the crew from TrailHawks - they were gentle popping my trail cherry :) - http://bit.ly/6ifZo10:25 PM Jun 17th
Heading to KC with @chibb to P&L to spend a night boozin' celebrating my 30th, father's day, buying a house and getting a babysitter :)2:10 PM Jun 19th
Video of Ben crawling around and standing himself up for the first time - http://bit.ly/t9eiU9:59 PM Jun 21st
@fxdgear we have too much crap!9:40 PM Jun 24th
Closing on Lenexa house in 12 hours! #fb11:19 PM Jun 25th
Homeowners!11:09 AM Jun 26th
Thanks everybody. @chibb and I are thrilled. And also a bit exposed, didn't realize that there wasn't any blinds :)12:23 PM Jun 26th
Everything is off the truck and into the house. Many thanks to my dad for helping and Chels mom for watching Ben #fb10:43 PM Jun 28th
First ever #bike commute. 2.91 miles in 11 minutes and 2 seconds. Pretty easy ride. A few hills, but nothing major.2:01 PM Jun 29th
@mintchaos The place is an absolute disaster of boxes. I couldn't find any clothes so I came work in gym clothes :)3:40 PM Jun 29th
Backed up pipes! Poop in the basement! Yay for home ownership!! - http://bkite.com/097G01:18 PM Jul 1st
We are moved in enough that we can park one car in the garage! - http://bkite.com/09c4i12:08 PM Jul 3rd
Enjoying a beautiful night with @chibb on our new deck. -http://bkite.com/09fMg9:16 PM Jul 4th
I ran a 5:46 mile this morning. My fastest ever since high school -http://bit.ly/16tKNT9:02 AM Jul 10th
I promise to be a better blogger :)
Monday, March 23, 2009
This is a great tool for those circumstances where ordinary sarcasm isn’t quite enough to get the point across.
http://lmgtfy.com
In action: http://tinyurl.com/d2yyxl
Friday, February 27, 2009
Since my Dell D610 is three years old, I decided it was time for a new lappy. I finally decided on a E6400 from Dell.
Finally a machine that can actually run Vista. Here is my score:
So far I love it. I can have VS 2008 plus a couple virtual machines open and it doesn’t even blink.
Technorati Tags:
Dell,
E6400
Friday, February 20, 2009
I asked my first question on stackoverflow today. I want to make sure I’m unsubscribing from PropertyChanged events properly. Please head over and put in your two cents.
Monday, January 05, 2009
In ParaPlan 4.0, we use twitter to maintain a change log. I wanted to display this information to our users, so I wrote a little class that calls the RSS feed and uses LINQ to parse the data. All I need is the message and the date, so that is all it pulls out.
Here is the class:
public class Twitter
{
public string Message { get; set; }
public DateTime PubDate { get; set; }
public static List<Twitter> Parse(string User)
{
var rv = new List<Twitter>();
var url = "http://twitter.com/statuses/user_timeline/" + User + ".rss";
var element = XElement.Load(url);
foreach (var node in element.Element("channel").Elements("item"))
{
var twit = new Twitter();
var message = node.Element("description").Value;
//remove username information
twit.Message = message.Replace(User + ": ", string.Empty);
twit.PubDate = DateTime.Parse(node.Element("pubDate").Value);
rv.Add(twit);
}
return rv;
}
}
Our calling code looks like this:
var changes = new List<string>();
var fromTwitter = Twitter.Parse("ParaPlan");
fromTwitter.ForEach(t =>
changes.Add(t.PubDate.ToString("MM/dd/yy") + " - " + t.Message));
var list = new ListBox();
list.ItemsSource = changes;
Friday, January 02, 2009
On 12-20-2008, my wife Chelsea, gave birth to our beautiful baby boy Benjamin. It’s been a crazy exciting and busy couple of weeks so far. I’ve tried to respond to all the many people that have congratulated us on twitter and facebook, but if I forgot, thank you so much. All of your kind words and well wishes have been very appreciated!!
Many more pictures here.
Technorati Tags:
Benjamin