void FPetraUtilsObject_DataSavingStarted(object Sender, EventArgs e)
        {
            // The user has clicked Save.  We need to consider if we need to make any Inverse currency additions...
            // We need to update the details and validate them first
            // When we return from this method the standard code will do the validation again and might not allow the save to go ahead
            FPetraUtilsObject.VerificationResultCollection.Clear();
            ValidateAllData(false, TErrorProcessingMode.Epm_None);

            if (!TVerificationHelper.IsNullOrOnlyNonCritical(FPetraUtilsObject.VerificationResultCollection))
            {
                return;
            }

            // Now go through all the grid rows (view) checking all the added rows.  Keep a list of inverses
            List <tInverseItem> lstInverses = new List <tInverseItem>();
            DataView            gridView    = ((DevAge.ComponentModel.BoundDataView)grdDetails.DataSource).DataView;

            for (int i = 0; i < gridView.Count; i++)
            {
                ACorporateExchangeRateRow ARow = (ACorporateExchangeRateRow)gridView[i].Row;

                if (ARow.RowState == DataRowState.Added)
                {
                    tInverseItem item = new tInverseItem();
                    item.FromCurrencyCode = ARow.ToCurrencyCode;
                    item.ToCurrencyCode   = ARow.FromCurrencyCode;
                    item.RateOfExchange   = Math.Round(1 / ARow.RateOfExchange, 10);
                    item.DateEffective    = ARow.DateEffectiveFrom;
                    lstInverses.Add(item);
                }
            }

            if (lstInverses.Count == 0)
            {
                return;
            }

            // Now go through our list and check if any items need adding to the data Table
            // The user may already have put an inverse currency in by hand
            DataView dv = new DataView(FMainDS.ACorporateExchangeRate);

            for (int i = 0; i < lstInverses.Count; i++)
            {
                tInverseItem item = lstInverses[i];

                // Does the item exist already?
                dv.RowFilter = String.Format(CultureInfo.InvariantCulture, "{0}='{1}' AND {2}='{3}' AND {4}=#{5}#",
                                             ACorporateExchangeRateTable.GetFromCurrencyCodeDBName(),
                                             item.FromCurrencyCode,
                                             ACorporateExchangeRateTable.GetToCurrencyCodeDBName(),
                                             item.ToCurrencyCode,
                                             ACorporateExchangeRateTable.GetDateEffectiveFromDBName(),
                                             item.DateEffective.ToString("d", CultureInfo.InvariantCulture));

                if (dv.Count == 0)
                {
                    ACorporateExchangeRateRow NewRow = FMainDS.ACorporateExchangeRate.NewRowTyped();
                    NewRow.FromCurrencyCode  = item.FromCurrencyCode;
                    NewRow.ToCurrencyCode    = item.ToCurrencyCode;
                    NewRow.DateEffectiveFrom = DateTime.Parse(item.DateEffective.ToLongDateString());
                    NewRow.RateOfExchange    = item.RateOfExchange;

                    FMainDS.ACorporateExchangeRate.Rows.Add(NewRow);
                }
            }

            // Now make sure to select the row that was currently selected when we started the Save operation
            SelectRowInGrid(grdDetails.DataSourceRowToIndex2(FPreviouslySelectedDetailRow) + 1);
        }
        private void FPetraUtilsObject_DataSavingValidated(object Sender, System.ComponentModel.CancelEventArgs e)
        {
            // The user has clicked Save.  We need to consider if we need to make any Inverse currency additions...
            // Now go through all the data checking all the added or modified rows.  Keep a list of inverses.
            //  (Remember that modified rows must by definition be unused so its ok to delete them.)
            DataView dv = new DataView(FMainDS.ADailyExchangeRate, String.Empty, SortByDateDescending, DataViewRowState.CurrentRows);

            List <tInverseItem>lstInverses = new List <tInverseItem>();

            for (int i = 0; i < dv.Count; i++)
            {
                ADailyExchangeRateRow row = (ADailyExchangeRateRow)dv[i].Row;
                TLogging.LogAtLevel(2, String.Format("Testing row {5} of {6} for auto-deletion: From {0}, To {1}, Date {2}, Rate {3}, Time {4}",
                        row.FromCurrencyCode, row.ToCurrencyCode, row.DateEffectiveFrom.ToString("yyyy-MM-dd"), row.RateOfExchange,
                        row.TimeEffectiveFrom, i + 1, dv.Count));

                if ((row.RowState == DataRowState.Added) || (row.RowState == DataRowState.Modified))
                {
                    // Remove it if it has a zero rate of exchange (from a modal screen)
                    if (row.RateOfExchange == 0.0m)
                    {
                        TLogging.LogAtLevel(2, String.Format("Zero rate of exchange auto-deletion: From {0}, To {1}, Date {2}, Time {3}",
                                row.FromCurrencyCode, row.ToCurrencyCode, row.DateEffectiveFrom.ToString("yyyy-MM-dd"), row.TimeEffectiveFrom));
                        row.Delete();
                        // The DataView will automatically remove this row from the view
                        i--;
                        continue;
                    }

                    // See if this is a duplicate.  If so we can remove it.
                    // Start by looking backwards
                    bool rowDeleted = false;

                    for (int k = i - 1; k >= 0; k--)
                    {
                        ADailyExchangeRateRow tryRow = (ADailyExchangeRateRow)dv[k].Row;

                        // Ignore rows we deleted in this loop
                        if ((tryRow.RowState == DataRowState.Deleted) || (tryRow.RowState == DataRowState.Detached))
                        {
                            continue;
                        }

                        if ((tryRow.FromCurrencyCode == row.FromCurrencyCode)
                            && (tryRow.ToCurrencyCode == row.ToCurrencyCode)
                            && (tryRow.DateEffectiveFrom == row.DateEffectiveFrom))
                        {
                            // same from/to/date so remove our working row
                            if (tryRow.RateOfExchange == row.RateOfExchange)
                            {
                                TLogging.LogAtLevel(2,
                                    String.Format(
                                        "Duplicate rate of exchange (B) auto-deletion: From {0}, To {1}, Date {2}, Rate {3}, Time deleted {4}, Time retained {5}",
                                        row.FromCurrencyCode, row.ToCurrencyCode, row.DateEffectiveFrom.ToString("yyyy-MM-dd"), row.RateOfExchange,
                                        row.TimeEffectiveFrom, tryRow.TimeEffectiveFrom));
                                row.Delete();
                                rowDeleted = true;
                                // The DataView will automatically remove this row from the view
                                i--;
                                break;
                            }
                        }
                        else
                        {
                            // no longer our from/to/date
                            break;
                        }
                    }

                    if (rowDeleted)
                    {
                        continue;
                    }

                    // Now do the same looking forwards
                    rowDeleted = false;

                    for (int k = i + 1; k < dv.Count; k++)
                    {
                        ADailyExchangeRateRow tryRow = (ADailyExchangeRateRow)dv[k].Row;

                        if ((tryRow.FromCurrencyCode == row.FromCurrencyCode)
                            && (tryRow.ToCurrencyCode == row.ToCurrencyCode)
                            && (tryRow.DateEffectiveFrom == row.DateEffectiveFrom))
                        {
                            // same from/to/date so remove our working row
                            if (tryRow.RateOfExchange == row.RateOfExchange)
                            {
                                TLogging.LogAtLevel(2,
                                    String.Format(
                                        "Duplicate rate of exchange (F) auto-deletion: From {0}, To {1}, Date {2}, Rate {3}, Time deleted {4}, Time retained {5}",
                                        row.FromCurrencyCode, row.ToCurrencyCode, row.DateEffectiveFrom.ToString("yyyy-MM-dd"), row.RateOfExchange,
                                        row.TimeEffectiveFrom, tryRow.TimeEffectiveFrom));
                                row.Delete();
                                rowDeleted = true;
                                // The DataView will automatically remove this row from the view
                                i--;
                                break;
                            }
                        }
                        else
                        {
                            // no longer our from/to/date
                            break;
                        }
                    }

                    if (rowDeleted)
                    {
                        continue;
                    }

                    // Now, as we have retained this added or modiified row, note the inverse settings
                    tInverseItem item = new tInverseItem();
                    item.FromCurrencyCode = row.ToCurrencyCode;
                    item.ToCurrencyCode = row.FromCurrencyCode;
                    item.RateOfExchange = Math.Round(1 / row.RateOfExchange, 10);
                    item.DateEffective = row.DateEffectiveFrom;
                    item.TimeEffective = row.TimeEffectiveFrom;
                    lstInverses.Add(item);
                }
            }

            if (lstInverses.Count > 0)
            {
                // Now go through our list and check if any items need adding to the data Table
                // The user may already have put an inverse currency in by hand
                dv = new DataView(FMainDS.ADailyExchangeRate);

                for (int i = 0; i < lstInverses.Count; i++)
                {
                    tInverseItem item = lstInverses[i];

                    // Does the item exist already?
                    dv.RowFilter = String.Format(CultureInfo.InvariantCulture, "{0}='{1}' AND {2}='{3}' AND {4}=#{5}# AND {6}={7}",
                        ADailyExchangeRateTable.GetFromCurrencyCodeDBName(),
                        item.FromCurrencyCode,
                        ADailyExchangeRateTable.GetToCurrencyCodeDBName(),
                        item.ToCurrencyCode,
                        ADailyExchangeRateTable.GetDateEffectiveFromDBName(),
                        item.DateEffective.ToString("d", CultureInfo.InvariantCulture),
                        ADailyExchangeRateTable.GetTimeEffectiveFromDBName(),
                        item.TimeEffective);

                    if (dv.Count == 0)
                    {
                        ADailyExchangeRateRow NewRow = FMainDS.ADailyExchangeRate.NewRowTyped();
                        NewRow.FromCurrencyCode = item.FromCurrencyCode;;
                        NewRow.ToCurrencyCode = item.ToCurrencyCode;
                        NewRow.DateEffectiveFrom = DateTime.Parse(item.DateEffective.ToLongDateString());
                        NewRow.TimeEffectiveFrom = item.TimeEffective;
                        NewRow.RateOfExchange = item.RateOfExchange;

                        FMainDS.ADailyExchangeRate.Rows.Add(NewRow);
                    }
                }
            }

            // Now make sure to select the row that was currently selected when we started the Save operation
            // If we auto-deleted the 'current' row, this method will select the first row
            // We need to skip validation for this row select because if there is a warning and we have now created an inverse
            //  we will raise a 'you can move away from this row' message box.
            FSkipValidation = true;
            FIgnoreFocusRowLeaving = true;

            SelectRowInGrid(grdDetails.DataSourceRowToIndex2(FPreviouslySelectedDetailRow) + 1);

            FIgnoreFocusRowLeaving = false;
            FSkipValidation = false;
        }
        void FPetraUtilsObject_DataSavingStarted(object Sender, EventArgs e)
        {
            // The user has clicked Save.  We need to consider if we need to make any Inverse currency additions...
            // We need to update the details and validate them first
            // When we return from this method the standard code will do the validation again and might not allow the save to go ahead
            FPetraUtilsObject.VerificationResultCollection.Clear();
            ValidateAllData(false, false);

            if (!TVerificationHelper.IsNullOrOnlyNonCritical(FPetraUtilsObject.VerificationResultCollection))
            {
                return;
            }

            // Now go through all the grid rows (view) checking all the added rows.  Keep a list of inverses
            List <tInverseItem>lstInverses = new List <tInverseItem>();
            DataView gridView = ((DevAge.ComponentModel.BoundDataView)grdDetails.DataSource).DataView;

            for (int i = 0; i < gridView.Count; i++)
            {
                ACorporateExchangeRateRow ARow = (ACorporateExchangeRateRow)gridView[i].Row;

                if (ARow.RowState == DataRowState.Added)
                {
                    tInverseItem item = new tInverseItem();
                    item.FromCurrencyCode = ARow.ToCurrencyCode;
                    item.ToCurrencyCode = ARow.FromCurrencyCode;
                    item.RateOfExchange = Math.Round(1 / ARow.RateOfExchange, 10);
                    item.DateEffective = ARow.DateEffectiveFrom;
                    lstInverses.Add(item);
                }
            }

            if (lstInverses.Count == 0)
            {
                return;
            }

            // Now go through our list and check if any items need adding to the data Table
            // The user may already have put an inverse currency in by hand
            DataView dv = new DataView(FMainDS.ACorporateExchangeRate);

            for (int i = 0; i < lstInverses.Count; i++)
            {
                tInverseItem item = lstInverses[i];

                // Does the item exist already?
                dv.RowFilter = String.Format(CultureInfo.InvariantCulture, "{0}='{1}' AND {2}='{3}' AND {4}=#{5}# AND {6}={7}",
                    ACorporateExchangeRateTable.GetFromCurrencyCodeDBName(),
                    item.FromCurrencyCode,
                    ACorporateExchangeRateTable.GetToCurrencyCodeDBName(),
                    item.ToCurrencyCode,
                    ACorporateExchangeRateTable.GetDateEffectiveFromDBName(),
                    item.DateEffective.ToString("d", CultureInfo.InvariantCulture),
                    ACorporateExchangeRateTable.GetRateOfExchangeDBName(),
                    item.RateOfExchange);

                if (dv.Count == 0)
                {
                    ACorporateExchangeRateRow NewRow = FMainDS.ACorporateExchangeRate.NewRowTyped();
                    NewRow.FromCurrencyCode = item.FromCurrencyCode;
                    NewRow.ToCurrencyCode = item.ToCurrencyCode;
                    NewRow.DateEffectiveFrom = DateTime.Parse(item.DateEffective.ToLongDateString());
                    NewRow.RateOfExchange = item.RateOfExchange;

                    FMainDS.ACorporateExchangeRate.Rows.Add(NewRow);
                }
            }

            // Now make sure to select the row that was currently selected when we started the Save operation
            SelectRowInGrid(grdDetails.DataSourceRowToIndex2(FPreviouslySelectedDetailRow) + 1);
        }