본문 바로가기
C & C++/MFC 컨트롤

스핀(Spin) 컨트롤 - 스핀

by izen8 2011. 12. 14.
반응형

스핀(Spin) 컨트롤 초기화


1. 단독으로 사용불가

 
2. 화살표를 이용하여 에디트 컨트롤에 출력되어 있는 숫자를 증가 또는 감소시킴
 
3. 속성
  - Auto Buddy : 스핀 컨트롤에 의해 숫자 값을 변경시킬 에디트 컨트롤을 결정할 때 탭 순서가
                        스핀 컨트롤 바로 이전의 에디트 컨트롤과 자동 연결
  - Set Buddy Integer : 스핀 컨트롤의 화살표가 눌리면 스핀 컨트롤에 연결된 에디트 컨트롤 값
                                 1을 증가하거나 감소시킴
  - Alignment : 스핀 컨트롤과 연결된 에디트 컨트롤과의 위치 관계 지정
  - Arrow Keys : 키보드 화살표 키를 눌러도 동작이 되게 함
  - No Thousands : 숫자를 출력할 때 1000단위마다 콤마를 안찍게 함
  - Orientation : 화살표 방향을 세로 또는 가로로 지정
  - Wrap : 범위 값을 넘어서면 최소값 또는 최대값에서 부터 다시 시작되도록 함
 
4. 기본 설정
  - Auto Buddy -> true, Set Buddy Integer -> true, Alignment -> Right Align 
 
5. 기본 설정 문제점
  - 위 버튼을 누르면 숫자가 감소, 아래 버튼을 누르면 숫자가 증가
 
6. 문제점 해결방법 -> 스핀컨트롤 변수선언 후 초기화
  - OnInitDialog() 함수에서 초기화
  - 스핀컨트롤 값의 범위 설정 ->SetRange()함수 호출
 
7. 현재 위치값 설정
  - SetPos() 함수 호출
 
8. 스핀에 따른 값 변화
void OnDeltaPos( NMHDR * pNMHDR, LRESULT * pResult )
{
   LPNMUPDOWN pNMUpDown = reinterpret_cast<LPNMUPDOWN>(pNMHDR);
  
 // spin 컨트롤에서 값을 가져온다.
   int nspinValue = pNMUpDown->iPos;
}
 

 


 

스핀(Spin) 컨트롤 활용 

 
1. 소수점 사용
MFC의 Spin 컨트롤은 기본값이 정수 단위로만 계산이 되어있습니다.
소수단위( 0.5 / 0.005 )로 증가시키거나 에디트 박스에 소스 단위로 표시 시키기 위해서는 추가적인 작업이 필요합니다.
codeproject나 codeguru를 찾아보면 외국의 사람들이 만들어준 좋은 자료들이 있는데
대부분 클래스단위로 작업을 해서 그 클래스만 갔다 쓰면 아무런 문제가 없도록 작업해 놓았습니다.
 
 
클래스 단위로 붙이기에는 좀 크다는 생각이 들어서 그 소스들에서 필요한 부분만 추출해서 적용시켜 보았는데
큰 문제 없이 잘 작동하기에 정리합니다.
 
굵게 표시된 함수는 편의상 분리하여 만든 함수 입니다.
문제가 되는 부분이 분명히 보이고, 수정해야할 부분들이 있지만 아직은 수정하기에는 좀 귀찮기도 하고..
일단 초안을 만들어 놓고 틈틈히 수정해 나가겠습니다.
 
Spin 컨트롤을 생성하고 범위를 지정합니다.
 
CSpinButtonCtrl spin;
float min = 0;
float max = 100;
float delta = 0.05;
float range = GetSpinRangeFloatToInt( min, max, delta );
spin.SetRange( 0, range );
GetSpinRangeFloatToInt는 Spin 컨트롤의 최소/최대/증가값을 받아서 범위를 계산해서 넘겨주고 컨트롤에 그 범위로 세팅합니다.
 
void GetSpinRangeFloatToInt( float lower, float upper, float delta )
{
   int resultRagnge;
   if( delta == 0.0f )
   {
      return 0;
   }
   float range = fabs( ( upper - lower ) / delta );
   if( range > ( float ) UD_MAXVAL )
   {
      resultRange = UD_MAXVAL;
   }
   else
   {
      resultRange = (int)range;
   }
   return resultRange;
}
Spin 컨트롤에서 값을 증가 시킬경우 ON_NOTIFY의 UDN_DELTAPOS 메시지가 오기 때문에 이 메시지를 받아서 원하는 처리를 해줍니다.
 
BEGIN_MESSAGE_MAP( ...... )
ON_NOTIFY( UDN_DELTAPOS, IDC_SPIN_CONTROL, OnDeltaPos )
END_MESSAGE_MAP()
메시지 를 처리할 함수를 살펴보면..
 
void OnDeltaPos( NMHDR * pNMHDR, LRESULT * pResult )
{
   NM_UPDOWN * pNMHDRSpin = (NM_UPDOWN * )pNMHDR;
   // spin 컨트롤에서 값을 가져온다.
   float spinValue = GetSpinValue( spin );
   // 가져온 값에 증가값을 더한다.
   float val = spinValue + delta * pNMHDRSpin->iDelta;
   const bool can_wrap = ( UDS_WRAP & spin->GetStyle() );
   // spin 컨트롤에 표시할 값을 구한다.
   CalculationSpinValue( val, can_wrap, min, max, pNMHDRSpin );
 
   CString str;
   str.Format( "%.3f", val );
   CWnd * edit = spin->GetBuddy();
   // spin 컨트롤에 Buddy상태인 에디트 박스에 값을 출력한다.
   edit->SetWindowText( temp );
}
스핀 컨트롤에서 값을 가져와서 실수형으로 변환하는 함수가 GetspinValue() 입니다.
 
float GetSpinValue( const CSpinButtonCtrl * spin )
{
   CWnd * edit = spin->GetBuddy();
   if( edit )
   {
      CString str;
      edit->GetWindowText( str );
      float val = atof( str );
      return val;
   }
   else
   {
      return 0;
   }
}
CalculationSpinValue()는 Spin 컨트롤의 Up/Down 상태에서 값을 계산하여 최대/최소값과 일치할 경우 설정합니다.
 
#define EPS 1.0e-9
void CalculationSpinValue( float & val, BOOL can_wrap, float min, float max, NM_UPDOWN * pNMHDSpin )
{
   if( pNMHDRSpin->iDelta < 0 ) // spin down
   {
      float abs_eps = fabs( EPS * ( val > min ? val : min ) );
      if( abs_eps < EPS ) abs_eps = EPS;
      if( min - val > abs_eps )
      {
         if( cal_wrap) val = max;
         else val = min;
      }
   }
   else
   {
      float abs_eps = fabs( EPS * (val > max ? val : max ) );
      if( abs_eps < EPS ) abs_eps = EPS;
      if( val - max > abs_eps )
      {
         if( cal_wrap ) val = min;
         else val = max;
      }
   }
}
 
Spin 컨트롤을 사용하다 보면 현재 Spin 컨트롤의 pos을 지정하기 위해서 SetPos()를 사용하는데 이때에도
별도의 계산을 거쳐서 값을 넣어줘야 합니다.
 
spin->SetPos( GetSpinPosFloatToInt( 20.15, min, max, range ) );
GetSpinPosFloatToInt()를 하게 되면 실수형으로 지정한 값으로 계산을 해서 int형으로 반환하게 됩니다.
 
void GetSpinCurPosFloatToInt( float pos, float min, float max, int range )
{
   float pos_in_range = ( pos - min ) / ( max - min );
   int int_pos = ( int ) ( range * pos_in_range + 0.5 );
   return int_pos;
}
이 정도의 세팅을 하게 되면 소수점을 출력하고 Up/Down을 소수점 단위로 하는데는 문제가 없었습니다.
물론 위의 코드를 그대로 가져다 사용할 경우에는 별도의 수정을 약간 해줘야 합니다.
독립적인 구조를 위하여 편의상 전역변수나 여러개의 Spin 컨트롤에 대한 부분은 고려하지 않고 작업했습니다.
확실히 클래스 단위로 설정하는게 편하고 좋은 방법인데 여의치 않은 경우여서 필요한 부분만 추출해서 썼습니다.

 


 

반응형

댓글