C#에서 실시간 그래프를 그리다 보면 그리기 화면이 깜빡이는 현상을 볼수 있다.

이 문제를 해결하기 위해서는 그리기 할 대상을 double buffering 처리를 해 주면 깔끔하게 해결이 된다.

보통은 System.Windows.Forms::Panel위에 그리기 작업을 하게 될텐데, 혹은 Bitmap에 먼저 그린 후 그 Bitmap을 Panle에 그리는 방식이거나...

아무튼 이 경우 Panel을 double buffering 처리를 해 주면 된다.

문제는 Form은 화면 디자인 시점에 DoubleBuffered라는 속성 값을 true로 해 주면 되지만 Panel의 경우는 double buffering을 설정하는 메소드나 속성(property)이 막바로 접근이 안된다.

즉 protected 메소드이고 protected 속성이다. 


MSDN을 보면 속성의 정의가 아래와 같이 protected이고

protected virtual bool DoubleBuffered { get; set; }


메소드도 아래와 같이 protected이다.

protected virtual bool DoubleBuffered { get; set; }


따라서 Panel에 double buffering을 설정할려면 Panel을 상속받은 사용자 정의 클래스를 만들어서 이 사용자 정의 Panel을 사용해야 가능하다.


(1) Visual Studio의 해당 프로젝트명에서 마우스 우측 클릭 ⇒ 추가(Add) ⇒ New Item(새 항목) ⇒ Class를 선택 후 Class 이름 지정


(2) 아래의 내용을 추가한다.

    class DoubleBufferPanel : Panel

    {

        public DoubleBufferPanel()

        {

            this.SetStyle(ControlStyles.OptimizedDoubleBuffer | 

   ControlStyles.UserPaint |

                                ControlStyles.AllPaintingInWmPaint, true

                );


            this.UpdateStyles();

        } 

    }


(3) Form1.Designer.cs에서 기존의 Panel을 사용자가 정의한 Panel로 변경한다.




위의 적색 사각형 영역 (Windows Form Designer generated code 영역)을 더블 클릭하여 InitializeComponent() 안에 있는


this.panel1 = new System.Windows.Forms.Panel();

this.panel1 = new Bitmap_Panel.DoubleBufferPanel();

와 같이 사용자 정의 Panel로 변경해 준다.


이후 부터 화면이 깜빡이는 현상 없이 잘 처리 될 것이다.


MSDN이 소개하는 DoubleBuffered에 대한 설명이다.


Gets or sets a value indicating whether this control should redraw its surface using a secondary buffer to reduce or prevent flicker.





Visual Studio 2015 Community 버전 사용시 종종 보여지는 문제 중 하나가 새로운 Project를 열때(File - 새로 만들기 - 프로젝트) 

아래와 같은 템플릿이 보여야 되는데 대부분이 보이지 않고 Silverlight나 WPF 정도만 보이는 경우가 있다.

혹은 ASP.NET 웹 응용 프로그램 템플릿이 보이지 않는다거나...




정작 Windows Forms 응용 프로그램을 개발할 템플릿이 없어 난감한 경우를 만난다.

이 문제에 대해 Stack Overflow 등 여러 해법이 있어나 잘 작동 안되는 경우들이 있다.


가장 손쉬운 해법은


 ① Visual Studio 2015가 설치된 다음 폴더(<설치폴더>\Common7\IDE)로 이동한다. 만일 아래 경로에 설치되어 있다면 

     C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE

 ② 위의 폴더에서 projecttemplatecache와 itemtemplatecache 폴더 2개를 삭제한다. 이 2개의 캐시 폴더를 삭제하지 않고서는 devenv /installvstemplates 명령어가 효과가 없다.


 ③ 위의 폴더에서 커맨더 창(DOS 창)을 띄워 다음 명령어를 차례로 실행한다.

     devenv /installvstemplates.

     devenv /ResetSettings.


또 하나의 해법은 다시 설치를 통해 "수정(Modify)"을 선택하여 진행하다 보면 원하는 기능이 체크 해제 되어 있는 것을 발견하게 될 것이다.

이를 체크해서 다시 설치를 함을 통해 문제를 해결할수도 있다.


이 문제에 대해 아래 사이트 참조.


https://social.msdn.microsoft.com/Forums/vstudio/en-US/0dd15332-7744-44ea-85a8-5ec6b36febcc/visual-studio-2015-missing-project-templates?forum=visualstudiogeneral


http://stackoverflow.com/questions/32613505/missing-mvc-template-in-visual-studio-2015




프로그래밍을 공부하고 개발을 하다보면 질문을 할 일들이 있게 되는데 

이런 경우 커뮤니티 사이트가 도움이 된다.


아래는 C#을 공부하면서 방문해 볼 만한 커뮤니티 사이트이다.


http://www.hoons.net/







C언어에서 2차원 배열인 "포인터의 배열"에 대해서


char *ptr;

⇒ 이렇게 선언되면 임의의 문자열을 저장할수 있는 char형 포인터 변수가 만들어 진 것이다.

임의의 문자열 자체가 가로축으로 가변인 1차원 배열인 것이다.


ex) 

ptr = "aldjsldjflsj asldjf  eof 임의의 길이의 문자열들 어쩌고 저쩌고 owqieul sdlj sdlfj  fowieowei";


ex)

int char_size = 123; //임의로 문자열 길이(가로 축이 가변인)를 지정할 수 있다.

ptr = (char*)malloc(sizeof(char) * char_size);


그런데 char *ptr[7]은 가로 축으로 문자열을 저장하는 1차원 배열(char형 포인터)이 7개 있다는 뜻이므로 가로축이 가변이면서 세로축이 고정(여기서는 7)인 2차원 배열이 되는 것이다.


char *ptr[3]의 형태 자체가 char *ptr이 배열로 3개 존재한다는 뜻이 되므로 char *ptr 자체가 가로 축으로 가변인 1차원 배열인데 1차원 배열 자체가 여러 개(여기서는 3개) 존재하는 형태이므로 char *ptr[] 자체가 곧 2차원 배열이 되는 것이다.


한 마디로 요약하자면 char *ptr[]의 형태는 갯수가 정해져 있고 길이는 서로 다른 여러 개의 문자열을 다룰 때 주로 사용이 되는 형태이다.


아래 코드는 명령행에서 임의의 길이의 문자열 인자를 줄수 있는 코드이다.

여기서 char *argv[]가 "포인터의 배열"이다.


#include <stdio.h>


void main(int argc, char *argv[])

{

int i = 0;

printf("%d개의 매개인자 입력\n", argc);


for (i = 0; i < argc; i++)

{

printf("%d : %s\n", i, argv[i]);

}

}


위 코드의 실행 파일이 test.exe라고 할때 다음과 같이 실행한다면

C:\>test source1.txt dest1.txt


출력 결과는 다음과 같다.


3개의 매개인자 입력

0 : test

1 : source1.txt

2 : dest1.txt





구조체 변수와 구조체 포인터 변수의 멤버변수 접근법

다음과 같은 구조체가 있다고 할때


typedef struct

{

int price;

int count;

} Item;


Item item; //구조체 변수

Item *ptrItem; //구조체 포인터 변수

ptrItem = &item;


일반적인 구조체 변수가 있고, 구조체 포인터 변수가 있다.

일반적인 구조체 변수가 구조체 멤버 변수에 접근코자 할 때는 . 연산자로 접근하고

예) item.price = 100;


반면에 구조체 포인터 변수가 구조체 멤버 변수에 접근코자 할 때는 -> 연산자로 접근한다.

예) ptrItem->price = 100;


아래는 샘플 코드이다.


#include <stdio.h>


typedef struct

{

char name[20];

int price;

int count;

} Item;


void main()

{

Item item = {"수박", 100, 30}; //item은 구조체 변수

Item *ptrItem; //ptrItem은 구조체 포인터 변수


ptrItem = &item;


printf("%s\n", item.name);

printf("%d\n", item.price);

printf("%d\n\n", item.count);


printf("%s\n", ptrItem->name);

printf("%d\n", ptrItem->price);

printf("%d\n", ptrItem->count);

}





C 언어에서 enum을 사용하는 예제이다. 

코드가 복잡하지도 않으니 설명은 굳이...


주목할 부분은 

enum을 선언하는 방식과 

선언 위치,

그리고 enum을 사용할 때 어떤 식으로 사용하는지만 주목하면 될 것이다.

아래는 코드.


#include <stdio.h>


enum Member {JAVA, CPP, ANDROID, PHP, JSP, C, DELPHI, JAVASCRIPT};


void main()

{

enum Member mem = ANDROID; //초기 값 지정

int num;


printf("1: Java, 2:CPP, 3:ANDROID, 4:PHP, 5:JSP, 6:C, 7:DELPHI, 8:JAVASCRIPT");

printf("\n\n사용할 언어를 선택하세요 : ");

scanf("%d", &num);

--num; //enum은 index가 0부터 시작되기 때문에

mem = num;


switch (mem)

{

case JAVA:

printf("Java로 코딩하세요\n");

break;

case CPP:

printf("C++로 코딩하세요\n");

break;

case ANDROID:

printf("Android로 코딩하세요\n");

break;

case PHP:

printf("PHP로 코딩하세요\n");

break;

case JSP:

printf("JSP로 코딩하세요\n");

break;

case C :

printf("C로 코딩하세요\n");

break;

case DELPHI :

printf("Delphi로 코딩하세요\n");

break;

case JAVASCRIPT :

printf("JavaScript로 코딩하세요\n");

break;

}

}



프로그램 개발하다 보면 플로우 차트(순서도)를 그릴 일이 종종 있다.

근데 대부분, 평가판으로 맛보기 정도이고 어떤 것들은 불편해서 시간 잡아 먹는 하마가 되는데 아래 사이트는 정말 지금껏 사용해 본 것 중에서 최고이고 걸작이라 말할수 있겠다.


https://www.draw.io/


이미지들도 멋있을뿐 아니라 사용자 편의가 곳곳에서 확인된다.

또 저장하는 형태로 png, PDF, HTML, XML 등 다양하고 아무튼 사용해 보면 정말 잘 만들었다건 체감할수 있다.





#include <stdio.h>

#include <malloc.h>


struct MySMS

{

//이거 자체가 1차원 배열을 구성

//char형 포인터로 선언한 것은 실행 타임에서 메시지의 길이를

//동적으로 지정하겠다는 뜻임.

char *message;


//여기서 아래와 같이 배열로 입력 받을 문자열 갯수를 지정할수도 있으나

//이렇게 되면 메시지의 길이에 따라 메모리가 낭비되거나

//메시지 내용이 잘릴수 있다.

//char message[50];

};


void main()

{

int row;

int col;


//아래와 같이 선언하는 건 2차원 배열이라는 뜻

//2차원 배열 중에서 행도 가변, 열도 가변의 2차원 포인터가 되시겠다.

//혹은 이런 식도 가능 하겠다.

//struct MySMS arr[10]; //이렇게 하면 열의 갯수는 가변이나

//행의 갯수가 고정이 되는 형태인데 이를 '포인터의 배열'이라고 한다.

//arr[0].message = (char*)malloc(sizeof(char) * 10); //문자열 9개를 받겠다는 뜻

struct MySMS *obj = NULL;


printf("메시지 갯수(행의 갯수) 입력 : ");

scanf("%d", &row);


//2차원 배열 중 행(row)의 갯수

//배열과 달리 실행 타임에 동적으로 크기를 결정 가능.

obj = (struct MySMS *)malloc(sizeof(MySMS) * row);


printf("문자열 갯수(열의 갯수) 입력 : ");

scanf("%d", &col);


//입력한 수치 만큼 실제로 문자열(메시지) 입력 받기 위해

//맨 끝에는 문자열의 끝을 나타내는 널 문자(\0)가 들어가기 때문이다.

col++; 

obj[0].message = (char*)malloc(sizeof(char) * col);


//printf("사이즈 : %d\n", sizeof(char) * 6); //이건 6 byte

//printf("사이즈 : %d\n", sizeof(char*) * 6); //이건 24 byte


scanf("%s", obj[0].message);

printf("%s\n", obj[0].message);


//아래와 같은 순으로 반드시 메모리 해제를 해줘야 된다.

free(obj[0].message);

free(obj);

}


만일 위의 scanf("%d", &col);에서 입력한 col의 수치 보다 더 많은 문자열을 입력하게 하면 다음과 같은 에러 발생






안드로이드 6.0 마시멜로에서 변경된 중요 사항 중 블루투스, WiFi 권한 관련


블루투스 관련 앱이 정상적으로 검색과 연결이 잘 되던 것이 안드로이드 버전 6.0의 기기에서는 검색해 내지를 못하는 경험이 있을 것이다.

이것은 6.0에서의 정책상의 변화로 인해 발생하는 문제이다.

6.0에서 블루투스가 정상적으로 동작하기 위해서는 아래 권한이 Manifest에 선언되어 있으야만 한다.


ACCESS_FINE_LOCATION 혹은 ACCESS_COARSE_LOCATION 권한이 없으면 주변 WiFi / Bluetooth 디바이스를 발견할 수 없다.


위의 권한이 선언되어 있지 않으면 다음과 같은 메서드가 정상적으로 동작하지 않는다.


WifiManager.getScanResults()

BluetoothDevice.ACTION_FOUND

BluetoothLeScanner.startScan()

...


아래는 구글 안드로이드 API Reference 문서에서 소개하고 있는 내용이다.


Access to Hardware Identifier


To provide users with greater data protection, starting in this release, Android removes programmatic access to the device’s local hardware identifier for apps using the Wi-Fi and Bluetooth APIs. The WifiInfo.getMacAddress() and the BluetoothAdapter.getAddress() methods now return a constant value of 02:00:00:00:00:00.

To access the hardware identifiers of nearby external devices via Bluetooth and Wi-Fi scans, your app must now have the ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permissions:

Note: When a device running Android 6.0 (API level 23) initiates a background Wi-Fi or Bluetooth scan, the operation is visible to external devices as originating from a randomized MAC address.


https://developer.android.com/about/versions/marshmallow/android-6.0-changes.html





C언어에서 2차원 배열의 형태가 몇 가지 있는데


(1) 행(row)이 고정 갯수, 열(column)이 고정 갯수 일때

  • '2차원 배열'을 이용하면 되고
  • 예) int [3][5]


(2) 행(row)이 고정 갯수, 열(column)이 가변 갯수 일때

  • '포인터의 배열'을 이용하면 되고
  • 예) char *pArr[5]

(3) 행(row)이 가변 갯수, 열(column)이 고정 갯수 일때
  • '배열의 포인터'를 이용하면 되고
  • 예) char (*pArr)[10]


(4) 행(row)이 가변 갯수, 열(column)이 가변 갯수 일때

  • 2차원 포인터를 이용하면 된다
  • 예) char **ptr


아래는 배열의 포인터를 이용해서 2차원 배열에 값을 저장하고 출력하는 간단한 개념을 구현한 것이다.

이때 scanf라는 좀 독특한 성격의 함수에 어떻게 값을 입력해 주어 2차원 배열에 값을 저장하는지를 예제로 보여주는 코드이다.


#include <stdio.h>

#include <malloc.h>

#define LESSONS 3


int main(void)

{

int l, s;

int sum;

int students;


char *lssn[] = {"Korean", "English", "Math"};


//2차원 배열. '배열의 포인터'

//'배열의 포인터'는 2차원 배열 중에서 열(column)은 고정 갯수이고

//행(row)는 가변 갯수일 때 사용할수 있는 2차원 배열의 형태이다.

int(*score)[LESSONS] = NULL;


printf("학생 수를 입력하세요 : ");

scanf("%d", &students);


score = (int(*)[LESSONS])malloc(students * sizeof(int[LESSONS]));

//score = (int(*)[3])malloc(sizeof(int[LESSONS]) * students);


for (s = 0; s < students; s++)

{

printf("%d번째 학생의 점수 : ", s + 1);

for (l = 0; l < LESSONS; l++)

{

//scanf함수는 공백이 있는 곳까지 읽거나 엔터 키까지 읽거나

//혹은 탭문자까지 읽는 성질머리를 가진 함수이다.

scanf("%d", &score[s][l]);

printf("%d, %d ==> %d\n", s, l, score[s][l]);

}

}


printf("%d\t", score[0][0]);

printf("%d\t", score[0][1]);

printf("%d\n", score[0][2]);


free(score);

return 0;

}


위를 실행하면 다음과 같은 결과를 출력한다.

이때 for문 안에서 scanf 값을 11 22 33과 같이 값을 입력한 후 엔터키를 치면 안쪽 for문이 한 바퀴 돌면서 2차원 배열에 값을 저장한다.

만일 다음과 같이 해도 결과는 동일하다.

11

22

33

혹은 11(탭키)22(탭키)33(엔터)






+ Recent posts