스플래시 화면은 응용 프로그램이 초기화되는 동안 응용 프로그램을 처음로드 할 때 표시되는 이미지 또는 페이지입니다. 
스플래시 화면은 앱의 첫 페이지가 표시 될 때까지 계속 표시됩니다. 
Xamarin Forms에는 스플래시 화면을 추가 할 수있는 기능이 없으므로 플랫폼별로 이 작업을 수행해야합니다.

UWP

UWP는 이미지를 사용하여 스플래시 화면을 표시합니다. 다음 이미지로 이동하여 이 이미지를 배치 할 수 있습니다.

Project > package.appxmanifest > Visual Assets> Splashscreen

WinRT 응용 프로그램과 UWP 응용 프로그램 사이에는 스플래시 화면과 큰 차이가 있습니다. WinRT는 전체 세로 이미지를 스플래시 화면으로 배치했으나 UWP는 가로 이미지를 허용하지만 전체 화면을 차지하지 않습니다. 대부분의 앱이 세로형으로 시작하기 때문에 이미지를 전체 화면으로 가져올 수 없습니다.

UWP는 일반 배경색을 깔고 그 위 화면 중앙에 로고를 표시합니다. 이 작업을 수행하려면 가운데에 로고가 지정된 크기로 이미지를 만들고 선택한 배경색을 단색으로 만듭니다. 그런 다음 화면의 배경색 옵션을 아래 예와 동일한 단색 배경색으로 설정하십시오.

UWP 시작 화면 설정

그림과 같이 스플래시 화면이 나타납니다.

tesla_splashscreen

UWP에서 이미지로 전체 화면을 채우는 완전히 사용자 정의 가능한 스플래시 화면을 만들려면 확장 스플래시 화면 만들기 글을 참고하세요.



Android

Android의 시작 화면은 새로운 액티비티를 만들어야 하므로 더 복잡합니다. Xamarin.Android를 처음 사용하는 경우 Activity는 사용자가 정상적으로 상호 작용할 수있는 단일 UI입니다. Xamarin Forms는 MainActivity를 사용하여 애플리케이션을 로드하고, 다른 Activity를 생성합니다. 이 예제는 여러분이 AppCompat 를 사용하고 있다고 가정합니다. 이것은 새로운 모든 안드로이드 어플리케이션에 권장됩니다.

  1. 적절한 풀다운 폴더에 전체 화면 이미지 (예 : splashscreen.png)를 배치하십시오. Drawable 폴더는 다음 크기와 연결됩니다.

    • MDPI는 320 x 480 dp = 320x480px (기본값 x1)입니다.
    • LDPI는 0.75 x MDPI = 240x360 픽셀입니다.
    • HDPI는 1.5 x MDPI = 480x720px입니다.
    • XHDPI는 2 x MDPI = 640x960 픽셀입니다.
    • XXHDPI는 3 x MDPI = 960x1440 픽셀입니다.
    • XXXHDPI는 4 x MDPI = 1280x1920 픽셀입니다.
  2. 이 새 이미지를 참조하는 새 스타일을 만듭니다. 이것을 Resources> values> styles.xml (Build Action : Android Resource)에 위치 시키 십시오.
    <?xml version="1.0" encoding="utf-8" ?>
    <resources>
      <style name="splashscreen" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="android:windowBackground">@drawable/splashscreen</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowIsTranslucent">false</item>
        <item name="android:windowIsFloating">false</item>
        <item name="android:backgroundDimEnabled">true</item>
      </style>
  3. 새 클래스 만들기 SplashActivity.cs

    Public class SplashActivity : AppCompatActivity { protected override void OnResume () { ActivLayout = true, NoHistory = true) public class SplashActivity : AppCompatActivity { { base.OnResume (); StartActivity (typeof (MainActivity)); } }

  4. MainActivity.cs 를 열고 MainLauncher = true 를 MainLauncher = false 로 변경하십시오.

더 나은 접근법

다른 방법을 통해보다 빠른 경험과로드 시간을 제공 할 수 있습니다.

  1. Resources> Values> styles.xml에서 스플래시 스크린 테마를 만듭니다.
    <?xml version="1.0" encoding="utf-8" ?>
    <resources>
      <style name="splashscreen" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="android:windowBackground">@drawable/splashscreen</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowIsTranslucent">false</item>
        <item name="android:windowIsFloating">false</item>
        <item name="android:backgroundDimEnabled">true</item>
      </style>
  2. MainActivity.cs로 가서 MainLauncher = true 인지 확인 하십시오 . MainLauncher = true 인 다른 Activity가 없는지 확인하십시오.
  3. MainActivity.cs에서 기본 테마를 스플래시 화면으로 변경하십시오.
    [Activity(Label = "Mobile App", Theme = "@style/splashscreen", Icon = "@drawable/icon", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation, LaunchMode = LaunchMode.SingleTop)]
     public class MainActivity : Xamarin.Forms.Platform.Android.FormsAppCompatActivity
  4. OnCreate에서 아래 코드로 테마를 전환하십시오.
    protected override void OnCreate(Bundle bundle)
    {
        base.Window.RequestFeature(WindowFeatures.ActionBar);
        // Name of the MainActivity theme you had there before.
        // Or you can use global::Android.Resource.Style.ThemeHoloLight
        base.SetTheme(Resource.Style.MainTheme);
    
        base.OnCreate(bundle);
    
        ...

9- 패치 이미지

밀도와 해상도를 변경하면 Android 용 스플래시 화면 이미지에 문제가 발생합니다. Xamarin Android 9-Patch Image Splashscreen 을 보시고 Android Splash 스크린 용 9 패치 이미지 를 만드십시오.

iOS

iOS는 응용 프로그램이 실행되고있는 화면 크기에 따라 이미지를 사용하여 스플래시 화면을 표시합니다.

  1. 다음과 같은 크기의 스플래시 화면 이미지를 만듭니다.
    1. 320 × 480
    2. 640 × 960
    3. 640 × 1136
  2. Properties> iOS Application> iPhone Launch Images로 이동하여 이미지를 가져 오십시오. iOS 프로젝트  Resources 폴더에 자동으로 추가됩니다 .
    ios_splashscreen_settings
  3. 속성 페이지를 아래로 이동하여 iPad 앱용 이미지를 추가하십시오.
  4. 이미지가 계속 표시되는 데 문제가있는 경우 가능한 원인이 몇 가지 있습니다. LaunchScreen.storyboard가 문제 일 수 있습니다. 열어서 수정하거나 심지어 삭제할 수도 있습니다. 이 작업을 완료하면 애플리케이션을 재구성하고 iOS 시뮬레이터를 재설정하거나 iOS가이 영역에 광범위하게 캐시되는 경향이 있으므로 iPhone에서 삭제하십시오.


원문 : https://xamarinhelp.com/creating-splash-screen-xamarin-forms/

지난주말 리갈코인을 무료가입 해두고 몇일이 흘렀다.

상위 추천인분을 잘만난 덕에 내 오른쪽 라인으로 열명이 넘게 밑으로 달렸더라.

그리고 너무 당연하게도(?) 나의 현재 수익은 0이다. 모두 나처럼 무료가입만 해두고 실제 리갈코인 보유를 하지 않으면서 간을 보고 있는 상황이라 그럴것이다.

그래서 어떤식으로 수익이 난다는것인지 (아주 조금) 공부를 해보았다.
(공부라고 해봐야 리갈코인 홈페이지에 소개된 내용을 읽어본 것 뿐이지만)

우선 리갈코인은 비트코인과 같이 블록체인 기법으로 만들어진 가상화폐이다.

이 가상화폐를 가지고 3가지의 방법으로 수익을 창출하는데 다음과 같다.
1. Trading : 사고팔고 시세차익으로 인한 수익
2. Staking : 예금과 같이 보유하고 있으면 발생하는 수익(연10%)
3. Lending : 대출해주고 받는 수익 월45%, 연540% (??? 고리대금업자??)

일단은 홈페이지에 나와있는 내용이 그렇다는 것이다.

그리고 여기에 지난번에 살짝 언급한 '다단계' 보상 시스템이 더해져있다.
아마 이 리갈코인만 그런것이 아니고 이런류의 비슷한 서비스들이 다들 취하는 모델인 것 같다.

추천인을 좌, 우로 붙여 늘려가면서 얻는 수익인데 이것도 두가지가 있다.
1. 직접추천인에 의한 단계별 수익쉐어 : 1대 7%, 2대 2%, 3대 1%
2. 추천인 네트워크트리에서 발생하는 바이너리 수익 : 1000$이상부터 2%~최대 5%, 주 최대 $21,000

일단 가장 관심을 가질수 있는것은 대출금리를 받는것(연540%)과 네트워크로 발생하는 부가수익이 될 것 같다.

리갈코인에서 어떤식으로 본인들의 자산을 운용하는지는 모르겠으나 항상 3일분의 수익률이 공개되고 있다. 가입 후 몇일간 지켜봤는데 하루수익률이 항상 1.2~3% 대로 표시되고 있었다. 물론 나는 아직 무료회원으로 랜딩을 하지 않았으니 실제로 그렇게 늘어났는지 확인할 방법은 아직 없다.

아무튼 이건 리갈코인을 구매해봐야 알겠지만 이렇게 초창기에는 당분간 상승세를 이어가지 않을까 싶긴 하다. 평균 1.2%씩 365일이면 무려 438%라는 수익률이 나오는데 1년적금이 기껏해야 3%도 안주는걸 생각해보면 무시무시한 이율이 아닐수 없다. 다른세계같은 느낌이랄까? 와닿지는 않지만 일단 그렇다고 한다.

이렇게 원하는 금액대로 리갈코인을 교환해서 보유하고 있으면 이것이 바로 투자금이 되는 것이고 이제부터는 무료회원이 아닌 유료회원으로 위의 5가지 수익발생으로 얻어지는 이익을 가져갈 수 있게 되는 것이다.
또한 홈페이지상의 주장으로는, 99일이면 투자금 회수가 가능하다고 하니 기간도 길지 않다.

비트코인을 구매하고 리갈코인으로 교환해 렌딩하는 과정은 다음번에 직접 진행하면서 정리해야겠다.

네트워크로 발생하는 부가수익은 내가 이런 다단계 시스템에 익숙치 않아서 이해하기가 쉽지 않았다.
일단 본인의 직접추천 1대, 2대, 3대의 유료매출(렌딩)을 쉐어하는건 계산이 단순하니 문제가 안된다.
네트워크 트리에서 발생하는 수익을 '바이너리'라고 하는데, 이 바이너리가 상당히 재미있는 놈이란다.

일단 나로 인해 가입된 추천인이 들어오면 이 사람을 내 왼쪽에 둘지, 오른쪽에 둘지 정하게 된다.
이걸 정해야 하는 이유는 밑으로 계속 이어지는 좌, 우측 가지에서 발생하는 유료매출의 밸런스를 맞춰가기 위해서인데, 각각의 가지에서 1000$ 이상의 매출이 발생되는 순간부터 바이너리수익이 발생하기 시작한다.

쉬운 설명을 위해 우선 현재 내 네트워크트리를 보면

내 오른쪽 라인으로는 계속해서 다른 사람이 붙고있다. 이건 내 위에서 오른쪽 라인을 지원해 주시기 때문에 계속 아래로 붙을 것이다. 지금 역시 대부분 무료회원이다. 이쪽에서 현재 100의 매출이 발생했음을 알 수 있다.

그리고 MADEINBT는 내가 붙여본 부계정이다. 왼쪽의 매출은 0이다.

이제 또 다른 사람이 나를 추천인으로 해서 가입을 하게 되면 내가 왼쪽, 오른쪽을 골라서 배치해 주면 된다. 지금은 내 윗선에서 계속 오른쪽 라인을 만들어 주고 있으니, 나는 왼쪽라인만 신경쓰면 된다.

내일 오른쪽 라인에서 2000의 유료매출이 일어날 예정이라고 했다. 그럼 지금 100이니 2100이 될 것이다.
그런데 내가 직접 배치한 왼쪽라인의 매출은 0이다. 그러면 나는 직접추천수익도 없고 바이너리수익도 없다. 내가 MADEINBT 에 1100$를 투자해서 왼쪽1100 : 2100오른쪽 이 된다면 1000이상부터 바이너리수익이 나오니까 둘 중 더 작은쪽에서 1000을 빼고 그 나머지 차액(100)에서 2%인 2$가 발생한다.

이 바이너리 수익은 누적되므로 한 번 발생하면 끝이다.

그다음날 위에서 키워주는 오른쪽라인에서 매출이 더일어나서 1100 : 3500이 되었지만 이미 작은쪽 1100에 대한 수익은 받은 상태이므로 바이너리수익은 다시 0이다.

다시 열심히 왼쪽에 몇명을 붙이고 거기서 유료매출이 나와서 1500 : 3500이 되었으면, 마찬가지로 작은쪽(1500)에서 이미 지난번 정산했던 1100을 뺀 나머지 400에 대한 2%로 8$가 발생하는 것이다.

이 네트워크트리는 한도끝도없이 이어질 수 있으므로 왼쪽, 오른쪽에 많은 추천인이 붙을수록 합산누적매출은 커질 것이고 그때마다 수익이 발생하게 된다.

뭐 난 아직 한번도 이런 네트워크를 경험해 보지 못해서 섣불리 판단할 수는 없다. 내 바로 윗분은 이 바이너리 매출이 가장 쏠쏠하다고 하신다. 뭐 하루 600$가 됐다나 -0-

아무튼 위에서 계속해서 오른쪽을 키워주고 있으므로 내 추천인으로 들어오시는 분들도 오른쪽라인은 계속해서 붙을 것이다. 결국은 왼쪽에서 얼만큼의 매출이 나와주는가에 따라 내 수입이 생긴다고 보면 될것 같다.

어쨋든 바이너리는 그렇다 치고, 1000$를 넣었을때 하루에 12~13$씩 불어나는것만 해도 어마무시할 것 같고,, 모든 다단계가 그러하듯, 윗쪽에 있는 사람은 어쨋건 떼돈을 번다.

이 리갈코인은 아직까지는 상위 자리를 선점할 수 있는 기회가 있는, 생긴지 얼마안되는 넘이니 일단 무료가입 해서 자리만 맡아두고, 밑에 추천인을 붙여가다가 바이너리 수익이 어느정도 생기면 그때 100$ 렌딩해서 수익을 얻어가면 나쁘지 않을 것 같다는 생각이다.

아래 링크로 가입하면 오른쪽라인은 내 윗대에서부터 계속해서 책임져줄 것이다.

https://regalcoin.co/ref/LOTIONY

회원가입에 대한 포스팅은 따로...

한 두어시간 가상화폐관련해서 서칭을 하다보니 비트커넥트코인이란곳을 알게되었다.

여전히 아무것도 모르겠지만 대충 보니 최소 $100 넣어두면 일단위로 수익금을 준다는것 같다. (http://blog.naver.com/minbaeo_o/221101451688)

11만원정도야 머 날려도 그만인셈 치고 한번 해볼까 싶어서 우선은 회원가입을 했다. 그리고 친절한 블로그 설명대로 너무 쉽게 2단계 인증까지 마치고, 그다음에 100$를 invest 해보려고 했는데? 안된다.

비트커넥트 지갑에 돈이 없단이야기라서 블로그 글을 조금더 디테일하게 읽어보니 국내거래소에서 비트코인을(비트캐쉬코인을?) 사서 그걸 여기로 옮겨와서 뭐 어쩌고~~~

아, 나는 그냥 이 사이트에서 페이팔이던 신용카드로 결제해서 충전해서 하는가보다 했는데 (그래야 쉬운것 아닌가;;) 빗썸에서 또 비트코인지갑을 만들고 그걸 딱 $100어치만 구매하고 이쪽으로 옮기고 그래야 되는건가? 일단 이렇게 되니 갑자기 하기가 싫어졌다...

공부도 안했고 시간투자도 안했고 아무것도 모르면서 11만원 없는셈치고 한번 깜깜이로 넣어보려 했으나 그렇게 해서 될 문제는 아닌건가보다.

에라 됐다 그냥 말지 뭐 하면서 너무 한 블로거님 글만 보고 쉽게 포기해버리나 싶어서 비트커넥트코인으로 다시 한번 검색해보니 이건 또 이제 다큰넘이라서 하지말란 글도 보이네.

그러면서 2017년 10월에 런칭한 리갈코인? 을 알게 됐다. 음.. 이 블로거도 나처럼 뭘 잘 모르는 상태에서 시작한것같애. 2017년 10월 13일이면 일주일전이네.. 여기 무료회원으로 돈 안넣고 그냥 등록만 했는데 4일뒤에 몇십달러가 수익이 나왔어? ㅋ 대충 보니까 전형적인 다단계방식이 결합되었다. 왼쪽 오른쪽 가지치기를 균형있게 맞춰야 돈 많이 주는.. 또 상위 스폰서가 가입승인을 해줘야 내가 활동을 할수 있는 구조.

어차피 오늘 뭐라도 좀 해볼라 했던 김에 더 알아보지도 않고 일단 가입했다. (https://regalcoin.co/)

음. 역시 소개해준사람이 계정활성화를 해줘야된다고 나온다. 밤이 늦었으니 활성화는 내일 부탁해보고 오늘은 일단 여기까지..

혹시나 이 글을 보는 분들께서 나를 최소한의 노력도 안하고 뭔가를 얻어가려는 인간으로 보실수도 있겠다 뭐, 반은 맞고 반은 틀리고.. 도박이나 별다를거 없다고 생각되는 이쪽에 그다지 내 시간과 노력을 들이고 싶지도 않고 다른사람들이 블로그 올린것마냥 진짜 돈을 벌거라고 기대하지도 않고.

다만그냥 다들 열광하는데 나만 너무 무관심에 시대의 흐름에 적응못하고 뒤떨어져가는가 싶어서 대충 발이라도 담궈볼까 하는 마음이 크다. 운좋게 몇푼 건지면 다행이고 아님 말고..

내주위엔 가상화폐에 관심가지는 사람이 단한명도 없고 나와 비슷한 생각이신 분들도 계실것이다.

몇일 지켜보면서 어떤 일이 일어나는지 다시 포스팅 할텐데 구경들 해보세요 ㅎ 제가 11만원 버리는셈 치고 대신 실험해드릴께요

아참, 내 상위 스폰서분한테 낼 활성화 연락해야 하니 남겨둬야지 http://blog.naver.com/shindlsdt/221116416535

참, 명색이 IT로 밥먹고 사는 사람이 이런건 또 왜이렇게 무관심이었는지

아니 비트코인이나 이더리움같은 가상화폐에 대해서는 진작에 들어서 알고 있었지만

블록체인이 도대체 뭔 개념인지도 모르겠고 그래픽카드 빵빵하게 물려서 채굴을 해서 돈을 번다질 않나..


어쨋든 오늘 목포 출장다녀오는 SRT에서 심심해 하던차에 단톡방에 한넘이 올려준 링크 보고 드디어 빗썸(https://www.bithumb.com/) 회원가입을 했다.

수십개월을 외면해왔던 가상화폐 시장에 첫 발을 들여놓게 잡아끈것은 다름아닌 '로그인하면 1퀀텀(1만4천원정도) 무상지급' 이벤트 ㅎ

 


요넘의 치킨값 1만4천원덕에 가상화폐쪽에 발을 들여놓게 되는구나.

빗썸 회원가입해서 준 1,000포인트로는 '리플'이라는 제일싼 코인 4개정도를 살수 있었다. 


도대체 이걸 사고팔아서 뭘하겠다는건가는 아직도 잘 모르겠다.

이런건 또 구글링보단 네이버한테 물어보는게 나은가 싶어서 검색을 좀 해봐도 답변이라는게 죄다

"가상화폐 투자하시려면 공부를 해라"라던가 "관련된 자료는 조금만 찾아보시면 많이 나와요" 따위가 대부분이다

뭐 친절하게 잘 설명된걸 기대한것은 아니지만 조금만 찾으니까 죄 쓸데없는 자료만 나오는데 뭘가지고 많이 공부를 하징;;


아직 그정도 노력을 들일 정도로 관심이 있지는 않아서 그러겠지 뭐


 

 

 

[.NET] 파일 이름 변경, 복사할 때 동일한 파일이 있으면 자동으로 넘버링 해 주기.

 

 

윈도우 탐색기에서 파일 복사하기. 많이 쓰이는 기능입니다.

윈도우 탐색기 파일 복사의 특징 중 하나는 동일한 파일을 동일한 위치에 복사&붙여넣기를 계속 시도하면

 

"파일명 - 복사본"

"파일명 - 복사본 - 복사본"

"파일명 - 복사본 - 복사본 - 복사본"

 

이와 같이 자동으로 " - 복사본" 이라는 명칭을 덧붙여 새로운 사본을 생성시킨다는 것을 알고 계시죠.

 

폴더의 경우, '새 폴더' - '새 폴더(1)' - '새 폴더(2)' - '새 폴더(3)'  과 같이 순차적으로 번호매김을 해 줍니다.

 

이것과 유사한 기능을 하는 함수입니다.

닷넷에서 임의로 디스크의 파일을 복사하거나 이동하거나 이름바꾸기를 할 때 해당 파일의 존재 여부를 확인하고 자동으로 적절한 넘버링을 붙여주는 함수입니다.

 

 

VB.NET 코드 :

 

    Public Shared Sub RenameFileExt(srcFileNM As String, ByRef tgtFileNM As String)
        Dim count As Integer = 1
        Dim FileNameOnly As String = System.IO.Path.GetFileNameWithoutExtension(tgtFileNM)
        Dim Extension As String = System.IO.Path.GetExtension(tgtFileNM)
        Dim path As String = System.IO.Path.GetDirectoryName(tgtFileNM)
        Dim newFullPath As String = tgtFileNM

        While File.Exists(newFullPath)
            count += 1
            Dim tmpFileNM As String = String.Format("{0} ({1})", FileNameOnly, count)
            newFullPath = System.IO.Path.Combine(path, tmpFileNM + Extension)
        End While

        Try
            File.Move(srcFileNM, newFullPath)
            tgtFileNM = newFullPath
        Catch ex As Exception
        End Try

    End Sub

 

 

C#.NET 코드 :

 

public static void RenameFileExt(string srcFileNM, ref string tgtFileNM)
{
	int count = 1;
	string FileNameOnly = System.IO.Path.GetFileNameWithoutExtension(tgtFileNM);
	string Extension = System.IO.Path.GetExtension(tgtFileNM);
	string path = System.IO.Path.GetDirectoryName(tgtFileNM);
	string newFullPath = tgtFileNM;

	while (File.Exists(newFullPath)) {
		string tmpFileNM = string.Format("{0} ({1})", FileNameOnly, count++);
		newFullPath = System.IO.Path.Combine(path, tmpFileNM + Extension);
	}

	try {
		File.Move(srcFileNM, newFullPath);
		tgtFileNM = newFullPath;
	} catch (Exception ex) {
	}

}

 

 

인수로 주어지는 타겟파일명을 ByRef로 한 이유는, 함수 내부에서 타겟파일명이 변경될 수 있기 때문입니다.

RenameFileExt("C:\111.txt", "C:\111.txt") 라고 넣는다면, 소스와 타겟이 동일하므로

C:\111 (1).txt 라는 파일로 타겟파일명을 바꿔주게 되겠죠. 이 최종 파일명 정보를 리턴하는 것입니다.

 

File.Copy 메서드로 바꿔서 사용하면  윈도우 탐색기의 복사 기능과 비슷한 로직으로 사본을 생성하게 됩니다.

저는 Rename을 위해 File.Move메서드를 사용했습니다.

 

많이 활용하시길...

 

 

 

TAB to XLSX.exe

 

 

[vb.NET] Tab 파일을 XLSX로 변환

 

 

TAB 형식의 파일을 Excel 2007 / 2010 / 2013용 XLSX 파일로 변환해 주는 간단한 유틸입니다.

필요에 의해서 만들어 보았습니다.

웬지 있을것 같아서 10여분 서치해 봤는데 딱 맘에들게 기능하는게 안찾아져서..그냥 후딱 만들었습니다.

파일들 또는 tab 파일이 들어있는 폴더채로 드래그하여  폼에 드롭하면 tab파일 옆에  xlsx로 일괄 변환이 됩니다..

아래 보시다시피 별다른 옵션이나 기능은 없습니다^^;

 

 

 

닷넷프레임워크2.0과  엑셀2007 이상이 설치되어 있으면 됩니다..

조금 변형하면 csv, txt 파일등도 같은방식으로 변환할 수 있을 것 같습니다.

 

 

 

 

Imports System.IO
Imports XL = Microsoft.Office.Interop.Excel

Public Class frmMain
    Private xApp As XL.Application
    Private xWB As XL.Workbook
    Private xWS As XL.Worksheet

    '******************************************************************************************************************************************************
    ' 파일드롭 처리
    '******************************************************************************************************************************************************
    Private Sub frmMain_DragDrop(sender As Object, e As System.Windows.Forms.DragEventArgs) Handles Me.DragDrop
        Dim FileNames As String() = e.Data.GetData(DataFormats.FileDrop, False)

        For Each fNames As String In FileNames

            Dim fInfo As DirectoryInfo = New DirectoryInfo(fNames)
            If fInfo.Exists Then
                Dim fList As FileInfo() = fInfo.GetFiles()

                For Each fItem As FileInfo In fList
                    If fItem.Extension = ".tab" Then
                        Try
                            tabtoxlsx(fItem.FullName)
                        Catch ex As Exception
                        End Try
                    End If
                Next
            Else

                Dim fin As FileInfo = New FileInfo(fNames)
                If fin.Extension = ".tab" Then
                    Try
                        tabtoxlsx(fNames)
                    Catch ex As Exception
                    End Try
                End If

            End If

        Next

        MessageBox.Show("파일 변환이 완료되었습니다.")
    End Sub

    Private Sub frmMain_DragOver(sender As Object, e As System.Windows.Forms.DragEventArgs) Handles Me.DragOver
        e.Effect = DragDropEffects.All
    End Sub

    Private Sub frmMain_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        xApp = New XL.Application
        'xApp.Visible = True
        xApp.IgnoreRemoteRequests = True
        xApp.ScreenUpdating = False
        xApp.DisplayAlerts = False
    End Sub

    Private Sub frmMain_Disposed(sender As Object, e As System.EventArgs) Handles Me.Disposed
        Try : xWB.Close(False) : Catch ex As Exception : End Try
        Try
            xApp.ScreenUpdating = True
            xApp.DisplayAlerts = True
        Catch ex As Exception : End Try
        Try : xApp.IgnoreRemoteRequests = False : Catch ex As Exception : End Try
        Try : xApp.Quit() : Catch ex As Exception : End Try
    End Sub


    '******************************************************************************************************************************************************
    ' 탭파일을 열어 엑셀파일로 저장. 형식은 모두 텍스트형식으로 읽는다.
    '******************************************************************************************************************************************************
    Private Sub tabtoxlsx(tabFileNM As String)
        Dim ColArray(0 To 1000, 0 To 1)
        For x = 0 To 1000
            ColArray(x, 0) = x + 1
            ColArray(x, 1) = 2
        Next

        xApp.Workbooks.OpenText(tabFileNM, Origin:=949, StartRow:=1, DataType:=XL.XlTextParsingType.xlDelimited, Tab:=True, FieldInfo:=ColArray)
        xWB = xApp.Workbooks(1)
        xWS = xWB.Sheets(1)

        xWS.Cells.Font.Size = 10
        With xWS.Rows(2)
            '.Interior.Pattern = XL.Constants.xlSolid
            .Cells.Interior.Color = 14211289
            .Font.Bold = True
        End With
        xWS.Rows(1).Delete(XL.XlDirection.xlUp)

        xApp.ActiveWindow.SplitRow = 1
        xApp.ActiveWindow.FreezePanes = True

        xWB.SaveAs(tabFileNM.Replace(".tab", ".xlsx"), XL.XlFileFormat.xlWorkbookDefault)
        xWB.Close(False)

    End Sub

End Class

 

 

 

 

 

[LocalDB] SQL2012 LocalDB의 Collation 문제 해결

 

 

MSSQL 2012의 LocalDB 사용 중 Join, Where명령에 대해서 아래와 같은 오류가 발생했다.

 

Cannot resolve the collation conflict between SQL_Latin1_General_CP1_CI_AS" and "Korean_Wansung_CS_AS" in the equal to operation.

 

 

데이터베이스의 데이터정렬 형식이 서로 맞지 않아서 작업을 수행할 수 없다는 메시지이다. 

왜 이러한 현상이 나타났는가?

MSSQL 2012  LocalDB 인스턴스는 기본적으로 'SQL_Latin1_General_CP1_CI_AS' 형식으로 셋팅되며 이를 변경할 수 없다.

 

http://technet.microsoft.com/en-us/library/hh510202.aspx

 

... 

The instance collation for LocalDB is set to SQL_Latin1_General_CP1_CI_AS and cannot be changed. Database-level, column-level, and expression-level collations are supported normally. Contained databases follow the metadata and tempdb collations rules defined by Contained Database Collations.

....

 

 

그래서 LocalDB에 데이터베이스를 생성 할 때에 옵션의 데이터정렬값을 설정해 주지 않으면 기본값인 SQL_Latin1_General_CP1_CI_AS 로 설정된다.

이 데이터정렬셋은 한글 데이터가 들어오면 ???? 과 같이 깨져서 들어오고  제대로 처리를 해주지 못한다.

데이터베이스에서 한글을 쓰려면  데이터베이스 생성시 Korean_Wansung_CS_AS 등으로 선택해 주어야 한다.

 

물론 데이터베이스가 생성된 이후에도  데이터정렬값을 바꿔줄 수는 있는데, 그 전에 생성한 테이블의 컬럼값들 중 string형(nVarchar, varchar 등) 컬럼들은  여전히 SQL_Latin1_General_CP1_CI_AS 로 설정되어 있고, 데이터정렬 설정 이후에 만들어진 테이블, 컬럼에만 Korean_Wansung_CS_AS 이 적용된다.

 

이 포스팅에서는 이미  SQL_Latin1_General_CP1_CI_AS 로 만들어진 테이블과 컬럼의 Collation 설정을 변경하는 방법을 알아본다.

 

 

 

 

sp_help TABLENAME

 

테이블의 정보를 조회할 수 있다. 두번째 결과테이블에 Column리스트업되고 Collation값을 확인할 수 있다.

숫자형 컬럼은 NULL 이고 문자열타입 컬럼에만 현재 Collation값이 보여진다.

 

 

이제 변경할 컬럼을 찾았으니 값을 변경해 준다.

 

ALTER TABLE T_TYPE
ALTER COLUMN 유형명 NVARCHAR(800) COLLATE Korean_Wansung_CI_AS;

ALTER TABLE T_TYPE
ALTER COLUMN 유형텍스트 NVARCHAR(1000) COLLATE Korean_Wansung_CI_AS;

ALTER TABLE T_TYPE
ALTER COLUMN USE_YN NVARCHAR(5) COLLATE Korean_Wansung_CI_AS;

 

 

Collation이 잘못 들어간 다른 테이블의 컬럼도 모두 찾아서 위와 같은 방법으로 바꿔준다.

 

물론 데이터베이스를 생성할 때 미리 Collation값을 지정해 줬다면 위와 같이 번거로운 작업을 하지 않아도 된다.

 

[.Net] Datatable을 Access에 Insert하는 방법. (Bulk Insert)

 

 

.NET과 Access를 가지고 놀던 중 대량의 엑셀 데이터를 엑세스DB로 업로드 하는 괜찮은 방법을 찾아서 혹시 필요하신 분이 계실까 하여 포스팅합니다.

 

 

vb.net 기준이고 엑셀 문서를 datatable로 한방에 읽어오는 방법은,,

닷넷에서 기본 지원하는 Oledb를 이용하면 간단하므로 생략하겠습니다.

DataAdapter를 만들어 Fill을 해도 되고

ExcuteReader로 읽어와도 되고..  여튼 엑셀을 datatable로 만드는것은 간단합니다.

 

여기서 From Source를 Datatable로 한 것은, 비단 엑셀 뿐 아니라 xml이나 txt, 기타 sql서버 등 여러 가지 다른 소스를 대상으로 하여도 동일한 사용성을 갖게 하기 위해서 입니다.

엑셀파일만 대상으로 엑세스로 덤프하는것은 http://cafe.naver.com/xlsvba/1101  이 방법이 더 간단합니다.

 

 

여튼, .NET의 알짜배기인 Dataset, DataTable을  이용합니다.

 

 

처음에는 그냥 단순하게 아래의 방법으로 인서트를 했습니다.

 

'***** 데이터를 업로드한다. 
For i = 0 To OrgDataTable.Rows.Count - 1 
    Application.DoEvents() 

    qq.Clear() 
    qq.AppendLine(" INSERT INTO [Data]") 
    qq.AppendLine(" ([이름], [주민등록번호])") 
    qq.AppendLine(" VALUES") 
    qq.AppendLine(" (") 
    qq.AppendLine("     '" + OrgDataTable.Rows(i).Item(0).ToString.Trim + "'") 
    qq.AppendLine("     ,'" + OrgDataTable.Rows(i).Item(1).ToString.Trim + "'") 
    qq.AppendLine(" )") 

    Try 
        dbCon.aceRS.Close() 
    Catch ex As Exception 
    End Try 
    dbCon.aceRS.Open(qq.ToString, dbCon.aceDB, 1) 
    'System.Threading.Thread.Sleep(20) 

    loFunctions.UpdateProgress(ProgressBar1, lbl_Progress, (i + 1), (i + 1).ToString + " / " + OrgDataTable.Rows.Count.ToString) 
Next

 

 

지극히 평범한 방식이죠. 오리지널을 순서대로 돌면서 한줄한줄 인서트 하는 방법.

 

10만여개의 이름,주민등록번호 데이터를 넣는데도 10분이 훨씬 넘는 시간이 걸리더군요.

 

 

그래서 다른 방법을 구글링해서 아래 소스를 발견하고 적용해 보았습니다. DataAdapter를 사용하는 방법이죠.

http://www.codeproject.com/Articles/17028/Bulk-Record-Insert-for-Access

벌크 인서트에 관해서는 대부분 저 링크가 걸려있더군요.

많은 샘플소스들이 이렇게 DataAdapter를 활용해서 인텔리전스한 Update메서드를 사용하는걸 추천했습니다.

그런데 저대로는 잘 안되었습니다. 왜인가 이유를 살펴보니, DataAdapter의 Update메서드는

지정된 DataTable을 훌륭하게 동기화 해주기는 하지만 그 이전에 DataTable의 각각의 Rows객체의 RowState를 보고

Insert인지, Update인지, Delete인지를 판단해서 해당 커맨드를 실행하니까요.

지금 하려는 것은 원본데이터를 몽땅 Insert하는 것이므로 DataTable의 모든 Rows의 rowstate는 RowAdded 값을 가져야만 합니다.

 

그래서 아래와 같이 해봤습니다.

 

Dim oConn As OleDbConnection = New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + AppDomain.CurrentDomain.BaseDirectory + My.Settings.DBFileNM + ";") 
Dim SQL As String = "SELECT * FROM Data WHERE 0=1" 
Dim insSQL As String = "INSERT INTO Data ([이름], [주민등록번호]) VALUES (@이름, @주민등록번호)" 
Dim oAdpt As OleDbDataAdapter = New OleDbDataAdapter(SQL, oConn) 

For Each dRow As DataRow In OrgDataTable.Rows 
    dRow.AcceptChanges() 
    dRow.SetAdded() 
Next 

oAdpt.InsertCommand = New OleDbCommand(insSQL, oConn) 
oAdpt.InsertCommand.Parameters.Add("@이름", OleDbType.Char, 255, "이름") 
oAdpt.InsertCommand.Parameters.Add("@주민등록번호", OleDbType.Char, 255, "주민등록번호") 
oAdpt.Update(OrgDataTable) 


 

 

 

DataTable의 모든 Rows를 순회하면서 임의로 RowState값을 RowAdded로 설정했습니다. 그래야 몽땅 Insert 적용되지요.

그리고 실행해보니 업로드는 잘 됩니다.

그러나 웬걸,, 시간이 아까보다 더 많이 걸리네요.

하도 오래 걸리기에 어느부분에서 시간이 걸리나 봤더니 oAdp.Update(OrgDataTable) 에서  하염없이.. 못넘어갑니다.

Progress로 진행률도 알아낼 수가 없어서 몇분 기다리다 답답해서 강종 해버리고 엑세스파일을 열어보니 고작 3만개 들어갔네요.

이딴 성능을 가지고 뭘 벌크로 인서트 하라고 --;;;;

 

 

방법이 이것말곤 없나 찾다가 드디어 궁극의 비기를 발견합니다.

 

http://stackoverflow.com/questions/7070011/writing-large-number-of-records-bulk-insert-to-access-in-net-c

 

정말 현답자이십니다. 6가지 사례를 들어  밀리세컨까지  시뮬레이션 결과를 정리해 주셨더군요.

메모리에 DAO Recordset객체를 만들어내는 방법이 가장 좋다합니다.

같은 일을 하는데 그 방법을 어떻게 하느냐에 따라 2.8초 vs 86초? 

 

어쨋든 해 보았습니다. 맨 아래에 친절하게도 클래스급으로 만들어진 소스가 있어서 그대로 따다가..

vb.net 코드로 컨버팅 하고.. 참조에 Microsoft Office Data Access Object 14.0 걸어주고.. (Dao3.6은 mdb까지밖에 안되므로)

 

Class uploadAccess 
    Public Sub BulkExportToAccess(dtOutData As DataTable, DBPath As [String], TableNm As [String]) 
        Dim dbEngine As New DAO.DBEngine() 
        Dim CheckFl As [Boolean] = False 

        Try 
            Dim db As DAO.Database = dbEngine.OpenDatabase(DBPath) 
            Dim AccesssRecordset As DAO.Recordset = db.OpenRecordset(TableNm) 
            Dim AccesssFields As DAO.Field() = New DAO.Field(dtOutData.Columns.Count - 1) {} 

            'Loop on each row of dtOutData 
            For rowCounter As Int32 = 0 To dtOutData.Rows.Count - 1 
                AccesssRecordset.AddNew() 
                'Loop on column 
                For colCounter As Int32 = 0 To dtOutData.Columns.Count - 1 
                    ' for the first time... setup the field name. 
                    If Not CheckFl Then 
                        AccesssFields(colCounter) = AccesssRecordset.Fields(dtOutData.Columns(colCounter).ColumnName) 
                    End If 
                    AccesssFields(colCounter).Value = dtOutData.Rows(rowCounter)(colCounter) 
                Next 

                AccesssRecordset.Update() 
                CheckFl = True 
            Next 

            AccesssRecordset.Close() 
            db.Close() 
        Finally 
            System.Runtime.InteropServices.Marshal.ReleaseComObject(dbEngine) 
            dbEngine = Nothing 
        End Try 
    End Sub 

End Class 

 

 

그리고 실행코드..

빨리빨리..빨리 해서 결과를 보고싶엉ㅇㅇ +_+ 대충 빨리..

 

Dim Acce As uploadAccess = New uploadAccess 
Acce.BulkExportToAccess(OrgDataTable, AppDomain.CurrentDomain.BaseDirectory + My.Settings.DBFileNM, "Data") 

 

 

매우매우 잘 됩니다. 10만5천건의 데이터가 눈깜짝할새에 쏵~ 들어가네요.

12만row 몇컬럼인진 모르겠으나 4초걸렸다고 했는데  제가 테스트한 10만5천row 2컬럼짜리는 0.5초만에 들어가네요.

 

전 개인적으로 어떤 DataTable을 다룰때 행,열 For문 돌려서 뭘 하는게 굉장히 맘에 안들었습니다.

SQL에서 레코드셋으로 받아온 결과를  엑셀에 뿌릴때에도..  컬럼, 로우 중첩for문 돌면서 처리하는거 절대 안썼고

쿼리문을 변경해서라도  copyfromrecordset 을 써서  한방에 뿌리는걸 선호했죠.

수십수백만개가 될지도 모르는걸  For문으로 처리해 버릇하면  후에 감당이 안될것 같아서요.

그래서 닷넷으로 와서도 copyfromrecordset이 지원되지 않는 닷넷의 ado 기본공급자들 안쓰고

따로 ado 6.0을 참조걸어서 쓸 정도였죠.

 

 

그런데 지금의 결과를 보니 for도 잘 쓰면 베스트가 될 수도 있구나 하는걸 깨달았습니다.

 

아마 이 방법은 db서버에 있는 데이터를 엑셀로 내려받기 해 줄 때에도 유용하게 쓸수 있을것 같네요.

어떤 데이터 원본이던지 DataTable로 만들어낼 수 있고,  DAO로 컨트롤할 수 있는 대상이면 적용 될 테니까요..

 

두서없이 제가 쓰던 코드를 고대로 갖다넣어버림으로써  샘플소스로 쓰기에는 부적절한 (lofunctions등 개별적으로 쓰는 클래스) 코드가 된 점 양해 부탁드립니다. 링크 원본을 가시면  다 있습니다^^;;;

 

 

 

 

 

  [ACCESS] Round 함수의 결과값 반환에 대한 이해.

 

 

 

몇일 전 Round함수가 잘못 작동하고 있다는 글을 포스팅하였다.

 

 

하지만  설마하니 Round같은 기초 함수를 설계하는데 버그를 낼리가 없을거라는 생각에 조금 더 조사를 해 보았더니 역시 이것은 의도된 것이었음을 알게 되었다.

그리고 그것이 보다 정확한 결과를 위해서 라는 것도..

 

 

 

이 글을 보니 단숨에 이해가 되었다.

 

 

요지는 이렇다.

 

정수 1개를 기준으로  0.1 ~ 0.9 까지 소수 9개가 반올림 대상 숫자가 된다.

0.1~0.4까지 4개의 소수는 내림 처리를 하게 되고

0.5~0.9까지 5개의 소수는 올림 처리를 하게 된다.

 

이렇게 되면 버려지는 숫자는 4개, 올려지는 숫자는 5개이므로 정수1개당  1/9만큼 불공평하게 나누어진다는 것이다.

살짝 표로 보자면..

 1.0

 2.0

 3.0

 4.0

 1.1 

 2.1

 3.1

 4.1

 1.2

 2.2

 3.2

 4.2

 1.3

 2.3

 3.3

 4.3

 1.4

 2.4

 3.4

 4.4

 1.5

 2.5

 3.5

 4.5

 1.6

 2.6

 3.6

 4.6

 1.7

 2.7

 3.7

 4.7

 1.8

 2.8

 3.8

 4.8

 1.9

 2.9

 3.9

 4.9

 

1.0~4.0까지 숫자 40개 중에서 변하지 않는 숫자는 4개이고

버려지는 숫자는 16개, 올려지는 숫자는 20개이다. 올려지는 숫자가 더 많다..

 

 

그럼 위 링크의 'Banker's Rounding'의 규칙을 적용해 본다면

 

 1.0

 2.0

 3.0

 4.0

 1.1 

 2.1

 3.1

 4.1

 1.2

 2.2

 3.2

 4.2

 1.3

 2.3

 3.3

 4.3

 1.4

 2.4

 3.4

 4.4

 1.5

 2.5

 3.5

 4.5

 1.6

 2.6

 3.6

 4.6

 1.7

 2.7

 3.7

 4.7

 1.8

 2.8

 3.8

 4.8

 1.9

 2.9

 3.9

 4.9

1.0~4.0까지 숫자 40개 중에서 변하지 않는 숫자는 똑같이 4개이고

버려지는 숫자는 18개올려지는 숫자도 18개이다.

 

 

실제로 여러 값의 반올림을 평균한다던지 하는 집계쪽에는 이 방법이  더 근사치와 가까워 질 것이 확실해 보인다.

하지만 개인의 성적을 처리할 경우에는 맞지 않는다. 97.5점을 받아도 98점이고, 98.5점을 받아도 98점이 되버리니..

 

여튼 이제껏 생각 못하고 그냥 마구잡이로 Round를 써왔는데 이번을 계기로 알게 되었으니 앞으로는 상황을 고려해 사용하도록 해야겠다.

[ACCESS] 엑세스의 Round 함수의 버그? 오작동 사례.

 

 

Access를 이용해 데이터 처리를 하던 도중 의도치 않는 결과가 나온다는 보고가 있어서 찾아보았다.

흔히 반올림 처리를 위해 사용하Round 함수가 특정 규칙을 가지고 오작동을 하고 있었다.

0.5 => 1

1.5 => 2

2.5 => 2

3.5 => 4

4.5 => 4

5.5 => 6

6.5 => 6

....

이렇게 격수로 하나씩 건너뛰면서 반올림, 내림, 반올림, 내림 처리를 하고 있는 것이었다.

 

 

증거자료..

 

 

 아래와 같이 실수(Single)타입의 값1~값6 필드를 가진 테이블을 만들어서 값들을 채워넣고..

 

 

 

아래의 쿼리를 돌려서 나온 결과..

SELECT
SUM(값1) AS 원값
,SUM(값2) AS [원값빼기05]
,SUM(값3) AS [원값빼기1]
,SUM(값4) AS [원값빼기15]
,SUM(값5) AS [원값빼기25]
,SUM(값6) AS [원값빼기35]
FROM 테이블1

UNION ALL

SELECT
ROUND(SUM(값1),0)
,ROUND(SUM(값2),0)
,ROUND(SUM(값3),0)
,ROUND(SUM(값4),0)
,ROUND(SUM(값5),0)
,ROUND(SUM(값6),0)
FROM 테이블1;

결과를 보면 17.5는 정상적으로 18로 반올림 처리가 되었으나  12.5의 반올림은 13이 되지 않고 12가 되었다.

역시 7.5는 반올림되어 8이 되었으나 2.5는 내림이 되어 2가 되어버렸다.

 

추측하기에는 실수(Single)값의 부동소숫점 연산 처리때문에 그런게 아닐까 싶지만..

별생각없이 반올림 처리하려고 Round를 자주 쓰게 되는데  이러면 곤란하다..

특히 이건 SAT 채점의 Omit처리를 하다 발견된 건데,, 점수의 계산이 틀어지면 끝장이지 않은가.

 

해서 해결방안은. Round함수를 뜯어고칠수는 없으니,,

Round(SUM(값1)+0.1 , 0) 으로  일괄적으로 0.1을 더해주었다. 정수반올림을 하는데에는 0.1 더해주는거면 충분함..

+ Recent posts