Tim Hibbard

CEO for EnGraph software
posts - 629 , comments - 1681 , trackbacks - 459

My Links

News



Add to Google

Twitter












Tag Cloud

Article Categories

Archives

Post Categories

Image Galleries

EnGraph Blogs

Links

Other

Roll

Wednesday, June 12, 2013

Version 1.7 of Christian Radio Locator available on the App Store

The latest version of Christian Radio Locator is now available as an update on the App Store. As discussed, this version adds support for Driving Mode. 

Favorites are great, but a recent road trip showed just how annoying it can be to have all your favorites at the top of the screen. With that in mind, we added Driving Mode. Go to Settings, and toggle on driving mode to have nearby stations display before the favorites. It also changes the long press behavior to show the station on a map (with its range) and your GPS location. That way you know if the static is because you are coming or going into the station’s range. Once you get back home, toggle Driver Mode to Off and your favorites will be back on top where they belong.

 

Driving Mode is another example of a great feature that is only available after an in-app purchase of $0.99 for the Pro version. The Pro version also includes the ability to mark a station as a favorite and syncs hidden and favorite stations with iCloud keeping all your iOS devices in sync.

Here is a sneak peek of the app running iOS 7:

Posted On Wednesday, June 12, 2013 10:08 AM | Comments (3) |

Monday, November 12, 2012

Podcast with AJI about iOS development coming from a .NET background

TheAJIReportLogo

I talked with Jeff and John from AJI Software the other day about developing for the iOS platform. We chatted about learning Xcode and Objective-C, provisioning devices and the app publishing process. We all have a .NET background and made lots of comparisons between the two platforms/ecosystems/fanbois. They even let me throw in a plug for Christian Radio Locator.

Jeff was my first contact with the Kansas City .NET community. It was probably about 10 years ago. He pushed me to talk more (and rescued me from my first talk that bombed) and blog more. One time a group of us took a 16 hour car trip to South Carolina for a code camp and live podcasted the whole thing. Good times.

Listen to the show

Click here to subscribe to more AJI Reports in the future.

Posted On Monday, November 12, 2012 6:50 AM | Comments (1) |

Saturday, September 8, 2012

Christian Radio Locator iPhone app

Mzl wqycyulm 175x175 75

For the last three months or so I've been working on an iPhone (and iPad) app in my spare time. It all started when I took the kids to Minneapolis and had a hard time finding radio stations to listen to on the trip. I looked in the App Store for an app that would use my GPS to show me Christian radio stations nearby, but there wasn't one. So I decided to build my own.

Using public information from the FCC and a few other sources, I built a database in Google docs that contains the frequency for all Christian radio stations, where the tower is located and how far the tower can reach. I also included any streaming audio information and other contact information like Facebook or Twitter that I could find.

Google spreadsheets publish in JSON format (yes, really) and Xcode can automatically deserialize JSON into a properly formatted entity. This is one area that Xcode is far superior to C#. In a just a few lines of code, I can have a list of in-memory strongly typed objects from a web-based JSON feed. To accomplish the same thing natively in .NET would be much more work and wouldn't feel nearly as clean when it was said and done.

The snazzy icon shown above was built by my very talented wife. She hasn't yet provided any feedback on the app's user interface, which is why it is so plain and boring.

I used a navigation view controller and EGO pull to refresh table view to construct the main window. Pulling down to refresh initiates a GPS lookup, which queries the database for radio stations in range (yes, you can pass parameters to Google spreadsheets and get a subset back in JSON). Pulling up on the table extends the range of the search and includes stations that may not be close enough to get clear audio. This feature is not that intuitive and the next version contains an update to that functionality.

Tapping a cell will show a detail view that displays additional information about the station. The user can click to view the station on a map, click to listen to an online stream (if available) or click to see the station's Facebook or Twitter pages.

Swiping back and forth on the table changes the information that is displayed on the right hand side of the table cell. It scrolls through the city where the tower is located, how far the phone is from the tower, the range of the tower and in the next version a signal strength indicator. This was pretty easy to implement once I figured out how to assign the gesture recognizer delegate. 

Tapping and holding on a cell will jump the user to the map view screen. Which is pretty cool, but very hard for even a power user to discover. To tackle the issue of discoverability, the next version has a series of instructions displayed at the bottom of the screen to show the user the various shortcuts. Once the user has performed the swipes and long holds, the instructions disappear.

I've learned a lot developing this app. Spending over a decade exclusively in .NET made the learning curve a bit steep, but once I learned the structure and syntax of Objective-C, I've learned to appreciate the power and simplicity of it.

Here are a few screenshots. I would really appreciate any feedback and especially iTunes reviews. Technically it is open source and a smart googler could probably find it. I just haven't promoted it as open source.

Mzl zlxgzdll 320x480 75

Mzl gceuawaz 320x480 75

 

Mzl cxmnezxi 320x480 75

 

Cross posted from timhibbard.com

 

 

Posted On Saturday, September 8, 2012 8:05 AM | Comments (0) |

Thursday, April 14, 2011

WPF TimeSpinner Control

spinner

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: }

Posted On Thursday, April 14, 2011 10:16 AM | Comments (4) |

Tuesday, April 12, 2011

Smarter DateTime.AddMinutes

times

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: }

Posted On Tuesday, April 12, 2011 8:26 AM | Comments (2) |

Friday, April 8, 2011

WPF–Show Dialog with backdrop

dialog

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: }

Posted On Friday, April 8, 2011 9:17 AM | Comments (2) |

Monday, June 21, 2010

Use Preprocessor Directives for better debugging

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.

Posted On Monday, June 21, 2010 1:08 PM | Comments (0) |

Sunday, June 20, 2010

Hibbard family is growing

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!

big brother ben

Posted On Sunday, June 20, 2010 9:43 PM | Comments (0) |

Thursday, June 17, 2010

Get to No as fast as possible

 

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.

Posted On Thursday, June 17, 2010 9:46 AM | Comments (11) |

Tuesday, June 8, 2010

Use Extension Methods to find first and last day of the month

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();
}

Posted On Tuesday, June 8, 2010 8:41 AM | Comments (1) |

Monday, June 7, 2010

Declaring variables in SQL

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

Posted On Monday, June 7, 2010 9:46 AM | Comments (1) |

Saturday, March 20, 2010

Associating your MentionNotifier subscriptions with OAuth

logo[1]

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.

Posted On Saturday, March 20, 2010 11:27 AM | Comments (0) |

Friday, January 22, 2010

Auto Starting Virtual Server

 

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.

Posted On Friday, January 22, 2010 11:14 AM | Comments (0) |

Wednesday, December 30, 2009

EnGraph is hiring – ASP.NET, Kansas City

EnGraphSoftware

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

Posted On Wednesday, December 30, 2009 1:07 PM | Comments (1) |

Friday, October 2, 2009

My first race win

P9260043

 

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.

Posted On Friday, October 2, 2009 2:11 PM | Comments (6) |

Wednesday, September 23, 2009

Get notified via email of twitter mentions

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.

Posted On Wednesday, September 23, 2009 11:44 AM | Comments (3) |

Friday, July 31, 2009

Finally able to get rid of Outlook

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!

Posted On Friday, July 31, 2009 4:49 PM | Comments (1) |

Friday, July 17, 2009

The last couple months of our life via twitter

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 :)

Posted On Friday, July 17, 2009 10:51 AM | Comments (3) |

Monday, March 23, 2009

Let me google that for you

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

Posted On Monday, March 23, 2009 11:01 PM | Comments (2) |

Friday, February 27, 2009

8GB RAM in laptop…mmmm

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.

E6400

Finally a machine that can actually run Vista.  Here is my score:

system

score2

So far I love it.  I can have VS 2008 plus a couple virtual machines open and it doesn’t even blink.

 

Technorati Tags: ,

Posted On Friday, February 27, 2009 3:00 PM | Comments (17) |

Friday, February 20, 2009

Stackoverflow

 

stackoverflow-logo-250

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.

 

Posted On Friday, February 20, 2009 2:47 PM | Comments (1) |

Monday, January 5, 2009

Simple class to parse Twitter with LINQ

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;

 

Technorati Tags: ,,,,

Posted On Monday, January 5, 2009 2:53 PM | Comments (9) |

Friday, January 2, 2009

Benjamin Allan Hibbard

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:

Posted On Friday, January 2, 2009 10:26 AM | Comments (5) |

Tuesday, December 9, 2008

WPF – Hide a listbox when it doesn’t have any items

This code example will show how to hide a listbox from the user when it doesn’t have any items. 

It involves binding the Visibility of the listbox to the Items.Count of the listbox.  We run the Items.Count through a converter that will return a Visibility.Visible object if the Item.Count is not zero.

Here is the converter class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Data;
using System.Windows;
 
namespace HideAListBox
{
    public class ZeroCollapsedNonZeroVisible : IValueConverter
    {
 
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            var rv = Visibility.Visible;
            var val = 0;
            int.TryParse(value.ToString(), out val);
            if (val == 0)
            {
                rv = Visibility.Collapsed;
            }
            return rv;
        }
 
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
 
    }
}

Here is the window code:

<Window x:Class="HideAListBox.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:converter="clr-namespace:HideAListBox"
        Title="Window1" Height="300" Width="300">
    <Window.Resources>
        <converter:ZeroCollapsedNonZeroVisible x:Key="hideListBox"/>
    </Window.Resources>
    <DockPanel LastChildFill="True">
        <Button Content="Add Item" Click="addItemClick" DockPanel.Dock="Top"/>
        <Button Content="Clear Items" Click="clearItemsClick" DockPanel.Dock="Top"/>
        <ListBox Margin="3" BorderBrush="Blue" Name="myListBox" 
                 Visibility="{Binding ElementName=myListBox, 
                              Path=Items.Count, 
                              Converter={StaticResource hideListBox}}"/>
    </DockPanel>
</Window>

The entire project can be downloaded here.

Technorati Tags: ,,

Posted On Tuesday, December 9, 2008 3:48 PM | Comments (24) |

Tuesday, November 25, 2008

Visual Studio Team System Unit Testing Keyboard Shortcuts

I try to use keyboard shortcuts as much as possible.  Here are some that are handy when unit testing with Visual Studio:

Ctrl + R, A – Run all tests
Ctrl + R, T – Run tests in context (based on if cursor is in function, class, or namespace)
Ctrl + R, F – Runs all tests that are checked in Test Results – very handy because that window is impossible to navigate without a mouse

Ctrl + R, Ctrl + A – Run all tests in debug mode
Ctrl + R, Ctrl + T – Run all tests in context in debug mode
Ctrl + R, Ctrl + F – Run all checked tests in debug mode

 

Posted On Tuesday, November 25, 2008 11:24 AM | Comments (21) |

Powered by: