void CXTPReportColumnOrder::DoPropExchange(CXTPPropExchange* pPX)
{
	if (pPX->IsStoring())
	{
		int nCount = GetCount();
		PX_Int(pPX, _T("Count"), nCount, 0);

		for (int i = 0; i < nCount; i++)
		{
			CXTPReportColumn* pColumn = GetAt(i);
			if (pColumn)
			{
				int nItemIndex = pColumn->GetItemIndex();
				CString strInternalName = pColumn->GetInternalName();
				CString strParamName;

				strParamName.Format(_T("Column%i"), i);
				PX_Int(pPX, strParamName, nItemIndex, 0);

				strParamName.Format(_T("InternalName%i"), i);
				PX_String(pPX, strParamName, strInternalName);
			}
		}
	}
	else
	{
		Clear();

		int nCount = 0;
		PX_Int(pPX, _T("Count"), nCount, 0);

		for (int i = 0; i < nCount; i++)
		{
			int nItemIndex = 0;
			CString strParamName;
			strParamName.Format(_T("Column%i"), i);
			PX_Int(pPX, strParamName, nItemIndex, 0);

			CXTPReportColumn* pColumn = NULL;
			if (pPX->GetSchema() > _XTP_SCHEMA_1100)
			{
				strParamName.Format(_T("InternalName%i"), i);
				CString strInternalName;
				PX_String(pPX, strParamName, strInternalName);

				if (!strInternalName.IsEmpty())
					pColumn = m_pColumns->Find(strInternalName);
			}
			if (!pColumn)
				pColumn = m_pColumns->Find(nItemIndex);

			if (pColumn)
				Add(pColumn);
		}
	}
}
CXTPReportColumn* CXTPReportColumns::Find(const CString& strInternalName) const
{
	for (int nColumn = 0; nColumn < GetCount(); nColumn++)
	{
		CXTPReportColumn* pColumn = GetAt(nColumn);
		if (pColumn->GetInternalName() == strInternalName)
			return pColumn;
	}
	return NULL;
}
void CXTPReportColumns::DoPropExchange(CXTPPropExchange* pPX)
{
	int nItemIndex;
	CString strInternalName;

	if (pPX->IsStoring())
	{
		int nCount = GetCount();

		CXTPPropExchangeEnumeratorPtr pEnumerator(pPX->GetEnumerator(_T("Column")));
		POSITION pos = pEnumerator->GetPosition(nCount, FALSE);

		for (int nColumn = 0; nColumn < nCount; nColumn++)
		{
			CXTPReportColumn* pColumn = GetAt(nColumn);
			CXTPPropExchangeSection secColumn(pEnumerator->GetNext(pos));

			nItemIndex = pColumn->GetItemIndex();
			strInternalName = pColumn->GetInternalName();

			PX_Int(&secColumn, _T("ItemIndex"), nItemIndex);
			PX_String(&secColumn, _T("InternalName"), strInternalName);

			pColumn->DoPropExchange(&secColumn);
		}
	}
	else
	{
		CXTPPropExchangeEnumeratorPtr pEnumerator(pPX->GetEnumerator(_T("Column")));
		POSITION pos = pEnumerator->GetPosition(0, FALSE);

		CXTPReportColumn tmpColumn(0, _T(""), 0);
		int i = 0;
		while (pos)
		{
			CXTPPropExchangeSection secColumn(pEnumerator->GetNext(pos));

			CXTPReportColumn* pColumn = NULL;
			PX_Int(&secColumn, _T("ItemIndex"), nItemIndex, -1);

			if (pPX->GetSchema() > _XTP_SCHEMA_1100)
			{
				PX_String(&secColumn, _T("InternalName"), strInternalName);

				if (!strInternalName.IsEmpty())
				{
					pColumn = Find(strInternalName);

					if (!pColumn && pPX->GetSchema() < _XTP_SCHEMA_1500) // before 15.0 release internal name was equal to caption
					{
						for (int nColumn = 0; nColumn < GetCount(); nColumn++)
						{
							CXTPReportColumn* p = GetAt(nColumn);
							if (p->GetCaption() == strInternalName)
							{
								pColumn = p;
								break;
							}
						}
					}

					// column data is exists but column is not in the collection.
					if (!pColumn)
					{
						// just read data to skeep (to be safe for array serialization)
						tmpColumn.DoPropExchange(&secColumn);
						continue;
					}
				}
			}

			if (!pColumn)
				pColumn = Find(nItemIndex);

			if (!pColumn)
				AfxThrowArchiveException(CArchiveException::badIndex);

			pColumn->DoPropExchange(&secColumn);
			ChangeColumnOrder(i, IndexOf(pColumn));
			i++;
		}
	}

	CXTPPropExchangeSection secGroupsOrder(pPX->GetSection(_T("GroupsOrder")));
	m_pGroupsOrder->DoPropExchange(&secGroupsOrder);

	CXTPPropExchangeSection secSortOrder(pPX->GetSection(_T("SortOrder")));
	m_pSortOrder->DoPropExchange(&secSortOrder);
}