Tom Knowlton
asked on
ArrayList not storing a NEW object each time I call Add( ) but a reference instead?
Do ArrayLists sometimes not work properly? Are there known bugs, or am I just not using it properly?
I have some ArrayLists I am using to store class objects, on the order of 50+ class objects per ArrayList.
Later, I loop through them, and I am getting output...but not what I expected. One of the values I access is the balance due. It is almost always zero.
It is almost as if the Add( ) method for the ArrayList is not adding a NEW object each time....but overwriting it or just storing the address to the object instead of the physical object itself (along with the data). That is the only thing that seems to explain what I am seeing happen.
Is this "normal" -- and how do I get past this problem?
I can't explain it any better than this.
Below I'll paste the DLL code as well as the demo app that uses the DLL
SOURCE CODE DUMP:
DLL code:
//Changes made on 31 July, 2007
//-Created parent class for both Heloc and Mortgage classes, which they now inherit from.
//-I did this so I could more easily distribute new changes that apply to both classes.
//
//=====
//rename this to anything OTHER than "debug" in order to turn-off the reporting to the Output window
#define debug
using System;
using System.Collections.Generic ;
using System.Collections;
using System.Text;
namespace Driven
{
//I wrote this class in an attempt to NOT have to write
//System.Diagnostics.Debug .WriteLine ( ) EVERY SINGLE TIME that I wanted some debug output
public class DebugOut
{
#region Constructors
public DebugOut()
{ }
#endregion
#region dOut
public static void dOut(string message)
{
//My intent is to only output debug statements if the conditional define is set to "debug"
#if debug
System.Diagnostics.Debug.W riteLine(m essage);
#endif
}
#endregion
}
//Parent class for Heloc and Mortgage classes
//Has Amortization Period support (enumerations)
public class CommonFinancedItems
{
private AmortizationPeriodType _amortType;
//Used to tag specific Mortgage or Heloc objects with specific integer Month tag, if needed
//This is for seeing data for specific months...
public int _monthTag;
public CommonFinancedItems()
{
}
#region enums
public enum AmortizationPeriodType : int
{
Weekly = 52,
BiWeekly = 24,
Monthly = 12
}
public AmortizationPeriodType AmortPeriod
{
get
{
return _amortType;
}
set
{
_amortType = value;
}
}
#endregion
}
//This class represents a Home Equity Line of Credit (Heloc)
//Inheirts from CommonFinancedItems so that it can have support
//for Amortization Periods
public class Heloc : CommonFinancedItems
{
#region Variables
//Events that this class can raise
public event EventHandler HelocAmountOwedIsZero;
public event EventHandler HelocAmountOwed;
public event EventHandler BothPaidOff;
public event EventHandler HelocError;
//internal variables
//Most are declared public for now (to ease in debugging)
//But later on should be declared private and accessed via Properties (setters and getters)
public int _helocReapplicationInterva lInMonths = 18;
public double _helocAmountOwed;
public double _helocTotalAmountAvailable NoRestrict ionsEnforc ed;
public double _helocAmountAllowed;
public double _helocInterestRate;
public double _helocMonthlyBills;
public string ErrorMessage;
private DebugOut dbg = new DebugOut();
#endregion
#region Constructors
//Used to initialize the internal variables for the Heloc class
//Also note that the events are initialized to null
public Heloc(double amountOwed, double initialAmountAvailable, double interestRate, double monthlyBills, Mortgage m)
{
_helocAmountOwed = amountOwed;
this._helocTotalAmountAvai lableNoRes trictionsE nforced = initialAmountAvailable;
_helocInterestRate = interestRate;
_helocMonthlyBills = monthlyBills;
this.HowMuchToApplyToMortg age(m);
HelocAmountOwedIsZero = null;
HelocAmountOwed = null;
BothPaidOff = null;
HelocError = null;
}
#endregion
#region HowMuchToApplyToMortgage
//Used to determine how much of the available Heloc balance we can
//apply to the Mortgage
//
//Notice a Mortgage object is passed in...so we can determine total equity, since
//the Heloc amount available is a function of the total equity in the home
public double HowMuchToApplyToMortgage(M ortgage m)
{
//Per my discussion with Kevin, we want to hold in reserve
// ( do not spend ) about twice the monthly bills amount
//This provides a cushion in case people run into financial troubles for
//a month or two
double safetyBuffer = 2 * this._helocMonthlyBills;
//This is what is returned from the method...initialized to zero
double amountToApply = 0;
//If the safety buffer (described above) is greater than the
//total available equity (set elsewhere in another method)
//then raise an error and fire the HelocError event
if (safetyBuffer > this._helocTotalAmountAvai lableNoRes trictionsE nforced)
{
if (HelocError != null)
{
this.ErrorMessage = "Equity must exceed safety buffer. Calculation STOPPED";
//raise the event...
HelocError(this, null);
}
}
//If the amount we have available is greater than the remaining mortgage balance, then
//just PAY OFF THE MORTGAGE
if (this._helocTotalAmountAva ilableNoRe strictions Enforced >= m._mortAmountOwed)
{
amountToApply = m._mortAmountOwed;
this._helocAmountAllowed = amountToApply;
}
else //Otherise, make a big payment using the amount available - the saftety buffer
{
if (this._helocTotalAmountAva ilableNoRe strictions Enforced > safetyBuffer)
{
this._helocAmountAllowed = this._helocTotalAmountAvai lableNoRes trictionsE nforced - safetyBuffer;
}
else
{
this._helocAmountAllowed = 0;
}
amountToApply = this._helocAmountAllowed - this._helocAmountOwed;
}
if (amountToApply < 0)
{
amountToApply = 0;
}
//Some debug output to determine if the process is working correctly
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut(" TOTAL TECHNICALLY AVAILABLE: " + this._helocTotalAmountAvai lableNoRes trictionsE nforced.To String());
Driven.DebugOut.dOut(" BUFFER: " + safetyBuffer.ToString());
Driven.DebugOut.dOut(" ALLOWED: " + this._helocAmountAllowed.T oString()) ;
Driven.DebugOut.dOut(" SUGGESTED AMOUNT TO USE FROM HELOC: " + amountToApply.ToString());
Driven.DebugOut.dOut(" CURRENTLY OWED: " + this._helocAmountOwed.ToSt ring());
Driven.DebugOut.dOut("");
return amountToApply;
}
#endregion
#region CheckDebtStatus
//Whenver this is called we ask the Heloc object "Do you owe anything?"
//Depending on the answer, we raise the appropriate event
//
//Notice we pass-in a mortgage object so we can check the balance on the mortgage as well
//--this is needed if we are to ever finish processing
public void CheckDebtStatus(Mortgage m)
{
//are both the heloc and the mortgage paid off?
if ((this._helocAmountOwed <= 0.0) && (m._mortAmountOwed <= 0.0))
{
if (BothPaidOff != null)
{
//raise the event...
BothPaidOff(this, null);
}
}
else if (this._helocAmountOwed > 0.0) // Is a Heloc balance still owed?
{
if (HelocAmountOwed != null)
{
//raise the event...
HelocAmountOwed(this, null);
}
}
else if(this._helocAmountOwed <= 0.0) // has the Heloc balance been paid-off???
{
if (HelocAmountOwedIsZero != null)
{
//raise the event...
HelocAmountOwedIsZero(this , null);
}
}
}
#endregion
#region MakeHelocPayment
//Pretty self-explanatory...this makes a payment on the Heloc balance owed
//The parameter passed in is the amount of the payment
public void MakeHelocPayment(double helocPayment)
{
//If we owe on the Heloc, then let's make a payment on it...
if (this._helocAmountOwed > 0.0)
{
double periodInterestPayment;
//calculate interest payment
periodInterestPayment = this._helocAmountOwed * (this._helocInterestRate / (int)this.AmortPeriod);
//is the amount owed on the heloc less than the payment amount?
if ((this._helocAmountOwed + periodInterestPayment) < helocPayment)
{
//If so, then pay-off the Heloc NOW!
this._helocAmountOwed = 0.0;
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut(">>>> >>>>>>>>>> >>>>>>>>>> >>>>>>>>>> >>>>>>>>>> >>>>> Heloc was just paid-off!!!");
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut("");
}
else // just make a normal Heloc payment
{
this._helocAmountOwed = (this._helocAmountOwed + periodInterestPayment) - helocPayment;
Driven.DebugOut.dOut("Helo c payment of " + helocPayment.ToString() + " was made, Heloc amount owed is: " + this._helocAmountOwed.ToSt ring());
}
}
}
#endregion
#region ReturnHomeEquity
//When called this sets the internal variable for "Total Amount of Equity"
//This calculation is performed by looking at the original amount owing and
//subtracting the current amount owed.
//The difference should be the total available equity
public double ReturnHomeEquity(Driven.Mo rtgage m)
{
return m._mortOriginalAmountOwed - m._mortAmountOwed;
}
#endregion
#region RenewHelocTotalAmountAvail ableNoRest rictions
//This method is used to determine if the Amount Available for the Heloc can be increased
//I seem to think this happens about every 18 months?
public void RenewHelocTotalAmountAvail ableNoRest rictions(d ouble numberOfMonthsAccrued, Driven.Mortgage m)
{
//Update available Heloc credit line every XX months...
if ((numberOfMonthsAccrued % this._helocReapplicationIn tervalInMo nths) == 0)
{
this._helocTotalAmountAvai lableNoRes trictionsE nforced = ReturnHomeEquity(m);
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut(" THE HELOC WAS RENEWED (" + numberOfMonthsAccrued.ToSt ring() + " MONTHS SINCE INCEPTION)");
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut("");
}
}
#endregion
#region HelocReadyToPayDownMortgag eBasedOnPe rcentageLe ft
//This method is called (returns true if ready to pay down) in order to
//determine if the amountOwed has been paid-down sufficiently to allow us to make
//yet another BIG down payment on the Mortgage Principal again
//
//This is determined by a Percentage Paydown "Trigger"
//For example, if 10% is owing and the Percentage Paydown Trigger is 10%...then we can pay down again.
public bool HelocReadyToPayDownMortgag eBasedOnPe rcentageLe ft(double percentageCEILINGLeftOfTot alBalance, Driven.Mortgage m)
{
//initialize to false...assume we are not going to be ready to pay down
bool readyToPayDown = false;
double ThreshholdMoneyAmount = this._helocAmountAllowed * percentageCEILINGLeftOfTot alBalance;
if ((this._helocAmountOwed <= ThreshholdMoneyAmount) && (m._mortAmountOwed > 0.0))
{
//As is my custom...some Debug Output to show that we are doing this and why
//We can check the numbers to see if they reflect accurately the logic that is in place
//only gets set to true if the conditions were met above...
readyToPayDown = true;
Driven.DebugOut.dOut("//// ////////// ////////// ////////// ////////// ////////// ///");
Driven.DebugOut.dOut("//// ////////// ////////// ////////// ////////// ////////// ///");
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut("READ Y TO PAY DOWN MORTGAGE USING HELOC AGAIN.");
Driven.DebugOut.dOut("PAYD OWN ONLY WHEN THIS MUCH IS OWED (OR LESS): " + ThreshholdMoneyAmount.ToSt ring("F")) ;
Driven.DebugOut.dOut(" HOW MUCH OWED: " + this._helocAmountOwed.ToSt ring("F")) ;
Driven.DebugOut.dOut("//// ////////// ////////// ////////// ////////// ////////// ///");
Driven.DebugOut.dOut("//// ////////// ////////// ////////// ////////// ////////// ///");
}
return readyToPayDown;
}
#endregion
}
//This class represents your Mortgage, a Mortgage, any Mortgage...
//Inheirts from CommonFinancedItems so that it can have support
//for Amortization Periods
public class Mortgage : CommonFinancedItems
{
#region Variables
//Events that this class can raise
public event EventHandler MortAmountOwedIsZero;
public event EventHandler MortAmountOwed;
public event EventHandler BothPaidOff;
//Internal variables
//Declared public for now to help with debugging
//but later on...should be declared private and modified
//using setters and getters (Properities)
public double _mortOriginalAmountOwed;
public double _mortAmountOwed;
public double _mortInterestRate;
public double _interestAccrued;
private DebugOut dbg = new DebugOut();
#endregion
#region Constructors
//Initializes the Mortgage object
public Mortgage(double amountOwed, double interestRate)
{
//initialize mortgage object...
_mortOriginalAmountOwed = amountOwed;
_mortAmountOwed = amountOwed;
_mortInterestRate = interestRate;
//set events to null...
MortAmountOwedIsZero = null;
MortAmountOwed = null;
BothPaidOff = null;
}
#endregion
#region CheckDebtStatus
//Asks the Mortgage object: "Do you owe any money?"
//If so...raise the correct event!
//
//Notice a Heloc object is passed-in so we can determine if anything is still oweing
//on the Heloc as well...
public void CheckDebtStatus(Heloc h)
{
//Are both the Mortgage and the Heloc paid-off now???
if ((h._helocAmountOwed <= 0.0) && (this._mortAmountOwed <= 0.0))
{
if (BothPaidOff != null)
{
//raise the event...
BothPaidOff(this, null);
}
}
else if (this._mortAmountOwed > 0.0) //Mortgage still owes something
{
if (MortAmountOwed != null)
{
//raise the event...
MortAmountOwed(this, null);
}
}
else if (this._mortAmountOwed <= 0.0) //Mortgage is paid-off now!!
{
//raise the event...
if (MortAmountOwedIsZero != null)
{
MortAmountOwedIsZero(this, null);
}
}
}
#endregion
#region CalcNormalTermNoHelocAppli ed
//This just does a straight amortization with no Heloc "pay down" involved at all...
//Payment amount is passed-in...
//Usually what will happen is a seperate and distinct Mortgage object will be declared
//and ONLY this methodw would be called.
//In other words...this method would never be called in conjunction with a Mortgage object
//that was participating in a Heloc pay-down program at the same time.
public void CalcNormalTermNoHelocAppli ed(double paymentAmount)
{
while (this._mortAmountOwed > 0.0)
{
this.MakeMortgagePayment(p aymentAmou nt);
}
}
#endregion
#region MakeMortgagePayment
//Call this method to make a mortgage payment
//Payment amount is passed in...
public void MakeMortgagePayment(double mortPayment)
{
double periodInterestPayment = 0;
//calculate the monthly interest
periodInterestPayment = this._mortAmountOwed * (this._mortInterestRate / (int)this.AmortPeriod);
//to calculate interest saved
_interestAccrued += periodInterestPayment;
//Only make a payment if there is still money owed...
if (this._mortAmountOwed > 0.0)
{
//If the amount owed is less than or equal to the payment amount, then pay-off the loan
if ((this._mortAmountOwed + periodInterestPayment) <= mortPayment)
{
//Some debug output to verify this happened
this._mortAmountOwed = 0.0;
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut(">>>> >>>>>>>>>> >>>>>>>>>> >>>>>>>>>> >>>>>>>>>> >>>>> Mortgage has been paid-off completely!!!!!!!");
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut("");
}
else //otherwise just make a normal payment
{
//For debug / reporting purposes
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut("Prev ious mortgage balance owed (" + this._mortAmountOwed.ToStr ing() + " + " + periodInterestPayment.ToSt ring() + "): " + (this._mortAmountOwed + periodInterestPayment));
Driven.DebugOut.dOut("Mort gage payment made: " + mortPayment.ToString());
//add monthly interest to entire amount owing, then pay on THAT
this._mortAmountOwed = (this._mortAmountOwed + periodInterestPayment) - mortPayment;
Driven.DebugOut.dOut("Afte r payment, now mortgage balance is now: " + this._mortAmountOwed.ToStr ing());
}
}
}
#endregion
}// end of Mortgage class
//INTENT: This class is the class that instantiates both the Mortgage and Heloc objects
//and then runs them through their paces!
public class DrivenExpert
{
#region Variables
//Heloc object to be used throughout the various class methods
public Heloc _heloc;
//Mortgage object to be used throughout the various class methods
public Mortgage _mortgage;
//SPECIAL Mortgage object that does not take advantage of the Heloc pay down
public Mortgage _mortgageNoHeloc;
//How many mortgage payments have been made?
private int _mortPaymentsAccrued = 0;
//How many heloc payments have been made?
private int _helocPaymentsAccrued = 0;
//When this percentage of the Heloc balance is remaining ... do another pay-down
public double _payDownPercentageTrigger;
//Accumulates the amortized heloc payments as they accrue
//For example, if you are making weekly payments, they would accrue in this variable
private double _helocPaymentGradualAccrua l;
//Boolean which says "the correct number of amortized payments have been made on the Heloc
//now you can pay on the Mortgage"
private bool _timeToPayMortgageNow = true;
//Mortgage payment amount
public double _monthlyMortPay;
//Amortized heloc payment (divided into weekly, biweekly or Monthly payments)
public double _helocAmortizedPayment;
//Monthly amount for our use (pre-amortization)
private double _originalHelocMonthlyPayme ntAmount;
//For the down line bonus program - the amount to add to our discretionary income
private double _downLineAccrualAmount;
//MAX amount that we can spend
private double _downLineAccrualCeiling;
//How often to add the down line bonus to the discretionary income
private int _downLineIncIntervalInMont hs;
//Keeps track of how much we have added to the discretionariy income so far
//using the bonus amount
private double _downLineAccrualAmountAccu mulator;
//Internal declaration of the Driven.CommonFinancedItem class
//Used to set the Amortization period
public Driven.CommonFinancedItems _commonFinancedItem;
//Used to store Mortgage objects as needed for showing specific mortgage information for a
//particular month
public ArrayList _specificMortMonths = new ArrayList();
//Used to store Heloc bjects as needed for showing specific heloc information for a
//particular month
public ArrayList _specificHelocMonths = new ArrayList();
//Used for debug output
private DebugOut dbg = new DebugOut();
#endregion
#region Constructors
//Initialized the DrivenExpert object
public DrivenExpert(double mortAmountOwed,
double mortInterestRate,
double monthlyMortPay,
double helocAmountOwed,
double helocAmountAvailable,
double helocInterestRate,
double helocMonthlyBills,
double helocPayment,
double helcoPayDownPercentageTrig ger)
{
_monthlyMortPay = monthlyMortPay;
_helocAmortizedPayment = helocPayment;
_originalHelocMonthlyPayme ntAmount = helocPayment;
_payDownPercentageTrigger = helcoPayDownPercentageTrig ger;
_commonFinancedItem = new CommonFinancedItems();
_mortgage = new Mortgage(mortAmountOwed, mortInterestRate);
_mortgageNoHeloc = new Mortgage(mortAmountOwed, mortInterestRate);
_heloc = new Heloc(helocAmountOwed, helocAmountAvailable, helocInterestRate, helocMonthlyBills, _mortgage);
//sign-up for the events we want to be notified of
//We want to be notified when either Mortgage or Heloc are still owing
_heloc.HelocAmountOwed += new EventHandler(this.Financed ItemOwing) ;
_mortgage.MortAmountOwed += new EventHandler(this.Financed ItemOwing) ;
//We want to be notified when either Mortgage or Heloc are paid-off
_heloc.HelocAmountOwedIsZe ro += new EventHandler(this.Financed ItemIsZero );
_mortgage.MortAmountOwedIs Zero += new EventHandler(this.Financed ItemIsZero );
//We want to be notified when BOTH Mortgage AND Heloc are paid-off
_heloc.BothPaidOff += new EventHandler(this.BothPaid Off);
_mortgage.BothPaidOff += new EventHandler(this.BothPaid Off);
}
#endregion
#region Properties
//Used to set the Down Line Bonus program variables
public double DownLineAccrualAmount
{
set
{
_downLineAccrualAmount = value;
}
}
public double DownLineAccrualCeiling
{
set
{
_downLineAccrualCeiling = value;
}
}
public int DownLineIncIntervalInMonth s
{
set
{
_downLineIncIntervalInMont hs = value;
}
}
//If Monthly, the multiplier is 1
//If BiWeekly, the multiplier is 2
//If Weekly, the multipler is 4.33(repeating)
public double HelocAmortizationPeriodMul tiplier
{
get
{
double AmortPeriodDivisor = (int)this._heloc.AmortPeri od;
double divisor = 12;
return AmortPeriodDivisor / divisor;
}
}
//Return Heloc months accrued (reflect amortization period)
public double HelocMonthsAccrued
{
get
{
double months = this._helocPaymentsAccrued / this.HelocAmortizationPeri odMultipli er;
return months;
}
}
//Return Mortgage months accrued (reflect amortization period)
public int MortgageMonthsAccrued
{
get
{
int AmortPeriodDivisor = (int)Driven.CommonFinanced Items.Amor tizationPe riodType.M onthly;
int divisor = 12;
int months = this._mortPaymentsAccrued / (AmortPeriodDivisor / divisor);
return months;
}
}
#endregion
//This method gets called when the BothPaid event gets raised in either the Heloc or the Mortgage class
public void BothPaidOff(object sender, EventArgs e)
{
Driven.DebugOut.dOut(sende r.ToString () + " says DONE, amount owing is zero on both accounts");
}
#region GetOutOfDebt
//As I have inidicated elsewhere...this method call does all the work
//It is the workhorse of the DrivenExpert class
public void GetOutOfDebt()
{
//Set the amortization period for the mortgage object (usually monthly)
_mortgage.AmortPeriod = Driven.CommonFinancedItems .Amortizat ionPeriodT ype.Monthl y;
//Set the amortization period for the Heloc object (Weekly, BiWeekly or Monthly)
_heloc.AmortPeriod = this._commonFinancedItem.A mortPeriod ;
//Initialize the Accumulator to the heloc amortized payemnt (not amortized at this point)
_downLineAccrualAmountAccu mulator = _helocAmortizedPayment;
//Set the Heloc Amortized Payment (Weekly, BiWeekly, or Monthly)
this.SetNewHelocAmortizedP ayment();
//Start the ball rolling by asking the _mortgage object if it owes anything
//The answer needs to be yes (of course it should be, since we want to pay down the mortgage)
_mortgage.CheckDebtStatus( _heloc);
//Same thing for the Heloc object
_heloc.CheckDebtStatus(_mo rtgage);
//By this point we are DONE....output the debug results
double mma = this.MortgageMonthsAccrued ;
double hma = this.HelocMonthsAccrued;
Driven.DebugOut.dOut("Mont hs to pay-off Mort: " + mma.ToString());
Driven.DebugOut.dOut("Mont hs to pay-off Heloc: " + hma.ToString());
Driven.DebugOut.dOut("Mont hs to pay-off Both: " + (Math.Min(mma, hma) + (Math.Max(mma, hma) - Math.Min(mma, hma))));
}
//This method is used to set the Amortized payment amount for the Heloc
public void SetNewHelocAmortizedPaymen t()
{
//downLineAccrualAmountAcc umulator cannot surpass the CELING ... if so make
//it EQUAL to the ceiling amount
if (_downLineAccrualAmountAcc umulator >= this._downLineAccrualCeili ng)
{
_downLineAccrualAmountAccu mulator = this._downLineAccrualCeili ng;
//Throw an exeception if the CELING is set to ZERO for some reason...
//this does not make sense
if (_downLineAccrualAmountAcc umulator == 0)
{
Driven.DebugOut.dOut("Erro r; downLineAccrualCeiling is zero");
throw new Exception("downLineAccrual Ceiling is zero");
}
}
//Set the _heloc amortized payment using the accumulator amount divided by the amortization period multiplier
//since _helocAmortizedPayment is internal, no need to return anything
_helocAmortizedPayment = _downLineAccrualAmountAccu mulator / this.HelocAmortizationPeri odMultipli er;
}
//Used to calculate interest saved by using the Heloc pay down program
//This is done by generating two mortgage objects...one that uses the heloc and one that does not
//then subtracting the difference between the interest accrued under the two programs
public double CalcInterestSaved()
{
try
{
_mortgageNoHeloc.AmortPeri od = Driven.CommonFinancedItems .Amortizat ionPeriodT ype.Monthl y;
_mortgageNoHeloc.CalcNorma lTermNoHel ocApplied( _monthlyMo rtPay);
return this._mortgageNoHeloc._int erestAccru ed - this._mortgage._interestAc crued;
}
catch (Exception ee)
{
Driven.DebugOut.dOut("Erro r in the CalcInterestSaved( ) method: " + ee.Message.ToString());
throw new Exception("An error occured in CalcInterestSaved( ) method" + ee.Message.ToString());
}
}
#endregion
#region FinancedItemOwing
//This method gets called when the Mortgage Or the Heloc objects OWE SOMETHING
public void FinancedItemOwing(object sender, EventArgs e)
{
//First thing we want to do is determine if a Heloc pay down is possible
bool canPayDownBasedUponPercent ageLeft = _heloc.HelocReadyToPayDown MortgageBa sedOnPerce ntageLeft( this._payD ownPercent ageTrigger , _mortgage);
//If it is the Mortgage Object that raised the event ("Mortgage is saying, 'I owe something still'")
//AND it is time to make a mortgage payment again (based upon the number of heloc payments each month)
//If those conditions are both true...then make a payment
if (sender is Driven.Mortgage && _timeToPayMortgageNow)
{
//Since a mortgage payment is due ... can we use the Heloc to pay down a big chunk
//of the principal?
if (canPayDownBasedUponPercen tageLeft)
{
//Determine how big of a chunk we can apply to the principal
double tempHowMuchToApply = this._heloc.HowMuchToApply ToMortgage (_mortgage );
this._heloc._helocAmountOw ed += tempHowMuchToApply;
_mortgage.MakeMortgagePaym ent(this._ monthlyMor tPay + tempHowMuchToApply);
_mortPaymentsAccrued++;
//Store the current information for the month in question
// as the Mort Payments Accrued increments, this represents ONE month.
_mortgage._monthTag = _mortPaymentsAccrued;
_heloc._monthTag = _mortPaymentsAccrued; //otherwise we won't get an even month
this._specificMortMonths.A dd(this._m ortgage);
this._specificHelocMonths. Add(this._ heloc);
Driven.DebugOut.dOut("Mort Month: " + this._mortPaymentsAccrued. ToString() );
//down line accrual
if (this.MortgageMonthsAccrue d % this._downLineIncIntervalI nMonths == 0)
{
Driven.DebugOut.dOut("OLD Amortized amount: " + this._helocAmortizedPaymen t.ToString ());
_downLineAccrualAmountAccu mulator += _downLineAccrualAmount;
SetNewHelocAmortizedPaymen t();
//see about not having to fully qualify this:
Driven.DebugOut.dOut("DONE setting new amortized amount now...");
Driven.DebugOut.dOut("NEW Amortized amount: " + this._helocAmortizedPaymen t.ToString ());
}
//Let's see if the Heloc object still OWES anything
_heloc.CheckDebtStatus(_mo rtgage);
}
else //Otherwise, just make a regular mortgage payment...
{
_mortgage.MakeMortgagePaym ent(this._ monthlyMor tPay);
_mortPaymentsAccrued++;
Driven.DebugOut.dOut("Mort Month: " + this._mortPaymentsAccrued. ToString() );
//Store the current information for the month in question
// as the Mort Payments Accrued increments, this represents ONE month.
_mortgage._monthTag = _mortPaymentsAccrued;
_heloc._monthTag = _mortPaymentsAccrued; //otherwise we won't get an even month
this._specificMortMonths.A dd(this._m ortgage);
this._specificHelocMonths. Add(this._ heloc);
//down line accrual bonus
if (this.MortgageMonthsAccrue d % this._downLineIncIntervalI nMonths == 0)
{
Driven.DebugOut.dOut("OLD Amortized amount: " + this._helocAmortizedPaymen t.ToString ());
_downLineAccrualAmountAccu mulator += _downLineAccrualAmount;
SetNewHelocAmortizedPaymen t();
//see about not having to fully qualify this:
Driven.DebugOut.dOut("DONE setting new amortized amount now...");
Driven.DebugOut.dOut("NEW Amortized amount: " + this._helocAmortizedPaymen t.ToString ());
}
//Let's see if the Heloc object still OWES anything
_heloc.CheckDebtStatus(_mo rtgage);
}
}
else if(sender is Driven.Mortgage && !_timeToPayMortgageNow)// Mortgage: I OWE SOMETHING, BUT IT'S NOT TIME TO MAKE A PAYMENT YET
{
//Even though it's not time to make a payment....we still need to also check the Heloc
//to see if IT owes something right now...
_heloc.CheckDebtStatus(_mo rtgage);
}
if (sender is Driven.Heloc) //Heloc object says: "I OWE SOMETHING"
{
//If Mortgage is paid-off, then we can use our discretionary income to make
//a much BIGGER payment now...
if (_mortgage._mortAmountOwed == 0.0)
{
_heloc.MakeHelocPayment(th is._helocA mortizedPa yment + this._monthlyMortPay);
}
else //otherwise, just make a regular payment (amortized payment)
{
_heloc.MakeHelocPayment(th is._helocA mortizedPa yment);
}
//Another payment was made...
_helocPaymentsAccrued++;
//We just made another amortized payment
_helocPaymentGradualAccrua l += this._helocAmortizedPaymen t;
//When our amortized payments are equal to or greater than our
//Montly discretionary income...then we can make a mortgage payment
//and the accumulator zeros-out
if (_helocPaymentGradualAccru al >= _originalHelocMonthlyPayme ntAmount)
{
_timeToPayMortgageNow = true;
_helocPaymentGradualAccrua l = 0.0;
}
else
{
_timeToPayMortgageNow = false;
}
//check to see if we can increase the amount of the Heloc (about every 18 months)
_heloc.RenewHelocTotalAmou ntAvailabl eNoRestric tions(this .HelocMont hsAccrued, _mortgage);
Driven.DebugOut.dOut("Helo c Month: " + this.HelocMonthsAccrued.To String());
//Now that we're done with the Heloc processing...let's check on Mortgage to see if it is
//still OWING something
_mortgage.CheckDebtStatus( _heloc);
}
}
#endregion
#region FinancedItemIsZero
//Event that gets called when either Heloc or Mort is zero
public void FinancedItemIsZero(object sender, EventArgs e)
{
Driven.DebugOut.dOut("Fina nced Item is Zero. Sender is: " + sender.ToString());
//if sender is Mortgage then Mortgage owing is $0
//if sender is Heloc then Heloc owing is $0
if (sender is Driven.Mortgage)
{
Driven.DebugOut.dOut("Mort gage payment should be applied to Heloc, since Mortgage owing is now: " + _mortgage._mortAmountOwed. ToString() );
_heloc.CheckDebtStatus(_mo rtgage);
}
if (sender is Driven.Heloc)
{
//This line was added because...during debugging we could manage to
//pay-off the Heloc without setting the "pay mortgage now" to TRUE
//This has to happen regardless of if another amortized payment was pending on the Heloc
//(would have normally been pending when the Heloc was paid-off, that is)
this._timeToPayMortgageNow = true;
//Ask the Morgage object is it is still OWING something
_mortgage.CheckDebtStatus( _heloc);
}
}
#endregion
}//end of DrivenExpert class
}//end of namespace driven
========================== ====
========================== ====
demo app (windows form) that uses the DLL:
//This code is for the Windows Form that I have been using to test
//the DrivenExpert class
//A series of text boxes have been placed on the form to gather the various
//pieces of information that is needed to exercise the DrivenExpert class.
using System;
using System.Collections.Generic ;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace WindowsApplicationDrivenTe stBed
{
public partial class Form1 : Form
{
//delcare the DrivenExpert object
private Driven.DrivenExpert de;
//set to true if the Text Boxes need to be cleared
//usually due to an error...
private bool _needToClearBoxes = false;
public Form1()
{
//As you know, .NET provides this, I didn't write it
InitializeComponent();
}
public bool PassedValidation()
{
double month_inc = Double.Parse(this.textBoxM onthlyInco me.Text);
double month_bills = Double.Parse(this.textBoxM onthlyBill s.Text);
double mort_pay = Double.Parse(this.textBoxM onthlyMort gagePaymen t.Text);
double out_pay = month_bills + mort_pay;
bool passedInspection = true;
if (month_inc < out_pay)
{
MessageBox.Show("Your monthly out pay exceeds your income");
passedInspection = false;
}
return passedInspection;
}
private void buttonCalculate_Click(obje ct sender, EventArgs e)
{
//A series of variables to collect information from the Window Form text boxes
double oma = Double.Parse(textBoxOrigMo rtAmount.T ext);
double omr = Double.Parse(this.textBoxO rigMortRat e.Text);
double ihb = Double.Parse(this.textBoxI nitialHelo cBalance.T ext);
double ihr = Double.Parse(this.textBoxH elocRate.T ext);
double di = Double.Parse(this.textBoxD iscretiona ryIncome.T ext);
double mb = Double.Parse(this.textBoxM onthlyBill s.Text);
double mmp = Double.Parse(this.textBoxM onthlyMort gagePaymen t.Text);
double pdpt = Double.Parse(this.textBoxH elocPayDow nPercentag eTrigger.T ext);
//recently added to support down line bonus every few months
double dlaa = Double.Parse(this.textBoxD ownLineBon us.Text);
double dlac = Double.Parse(this.textBoxD ownLineBon usCeiling. Text);
int dliiim = Int32.Parse(this.textBoxBo nusAccrual IntervalIn Months.Tex t);
//Now that we have gathered the needed information (above) ... we can initialize
//the DrivenExpert object and pass in the values into the constructor
//First we need to pass some simple validation to make sure that income is not less
//than the outflow
if (PassedValidation())
{
de = new Driven.DrivenExpert(oma, omr, mmp, 0, ihb, ihr, mb, di, pdpt);
//Since I already deployed the DLL for integration by Eric,
//I decided to implement these as properties instead of changing the
//parameter count of the constructor
//...I suppose I could just overload the Constructor and be just fine
//adding in these 3 variables (so they can get initialized as well?
de.DownLineAccrualAmount = dlaa;
de.DownLineAccrualCeiling = dlac;
de.DownLineIncIntervalInMo nths = dliiim;
//Here...I set the Amortization Period using an object of type
// Driven.CommonFinancedItems , which contains the amortization enumeration
//NOTE...both the Heloc class and Mortgage class now inherit from Driven.CommonFinancedItem
//I plan to explain why elsewhere, but the short answer is I needed amortization period support
//in both the Heloc and Mortgage classes, and creating a parent for them both seemed to be the
//easiest way to do this (kind of backwards, I know; originally, the Heloc and Mortgage classes did
//not have a parent class.
de._commonFinancedItem.Amo rtPeriod = ReturnChosenAmortType(this .comboBoxA mortizatio nPeriod.Te xt);
//Here I am signing-up for the HelocError event
//Mortgage class needs this as well! So much to do, so little time...
de._heloc.HelocError += new EventHandler(this.ErrorOcc urred);
//This small little method call is actually the CRUX of how the
//DrivenExpert class does it's "magic"
//It is THIS call that does all the work.
de.GetOutOfDebt();
//Okay....now that we're done calculating everything via the call to
// de.GetOutOfDebt(); ---- now it is time for some OUTPUT of our results:
//Display how many months it takes to pay-off the Heloc
this.textBoxHowLongToPayOf fHeloc.Tex t = de.HelocMonthsAccrued.ToSt ring();
//Display how many months it takes to pay-off the Mortgage (using the Heloc to pay-down
//the Mortgage Principal the whole time, of course...
this.textBoxHowLongToPayOf fMortgage. Text = de.MortgageMonthsAccrued.T oString();
//Convert the amount of months it took to pay off the Heloc into YEARS
double years = de.HelocMonthsAccrued / 12.0;
//Display the number of months (with years in parentheses) to pay off BOTH the mortgage and heloc both
//As I understand it...it should always take longer to pay-off the Heloc
this.textBoxHowLongToPayOf fMortgageA ndHeloc.Te xt = de.HelocMonthsAccrued.ToSt ring() + " months (" + years.ToString("F") + " years)";
//Display the interest saved using the Heloc "program"
this.textBoxInterestSavedU singHeloc. Text = de.CalcInterestSaved().ToS tring("F") ;
//Display the interest accrued while paying-off the Mortgage WITHOUT USING the Heloc pay down program
this.textBoxInterestOwedWi thoutHeloc .Text = de._mortgageNoHeloc._inter estAccrued .ToString( "F");
//Display the amount of interest accrued while paying-off the Mortgage USING the Heloc pay down program
this.textBoxInterestOwedUs ingHeloc.T ext = de._mortgage._interestAccr ued.ToStri ng("F");
//If the Heloc class raised an error, clear the text boxes
//notice that we reset the boolean afterwards, so we're ready for the next error
if (this._needToClearBoxes)
{
this.ClearResultsBoxes();
this._needToClearBoxes = false;
}
//SKIP THIS, NOT NEEDED
//used while debugging...just leaving it here for grins
//a good way to verify that the Amort Period got set I guess
//MessageBox.Show(de.Heloc Amortizati onPeriodMu ltiplier.T oString()) ;
}
}
//Okay, this method is responsible for displaying in the DataGrid
//the "spread" of values using a different "Percentage Trigger" each time
//By "Percentage Trigger" I mean the percentage value which represents
//how much is left owing on the Heloc before another pay down can happen
//
//So ".10" means that when 10% of the total balance owed is remaning to be paid...another pay down can happen
private void buttonCalcPercentageSpread _Click(obj ect sender, EventArgs e)
{
//Clear the datagrid because we're getting ready to use it
this.dataGridViewResults.R ows.Clear( );
//An ArrayList object used to collect the data as it is generated and saved to each string array
System.Collections.ArrayLi st ar = new System.Collections.ArrayLi st();
//Declar the array of percentages
double[] percentageArray = new double[9];
//initialize it
percentageArray[0] = .10;
percentageArray[1] = .20;
percentageArray[2] = .30;
percentageArray[3] = .40;
percentageArray[4] = .50;
percentageArray[5] = .60;
percentageArray[6] = .70;
percentageArray[7] = .80;
percentageArray[8] = .90;
//Now, loop through N times where N is the length of the array above
//Each time through we are using a new "Percentage Trigger" value (10% left THRU 90% left)
for (int i = 0; i < percentageArray.Length; i++)
{
//Just like we did for the Calculate button, now we need to gather data for use
//by the "Calculate Spread" functionality
double oma = Double.Parse(textBoxOrigMo rtAmount.T ext);
double omr = Double.Parse(this.textBoxO rigMortRat e.Text);
double ihb = Double.Parse(this.textBoxI nitialHelo cBalance.T ext);
double ihr = Double.Parse(this.textBoxH elocRate.T ext);
double di = Double.Parse(this.textBoxD iscretiona ryIncome.T ext);
double mb = Double.Parse(this.textBoxM onthlyBill s.Text);
double mmp = Double.Parse(this.textBoxM onthlyMort gagePaymen t.Text);
double pdpt = percentageArray[i];
//recently added support for the down line bonus program
double dlaa = Double.Parse(this.textBoxD ownLineBon us.Text);
double dlac = Double.Parse(this.textBoxD ownLineBon usCeiling. Text);
int dliiim = Int32.Parse(this.textBoxBo nusAccrual IntervalIn Months.Tex t);
//Declare and initialize the "tempde" DrivenExpert object
Driven.DrivenExpert tempde = new Driven.DrivenExpert(oma, omr, mmp, 0, ihb, ihr, mb, di, pdpt);
//Set the amortization period using an object of type
// Driven.CommonFinancedItems , which contains the amortization enumeration
tempde._commonFinancedItem .AmortPeri od = ReturnChosenAmortType(this .comboBoxA mortizatio nPeriod.Te xt);
//Set the properties for the Down Line bonus program
tempde.DownLineAccrualAmou nt = dlaa;
tempde.DownLineAccrualCeil ing = dlac;
tempde.DownLineIncInterval InMonths = dliiim;
//We want to sign-up for the HelocError in case we get an Error
tempde._heloc.HelocError += new EventHandler(this.ErrorOcc urred);
//Just like in the Calcute Button event handler....here is where
//we do ALL the work...where the "magic" happens in the DrivenExpert class
tempde.GetOutOfDebt();
//Here we are collecting the Interest Saved, Interest Normal (no Heloc) and Interest Using Heloc
double interestSaved = tempde.CalcInterestSaved() ;
double interestNormal = tempde._mortgageNoHeloc._i nterestAcc rued;
double interestHeloc = tempde._mortgage._interest Accrued;
//We want to know how many years it takes to pay-off both the Heloc and Mortgage
//Which is usually however long it takes to pay-off the Heloc
double years = tempde.HelocMonthsAccrued / 12.0;
//Here is where we create a new array of strings to hold our values
string[] newrow = new string[] {
pdpt.ToString(),
tempde.HelocMonthsAccrued. ToString() + " (" + years.ToString("F") + " years)",
interestNormal.ToString("F "),
interestHeloc.ToString("F" ),
interestSaved.ToString("F" )};
//Store results to an ArrayList (will loop through it later to get the results)
//For now we are just gathering information ONLY
ar.Add(newrow);
}
//Add results to data grid...
//After this for loop runs you will see the results displayed in the datagrid
for(int i = 0; i < ar.Count; i++)
{
this.dataGridViewResults.R ows.Add((( string[])a r[i]));
}
//Highlight the columns of interest in the data grid
this.dataGridViewResults.C olumns[0]. DefaultCel lStyle.Bac kColor = Color.Aqua;
this.dataGridViewResults.C olumns[1]. DefaultCel lStyle.Bac kColor = Color.Aqua;
this.dataGridViewResults.C olumns[4]. DefaultCel lStyle.Bac kColor = Color.Aqua;
//If an error occurred in the Heloc class, we want to clear the results
//notice, we reset the boolean so we are ready for the next error
if(this._needToClearBoxes)
{
this.ClearResultsBoxes();
this._needToClearBoxes = false;
}
}
#region ClearResultsBoxes
//This method clears the Text Boxes AND the DataGrid (the one which displays the percentage spread)
public void ClearResultsBoxes()
{
//Clear the TextBoxes that show the results
this.textBoxHowLongToPayOf fHeloc.Tex t = "";
this.textBoxHowLongToPayOf fMortgage. Text = "";
this.textBoxHowLongToPayOf fMortgageA ndHeloc.Te xt = "";
this.textBoxHowMuchToApply ToMortgage .Text = "";
this.textBoxInterestOwedUs ingHeloc.T ext = "";
this.textBoxInterestOwedWi thoutHeloc .Text = "";
this.textBoxInterestSavedU singHeloc. Text = "";
//Clear the datagrid...
this.dataGridViewResults.R ows.Clear( );
}
#endregion
#region ErrorOccurred
//This is the Event Handler for any errors that are raised
public void ErrorOccurred(object sender, EventArgs e)
{
//Right now, only the Heloc class is raising these kinds of errors
if(sender is Driven.Heloc)
{
//Show the error message
MessageBox.Show(((Driven.H eloc)sende r).ErrorMe ssage);
//The boolean tracks "globally" the need to clear the results due to an error
_needToClearBoxes = true;
}
//If this line is not run, the error will never go away
((Driven.Heloc)sender).Hel ocError -= new EventHandler(this.ErrorOcc urred);
}
#endregion
//This method was written to support the Drop Down Box that has the Amortization Period Types
//It would be nice at some future time to set the Amortization Period Type directly using the
//Drop down
//Until that day...I'm using this to do it
private Driven.CommonFinancedItems .Amortizat ionPeriodT ype ReturnChosenAmortType(stri ng chosenItem)
{
if (chosenItem == "Weekly") //I know, I know...yucky to hard-code this
{
return Driven.CommonFinancedItems .Amortizat ionPeriodT ype.Weekly ;
}
if (chosenItem == "BiWeekly")
{
return Driven.CommonFinancedItems .Amortizat ionPeriodT ype.BiWeek ly;
}
return Driven.CommonFinancedItems .Amortizat ionPeriodT ype.Monthl y;
}
private void buttonShowSpecificMonths_C lick(objec t sender, EventArgs e)
{
for (int i = 0; i < de._specificMortMonths.Cou nt; i++)
{
Driven.DebugOut.dOut("MORT GAGE");
Driven.DebugOut.dOut("Mont h: " + ((Driven.Mortgage)de._spec ificMortMo nths[i])._ monthTag.T oString()) ;
Driven.DebugOut.dOut("Bala nce: " + ((Driven.Mortgage)de._spec ificMortMo nths[i])._ mortAmount Owed.ToStr ing());
Driven.DebugOut.dOut("Inte rest Accrued: " + ((Driven.Mortgage)de._spec ificMortMo nths[i])._ interestAc crued.ToSt ring());
}
for (int i = 0; i < de._specificHelocMonths.Co unt; i++)
{
Driven.DebugOut.dOut("HELO C");
Driven.DebugOut.dOut("Mont h: " + ((Driven.Heloc)de._specifi cHelocMont hs[i])._mo nthTag.ToS tring());
Driven.DebugOut.dOut("Bala nce: " + ((Driven.Heloc)de._specifi cHelocMont hs[i])._he locAmountO wed.ToStri ng());
//Driven.DebugOut.dOut("In terest Accrued: " + ((Driven.Heloc)de._specifi cHelocMont hs[i]).._i nterestAcc rued.ToStr ing());
}
MessageBox.Show("Done");
}
private void buttonRefresh_Click(object sender, EventArgs e)
{
double month_inc = Double.Parse(this.textBoxM onthlyInco me.Text);
double month_bills = Double.Parse(this.textBoxM onthlyBill s.Text);
double mort_pay = Double.Parse(this.textBoxM onthlyMort gagePaymen t.Text);
double out_pay = month_bills + mort_pay;
if (PassedValidation())
{
double discretionaryInc = month_inc - out_pay;
this.textBoxDiscretionaryI ncome.Text = discretionaryInc.ToString( "F");
}
}
}//End of Form1 class
}//End of WindowsApplicationDrivenTe stBed Namespac
I have some ArrayLists I am using to store class objects, on the order of 50+ class objects per ArrayList.
Later, I loop through them, and I am getting output...but not what I expected. One of the values I access is the balance due. It is almost always zero.
It is almost as if the Add( ) method for the ArrayList is not adding a NEW object each time....but overwriting it or just storing the address to the object instead of the physical object itself (along with the data). That is the only thing that seems to explain what I am seeing happen.
Is this "normal" -- and how do I get past this problem?
I can't explain it any better than this.
Below I'll paste the DLL code as well as the demo app that uses the DLL
SOURCE CODE DUMP:
DLL code:
//Changes made on 31 July, 2007
//-Created parent class for both Heloc and Mortgage classes, which they now inherit from.
//-I did this so I could more easily distribute new changes that apply to both classes.
//
//=====
//rename this to anything OTHER than "debug" in order to turn-off the reporting to the Output window
#define debug
using System;
using System.Collections.Generic
using System.Collections;
using System.Text;
namespace Driven
{
//I wrote this class in an attempt to NOT have to write
//System.Diagnostics.Debug
public class DebugOut
{
#region Constructors
public DebugOut()
{ }
#endregion
#region dOut
public static void dOut(string message)
{
//My intent is to only output debug statements if the conditional define is set to "debug"
#if debug
System.Diagnostics.Debug.W
#endif
}
#endregion
}
//Parent class for Heloc and Mortgage classes
//Has Amortization Period support (enumerations)
public class CommonFinancedItems
{
private AmortizationPeriodType _amortType;
//Used to tag specific Mortgage or Heloc objects with specific integer Month tag, if needed
//This is for seeing data for specific months...
public int _monthTag;
public CommonFinancedItems()
{
}
#region enums
public enum AmortizationPeriodType : int
{
Weekly = 52,
BiWeekly = 24,
Monthly = 12
}
public AmortizationPeriodType AmortPeriod
{
get
{
return _amortType;
}
set
{
_amortType = value;
}
}
#endregion
}
//This class represents a Home Equity Line of Credit (Heloc)
//Inheirts from CommonFinancedItems so that it can have support
//for Amortization Periods
public class Heloc : CommonFinancedItems
{
#region Variables
//Events that this class can raise
public event EventHandler HelocAmountOwedIsZero;
public event EventHandler HelocAmountOwed;
public event EventHandler BothPaidOff;
public event EventHandler HelocError;
//internal variables
//Most are declared public for now (to ease in debugging)
//But later on should be declared private and accessed via Properties (setters and getters)
public int _helocReapplicationInterva
public double _helocAmountOwed;
public double _helocTotalAmountAvailable
public double _helocAmountAllowed;
public double _helocInterestRate;
public double _helocMonthlyBills;
public string ErrorMessage;
private DebugOut dbg = new DebugOut();
#endregion
#region Constructors
//Used to initialize the internal variables for the Heloc class
//Also note that the events are initialized to null
public Heloc(double amountOwed, double initialAmountAvailable, double interestRate, double monthlyBills, Mortgage m)
{
_helocAmountOwed = amountOwed;
this._helocTotalAmountAvai
_helocInterestRate = interestRate;
_helocMonthlyBills = monthlyBills;
this.HowMuchToApplyToMortg
HelocAmountOwedIsZero = null;
HelocAmountOwed = null;
BothPaidOff = null;
HelocError = null;
}
#endregion
#region HowMuchToApplyToMortgage
//Used to determine how much of the available Heloc balance we can
//apply to the Mortgage
//
//Notice a Mortgage object is passed in...so we can determine total equity, since
//the Heloc amount available is a function of the total equity in the home
public double HowMuchToApplyToMortgage(M
{
//Per my discussion with Kevin, we want to hold in reserve
// ( do not spend ) about twice the monthly bills amount
//This provides a cushion in case people run into financial troubles for
//a month or two
double safetyBuffer = 2 * this._helocMonthlyBills;
//This is what is returned from the method...initialized to zero
double amountToApply = 0;
//If the safety buffer (described above) is greater than the
//total available equity (set elsewhere in another method)
//then raise an error and fire the HelocError event
if (safetyBuffer > this._helocTotalAmountAvai
{
if (HelocError != null)
{
this.ErrorMessage = "Equity must exceed safety buffer. Calculation STOPPED";
//raise the event...
HelocError(this, null);
}
}
//If the amount we have available is greater than the remaining mortgage balance, then
//just PAY OFF THE MORTGAGE
if (this._helocTotalAmountAva
{
amountToApply = m._mortAmountOwed;
this._helocAmountAllowed = amountToApply;
}
else //Otherise, make a big payment using the amount available - the saftety buffer
{
if (this._helocTotalAmountAva
{
this._helocAmountAllowed = this._helocTotalAmountAvai
}
else
{
this._helocAmountAllowed = 0;
}
amountToApply = this._helocAmountAllowed - this._helocAmountOwed;
}
if (amountToApply < 0)
{
amountToApply = 0;
}
//Some debug output to determine if the process is working correctly
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut(" TOTAL TECHNICALLY AVAILABLE: " + this._helocTotalAmountAvai
Driven.DebugOut.dOut(" BUFFER: " + safetyBuffer.ToString());
Driven.DebugOut.dOut(" ALLOWED: " + this._helocAmountAllowed.T
Driven.DebugOut.dOut(" SUGGESTED AMOUNT TO USE FROM HELOC: " + amountToApply.ToString());
Driven.DebugOut.dOut(" CURRENTLY OWED: " + this._helocAmountOwed.ToSt
Driven.DebugOut.dOut("");
return amountToApply;
}
#endregion
#region CheckDebtStatus
//Whenver this is called we ask the Heloc object "Do you owe anything?"
//Depending on the answer, we raise the appropriate event
//
//Notice we pass-in a mortgage object so we can check the balance on the mortgage as well
//--this is needed if we are to ever finish processing
public void CheckDebtStatus(Mortgage m)
{
//are both the heloc and the mortgage paid off?
if ((this._helocAmountOwed <= 0.0) && (m._mortAmountOwed <= 0.0))
{
if (BothPaidOff != null)
{
//raise the event...
BothPaidOff(this, null);
}
}
else if (this._helocAmountOwed > 0.0) // Is a Heloc balance still owed?
{
if (HelocAmountOwed != null)
{
//raise the event...
HelocAmountOwed(this, null);
}
}
else if(this._helocAmountOwed <= 0.0) // has the Heloc balance been paid-off???
{
if (HelocAmountOwedIsZero != null)
{
//raise the event...
HelocAmountOwedIsZero(this
}
}
}
#endregion
#region MakeHelocPayment
//Pretty self-explanatory...this makes a payment on the Heloc balance owed
//The parameter passed in is the amount of the payment
public void MakeHelocPayment(double helocPayment)
{
//If we owe on the Heloc, then let's make a payment on it...
if (this._helocAmountOwed > 0.0)
{
double periodInterestPayment;
//calculate interest payment
periodInterestPayment = this._helocAmountOwed * (this._helocInterestRate / (int)this.AmortPeriod);
//is the amount owed on the heloc less than the payment amount?
if ((this._helocAmountOwed + periodInterestPayment) < helocPayment)
{
//If so, then pay-off the Heloc NOW!
this._helocAmountOwed = 0.0;
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut(">>>>
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut("");
}
else // just make a normal Heloc payment
{
this._helocAmountOwed = (this._helocAmountOwed + periodInterestPayment) - helocPayment;
Driven.DebugOut.dOut("Helo
}
}
}
#endregion
#region ReturnHomeEquity
//When called this sets the internal variable for "Total Amount of Equity"
//This calculation is performed by looking at the original amount owing and
//subtracting the current amount owed.
//The difference should be the total available equity
public double ReturnHomeEquity(Driven.Mo
{
return m._mortOriginalAmountOwed - m._mortAmountOwed;
}
#endregion
#region RenewHelocTotalAmountAvail
//This method is used to determine if the Amount Available for the Heloc can be increased
//I seem to think this happens about every 18 months?
public void RenewHelocTotalAmountAvail
{
//Update available Heloc credit line every XX months...
if ((numberOfMonthsAccrued % this._helocReapplicationIn
{
this._helocTotalAmountAvai
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut(" THE HELOC WAS RENEWED (" + numberOfMonthsAccrued.ToSt
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut("");
}
}
#endregion
#region HelocReadyToPayDownMortgag
//This method is called (returns true if ready to pay down) in order to
//determine if the amountOwed has been paid-down sufficiently to allow us to make
//yet another BIG down payment on the Mortgage Principal again
//
//This is determined by a Percentage Paydown "Trigger"
//For example, if 10% is owing and the Percentage Paydown Trigger is 10%...then we can pay down again.
public bool HelocReadyToPayDownMortgag
{
//initialize to false...assume we are not going to be ready to pay down
bool readyToPayDown = false;
double ThreshholdMoneyAmount = this._helocAmountAllowed * percentageCEILINGLeftOfTot
if ((this._helocAmountOwed <= ThreshholdMoneyAmount) && (m._mortAmountOwed > 0.0))
{
//As is my custom...some Debug Output to show that we are doing this and why
//We can check the numbers to see if they reflect accurately the logic that is in place
//only gets set to true if the conditions were met above...
readyToPayDown = true;
Driven.DebugOut.dOut("////
Driven.DebugOut.dOut("////
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut("READ
Driven.DebugOut.dOut("PAYD
Driven.DebugOut.dOut(" HOW MUCH OWED: " + this._helocAmountOwed.ToSt
Driven.DebugOut.dOut("////
Driven.DebugOut.dOut("////
}
return readyToPayDown;
}
#endregion
}
//This class represents your Mortgage, a Mortgage, any Mortgage...
//Inheirts from CommonFinancedItems so that it can have support
//for Amortization Periods
public class Mortgage : CommonFinancedItems
{
#region Variables
//Events that this class can raise
public event EventHandler MortAmountOwedIsZero;
public event EventHandler MortAmountOwed;
public event EventHandler BothPaidOff;
//Internal variables
//Declared public for now to help with debugging
//but later on...should be declared private and modified
//using setters and getters (Properities)
public double _mortOriginalAmountOwed;
public double _mortAmountOwed;
public double _mortInterestRate;
public double _interestAccrued;
private DebugOut dbg = new DebugOut();
#endregion
#region Constructors
//Initializes the Mortgage object
public Mortgage(double amountOwed, double interestRate)
{
//initialize mortgage object...
_mortOriginalAmountOwed = amountOwed;
_mortAmountOwed = amountOwed;
_mortInterestRate = interestRate;
//set events to null...
MortAmountOwedIsZero = null;
MortAmountOwed = null;
BothPaidOff = null;
}
#endregion
#region CheckDebtStatus
//Asks the Mortgage object: "Do you owe any money?"
//If so...raise the correct event!
//
//Notice a Heloc object is passed-in so we can determine if anything is still oweing
//on the Heloc as well...
public void CheckDebtStatus(Heloc h)
{
//Are both the Mortgage and the Heloc paid-off now???
if ((h._helocAmountOwed <= 0.0) && (this._mortAmountOwed <= 0.0))
{
if (BothPaidOff != null)
{
//raise the event...
BothPaidOff(this, null);
}
}
else if (this._mortAmountOwed > 0.0) //Mortgage still owes something
{
if (MortAmountOwed != null)
{
//raise the event...
MortAmountOwed(this, null);
}
}
else if (this._mortAmountOwed <= 0.0) //Mortgage is paid-off now!!
{
//raise the event...
if (MortAmountOwedIsZero != null)
{
MortAmountOwedIsZero(this,
}
}
}
#endregion
#region CalcNormalTermNoHelocAppli
//This just does a straight amortization with no Heloc "pay down" involved at all...
//Payment amount is passed-in...
//Usually what will happen is a seperate and distinct Mortgage object will be declared
//and ONLY this methodw would be called.
//In other words...this method would never be called in conjunction with a Mortgage object
//that was participating in a Heloc pay-down program at the same time.
public void CalcNormalTermNoHelocAppli
{
while (this._mortAmountOwed > 0.0)
{
this.MakeMortgagePayment(p
}
}
#endregion
#region MakeMortgagePayment
//Call this method to make a mortgage payment
//Payment amount is passed in...
public void MakeMortgagePayment(double
{
double periodInterestPayment = 0;
//calculate the monthly interest
periodInterestPayment = this._mortAmountOwed * (this._mortInterestRate / (int)this.AmortPeriod);
//to calculate interest saved
_interestAccrued += periodInterestPayment;
//Only make a payment if there is still money owed...
if (this._mortAmountOwed > 0.0)
{
//If the amount owed is less than or equal to the payment amount, then pay-off the loan
if ((this._mortAmountOwed + periodInterestPayment) <= mortPayment)
{
//Some debug output to verify this happened
this._mortAmountOwed = 0.0;
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut(">>>>
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut("");
}
else //otherwise just make a normal payment
{
//For debug / reporting purposes
Driven.DebugOut.dOut("");
Driven.DebugOut.dOut("Prev
Driven.DebugOut.dOut("Mort
//add monthly interest to entire amount owing, then pay on THAT
this._mortAmountOwed = (this._mortAmountOwed + periodInterestPayment) - mortPayment;
Driven.DebugOut.dOut("Afte
}
}
}
#endregion
}// end of Mortgage class
//INTENT: This class is the class that instantiates both the Mortgage and Heloc objects
//and then runs them through their paces!
public class DrivenExpert
{
#region Variables
//Heloc object to be used throughout the various class methods
public Heloc _heloc;
//Mortgage object to be used throughout the various class methods
public Mortgage _mortgage;
//SPECIAL Mortgage object that does not take advantage of the Heloc pay down
public Mortgage _mortgageNoHeloc;
//How many mortgage payments have been made?
private int _mortPaymentsAccrued = 0;
//How many heloc payments have been made?
private int _helocPaymentsAccrued = 0;
//When this percentage of the Heloc balance is remaining ... do another pay-down
public double _payDownPercentageTrigger;
//Accumulates the amortized heloc payments as they accrue
//For example, if you are making weekly payments, they would accrue in this variable
private double _helocPaymentGradualAccrua
//Boolean which says "the correct number of amortized payments have been made on the Heloc
//now you can pay on the Mortgage"
private bool _timeToPayMortgageNow = true;
//Mortgage payment amount
public double _monthlyMortPay;
//Amortized heloc payment (divided into weekly, biweekly or Monthly payments)
public double _helocAmortizedPayment;
//Monthly amount for our use (pre-amortization)
private double _originalHelocMonthlyPayme
//For the down line bonus program - the amount to add to our discretionary income
private double _downLineAccrualAmount;
//MAX amount that we can spend
private double _downLineAccrualCeiling;
//How often to add the down line bonus to the discretionary income
private int _downLineIncIntervalInMont
//Keeps track of how much we have added to the discretionariy income so far
//using the bonus amount
private double _downLineAccrualAmountAccu
//Internal declaration of the Driven.CommonFinancedItem class
//Used to set the Amortization period
public Driven.CommonFinancedItems
//Used to store Mortgage objects as needed for showing specific mortgage information for a
//particular month
public ArrayList _specificMortMonths = new ArrayList();
//Used to store Heloc bjects as needed for showing specific heloc information for a
//particular month
public ArrayList _specificHelocMonths = new ArrayList();
//Used for debug output
private DebugOut dbg = new DebugOut();
#endregion
#region Constructors
//Initialized the DrivenExpert object
public DrivenExpert(double mortAmountOwed,
double mortInterestRate,
double monthlyMortPay,
double helocAmountOwed,
double helocAmountAvailable,
double helocInterestRate,
double helocMonthlyBills,
double helocPayment,
double helcoPayDownPercentageTrig
{
_monthlyMortPay = monthlyMortPay;
_helocAmortizedPayment = helocPayment;
_originalHelocMonthlyPayme
_payDownPercentageTrigger = helcoPayDownPercentageTrig
_commonFinancedItem = new CommonFinancedItems();
_mortgage = new Mortgage(mortAmountOwed, mortInterestRate);
_mortgageNoHeloc = new Mortgage(mortAmountOwed, mortInterestRate);
_heloc = new Heloc(helocAmountOwed, helocAmountAvailable, helocInterestRate, helocMonthlyBills, _mortgage);
//sign-up for the events we want to be notified of
//We want to be notified when either Mortgage or Heloc are still owing
_heloc.HelocAmountOwed += new EventHandler(this.Financed
_mortgage.MortAmountOwed += new EventHandler(this.Financed
//We want to be notified when either Mortgage or Heloc are paid-off
_heloc.HelocAmountOwedIsZe
_mortgage.MortAmountOwedIs
//We want to be notified when BOTH Mortgage AND Heloc are paid-off
_heloc.BothPaidOff += new EventHandler(this.BothPaid
_mortgage.BothPaidOff += new EventHandler(this.BothPaid
}
#endregion
#region Properties
//Used to set the Down Line Bonus program variables
public double DownLineAccrualAmount
{
set
{
_downLineAccrualAmount = value;
}
}
public double DownLineAccrualCeiling
{
set
{
_downLineAccrualCeiling = value;
}
}
public int DownLineIncIntervalInMonth
{
set
{
_downLineIncIntervalInMont
}
}
//If Monthly, the multiplier is 1
//If BiWeekly, the multiplier is 2
//If Weekly, the multipler is 4.33(repeating)
public double HelocAmortizationPeriodMul
{
get
{
double AmortPeriodDivisor = (int)this._heloc.AmortPeri
double divisor = 12;
return AmortPeriodDivisor / divisor;
}
}
//Return Heloc months accrued (reflect amortization period)
public double HelocMonthsAccrued
{
get
{
double months = this._helocPaymentsAccrued
return months;
}
}
//Return Mortgage months accrued (reflect amortization period)
public int MortgageMonthsAccrued
{
get
{
int AmortPeriodDivisor = (int)Driven.CommonFinanced
int divisor = 12;
int months = this._mortPaymentsAccrued / (AmortPeriodDivisor / divisor);
return months;
}
}
#endregion
//This method gets called when the BothPaid event gets raised in either the Heloc or the Mortgage class
public void BothPaidOff(object sender, EventArgs e)
{
Driven.DebugOut.dOut(sende
}
#region GetOutOfDebt
//As I have inidicated elsewhere...this method call does all the work
//It is the workhorse of the DrivenExpert class
public void GetOutOfDebt()
{
//Set the amortization period for the mortgage object (usually monthly)
_mortgage.AmortPeriod = Driven.CommonFinancedItems
//Set the amortization period for the Heloc object (Weekly, BiWeekly or Monthly)
_heloc.AmortPeriod = this._commonFinancedItem.A
//Initialize the Accumulator to the heloc amortized payemnt (not amortized at this point)
_downLineAccrualAmountAccu
//Set the Heloc Amortized Payment (Weekly, BiWeekly, or Monthly)
this.SetNewHelocAmortizedP
//Start the ball rolling by asking the _mortgage object if it owes anything
//The answer needs to be yes (of course it should be, since we want to pay down the mortgage)
_mortgage.CheckDebtStatus(
//Same thing for the Heloc object
_heloc.CheckDebtStatus(_mo
//By this point we are DONE....output the debug results
double mma = this.MortgageMonthsAccrued
double hma = this.HelocMonthsAccrued;
Driven.DebugOut.dOut("Mont
Driven.DebugOut.dOut("Mont
Driven.DebugOut.dOut("Mont
}
//This method is used to set the Amortized payment amount for the Heloc
public void SetNewHelocAmortizedPaymen
{
//downLineAccrualAmountAcc
//it EQUAL to the ceiling amount
if (_downLineAccrualAmountAcc
{
_downLineAccrualAmountAccu
//Throw an exeception if the CELING is set to ZERO for some reason...
//this does not make sense
if (_downLineAccrualAmountAcc
{
Driven.DebugOut.dOut("Erro
throw new Exception("downLineAccrual
}
}
//Set the _heloc amortized payment using the accumulator amount divided by the amortization period multiplier
//since _helocAmortizedPayment is internal, no need to return anything
_helocAmortizedPayment = _downLineAccrualAmountAccu
}
//Used to calculate interest saved by using the Heloc pay down program
//This is done by generating two mortgage objects...one that uses the heloc and one that does not
//then subtracting the difference between the interest accrued under the two programs
public double CalcInterestSaved()
{
try
{
_mortgageNoHeloc.AmortPeri
_mortgageNoHeloc.CalcNorma
return this._mortgageNoHeloc._int
}
catch (Exception ee)
{
Driven.DebugOut.dOut("Erro
throw new Exception("An error occured in CalcInterestSaved( ) method" + ee.Message.ToString());
}
}
#endregion
#region FinancedItemOwing
//This method gets called when the Mortgage Or the Heloc objects OWE SOMETHING
public void FinancedItemOwing(object sender, EventArgs e)
{
//First thing we want to do is determine if a Heloc pay down is possible
bool canPayDownBasedUponPercent
//If it is the Mortgage Object that raised the event ("Mortgage is saying, 'I owe something still'")
//AND it is time to make a mortgage payment again (based upon the number of heloc payments each month)
//If those conditions are both true...then make a payment
if (sender is Driven.Mortgage && _timeToPayMortgageNow)
{
//Since a mortgage payment is due ... can we use the Heloc to pay down a big chunk
//of the principal?
if (canPayDownBasedUponPercen
{
//Determine how big of a chunk we can apply to the principal
double tempHowMuchToApply = this._heloc.HowMuchToApply
this._heloc._helocAmountOw
_mortgage.MakeMortgagePaym
_mortPaymentsAccrued++;
//Store the current information for the month in question
// as the Mort Payments Accrued increments, this represents ONE month.
_mortgage._monthTag = _mortPaymentsAccrued;
_heloc._monthTag = _mortPaymentsAccrued; //otherwise we won't get an even month
this._specificMortMonths.A
this._specificHelocMonths.
Driven.DebugOut.dOut("Mort
//down line accrual
if (this.MortgageMonthsAccrue
{
Driven.DebugOut.dOut("OLD Amortized amount: " + this._helocAmortizedPaymen
_downLineAccrualAmountAccu
SetNewHelocAmortizedPaymen
//see about not having to fully qualify this:
Driven.DebugOut.dOut("DONE
Driven.DebugOut.dOut("NEW Amortized amount: " + this._helocAmortizedPaymen
}
//Let's see if the Heloc object still OWES anything
_heloc.CheckDebtStatus(_mo
}
else //Otherwise, just make a regular mortgage payment...
{
_mortgage.MakeMortgagePaym
_mortPaymentsAccrued++;
Driven.DebugOut.dOut("Mort
//Store the current information for the month in question
// as the Mort Payments Accrued increments, this represents ONE month.
_mortgage._monthTag = _mortPaymentsAccrued;
_heloc._monthTag = _mortPaymentsAccrued; //otherwise we won't get an even month
this._specificMortMonths.A
this._specificHelocMonths.
//down line accrual bonus
if (this.MortgageMonthsAccrue
{
Driven.DebugOut.dOut("OLD Amortized amount: " + this._helocAmortizedPaymen
_downLineAccrualAmountAccu
SetNewHelocAmortizedPaymen
//see about not having to fully qualify this:
Driven.DebugOut.dOut("DONE
Driven.DebugOut.dOut("NEW Amortized amount: " + this._helocAmortizedPaymen
}
//Let's see if the Heloc object still OWES anything
_heloc.CheckDebtStatus(_mo
}
}
else if(sender is Driven.Mortgage && !_timeToPayMortgageNow)// Mortgage: I OWE SOMETHING, BUT IT'S NOT TIME TO MAKE A PAYMENT YET
{
//Even though it's not time to make a payment....we still need to also check the Heloc
//to see if IT owes something right now...
_heloc.CheckDebtStatus(_mo
}
if (sender is Driven.Heloc) //Heloc object says: "I OWE SOMETHING"
{
//If Mortgage is paid-off, then we can use our discretionary income to make
//a much BIGGER payment now...
if (_mortgage._mortAmountOwed
{
_heloc.MakeHelocPayment(th
}
else //otherwise, just make a regular payment (amortized payment)
{
_heloc.MakeHelocPayment(th
}
//Another payment was made...
_helocPaymentsAccrued++;
//We just made another amortized payment
_helocPaymentGradualAccrua
//When our amortized payments are equal to or greater than our
//Montly discretionary income...then we can make a mortgage payment
//and the accumulator zeros-out
if (_helocPaymentGradualAccru
{
_timeToPayMortgageNow = true;
_helocPaymentGradualAccrua
}
else
{
_timeToPayMortgageNow = false;
}
//check to see if we can increase the amount of the Heloc (about every 18 months)
_heloc.RenewHelocTotalAmou
Driven.DebugOut.dOut("Helo
//Now that we're done with the Heloc processing...let's check on Mortgage to see if it is
//still OWING something
_mortgage.CheckDebtStatus(
}
}
#endregion
#region FinancedItemIsZero
//Event that gets called when either Heloc or Mort is zero
public void FinancedItemIsZero(object sender, EventArgs e)
{
Driven.DebugOut.dOut("Fina
//if sender is Mortgage then Mortgage owing is $0
//if sender is Heloc then Heloc owing is $0
if (sender is Driven.Mortgage)
{
Driven.DebugOut.dOut("Mort
_heloc.CheckDebtStatus(_mo
}
if (sender is Driven.Heloc)
{
//This line was added because...during debugging we could manage to
//pay-off the Heloc without setting the "pay mortgage now" to TRUE
//This has to happen regardless of if another amortized payment was pending on the Heloc
//(would have normally been pending when the Heloc was paid-off, that is)
this._timeToPayMortgageNow
//Ask the Morgage object is it is still OWING something
_mortgage.CheckDebtStatus(
}
}
#endregion
}//end of DrivenExpert class
}//end of namespace driven
==========================
==========================
demo app (windows form) that uses the DLL:
//This code is for the Windows Form that I have been using to test
//the DrivenExpert class
//A series of text boxes have been placed on the form to gather the various
//pieces of information that is needed to exercise the DrivenExpert class.
using System;
using System.Collections.Generic
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace WindowsApplicationDrivenTe
{
public partial class Form1 : Form
{
//delcare the DrivenExpert object
private Driven.DrivenExpert de;
//set to true if the Text Boxes need to be cleared
//usually due to an error...
private bool _needToClearBoxes = false;
public Form1()
{
//As you know, .NET provides this, I didn't write it
InitializeComponent();
}
public bool PassedValidation()
{
double month_inc = Double.Parse(this.textBoxM
double month_bills = Double.Parse(this.textBoxM
double mort_pay = Double.Parse(this.textBoxM
double out_pay = month_bills + mort_pay;
bool passedInspection = true;
if (month_inc < out_pay)
{
MessageBox.Show("Your monthly out pay exceeds your income");
passedInspection = false;
}
return passedInspection;
}
private void buttonCalculate_Click(obje
{
//A series of variables to collect information from the Window Form text boxes
double oma = Double.Parse(textBoxOrigMo
double omr = Double.Parse(this.textBoxO
double ihb = Double.Parse(this.textBoxI
double ihr = Double.Parse(this.textBoxH
double di = Double.Parse(this.textBoxD
double mb = Double.Parse(this.textBoxM
double mmp = Double.Parse(this.textBoxM
double pdpt = Double.Parse(this.textBoxH
//recently added to support down line bonus every few months
double dlaa = Double.Parse(this.textBoxD
double dlac = Double.Parse(this.textBoxD
int dliiim = Int32.Parse(this.textBoxBo
//Now that we have gathered the needed information (above) ... we can initialize
//the DrivenExpert object and pass in the values into the constructor
//First we need to pass some simple validation to make sure that income is not less
//than the outflow
if (PassedValidation())
{
de = new Driven.DrivenExpert(oma, omr, mmp, 0, ihb, ihr, mb, di, pdpt);
//Since I already deployed the DLL for integration by Eric,
//I decided to implement these as properties instead of changing the
//parameter count of the constructor
//...I suppose I could just overload the Constructor and be just fine
//adding in these 3 variables (so they can get initialized as well?
de.DownLineAccrualAmount = dlaa;
de.DownLineAccrualCeiling = dlac;
de.DownLineIncIntervalInMo
//Here...I set the Amortization Period using an object of type
// Driven.CommonFinancedItems
//NOTE...both the Heloc class and Mortgage class now inherit from Driven.CommonFinancedItem
//I plan to explain why elsewhere, but the short answer is I needed amortization period support
//in both the Heloc and Mortgage classes, and creating a parent for them both seemed to be the
//easiest way to do this (kind of backwards, I know; originally, the Heloc and Mortgage classes did
//not have a parent class.
de._commonFinancedItem.Amo
//Here I am signing-up for the HelocError event
//Mortgage class needs this as well! So much to do, so little time...
de._heloc.HelocError += new EventHandler(this.ErrorOcc
//This small little method call is actually the CRUX of how the
//DrivenExpert class does it's "magic"
//It is THIS call that does all the work.
de.GetOutOfDebt();
//Okay....now that we're done calculating everything via the call to
// de.GetOutOfDebt(); ---- now it is time for some OUTPUT of our results:
//Display how many months it takes to pay-off the Heloc
this.textBoxHowLongToPayOf
//Display how many months it takes to pay-off the Mortgage (using the Heloc to pay-down
//the Mortgage Principal the whole time, of course...
this.textBoxHowLongToPayOf
//Convert the amount of months it took to pay off the Heloc into YEARS
double years = de.HelocMonthsAccrued / 12.0;
//Display the number of months (with years in parentheses) to pay off BOTH the mortgage and heloc both
//As I understand it...it should always take longer to pay-off the Heloc
this.textBoxHowLongToPayOf
//Display the interest saved using the Heloc "program"
this.textBoxInterestSavedU
//Display the interest accrued while paying-off the Mortgage WITHOUT USING the Heloc pay down program
this.textBoxInterestOwedWi
//Display the amount of interest accrued while paying-off the Mortgage USING the Heloc pay down program
this.textBoxInterestOwedUs
//If the Heloc class raised an error, clear the text boxes
//notice that we reset the boolean afterwards, so we're ready for the next error
if (this._needToClearBoxes)
{
this.ClearResultsBoxes();
this._needToClearBoxes = false;
}
//SKIP THIS, NOT NEEDED
//used while debugging...just leaving it here for grins
//a good way to verify that the Amort Period got set I guess
//MessageBox.Show(de.Heloc
}
}
//Okay, this method is responsible for displaying in the DataGrid
//the "spread" of values using a different "Percentage Trigger" each time
//By "Percentage Trigger" I mean the percentage value which represents
//how much is left owing on the Heloc before another pay down can happen
//
//So ".10" means that when 10% of the total balance owed is remaning to be paid...another pay down can happen
private void buttonCalcPercentageSpread
{
//Clear the datagrid because we're getting ready to use it
this.dataGridViewResults.R
//An ArrayList object used to collect the data as it is generated and saved to each string array
System.Collections.ArrayLi
//Declar the array of percentages
double[] percentageArray = new double[9];
//initialize it
percentageArray[0] = .10;
percentageArray[1] = .20;
percentageArray[2] = .30;
percentageArray[3] = .40;
percentageArray[4] = .50;
percentageArray[5] = .60;
percentageArray[6] = .70;
percentageArray[7] = .80;
percentageArray[8] = .90;
//Now, loop through N times where N is the length of the array above
//Each time through we are using a new "Percentage Trigger" value (10% left THRU 90% left)
for (int i = 0; i < percentageArray.Length; i++)
{
//Just like we did for the Calculate button, now we need to gather data for use
//by the "Calculate Spread" functionality
double oma = Double.Parse(textBoxOrigMo
double omr = Double.Parse(this.textBoxO
double ihb = Double.Parse(this.textBoxI
double ihr = Double.Parse(this.textBoxH
double di = Double.Parse(this.textBoxD
double mb = Double.Parse(this.textBoxM
double mmp = Double.Parse(this.textBoxM
double pdpt = percentageArray[i];
//recently added support for the down line bonus program
double dlaa = Double.Parse(this.textBoxD
double dlac = Double.Parse(this.textBoxD
int dliiim = Int32.Parse(this.textBoxBo
//Declare and initialize the "tempde" DrivenExpert object
Driven.DrivenExpert tempde = new Driven.DrivenExpert(oma, omr, mmp, 0, ihb, ihr, mb, di, pdpt);
//Set the amortization period using an object of type
// Driven.CommonFinancedItems
tempde._commonFinancedItem
//Set the properties for the Down Line bonus program
tempde.DownLineAccrualAmou
tempde.DownLineAccrualCeil
tempde.DownLineIncInterval
//We want to sign-up for the HelocError in case we get an Error
tempde._heloc.HelocError += new EventHandler(this.ErrorOcc
//Just like in the Calcute Button event handler....here is where
//we do ALL the work...where the "magic" happens in the DrivenExpert class
tempde.GetOutOfDebt();
//Here we are collecting the Interest Saved, Interest Normal (no Heloc) and Interest Using Heloc
double interestSaved = tempde.CalcInterestSaved()
double interestNormal = tempde._mortgageNoHeloc._i
double interestHeloc = tempde._mortgage._interest
//We want to know how many years it takes to pay-off both the Heloc and Mortgage
//Which is usually however long it takes to pay-off the Heloc
double years = tempde.HelocMonthsAccrued / 12.0;
//Here is where we create a new array of strings to hold our values
string[] newrow = new string[] {
pdpt.ToString(),
tempde.HelocMonthsAccrued.
interestNormal.ToString("F
interestHeloc.ToString("F"
interestSaved.ToString("F"
//Store results to an ArrayList (will loop through it later to get the results)
//For now we are just gathering information ONLY
ar.Add(newrow);
}
//Add results to data grid...
//After this for loop runs you will see the results displayed in the datagrid
for(int i = 0; i < ar.Count; i++)
{
this.dataGridViewResults.R
}
//Highlight the columns of interest in the data grid
this.dataGridViewResults.C
this.dataGridViewResults.C
this.dataGridViewResults.C
//If an error occurred in the Heloc class, we want to clear the results
//notice, we reset the boolean so we are ready for the next error
if(this._needToClearBoxes)
{
this.ClearResultsBoxes();
this._needToClearBoxes = false;
}
}
#region ClearResultsBoxes
//This method clears the Text Boxes AND the DataGrid (the one which displays the percentage spread)
public void ClearResultsBoxes()
{
//Clear the TextBoxes that show the results
this.textBoxHowLongToPayOf
this.textBoxHowLongToPayOf
this.textBoxHowLongToPayOf
this.textBoxHowMuchToApply
this.textBoxInterestOwedUs
this.textBoxInterestOwedWi
this.textBoxInterestSavedU
//Clear the datagrid...
this.dataGridViewResults.R
}
#endregion
#region ErrorOccurred
//This is the Event Handler for any errors that are raised
public void ErrorOccurred(object sender, EventArgs e)
{
//Right now, only the Heloc class is raising these kinds of errors
if(sender is Driven.Heloc)
{
//Show the error message
MessageBox.Show(((Driven.H
//The boolean tracks "globally" the need to clear the results due to an error
_needToClearBoxes = true;
}
//If this line is not run, the error will never go away
((Driven.Heloc)sender).Hel
}
#endregion
//This method was written to support the Drop Down Box that has the Amortization Period Types
//It would be nice at some future time to set the Amortization Period Type directly using the
//Drop down
//Until that day...I'm using this to do it
private Driven.CommonFinancedItems
{
if (chosenItem == "Weekly") //I know, I know...yucky to hard-code this
{
return Driven.CommonFinancedItems
}
if (chosenItem == "BiWeekly")
{
return Driven.CommonFinancedItems
}
return Driven.CommonFinancedItems
}
private void buttonShowSpecificMonths_C
{
for (int i = 0; i < de._specificMortMonths.Cou
{
Driven.DebugOut.dOut("MORT
Driven.DebugOut.dOut("Mont
Driven.DebugOut.dOut("Bala
Driven.DebugOut.dOut("Inte
}
for (int i = 0; i < de._specificHelocMonths.Co
{
Driven.DebugOut.dOut("HELO
Driven.DebugOut.dOut("Mont
Driven.DebugOut.dOut("Bala
//Driven.DebugOut.dOut("In
}
MessageBox.Show("Done");
}
private void buttonRefresh_Click(object
{
double month_inc = Double.Parse(this.textBoxM
double month_bills = Double.Parse(this.textBoxM
double mort_pay = Double.Parse(this.textBoxM
double out_pay = month_bills + mort_pay;
if (PassedValidation())
{
double discretionaryInc = month_inc - out_pay;
this.textBoxDiscretionaryI
}
}
}//End of Form1 class
}//End of WindowsApplicationDrivenTe
ASKER
Later on......here is where I output the contents of the ArrayList (this happens in the Windows Form)
private void buttonShowSpecificMonths_C lick(objec t sender, EventArgs e)
{
for (int i = 0; i < de._specificMortMonths.Cou nt; i++)
{
Driven.DebugOut.dOut("MORT GAGE");
Driven.DebugOut.dOut("Mont h: " + ((Driven.Mortgage)de._spec ificMortMo nths[i])._ monthTag.T oString()) ;
Driven.DebugOut.dOut("Bala nce: " + ((Driven.Mortgage)de._spec ificMortMo nths[i])._ mortAmount Owed.ToStr ing());
Driven.DebugOut.dOut("Inte rest Accrued: " + ((Driven.Mortgage)de._spec ificMortMo nths[i])._ interestAc crued.ToSt ring());
}
for (int i = 0; i < de._specificHelocMonths.Co unt; i++)
{
Driven.DebugOut.dOut("HELO C");
Driven.DebugOut.dOut("Mont h: " + ((Driven.Heloc)de._specifi cHelocMont hs[i])._mo nthTag.ToS tring());
Driven.DebugOut.dOut("Bala nce: " + ((Driven.Heloc)de._specifi cHelocMont hs[i])._he locAmountO wed.ToStri ng());
//Driven.DebugOut.dOut("In terest Accrued: " + ((Driven.Heloc)de._specifi cHelocMont hs[i]).._i nterestAcc rued.ToStr ing());
}
MessageBox.Show("Done");
}
private void buttonShowSpecificMonths_C
{
for (int i = 0; i < de._specificMortMonths.Cou
{
Driven.DebugOut.dOut("MORT
Driven.DebugOut.dOut("Mont
Driven.DebugOut.dOut("Bala
Driven.DebugOut.dOut("Inte
}
for (int i = 0; i < de._specificHelocMonths.Co
{
Driven.DebugOut.dOut("HELO
Driven.DebugOut.dOut("Mont
Driven.DebugOut.dOut("Bala
//Driven.DebugOut.dOut("In
}
MessageBox.Show("Done");
}
Tom,
You have got to stop putting up all the code, it is just confusing. We need to find a way to show just the pertinent code.
Normally, this occurs if you don't create a new object using the 'new' keyword, but reuse the same object.
Bob
You have got to stop putting up all the code, it is just confusing. We need to find a way to show just the pertinent code.
Normally, this occurs if you don't create a new object using the 'new' keyword, but reuse the same object.
Bob
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Gary:
I think you are right....that must be what I am doing.....and looking at my code....that IS what I am doing.
Perhaps I can delare a "temp" object of the same time, assign the "global" (internal) copy to it....and then add the "temp" object to the ArrayList?
I think you are right....that must be what I am doing.....and looking at my code....that IS what I am doing.
Perhaps I can delare a "temp" object of the same time, assign the "global" (internal) copy to it....and then add the "temp" object to the ArrayList?
ASKER
This does not seem to help:
//Store the current information for the month in question
// as the Mort Payments Accrued increments, this represents ONE month.
Driven.Mortgage tempm;
tempm = _mortgage;
Driven.Heloc temph;
temph = _heloc;
tempm._monthTag = _mortPaymentsAccrued;
temph._monthTag = _mortPaymentsAccrued;
_mortgage._monthTag = _mortPaymentsAccrued;
_heloc._monthTag = _mortPaymentsAccrued; //otherwise we won't get an even month
this._specificMortMonths.A dd(tempm);
this._specificHelocMonths. Add(temph) ;
//Store the current information for the month in question
// as the Mort Payments Accrued increments, this represents ONE month.
Driven.Mortgage tempm;
tempm = _mortgage;
Driven.Heloc temph;
temph = _heloc;
tempm._monthTag = _mortPaymentsAccrued;
temph._monthTag = _mortPaymentsAccrued;
_mortgage._monthTag = _mortPaymentsAccrued;
_heloc._monthTag = _mortPaymentsAccrued; //otherwise we won't get an even month
this._specificMortMonths.A
this._specificHelocMonths.
ASKER
Bob:
I'm sorry...I'll stop posting my entire source and I'll just try and post the relevant code.
I guess I don't know what will be relevant.
I'm sorry...I'll stop posting my entire source and I'll just try and post the relevant code.
I guess I don't know what will be relevant.
This is a case of Too Much Information!!! Plus, it scares a lot of people off. Who wants to read through all that code? Also, there is a significant amount of scrolling required.
Bob
Bob
This creates a reference pointer, not a clone:
temph = _heloc;
Bob
temph = _heloc;
Bob
> Perhaps I can delare a "temp" object of the same time, assign the "global" (internal) copy to
> it....and then add the "temp" object to the ArrayList?
Yes.
This example instantiates the object and has to be done at the appropriate time. Then you can add it to the ArrayList.
_mortgage = new Mortgage(mortAmountOwed, mortInterestRate);
Gary
> it....and then add the "temp" object to the ArrayList?
Yes.
This example instantiates the object and has to be done at the appropriate time. Then you can add it to the ArrayList.
_mortgage = new Mortgage(mortAmountOwed, mortInterestRate);
Gary
ASKER
How would I change this code so that I create a seperate "clone" or distinct copy of the _mortgage object?
//Store the current information for the month in question
// as the Mort Payments Accrued increments, this represents ONE month.
Driven.Mortgage tempm;
tempm = _mortgage;
Driven.Heloc temph;
temph = _heloc;
tempm._monthTag = _mortPaymentsAccrued;
temph._monthTag = _mortPaymentsAccrued;
_mortgage._monthTag = _mortPaymentsAccrued;
_heloc._monthTag = _mortPaymentsAccrued; //otherwise we won't get an even month
this._specificMortMonths.A dd(tempm);
this._specificHelocMonths. Add(temph) ;
//Store the current information for the month in question
// as the Mort Payments Accrued increments, this represents ONE month.
Driven.Mortgage tempm;
tempm = _mortgage;
Driven.Heloc temph;
temph = _heloc;
tempm._monthTag = _mortPaymentsAccrued;
temph._monthTag = _mortPaymentsAccrued;
_mortgage._monthTag = _mortPaymentsAccrued;
_heloc._monthTag = _mortPaymentsAccrued; //otherwise we won't get an even month
this._specificMortMonths.A
this._specificHelocMonths.
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Please show me how the new code would look given your instructions:
1) Create a new object, and copy the values.
2) Implement ICloneable.
3) Add _heloc to the the ArrayList instead of a temporary copy.
1) Create a new object, and copy the values.
2) Implement ICloneable.
3) Add _heloc to the the ArrayList instead of a temporary copy.
Did you still want that information?
Bob
Bob
ASKER
TheLearnedOne:
Did you still want that information?
===========
If it's not too much to ask....please.
I ended-up going a different route, for now....but it would still be nice to have your solution.
Did you still want that information?
===========
If it's not too much to ask....please.
I ended-up going a different route, for now....but it would still be nice to have your solution.
1) Create new and copy:
Driven.Heloc temph = new Driven.Heloc();
temph = _heloc._helocAmountOwed;
...
2) ICloneable:
Base class for cloning an object in C#
http://www.codeproject.com/csharp/cloneimpl_class.asp
There are some who think that you should create your own Copy method, instead of implementing ICloneable.
3) this._specificHelocMonths. Add(_heloc );
Bob
Driven.Heloc temph = new Driven.Heloc();
temph = _heloc._helocAmountOwed;
...
2) ICloneable:
Base class for cloning an object in C#
http://www.codeproject.com/csharp/cloneimpl_class.asp
There are some who think that you should create your own Copy method, instead of implementing ICloneable.
3) this._specificHelocMonths.
Bob
The problem with #1 is that you have to explicitly specify properties, and if you add a property, then you could forget to add the property to the Clone method.
Bob
Bob
ASKER
Thanks for posting your solution.
ASKER
public void FinancedItemOwing(object sender, EventArgs e)
{
//First thing we want to do is determine if a Heloc pay down is possible
bool canPayDownBasedUponPercent
//If it is the Mortgage Object that raised the event ("Mortgage is saying, 'I owe something still'")
//AND it is time to make a mortgage payment again (based upon the number of heloc payments each month)
//If those conditions are both true...then make a payment
if (sender is Driven.Mortgage && _timeToPayMortgageNow)
{
//Since a mortgage payment is due ... can we use the Heloc to pay down a big chunk
//of the principal?
if (canPayDownBasedUponPercen
{
//Determine how big of a chunk we can apply to the principal
double tempHowMuchToApply = this._heloc.HowMuchToApply
this._heloc._helocAmountOw
_mortgage.MakeMortgagePaym
_mortPaymentsAccrued++;
//Store the current information for the month in question
// as the Mort Payments Accrued increments, this represents ONE month.
_mortgage._monthTag = _mortPaymentsAccrued;
_heloc._monthTag = _mortPaymentsAccrued; //otherwise we won't get an even month
this._specificMortMonths.A
this._specificHelocMonths.
Driven.DebugOut.dOut("Mort
//down line accrual
if (this.MortgageMonthsAccrue
{
Driven.DebugOut.dOut("OLD Amortized amount: " + this._helocAmortizedPaymen
_downLineAccrualAmountAccu
SetNewHelocAmortizedPaymen
//see about not having to fully qualify this:
Driven.DebugOut.dOut("DONE
Driven.DebugOut.dOut("NEW Amortized amount: " + this._helocAmortizedPaymen
}
//Let's see if the Heloc object still OWES anything
_heloc.CheckDebtStatus(_mo
}
else //Otherwise, just make a regular mortgage payment...
{
_mortgage.MakeMortgagePaym
_mortPaymentsAccrued++;
Driven.DebugOut.dOut("Mort
//Store the current information for the month in question
// as the Mort Payments Accrued increments, this represents ONE month.
_mortgage._monthTag = _mortPaymentsAccrued;
_heloc._monthTag = _mortPaymentsAccrued; //otherwise we won't get an even month
this._specificMortMonths.A
this._specificHelocMonths.
//down line accrual bonus
if (this.MortgageMonthsAccrue
{
Driven.DebugOut.dOut("OLD Amortized amount: " + this._helocAmortizedPaymen
_downLineAccrualAmountAccu
SetNewHelocAmortizedPaymen
//see about not having to fully qualify this:
Driven.DebugOut.dOut("DONE
Driven.DebugOut.dOut("NEW Amortized amount: " + this._helocAmortizedPaymen
}
//Let's see if the Heloc object still OWES anything
_heloc.CheckDebtStatus(_mo
}
}
else if(sender is Driven.Mortgage && !_timeToPayMortgageNow)// Mortgage: I OWE SOMETHING, BUT IT'S NOT TIME TO MAKE A PAYMENT YET
{
//Even though it's not time to make a payment....we still need to also check the Heloc
//to see if IT owes something right now...
_heloc.CheckDebtStatus(_mo
}
if (sender is Driven.Heloc) //Heloc object says: "I OWE SOMETHING"
{
//If Mortgage is paid-off, then we can use our discretionary income to make
//a much BIGGER payment now...
if (_mortgage._mortAmountOwed
{
_heloc.MakeHelocPayment(th
}
else //otherwise, just make a regular payment (amortized payment)
{
_heloc.MakeHelocPayment(th
}
//Another payment was made...
_helocPaymentsAccrued++;
//We just made another amortized payment
_helocPaymentGradualAccrua
//When our amortized payments are equal to or greater than our
//Montly discretionary income...then we can make a mortgage payment
//and the accumulator zeros-out
if (_helocPaymentGradualAccru
{
_timeToPayMortgageNow = true;
_helocPaymentGradualAccrua
}
else
{
_timeToPayMortgageNow = false;
}
//check to see if we can increase the amount of the Heloc (about every 18 months)
_heloc.RenewHelocTotalAmou
Driven.DebugOut.dOut("Helo
//Now that we're done with the Heloc processing...let's check on Mortgage to see if it is
//still OWING something
_mortgage.CheckDebtStatus(
}
}