June 23, 2013

expense tracker - part 7

Gosh, when I started this project I had no idea it would be this complicated.  The concept seems so simple, doesn't it?  A working prototype took maybe a day, and that's with me barely knowing XAML or WPF or Silverlight at all.  But doing it RIGHT, and doing it so it's USABLE, can get complicated so fast.

I completed reorganized my code again.  I'm a huge fan of folders.. I made a folder for all of my converter classes, for variables, even a folder for little helper functions.  I tried segmenting my code even more.  And I separated the two Default fields in my app - the Other field and the Running tally field, as they operate a little differently than every other <name, value> pair the user types in so they might as well be separated into yet another class.  I'm still playing around with how all of the data and its corresponding logic should be organized.

I kind of hated the huge "Add" button in my previous implementation, even though it enables Command binding and so implements MVVM.  I looked up BindableApplicationBar and was excited to use it, but I don't think it works with Windows 8.  Sad face.  I think in this instance I'm willing to give up pure MVVM implementation in return for a better user interface, as I don't like using my app with the huge "Add" button, but I do like looking at it with everything neatly contained in the app bar.  As I've tried to be careful to use good coding practices in the rest of my app, I don't think it'll hurt too much to break MVVM here.

I'd only learned about singleton design patterns in school, this is actually the first time I've ever used one (well, knowingly).  My dilemma was - how do you implement INotifyPropertyChanged for a static class?  Sometimes you want to access a field that is used across multiple files in your application.  So this field must be static - but now you can't make it implement INotifyPropertyChanged.  Ahh, but here is where you can use the singleton pattern to implement that functionality.  I can't fully explain the details - but INotifyPropertyChanged only works for an instance of a class.  So you just create one instance of that class, and it can implement all of the INotifyPropertyChanged functionality you need.  As I said earlier, I decided to separate out the variables into separate classes - so I have a class for all of the text fields called Changing (since I have another class called Default that holds all of the variables that don't change).  Here is the implementation for that class:

public sealed class Changing : INotifyPropertyChanged {

  #region Singleton stuff

  private static readonly Changing _Instance = new Changing();
  private Changing() { }
  public static Changing Instance {
    get { return _Instance; }
  }

  #endregion

  #region Text Information

  private string _CurrentTxt = Default.DEFAULT_TXT;
  public string CurrentTxt {
    get { return _CurrentTxt; }
    set {
      _CurrentTxt = value;
      OnPropertyChanged("CurrentTxt");
    }
  }

  private bool _IsTxtDefault = Default.DEFAULT_SETTINGS;
  public bool IsTxtDefault {
    get { return _IsTxtDefault; }
    set {
      _IsTxtDefault = value;
      OnPropertyChanged("IsTxtDefault");
    }
  }

  #endregion

  #region Num Information

  private decimal _CurrentNum = Default.DEFAULT_NUM;
  public decimal CurrentNum {
    get { return _CurrentNum; }
    set {
      _CurrentNum = value;
      OnPropertyChanged("CurrentNum");
    }
  }

  private bool _IsNumDefault = Default.DEFAULT_SETTINGS;
  public bool IsNumDefault {
    get { return _IsNumDefault; }
    set {
      _IsNumDefault = value;
      OnPropertyChanged("IsNumDefault");
    }
  }

  #endregion

  #region INotifyPropertyChanged Implementation

  private void OnPropertyChanged(string p) {
    if (PropertyChanged != null) {
      PropertyChanged(this, new PropertyChangedEventArgs(p));
    }
  }

  public event PropertyChangedEventHandler PropertyChanged;

  #endregion
 
}

Yay and now I have static variables that can work with data binding.

Another small change is that I changed the numbers from being int to double to decimal.  Int was a bad choice because not everything you buy falls perfectly into an int value.  I wouldn't mind adding only int values to my phone but if you go through the trouble of adding a number with a decimal point, the TryParse just silently fails and drops the user's input.  Double seemed like overkill, I think decimal is more appropriate for this purpose.

Another thing I noticed was that I previously was loading all of the data in a ViewModel for my Display Expenses user control, but I don't think that was the place for it.  A lot of my debugging efforts were stymied by this code dependency.  So I just loaded the data in a separate class and called it in the constructor for my MainPage, I don't know if that's good practice or not but I know that separating those two different functionalities makes my life a lot easier when trying to debug code, and a lot less confusing when trying to navigate around.

Yeah.. a lot of changes.. My next problem to tackle is that fact that TextBoxes don't lose focus when you tap on an appbar item.  With my previous implementation, with the huge "Add" button, the TextBoxes would lose focus and the corresponding fields were correctly updated.  But appbars are annoying to work with and they keep the focus on the TextBoxes, which means my app has a bug now and I have to do some fanagling to make it right again.