Calculate credit payments in WPF and C#

[example]

This is a WPF version of the example Calculate credit payments in C#. The calculations are the same. Unfortunately building and using the user interface is much harder in WPF, particularly working with the ListView control. (The unofficial WPF slogan: Twice as flexible and only five times as hard!)

The program’s XAML code uses grid rows and columns together with labels, text boxes, buttons, and other controls. Building most of the interface is relatively slow but straightforward. Download the example to see how most of the controls are created.

This post focuses on the ListView control. The program uses the following XAML code to define the control.

<ListView Grid.Row="0" Grid.Column="2" Grid.RowSpan="6"
    Name="lvwPayments">
    <ListView.ItemContainerStyle>
        <Style TargetType="ListViewItem">
            <Setter Property="HorizontalContentAlignment"
            Value="Stretch" />
        </Style>
    </ListView.ItemContainerStyle>
    <ListView.View>
        <GridView>
            <GridViewColumn Header="Period" Width="50"
                DisplayMemberBinding="{Binding Path=Period}"/>

            <GridViewColumn Header="Payment" Width="75">
                <GridViewColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Payment}"
                            TextAlignment="Right"/>
                    </DataTemplate>
                </GridViewColumn.CellTemplate>
            </GridViewColumn>

            <GridViewColumn Header="Interest" Width="75">
                <GridViewColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Interest}"
                            TextAlignment="Right"/>
                    </DataTemplate>
                </GridViewColumn.CellTemplate>
            </GridViewColumn>

            <GridViewColumn Header="Balance" Width="75">
                <GridViewColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Balance}"
                            TextAlignment="Right"/>
                    </DataTemplate>
                </GridViewColumn.CellTemplate>
            </GridViewColumn>
        </GridView>
    </ListView.View>
</ListView>

There are several important points in this code.

In basic structure, the ListView contains a GridView control that provides the rows and columns. The GridView contains GridViewColumn objects to define its columns. More on thos shortly.

By default, the item containers inside the GridView that display the data do not stretch their contents to fill the available area. I wanted to right-align the currency columns. Because the containers don’t stretch their contents, no matter what I did, the contents of each column were placed at the left of the column. To allow the contents to be right aligned (although this isn’t the only step), I gave the ListView an ItemContainerStyle that makes the containers stretch their contents. (Note that you could set this property to Right instead of Stretch to align all of the columns.)

The ListView.View property indicates how items are displayed by the control. This example uses a Grid control. That control contains a collection of GridViewColumn objects that define the data’s columns.

The first GridViewColumn displays the payment period. Its DisplayBindingMember property indicates what piece of data from the control’s data binding this column should display. This column displays the Period property of the data object. You’ll see how that value is defined shortly.

The second GridViewColumn displays the payment amount. It uses a CellTemplate to define how its data should be displayed.

Important: The GridViewColumn must not have a DisplayBindingMember value set or it sill ignore its CellTemplate! I (and many others) have wasted a lot of time trying to figure out why a CellTemplate didn’t work because its GridViewColumn also included a DisplayBindingMember.

The payment column’s CellTemplate tells the control to display its data in a TextBlock. That control should set its Text property to the data binding value Payment and it should align its text on the right. (This is the magic to aligning values in a column.)

The rest of the ListView control’s columns are defined similarly.

The remaining interesting part of this example is how the code manages the ListView control. The program uses the following PaymentData class to hold data about a credit card payment.

public class PaymentData
{
    public string Period { get; set; }
    public string Payment { get; set; }
    public string Interest { get; set; }
    public string Balance { get; set; }
}

This class simply holds payment information for a single payment. It stores the values as text so I can easily use blank and formatted values.

The bindings inside the ListView and its contained objects use the class’s property names to determine what values to display. For example, the second column’s CellTemplate object’s DataTemplate uses {Binding Payment} to find its data. That value means it should look in the bound object (which is a PaymentData object in this example) for its Payment property and then display that.

The following code shows how the program calculates and displays the payments. The code that deals with the ListView is highlighted in blue.

private void btnGo_Click(object sender, RoutedEventArgs e)
{
    // Get the parameters.
    decimal balance =
        decimal.Parse(txtInitialBalance.Text, NumberStyles.Any);
    decimal interest_rate =
        decimal.Parse(txtInterestRate.Text.Replace("%", "")) / 100;
    decimal payment_percent =
        decimal.Parse(txtPaymentPercent.Text.Replace("%", "")) / 100;
    decimal min_payment =
        decimal.Parse(txtMinPayment.Text, NumberStyles.Any);
    interest_rate /= 12;

    lblTotalPayments.Content = null;
    decimal total_payments = 0;

    // Display the initial balance.
    lvwPayments.Items.Clear();
    PaymentData data = new PaymentData()
    {
        Period = "0",
        Payment = null,
        Interest = null,
        Balance = balance.ToString("c"),
    };
    lvwPayments.Items.Add(data);

    // Loop until balance == 0.
    for (int i = 1; balance > 0; i++)
    {
        // Calculate the payment.
        decimal payment = balance * payment_percent;
        if (payment < min_payment) payment = min_payment;

        // Calculate interest.
        decimal interest = balance * interest_rate;
        balance += interest;

        // See if we can pay off the balance.
        if (payment > balance) payment = balance;
        total_payments += payment;
        balance -= payment;

        // Display results.
        data = new PaymentData()
        {
            Period = i.ToString(),
            Payment = payment.ToString("c"),
            Interest = interest.ToString("c"),
            Balance = balance.ToString("c"),
        };
        lvwPayments.Items.Add(data);
    }

    // Display the total payments.
    lblTotalPayments.Content = total_payments.ToString("c");
}

This code calculates the payments as in the previous example. Look at it to see how that works.

To add a row to the ListView control, the program creates a PaymentData object containing the row’s data values. It then simply adds that object to the ListView control’s list of items. The grid’s columns dig through the object to find the pieces of data that they should display. if a column has a CellTemplate, that object figures out how to display the data.

Download the example for more details.


Download Example   Follow me on Twitter   RSS feed   Donate




About RodStephens

Rod Stephens is a software consultant and author who has written more than 30 books and 250 magazine articles covering C#, Visual Basic, Visual Basic for Applications, Delphi, and Java.
This entry was posted in algorithms, calculations, finance, mathematics and tagged , , , , , , , , , , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *