블로그 이미지
This blog covers the latest technologies in Microsoft .Net. In 2020, I will be talking about Uno Platform frequently. http://youtube.com/FutureOfDotNet kaki104

카테고리

List All (621)
Uno Platform (5)
Visual Studio (7)
Blazor (2)
Windows App(Universa.. (110)
Xamarin Forms (4)
Bot Framework (19)
Azure (10)
Windows 10 (53)
WPF (7)
Facebook News & Tips (158)
Windows 8&8.1 (113)
Windows Phone 8 (42)
Silverlight (37)
HTML5 & MVC4 (16)
Portable Class Library (2)
Uncategorised Tips a.. (3)
Kinect for Windows (2)
ETC (12)
kaki104 Scrap (4)
App News (13)
Total565,953
Today10
Yesterday106

UWP 앱을 개발 완료 후 스토어에 등록하고, 사용자들이 설치를 하는 것까지 어느정도 정상적으로 이루어진다고 생각하면, 이제 앱을 지속적으로 사용할 수 있도록 관심을 끌어주어야 합니다. 그래야 궁금해서 앱을 자꾸 실행 시켜보게 되겠죠?



타일에 새로운 컨텐츠가 있다가 알려주니 자꾸 누르고 싶죠?



이렇게 Notification을 발생시켜서 관심을 끌 수도 있습니다.



성공적인 앱 개발을 위한 Tile과 Notification 발생하는 방법에 대해서 알아 보도록 하겠습니다.



0. 참고


UWP 앱에 대한 타일, 배지 및 알림

Adaptive and interactive toast notifications for Windows 10


이 예제는

Windows 10 Version 1607

Visual Studio 2015 Update 3

Windows 10 SDK 14393(Anniversary)

에서 만들어 졌습니다. 이전 버전에서는 필수 구성요소를 설치하거나 업데이트 하셔야 합니다.




적응형 타일을 만드는 방법에 대해 설명하도록 하겠습니다.

Visual Studio 2015를 실행하고, File -> New -> Project를 선택하고, 아래 그림처럼 TileSample이라고 입력합니다.



다음에 나오는 대상 버전과 최소 버전은 기본 상태로 나두고 OK를 눌러서 프로젝트를 생성합니다.
새로운 프로젝트가 생성이 되었습니다~

Tile이나 Toast Notification은 모두 xml 형태의 일정 약식을 이용해서 만들어 지는 녀석들입니다. 그런데, xml을 동적으로 생성하기위해 쉽지 않기 때문에, 이 부분을 코드로 쉽게 만들 수 있도록 지원하는 Nuget Packages를 제공하고 있습니다. 이제 NuGet Package를 설치하도록 하겠습니다.

프로젝트를 선택 -> 마우스 오른쪽 버튼 클릭 -> Manage NuGet Packages 클릭


NotificationsExtensions 을 검색창에 입력한 후 NotificationsExtensions.Win10을 설치합니다.



설치를 하면, 코드나 Xaml을 이용해서 타일을 자신이 원하는 형태로 100%만들 수 있는 것이아니라, 기본적으로 일정 양식이 존재합니다. 그래서, 그 양식을 쉽게 볼 수 있도록 Notifications Visualizer라는 UWP 앱을 이용해서 볼 수 있으며,  이 블로그 게시물에서 자세한 정보를 얻을 수 있고 여기에서 알림 시각화 도우미를 다운로드할 수 있습니다.

(다운로드하는데 시간이 오래걸리는 것은 함정!!)


알림 시각화 도우미 앱 화면





2. 코딩으로 타일 만들기


Xaml로 타일을 구성할 수 있기는 하지만, 타일은 동적으로 내용을 변경 할 수 없기 때문에 대부분 코딩으로 만들어서 사용합니다. 그럼 현재 타일 모양을 한번 살펴 보도록 하죠


F5를 눌러서 실행 합니다. 실행하시기 전에 x86, Local Machine가 선택되어 있는지 확인 부탁드립니다. 예제로 올리는 셈플들은 프로젝트를 열면, 기본이 ARM, Device로 설정이 되어있기 때문에 이대로 실행하면 에러가 발생합니다.


한번 실행하면 컴퓨터에 TileSample 앱이 설치 됩니다. 시작 버튼을 누른 후 찾아서 마우스 오른쪽 버튼 클릭해서 Pin To Start를 클릭합니다.




클릭하면 아래와 같이 시작화면에 추가됩니다. 이제 여기에 X자 모양 말고 원하는 택스트가 출력되도록 해보겠습니다.




3. MainPage.xaml


    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
            <Button Content="1. Basic Update Tile" Click="ButtonBase_OnClick"/>
        </StackPanel>
    </Grid>



4. MainPage.xaml.cs


        private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
        {
            var content = new TileContent
            {
                Visual = new TileVisual
                {
                    TileSmall = new TileBinding
                    {
                        Content = new TileBindingContentAdaptive
                        {
                            Children =
                            {
                                new TileText {Text = "Small"}
                            }
                        }
                    },
                    TileMedium = new TileBinding
                    {
                        Content = new TileBindingContentAdaptive
                        {
                            Children =
                            {
                                new TileText {Text = "Medium"}
                            }
                        }
                    },
                    TileWide = new TileBinding
                    {
                        Content = new TileBindingContentAdaptive
                        {
                            Children =
                            {
                                new TileText {Text = "Wide"}
                            }
                        }
                    },
                    TileLarge = new TileBinding
                    {
                        Content = new TileBindingContentAdaptive
                        {
                            Children =
                            {
                                new TileText {Text = "Large"}
                            }
                        }
                    }
                }
            };

            var manager = TileUpdateManager.CreateTileUpdaterForApplication();
            manager.Update(new TileNotification(content.GetXml()));

        }


소스를 보시면 간단하게 몇줄만 입력하면 타일을 만들 수 있는걸 알 수 있습니다. 맨 아래 manager는 내가만든 타일을 앱의 타일에 반영하는 부분입니다.


여기서 알 수 있는 내용은 타일의 크기가 4가지 이기 때문에 타일을 설정할 때 4가지 크기에 맞는 내용을 입력해 넣어야 한다는 것입니다. 타일의 크기는 앱 사용자가 자신이 원하는 형태로 변경할 수 있으며, 기본적으로 시작화면에 넣을 때는 Medium 형태로 추가됩니다.  하지만, 4가지를 넣는다고 하더라도, 모바일은 3가지 종류만 지원하기 때문에 3가지만 보여집이다. ^^


에..그런데 지금 확인했는데..제일 큰 타일에 대한 정보를 제공해도, 꼭 변경이 가능하지는 않군요;; 지금 TileSample의 경우 가장 큰 넘으로 변경하는 메뉴가 표시되지 않습니다. 뭔가 사연이 있을 것 같은데..나중에 찾으면 다시 올리도록 하겠습니다.




5. 텍스트 스타일 적용


        private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
        {
            var content = new TileContent
            {
                Visual = new TileVisual
                {
                    TileSmall = new TileBinding
                    {
                        Content = new TileBindingContentAdaptive
                        {
                            Children =
                            {
                                new TileText {Text = "Small Header", Style = TileTextStyle.Base},
                                new TileText {Text = "Small content", Style = TileTextStyle.CaptionSubtle},

                            }
                        }
                    },
                    TileMedium = new TileBinding
                    {
                        Content = new TileBindingContentAdaptive
                        {
                            Children =
                            {
                                new TileText {Text = "Medium Header", Style = TileTextStyle.Base},
                                new TileText {Text = "Medium content", Style = TileTextStyle.CaptionSubtle},

                            }
                        }
                    },
                    TileWide = new TileBinding
                    {
                        Content = new TileBindingContentAdaptive
                        {
                            Children =
                            {
                                new TileText {Text = "Wide Header", Style = TileTextStyle.CaptionSubtle},
                                new TileText {Text = "Wide content", Style = TileTextStyle.Base},

                            }
                        }
                    },
                    TileLarge = new TileBinding
                    {
                        Content = new TileBindingContentAdaptive
                        {
                            Children =
                            {
                                new TileText {Text = "Large Header", Style = TileTextStyle.Base},
                                new TileText {Text = "Large content", Style = TileTextStyle.CaptionSubtle},

                            }
                        }
                    }
                }
            };


과거와 다른점은 택스트의 어느줄이나 원하는 스타일을 적용할 수 있다는 것이군요. 좀 편하게 변경된 것 같습니다.




캬오~ 그 아래 내용들을 보니 옛날에는 꿈도 못꾸던 것들이 잔득 들어가 있군요, 하위 그룹 나누는 것 부터 신세계입니다..하하;;; 그룹에 대한 내용들은 직접 살펴 보세용



6. 이미지


이미지를 표시하는 방법을 살펴 보겠습니다. 이미지의 파일 크기와 이미지 크기에 대한 제한이 있다고 합니다.


  • Small (70x70) (Windows only)
  • Small (71x71) (Windows Phone only)
  • Medium (150x150)
  • Wide (310x150)
  • Large (310x310) (Windows only)

웹에서 이미지 땡겨서 보여줄라구 해도 사이즈가 정확하게 맞아야 할 것 같기는 한데.. 한번 시도를 해보았는데, 실패했습니다. 일단, 이미지를 짤라서 앱에 포함한 후에 다시 시도해 보았습니다. 이미지를 복사한 후 프로젝트에 있는 Assets 폴터를 선택하고 붙여 넣기를 하면 해당 폴더에 이미지가 복사 됩니다.


                    TileWide = new TileBinding
                    {
                        Content = new TileBindingContentAdaptive
                        {
                            Children =
                            {
                                new TileText {Text = "우주소녀 - 성소", Style = TileTextStyle.Base},
                                new TileImage {Source = new TileImageSource("Assets/girl.png"), RemoveMargin = true}
                            }
                        }
                    },


이미지 출력이 잘되네요.. 참 버튼을 클릭한 후에 좀 기다려야 타일이 변하면서 내용이 표시됩니다. 참고하세요




7. Toast Notification



이번에는 Toast Notification에 대해서 살펴 보겠습니다.  위에서 설치했던 NotificationsExtensions.Win10 nugget package project url로 이동하면, 셈플을 다운로드 할 수 있습니다.


셈플을 실행 시켜 보면 다음과 같이 한눈에 보기 좋은 내용이 표시 됩니다. (Notifications Visualizer를 이용용해도 비슷합니다.)



간단하게 작업 완료 메시지를 출력할 수도 있고, 버튼이나 텍스트 박스를 추가해서 사용자의 입력을 받을 수도 있습니다. 과거에는 생각도 못하던 기능들이 추가 되어있습니다.



1) 간단한 메시지 출력


MainPage.xaml에 버튼을 추가 합니다.


    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
            <Button Content="1. Basic Update Tile" Click="ButtonBase_OnClick"/>
            <Button x:Name="BasicToast" Content="2. Basic Toast" Click="BasicToast_OnClick"/>
        </StackPanel>
    </Grid>


MainPage.xaml.cs에 2개의 메소드를 추가합니다.


        private void BasicToast_OnClick(object sender, RoutedEventArgs e)
        {
            //토스트 컨텐츠 생성
            var toastContent = new ToastContent
            {
                Visual = new ToastVisual
                {
                    BindingGeneric = new ToastBindingGeneric
                    {
                        Children =
                        {
                            new AdaptiveText {Text = "TileSample - First Toast"}
                        }
                    }
                }
            };
            ShowToast(toastContent);
        }
        /// <summary>
        /// 토스트 출력
        /// </summary>
        /// <param name="toastContent"></param>
        private void ShowToast(ToastContent toastContent)
        {
            try
            {
                Debug.WriteLine(toastContent.GetContent());
                ToastNotificationManager.CreateToastNotifier().Show(new ToastNotification(toastContent.GetXml()));
            }
            catch (Exception ex)
            {
                var dontWait = new MessageDialog(ex.ToString()).ShowAsync();
            }
        }


정말 간단한 토스트가 출력되었습니다. 아이콘이 표시되지 않는 것은 해당 아이콘이 앱에 포함되어 있지 않기 때문입니당~




2) 아이콘과 버튼을 가지는 토스트를 만들어 보겠습니다.


MainPage.xaml 을 수정합니다.


    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
            <Button Content="1. Basic Update Tile" Click="ButtonBase_OnClick"/>
            <Button x:Name="BasicToast" Content="2. Basic Toast" Click="BasicToast_OnClick"/>
            <Button x:Name="ButtonToast" Content="3. Button Toast" Click="ButtonToast_OnClick"/>
        </StackPanel>
    </Grid>


성소 이미지는 위에서 사용했던 그 이미지 입니당~


        private void ButtonToast_OnClick(object sender, RoutedEventArgs e)
        {
            //토스트 컨텐츠 생성
            var toastContent = new ToastContent
            {
                Visual = new ToastVisual
                {
                    BindingGeneric = new ToastBindingGeneric
                    {
                        Children =
                        {
                            new AdaptiveText {Text = "우주소녀", HintStyle = AdaptiveTextStyle.Header},
                            new AdaptiveText {Text = "성소 입니다.", HintStyle = AdaptiveTextStyle.Body},
                        },
                        AppLogoOverride = new ToastGenericAppLogo()
                        {
                            Source = "Assets/girl.png",
                            HintCrop = ToastGenericAppLogoCrop.Circle
                        },
                    }
                },
                Launch = "ButtonToast",
                Actions = new ToastActionsCustom
                {
                    Buttons =
                    {
                        new ToastButton("좋아요", "like"),
                        new ToastButton("싫어요", "hate")
                    }
                }
            };
            ShowToast(toastContent);
        }


자~ 아이콘과 버튼이 포함된 토스트가 출력되었습니다. 하하..여기서 뭘 눌러야 하는지는 아시겠죠?



8. 그런데..


Toast Notification은 단순히 알림 수준의 내용이 아니라, 선택을 했을 때 후속 처리를 해줘야 하는 녀석입니다. 하지만, 안타깝게도 Toast Notification 포스트들을 살펴보니 자세하게 나와있는 녀석은 없는 것 같습니다.

혹시라도 찾게 되면 추가하도록 하겠습니다. (혹시라도 요청하시면 제가 작성할수는 있습니다..쿨럭)



9. Source

TileSample.zip


Posted by kaki104

댓글을 달아 주세요

Microsoft의 Cognitive Service에는 여러가지 신기한 서비스가 있습니다만, 그 중에서 이번에 Face API에 대한 포스트를 만들어 보도록 하겠습니다.


0. Face API 소개

Microsoft Face API는 클라우드 기반의 얼굴 인식 서비스 입니다. Face API는 크게 얼굴 디텍팅과, 얼굴 인식의 2가지 기능이 있습니다. 더 자세한 사항은 Face API Overview 페이지를 참고 하시면 됩니다.





2016/09/28 일자 내용 입니다.

현재 공개되어있는 예제는 WPF용 예제 뿐이라서 UWP용 예제로 만드는데 시간이 좀 걸렸습니다. UWP 앱 개발에 관심있는 분들에게 도움이 되었으면 좋겠네요.

UWP Target version Windows 10 Anniversary Edition (10.0; Build 14393)



1. 참고

Get Started with Face API in C#



2. 준비


우선 비주얼 스튜디오 2015가 필요합니다. 그리고, Microsoft Cognitive Services에 가입을 해야 합니다. 가입을 하면 어떤 서비스를 사용할지 물어보고 subscription을 하라고 하는데, 저는 모두 선택 했습니다. 물론 Face API만 선택해도 상관 없습니다~.


Face - Preview는 한달에 3만건, 1분에 20번 호출하는 것이 무료이기 때문에 연습용으로 사용하시는데는 문제가 없을 것으로 생각됩니다.



3. 시작하기


1) 비주얼 스튜디오 2015 실행


2) New Project를 선택하고, Universal을 선택하고, Blank UWP app으로 만들어 봅니다.


3) 이름은 FaceSample으로 지정하시구요.. 저는 Target 버전을 Windows 10 Anniversary Edition 10.0; Build 14393)으로 지정 했습니다.


4) Newtonsoft.JsonWriteableBitmapEx 2개의 NuGet Package를 설치하세요. WriteableBitmapEx는 이미지 파일에 얼굴 표시를 하기 위해서 반드시 필요한 것입니다.


5) MainPage.xaml을 열어서 아래의 코드 Grid안에 입력하세용, BrowserButton_Click 이벤트 핸들러는 Ctrl+. 을 눌러서 메뉴에서 나오는 Create event handler를 선택하시면, 자동으로 MainPage.xaml.cs에 이벤트 핸들러가 추가 됩니다.


        <Image x:Name="FacePhoto" Stretch="Uniform" Margin="0,0,0,30"/>
        <Button x:Name="BrowseButton" Margin="20,5" Height="20"        
                VerticalAlignment="Bottom" Content="Browse..."               
                Click="BrowseButton_Click"/>


6) BrowserButton_Click 이벤트 핸들러에 아래의 코드를 우선 추가 합니다.


        private async void BrowseButton_Click(object sender, RoutedEventArgs e)
        {
            //FileOpePicker 설정
            var openDlg = new FileOpenPicker();
            openDlg.FileTypeFilter.Add(".jpg");
            openDlg.FileTypeFilter.Add(".jpeg");
            openDlg.FileTypeFilter.Add(".png");
            //파일 선택 창 출력
            var result = await openDlg.PickSingleFileAsync();
            if (result == null || !result.IsAvailable) return;

            var file = await StorageFile.GetFileFromPathAsync(result.Path);
            var property = await file.Properties.GetImagePropertiesAsync();
            //이미지 크기 만큼 비트맵 생성
            var writeableBmp = BitmapFactory.New((int) property.Width, (int) property.Height);
            using (writeableBmp.GetBitmapContext())
            {
                //선택한 파일 이미지로 불러오기
                using (var fileStream = await file.OpenAsync(FileAccessMode.Read))
                {
                    writeableBmp = await BitmapFactory.New(1, 1).FromStream(fileStream, BitmapPixelFormat.Bgra8);
                }
            }
            FacePhoto.Source = writeableBmp;


        }



7) 실행 한 후에 이미지를 불러오 보아요~


요즘 핫한 우주소녀 성소입니다.~ 캬캬. 이미지가 잘 불러와 지면 다음으로 넘어가지요~




4. Face API 클라이언트 라이브러리 설정


Face API는 HTTPS 요청에 의해 호출되는 클라우드 API이며, 사용하기 쉽게 라이브러리를 제공합니다. 라이브러리를 사용하기 위해서는 다음과 같은 순서를 따라하시면 됩니다.


1) Solution Explorer에서 마우스 오른쪽 버튼을 눌러서 Manage NuGet Packages를 선택합니다.


2) Browse에서 projectoxford를 입력한 후 face를 선택해서 Install 버튼을 눌러 설치합니다.



3) MainPage.xaml.cs에 아래의 코드를 복사해서 붙여 넣습니다.


private readonly IFaceServiceClient faceServiceClient = new FaceServiceClient("Your subscription key");


Your subscription key에는 https://www.microsoft.com/cognitive-services/en-US/subscriptions 페이지에 들어가서 Face preview에 해당하는 곳에서 Copy를 클릭해서 Key를 복사하고 붙여 넣습니다.


여기까지 진행하면 Face API를 호출할 준비가 완료된 것입니다.


4) 이제 나머지 코드를 추가하도록 하겠습니다. 이전 코드 중간에 굵은색 글씨의 코드를 추가하면 됩니다.


                //선택한 파일 이미지로 불러오기
                using (var fileStream = await file.OpenAsync(FileAccessMode.Read))
                {
                    writeableBmp = await BitmapFactory.New(1, 1).FromStream(fileStream, BitmapPixelFormat.Bgra8);
                }


                //Face API이 DetectAsync를 이용해서 얼굴 찾기
                using (var imageFileStream = await file.OpenStreamForReadAsync())
                {
                    var faces = await faceServiceClient.DetectAsync(imageFileStream);
                    if (faces == null) return;
                    //얼굴 위치 표시
                    foreach (var face in faces)
                    {
                        writeableBmp.DrawRectangle(face.FaceRectangle.Left, face.FaceRectangle.Top,
                            face.FaceRectangle.Left + face.FaceRectangle.Width,
                            face.FaceRectangle.Top + face.FaceRectangle.Height, Colors.Red);
                    }
                }


5) 이제 최종 화면을 보도록 하겠습니다.~


아래와 같이 붉은색으로 얼굴을 찾아서 표시 했습니다. 음..약간 흐리고, 뭉개진 얼굴도 찾아주는 군요..후후

몇가지 더 찾아 볼까요?





5. 소스


소스에 API 키는 포함되어있지 않습니다~

FaceSample.zip


Posted by kaki104
TAG Face API, UWP

댓글을 달아 주세요

오프라인에서 다루고자하는 주제에 대해서 이야기를 주세요~라고 했더니 Xaml 사용법에 대해서 알고 싶다고 하셔서 간단하게 정리하도록 하겠습니다.

 

 

 

 

1. XAML에서 namespace 사용하기 줄여서 xmlns

 

xmlns는 XML Namespace를 정의한 것을 의미하며, 일종의 Prefix로 사용합니다. 만약, 여러분이 만든 클래스를 사용하려면, 반드시 xmlns를 이용해서 Prefix를 정한 후에야 사용이 가능합니다.

 

namespace App1.Test
{
    public class NameSpaceTestClass : DependencyObject
    {
        public string Name { get; set; }
    }
}

 

위와 같은 클래스를 만들고 xaml에서 불러서 사용하려면 App1.Test라는 namespace를 대체할 것이 필요합니다.

빌드를 하고 XAML에서 xmlns를 입력 후 =를 입력하면, 입력할 수 있는 내용이 목록으로 출력되고, App1을 선택 후 . 을 눌러주면 Test가 표시 됩니다. 그 후에 이 녀석의 Prefix를 test로 지정 합니다.

 

 

그 이 후에 <test:을 입력하면, 해당 네임스페이스에 있는 클래스를 사용할 수 있게 됩니다. 예제로 만든 NameSpaceTestClass는 의존성 객체(DependencyObject)로 리소스(Resources)에 등록해서 사용할 수 있습니다.

 

 

 

2. x: 뒤에 사용하는 내용들에 대해서

 

 

1) x:Key :

리소스에 등록되는 객체를 다른 곳에서 사용하기 위해서 입력합니다.

 

    <UserControl.Resources>
        <test:NameSpaceTestClass x:Key="TestClass" Name="Hello x:Key"/>
    </UserControl.Resources>
   
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <TextBlock Text="{Binding Name, Source={StaticResource TestClass}}"
                   VerticalAlignment="Center" FontSize="20" HorizontalAlignment="Center"/>
    </Grid>

 

 

2) x:Name :

컨트롤을 코드 비하인드에서 사용하기 위해 작성 합니다.

 

    <UserControl.Resources>
        <test:NameSpaceTestClass x:Key="TestClass" Name="Hello x:Key"/>
    </UserControl.Resources>
   
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
            <TextBlock Text="{Binding Name, Source={StaticResource TestClass}}" FontSize="20" Foreground="Green" />
            <TextBlock x:Name="Block"  FontSize="20" Foreground="Blue"/>
        </StackPanel>
    </Grid>

 

 

    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();

            //텍스트 입력
            Block.Text = "Hello x:Name";
        }
    }

 

 

3) x:Class :

XAML의 클래스 명을 입력합니다.

 

<Page
    x:Class="App1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:App1"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:test="using:App1.Test"
    mc:Ignorable="d">

 

 

4) x:DataType :

WinRT에서 사용하는 컴파일 바인딩을 하기위해 Data의 형을 지정하는 것입니다.

 

public class BindModel
{
   
public Windows.UI.Color Color { get; set; }
}

<DataTemplate x:Key="test" x:DataType="local:BindModel">
   
<TextBlock>
       
<TextBlock.Foreground>
           
<SolidColorBrush Color="{x:Bind Color}"></SolidColorBrush>
       
</TextBlock.Foreground>
   
</TextBlock>
</DataTemplate>

 

 

5) x:DeferLoadStrategy :

성능 향상을 위한 속성으로 이 속성을 사용하면 화면 출력되는 아이템들을 지연 시킬 수 있습니다. 다만, 이 내용을 구현하기 위해 각 항목 당 600 bytes의 메모리를 더 차지 합니다.

 

<object x:DeferLoadStrategy="Lazy" .../>

 

 

 

6) x:FieldModifier :

정확하게 어떤 동작을하는지..모르겠내요 아래 페이지를 참고하세요

https://msdn.microsoft.com/ko-kr/library/aa970905(v=vs.110).aspx

 

 

7) x:Phase :

ListView 및 GridView 의 아이템 내부에 항목들을 랜더링하는 방법을 정의해서, 빠른 스크롤 이동에 대응 하기위한 속성

 

<DataTemplate x:Key="PhasedFileTemplate" x:DataType="model:FileItem">
    <Grid Width="200" Height="80">
        <Grid.ColumnDefinitions>
           <ColumnDefinition Width="75" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Image Grid.RowSpan="4" Source="{x:Bind ImageData}" MaxWidth="70" MaxHeight="70" x:Phase="3"/>
        <TextBlock Text="{x:Bind DisplayName}" Grid.Column="1" FontSize="12"/>
        <TextBlock Text="{x:Bind prettyDate}"  Grid.Column="1"  Grid.Row="1" FontSize="12" x:Phase="1"/>
        <TextBlock Text="{x:Bind prettyFileSize}"  Grid.Column="1"  Grid.Row="2" FontSize="12" x:Phase="2"/>
        <TextBlock Text="{x:Bind prettyImageSize}"  Grid.Column="1"  Grid.Row="3" FontSize="12" x:Phase="2"/>
    </Grid>
</DataTemplate>

 

 

8) x:Shared="false" :

리소스에만 사용이 가능하며, 리소스를 호출하는 경우 각 개별 인스턴스를 만들어서 전송합니다. 기본값은 true입니다. 그런데 WPF에서만 사용이 가능합니다;; 왜 UWP에서도 튀어나오는거얌;;

https://msdn.microsoft.com/ko-kr/library/aa970778(v=vs.110).aspx

 

<ResourceDictionary>
  <object x:Shared="false".../>
</ResourceDictionary>

 

사용하면 아래와 같은 오류 메시지가 출력됩니다.

 

Severity Code Description Project File Line Suppression State
Error  XBF generation error code 0x09c4. App1 C:\Sample\App1\App1\MainPage.xaml 12 

 

 

9) x:Subclass

기본적으로 partial 클래스 선언을 지원하지 않는 언어를 위한 것으로 UWP앱에서는 사용할 필요가 없습니다.

 

 

10) x:TypeArguments

음 거의 사용하지 않는것 같은데..자세한 설명은 아래 링크를 참고하세요

https://msdn.microsoft.com/ko-kr/library/ms750476(v=vs.110).aspx

 

 

11) x:Uid

지역화 리소스를 XAML에서 사용하기 위한 키를 입력합니다. 하지만, 이걸로 리소스를 사용하면 마니 힘듭니다...

https://msdn.microsoft.com/ko-kr/library/bb613571(v=vs.110).aspx

 

 

3. UWP Tool 소개

 

1) WinRTXamlToolkit UWP 버전

 

옛날 옛날 윈도 망고 폰이 나왔을 때부터 계속 버전업을 하면서 꾸준히 관리되는 툴킷으로 무료 컴포넌트 툴킷 중 최고라고 할 수 있습니다.

 

설치

 

프로젝트 페이지

https://github.com/xyzzer/WinRTXamlToolkit

 

소스를 다운로드 받아서 데모를 확인하면서 컨트롤 사용법 확인합니다.

 

 

2) Telerik UI for Universal Windows Platform

유명한 텔레릭 사의 컴포넌트 입니다. 텔레릭 컴포넌트도 상당히 오래부터 계속 만들어지고 있으며 좋은 성능을 가지고 있어서, 상용 앱을 만들 때 사용하면 좋습니다.

http://www.telerik.com/universal-windows-platform-ui

 

 

3) ComponentOne Studio UWP Edition

과거 FarpointSpread라는 컴포넌트를 만들었던 회사로 UWP 앱 개발 컴포넌트도 정식 출시 했습니다.

https://www.componentone.com/Studio/Platform/UWP

 

 

4) DevExpress Windows 10 Apps

국내에 많은 회사들이 사용하는 DevExpress의 Windows 10 용 컴포넌트 입니다.

https://www.devexpress.com/Products/NET/Controls/Win10Apps/

 

 

4. XAML이 WinForm과 다른 점..

 

Winform 개발 환경에서는 고객의 입맛에 맞게 컴포넌트 자체를 커스터 마이징을 하는 것이 한계가 있습니다. 하지만, XAML 환경에서 사용하는 컴포넌트들은 대부분 보이는 대부분의 내용을 수정이 가능하기 때문에 요즘 프로젝트들이 WPF를 사용하고 있다고 생각합니다.

 

정말 원하는 모양의 원하는 기능의 컴포넌트가 필요하다면, 자신이 직접 만들고 다듬어보는 것도 개발의 재미있는 요소가 아닐까 생각합니다.

 

Posted by kaki104

댓글을 달아 주세요

Microsoft MVP 박문찬입니다. 4월 정기 모임에서 사용할 앱 만들기를 초보분들이 따라하기 쉽게 정리 합니다.

 

 

1. 필요사항

Windows 10이 설치된 PC or 테블릿

Visual Studio 2015 Update 2가 적용된 것으로..(아무래도 최신이 좋겠죠?)

Windows 10 SDK

https://developer.microsoft.com/en-US/windows/downloads/windows-10-sdk

 

Windows 10 UWP App Sample 모음

https://github.com/Microsoft/Windows-universal-samples

 

이 셈플 중 OCR 셈플을 기준으로 설명합니다.

 

 

2. OCR 셈플을 실행

 

위에 셈플 코드를 다운 받은 후 압축을 풀어주면, Samples 폴더 아래 OCR/cs 폴더가 있습니다. OCR.sln 파일을 열고 실행 합니다.

 

2-1. OCR image file

이미지에서 택스트를 뽑아내는 셈플이고, 여기에서 간단하게 작동 원리를 알아보고, 만들어 보겠습니다.

 

 

 

3. Blank App 템플릿으로 만들면 뭐하나요?

 

일반적으로 UWP(Universal Windows Platform) 앱을 만들려고, New Project 를 선택하면, Blank App 말고는 새로운 템플릿이 없습니다.

 

그걸로 만들면 정말 성의 없다는 느낌이 있죠? 여기서는 Intense Templates을 이용하도록 하겠습니다.

 

Intense Templates를 이용하기 위해서는 Tools -> Extensions and Updates를 선택하고, Online을 선택 후 오른쪽 상단 검색하는 부분에서 Intense를 입력해서 검색한 후 설치하시면 돼요.

 

그런데..요즘 비주얼 스튜디오 이용자가 폭주를 해서 그런지 Online을 선택 후에 데이터가 표시되지 않는 현상이 자주 발생하고 있다. 이 경우에는 그냥 검색하시고, 다운로드 후 설치하세요

 

 

직접 다운로드

https://visualstudiogallery.msdn.microsoft.com/b7076e96-d4ab-4150-b2c6-12730abd5666

 

 

 

 

IntenseTemplates.vsix 파일을 더블 클릭하면 설치가 됩니다.

 

설치가 완료 된 후 New Project를 선택하면, 아래와 같은 다양한 템플릿이 추가되어 있다. 이중에서 제일 아래있는 항목을 선택하고 프로젝트 이름을 입력하고 위치를 지정(반드시 C:\ 경로 사용) OK를 누르면 생성~

 

 

그리고 실행해보면..................

 

짜잔~ 손 앙대고 코풀기!!!!!! 이렇게 쉽게 멋진 기본 앱을 만들 수 있답니당. ㅋㅋ

 

 

 

4. 기본 흐름

 

앱 시작 -> 이미지 불러오기 -> 택스트 추출 -> 추출한 택스트 화면에 출력

 

 

5. WelcomePage.xaml

프로젝트에서 WelcomePage.xaml에 작업을 합니다.

* 이 예제는 초보 대상 강좌이기 때문에 MVVM 패턴을 제외하고 이벤트 드리븐 방식으로만 만듭니다.

 

하단 앱바 추가

<Page.BottomAppBar>
    <CommandBar>
        <AppBarButton x:Name="OpenFile" Icon="OpenFile" Click="OpenFile_Click"/>
        <AppBarButton x:Name="Extract" Icon="Character" Click="Extract_Click"/>
    </CommandBar>
</Page.BottomAppBar>


Grid를 2개 영역으로 분리

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid.RowDefinitions>
        <RowDefinition Height="3*"/>
        <RowDefinition/>
    </Grid.RowDefinitions>
</Grid>

 

바로 아래 이미지와 텍스트 블럭 추가

 

<Image x:Name="image"  Grid.Row="0"/>
<TextBlock Name="ExtractedTextBox" Grid.Row="1" IsTextSelectionEnabled="True" TextWrapping="WrapWholeWords"  />

 

 

6. WelcomePage.xaml.cs

 

6-1. 오픈 파일 버튼 클릭

private async void OpenFile_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e) 

{
    //이미지 파일을 선택하기 위해서 파일 피커를 사용합니다.
    var picker = new FileOpenPicker()
    {
        SuggestedStartLocation = PickerLocationId.PicturesLibrary,
        FileTypeFilter = { ".jpg", ".jpeg", ".png" },
    };
    //선택된 파일하나를 가지고 옵니다.
    var file = await picker.PickSingleFileAsync();
    if (file != null)
    {
        //화면에 출력된 텍스트를 지웁니다.
        ClearResults();
        //이미지를 로딩합니다.
        await LoadImage(file);
    }

}

 

6-2. 이미지 로딩

private async Task LoadImage(StorageFile file)
{
    //전달받은 file을 읽기 전용으로 열고, 스트림을 얻습니다.
    using (var stream = await file.OpenAsync(FileAccessMode.Read))
    {
        //비트맵 디코더를 생성
        var decoder = await BitmapDecoder.CreateAsync(stream);

        //디코더를 이용해서 SoftwareBitmap을 생성합니다.
        _bitmap = await decoder.GetSoftwareBitmapAsync(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied);

        //화면에 출력하기위해 WriteableBitmap을 만듭니다.
        var imgSource = new WriteableBitmap(_bitmap.PixelWidth, _bitmap.PixelHeight);
        _bitmap.CopyToBuffer(imgSource.PixelBuffer);
        //화면 컨크롤에 출력합니다.
        PreviewImage.Source = imgSource;
    }
}

 

OCR이나 기타 이미지 처리용 API를 이용하기 위해서는 반드시 SoftwareBitmap을 이용해야 합니다. 이 SoftwareBitmap 클래스는 윈도우 10 10.0.10240.0 이상 버전에 사용이 가능합니다.

 

SoftwareBitmap class

https://msdn.microsoft.com/en-us/library/windows/apps/windows.graphics.imaging.softwarebitmap.aspx

 

추가적으로 화면에 이미지를 출력하기 위해서 SoftwareBitmap을 WriteableBitmap으로 변경을 합니다.

이렇체 처리한 이유는 처음부터 BitmapImage로 그냥 읽었다면 바로 출력이 가능하지만, 그렇게 하지 않았기 때문입니다.

 

여기까지 작업한 후에 이미지를 불러와서 화면에 출력한 내용 입니다.

 

 

6-3. 텍스트 추출 버튼 클릭

 

private async void Extract_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
    if (_bitmap == null)
    {
        await MsgBox("이미지가 없지 말입니다.");
        return;
    }
    // OcrEngine이 지원하는 이미지 사이즈인지 확인
    if (_bitmap.PixelWidth > OcrEngine.MaxImageDimension || _bitmap.PixelHeight > OcrEngine.MaxImageDimension)
    {
        await MsgBox("이미지가 너무 커서 않되지 말입니다.");
        return;
    }

    //드디어 출격!!
    //한국어 인식하도록 지정
    var ocrEngine = OcrEngine.TryCreateFromLanguage(new Language("ko"));
    if (ocrEngine == null)
    {
        await MsgBox("지원 가능한 언어가 없지 말입니다.");
        return;
    }

    // 이미지에서 택스트를 인식합니다.
    var ocrResult = await ocrEngine.RecognizeAsync(_bitmap);

    // 인식된 텍스트를 텍스트블럭에 출력
    Extracted.Text = ocrResult.Text;

}

 

이렇게만 하면 이미지에거 택스트 뽑아내기 성공~

정말 쉽죠?? ㅎㅎㅎ

 

자자 최종 화면을 보도록 할까요?

 

 

이미지 하단에 텍스트가 출력되었습니다~ 100% 완벽하지 않으니, 약간의 보정이 필요하겠죠? 보정은 개발자의 몫인것 같아요.. 그러고보니, 금주 토요일날 Build 2016 특집 세미나가 있네요~

 

 

7. 마치며

OCR 기능 이용해서 앱 만들기 참 쉽죠? Offline에서도 동작을하고, 사용에 제한도 없습니다. 마구마구 사용해서 좋은 앱 많이 만들어 주세요

 

 

8. 소스

 

OCRSample.zip

 

 

Posted by kaki104

댓글을 달아 주세요

앱을 개발하다 보면 NuGet package를 여러가지 사용하게 된다. 특히, Azure를 이용한 서비스를 만들게 되면 그 서비스에서 사용하는 것이 한 10~20개는 되는 듯하다.


처음 개발 시작할 때는 분명 최신 버전이였는데..시간이 지날 수록 옛날 버전이되어 가는데..일일이 버전업을 해주는 것도 미치고 펄쩍뛸 노릇이다.


기본적으로 Visual Studio 2013에는 Manage NuGet Packages를 이용하면 한번에 업데이트를 하는 것이 가능하다.

아래 화면에서 Update All 버튼을 누르면 업데이트 가능한 것은 모두 업데이트 해준다.


그런데, 문제는 중간에 하나라도 업데이트 하다가 오류가 나면 거기서 업데이트가 끝나버린다는 단점이 있다.

그 단점을 극복하고, 한번에 모든 업데이트를 하려면 다음과 같이 하면된다.


TOOLS -> NuGet Package Manager -> Package Manager Console 을 선택하로 들어가서


update-package


를 입력하고 엔터를 처보자! 시간은 좀 오래 걸리지만..모든 프로젝트와 모든 패키지를 확인해서 업데이트가 가능한 넘들은 업데이트를 해준다.


아래 처럼 업데이트 오류가 발생하더라도, 멈추지 않고 끝~까지 간다.하하하;;;


NuGet Package Update 스트레스에서 자유로운 영혼으로 돌아가자~


그런데....Azure 서비스를 이용하는 경우 무조건 최신으로 업데이트를 해버리면, 서버에 올렸을 때 오류가 발생할 수 있습니다.

그러니, 과신은 금물~ 언제나 롤빽이 가능하도록 준비하신 후에 작업하세요

Posted by kaki104

댓글을 달아 주세요

다음 중 당신이 알고 있는 Data Binding에 대해 알고 있는 것을 선택하세요~

. 전혀 모르는 내용임

. Visual Studio 6.0 때 사용했던 Binding

. IOS에도 사용하는 Binding

. XAML에서 사용하는 Binding

.해당사항 없음


데이터 바인딩~ 이 단어에 대한 다양한 생각이 존재할 것이다. 그만큼 여러 사람들이 사용하고 있기 때문이겠지.. 그런데, 누구나 알고, 쉽게 사용하다보니 그 진정한 의미에 대해서 생각하고 고민한 사람이 얼마나 있을까?

여기에, 내가 생각하고, 고민한 결과를 적어 보도록 하겠다. 



1. Data Binding의 의의

여기 제임스라는 남자가 한명 있다고 가정하자.

제임스는 하나의 객체(object)이고 그 객체는 이름, 성별, 나이, 국적이라는 프로퍼티를 가진다. 이 객체를 카메라를 이용해서 촬영을 하나 해 놓자. 찰칵~ 찰칵~



그런데, 잠시 후에 흑마법사(?)가 나타나서 이 남자에게 저주를 걸어서 좀비로 바꾸어 버렸다!!!


짜잔~ 헉..누구세요;;




여기서 당신이 조금 전에 촬영한 사진과 지금의 모습을 비교해 보자. 틀린 그림 찾기를 해도 괜찮다.;;;

아마, 당신의 사진과 지금 좀비의 모습과는 너무나도 다른 모습을 확인 할 수 있을 것이다. 즉, 사진은 자동으로 변화하지 않는다!!!



그러나, 만약 당신이 비디오 촬영을 하고 있었다면 변신하는 과정을 포함하여, 변신 후의 모습도 촬영을 하여.. 방송국에 제보를 하려고 했을지도 모른다;; (제보는 JTBC로..손석희 짱~)



왜 갑자기 제임스가 좀비로 변하는 것을 이야기하는 것일까?

사진은 이벤트드리븐 방식의 프로그램으로 만들어 놓은 화면이고, 비디오는 바인딩을 사용해서 만든 프로그램의 화면과 같은 것이다.


Batch data process(Event-driven application)

변신 전 James -> 사진 촬영 이벤트 -> James의 복사본을 사진으로 만듬

변신~

변신 후 James -> 사진 촬영 이벤트 -> 변신 후 James의 복사본을 사진으로 만듬


사진 촬영 이벤트가 발생하지 않으면, 사진이 새로 만들어 지지 않듯이, 화면 갱신을 위한 별도의 이벤트가 발생하지 않으면, 사용자 화면의 내용은 변하지 않는다.


Realtime data process(Data Binding, MVVM Pattern)

변신 전 James -> 비디오 촬영 시작 -> James와 화면 사이에 Data binding

변신~             -> 변하는 과정이 실시간으로 화면에 반영됨

변신 후 James -> 비디오 촬영 종료


비디오 촬영은 시작, 종료 2개의 이벤트가 필요하지만, 중간에 다른 이벤트가 없이 현재의 모습을 촬영하게된다. 바인딩을 이용하면, 각 속성이 변경되면, 즉시 사용자 화면에 내용을 갱신 시킨다.



우리가 사는 세상은 리얼 월드이다. 실시간으로 데이터가 변하며, 사용자는 변경된 데이터가 바로 반영되어 화면에 보기를 원한다. 이런 세상에 살고 있는 사용자를 만족시키려면, 개발자 또한 리얼타임(Realtime)으로 화면을 갱신 시키는 기술이 필요한 것이다.

 


이제는 서버와 클라이언트도 함께 엮어서 리얼타임 월드르 만들 수 있는 기술이 존재하는데..

리얼타임 월드 기술!!

Data Binding, SignalR, Reactive Extension

100마디 말보다 한번 해보면 대번에 알 수 있다.



* 아래 셈플은 오래전에 만들어진 것이라 Reactive Extension은 사용하지 않았습니다~

실버라이트 - 실시간 영화 예매 시스템 셈플

http://kaki104.tistory.com/160


실버라이트 - 1-50 Silverlight game sample

http://kaki104.tistory.com/190



객체의 속성을 실시간적으로 화면에 반영하는 기술..빨대 한번 꼽아 놓으면 빨대 빼기전까지 쭈우우욱~ 빨려오는 강력한 흡인력!! 한번 연결 시켜 놓으면 오래오래 지속되는 선언적 기술!!! 이 것이 .Net에서 이야기하는 Data Binding 입니다.



이제 리얼타임 월드에 입성 할 준비가 되었나요?.





2. 데이터 바인딩 개요

https://msdn.microsoft.com/ko-kr/library/ms752347(v=vs.110).aspx


오프라인 모임때 아래 내용을 중심으로 설명 드렸습니다. 글로 풀어서 쓰기가 어려워서 아래 내용만 남겨 놓습니다. 다음에 기회가 되면, 다시 설명하도록 하겠습니다.


* 기본 데이터 바인딩 개념

- 데이터 흐름의 방향

- 바인딩 만들기, 비주얼 스튜디오를 이용하기, 블랜드를 이용하기, 디자인 타임 소스, 디자인 타임에 데이터 보기

* 데이터 변환

* 컬렉션에 바인딩

- IEnumerable, IList, ICollection, ObservableCollection, ConcurrencyBag...

- CollectionViewSource

* 데이터 템플릿

* 데이터 유효성 검사


데이터 바인딩 방법 항목

https://msdn.microsoft.com/ko-kr/library/ms752039(v=vs.110).aspx


연습문제

1. 콤보박스에 바인딩하기

2. 2개의 콤보박스에 카테고리1, 카테고리2 바인딩하기


연습용 소스

DataBindingSample.zip


연습문제 풀이하신 후 블로그나 페이스북에 올려주시면 제가 작업핸 소스를 보내드리겠습니다~

직접 작업하는 즐거움을 누리세요~


참고

Data binding (XAML)

https://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh758318.aspx


XAML Binding Basics 101

http://blogs.msdn.com/b/jerrynixon/archive/2012/10/12/xaml-binding-basics-101.aspx


Posted by kaki104

댓글을 달아 주세요

Windows store app template 모음입니다.

 

 

 

 

프로젝트 형태의 템플릿도 있고, 단순 이미지 형태의 템플릿도 있습니다.

디자인하실 때 참고하는 용도로 사용하시면 좋을 것 같습니다.

 

이 외에 pptx로 되어 있는 디자인 가이드도 폴더에 올렸습니다. 참고하세요

 

다운로드 링크

http://1drv.ms/1zjC8tG

Posted by kaki104

댓글을 달아 주세요

Screen Orientation 가로/세로 방향 지원 및 자동 변환 대응

 

앱 개발 중 여러가지 삽질을 하고 있다.

첫번째 삽질은 가로/세로 방향 전환에 적응하는 컨텐츠 뷰가 필요한 상황이다.

 

즉 아래와 같이 사진 보는 화면의 경우 가로 방향으로 뉘우면 넓게 보는 것이 목표 였는데..

음 약 한시간의 삽질이 필요했다.

 

 

 

1. 참고

Windows Phone 8.1 Screen Orientation - What’s changed for #WPDev?

http://www.kunal-chowdhury.com/2014/08/whats-new-for-wp8dev-orientation.html

 

 

2. 삽질

기존 Windows Phone 8, Windows Phone 8.1 Silverlight에서는 각 페이지에서 프로퍼티로 제어를 할 수 있도록 되어있다. 하지만 Windows Phone 8.1 RT는 Windows 8.1 처럼 Package.appxmanifest에서 지정을 할 수 있다.

 

 

머 여기까지는 쉽게 할 수 있다. 그런데, 이렇게 한 후에

 

 

DisplayInformation.AutoRotationPreferrences = DisplayOrientations.Landscape;

 

 

이런 내용을 넣으면 되고, 4가지가 더 있다고 하는데 내가 원하는 건 자동으로 가로, 세로가 변경이 되는 것이라구!!!

 

그 때 부터 이 산 저 산을 방황하다 찾은 결론..

 

 

DisplayInformation.AutoRotationPreferences = DisplayOrientations.None;

 

 

이렇게 None로 입력을 하면, 화면이 고정되지 않고, 자동 방향 전환이 된다는..

으흠;; 알면 쉽지만, 모르면 삽질하는..쿨럭

그리고, Windows 8.1은 Package.appxmanifest 설정만 해주면 자동으로 된다.

 

xaml 구성은 아래와 같이 했다.

 

 

<Page
    x:Class="OneSearch.Universal.Views.ContentViewPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:OneSearch.Universal.Views"
    xmlns:ViewModels="using:OneSearch.Universal.ViewModels"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Page.Resources>
        <DataTemplate x:Key="ImageItemTemplate">
            <Viewbox>
                <Image Source="{Binding BaseImageSource}" Stretch="UniformToFill" />
            </Viewbox>
        </DataTemplate>
    </Page.Resources>

    <d:DesignInstance.DataContext>
        <ViewModels:ContentViewPageVM />
    </d:DesignInstance.DataContext>

    <Grid>
        <Grid.ChildrenTransitions>
            <TransitionCollection>
                <EntranceThemeTransition />
            </TransitionCollection>
        </Grid.ChildrenTransitions>

        <FlipView Grid.RowSpan="2" ItemsSource="{Binding CurrentFolder.Original.Items}"
                  SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
                  ItemTemplate="{StaticResource ImageItemTemplate}" />

        <ProgressBar VerticalAlignment="Top"
                     IsIndeterminate="{Binding IsBusy, Mode=OneWay, Source={StaticResource PublicHelper}}"
                     Background="Transparent" />

    </Grid>
</Page>

Posted by kaki104

댓글을 달아 주세요

Universal Windows Platform app(UWP app) 개발을 시작하는 분들을 위한 가이드

 

Windows 10과 Visual Studio 2015 출시에 맞추어서 내용 업데이트 합니다.

 

Visual Studio Community 2013/2015 버전을 이용하여, 개인 개발자 / 오픈소스 프로젝트 / 학생 등은 사용자 제한 없이 사용이 가능하고, 일반 기업은 PC 250대 미만 또는 연매출 100만달러 미만인 경우 최대 5개까지 사용 가능합니다.

 

윈도우 앱 개발을 정말 시작할 때가 아닌가 생각합니다. 그래서, 앱 개발을 시작하려는 분들을 위한 가이드를 작성해 보았습니다. 잘못된 부분이나 추가할 부분이 있으면 알려주세용

 

* 용어 정리

UWP app :

. Universal Windows Platform app의 약자로 Windows 10 core에서 실행되는 앱을 이야기 합니다.

. Windows 10이 설치된 모든 윈도우에서 실행

 

Universal app :

. Windows 8.1 & Windows Phone 8.1 두개의 앱을 하나의 솔루션(3개의 프로젝트)으로 만드는 것을 이야기 합니다.

. Windows 8.1과 Windows Phone 8.1 이상에서 실행됨(Windows 10에서도 실행됨)

 

 

0. OS 및 하드웨어

Universal app : Windows 8.1 이상 필수

UWP app : Windows 10 필수

 

윈도우 폰 : 

. 고사양 단말기를 좋아하시는 분이라면 930, 830 추천 합니다.

. 아직 Windows 10 전용 폰은 출시 하지 않았습니다.

. Windows 10 for Mobile은 가을 정식 출시 예정입니다.

 

 

1. 개발툴

Visual Studio Community 2013, 2015

다운로드 페이지

 

http://www.visualstudio.com/en-us/products/visual-studio-community-vs

 

 

Microsoft/Windows-universal-samples

윈도우 10 UWP 앱 셈플 모음

https://github.com/Microsoft/Windows-universal-samples

 

 

 

2. 윈도우폰 앱 개발 강좌 & 책 소개


A Developer's Guide to Windows 10 Preview

윈도우10 개발자 가이드 (영문)

http://www.microsoftvirtualacademy.com/training-courses/a-developers-guide-to-windows-10-preview?prid=ch9videolink&CR_CC=200617771


Windows 10 Technical Preview tools

http://dev.windows.com/en-US/windows-10-developer-preview-tools


초보용 강좌(영문)

Windows 10 development for absolute beginners

https://channel9.msdn.com/Series/Windows-10-development-for-absolute-beginners

 

Windows Phone 8.1 Development for Absolute Beginners

http://channel9.msdn.com/Series/Windows-Phone-8-1-Development-for-Absolute-Beginners


C#을 사용한 Windows 스토어 앱 개발 기초 과정 빠른 시작

http://www.microsoftvirtualacademy.com/training-courses/4d547965-2855-44f2-ae92-df155949f079

MVA에 등록되어있는 기초 과정입니다. 진행자는 영어로 이야기하지만, 한글 자막이 친절하게 표시됩니다. ^^


Jump Start(영문)

Building Apps for Windows Phone 8.1 Jump Start

http://www.microsoftvirtualacademy.com/training-courses/building-apps-for-windows-phone-8-1-jump-start


WP 8.1 Jump Start Slides PDFs
https://onedrive.live.com/?cid=635551367e71f947&id=635551367E71F947!9356&ithint=folder,.pdf&authkey=!AOeQrnexu96M3O8

무료 EBook 모음(영문)

LINQ, Windows 8 store app dev 관련 서적

http://1drv.ms/1D1Y1yU

 

추천!!

http://www.c-sharpcorner.com/ebooks/universal-windows-platform-complete-solution


국내 출판 서적

내용 확인은 못했습니다. 그리고 8.1 버전은 아닙니다. Universal에 관한 내용도 없을 것이라 생각합니다.

윈도우 런타임을 이용한 실전 앱 개발 Windows 8 앱 개발 가이드

찰스 페졸드의 Programming Windows C#과 XAML을 이용한 윈도우 앱 개발

 

Developer's Guide to Windows 10 Preview

https://channel9.msdn.com/Series/Developers-Guide-to-Windows-10-Preview

 

 

MVA 강좌

 

윈도우 서버 2012 R2 주요기술 - http://bit.ly/1IdwLOC
엑티브 디렉토리 이해 - http://bit.ly/1CJToqk
클라우드 시대에 적절한 Identity 관리방안 - http://bit.ly/1CJTxKk

 

Windows 8.1: 개발자용 새로운 API 및 기능

https://msdn.microsoft.com/ko-kr/library/windows/apps/dn751496.aspx
 

Azure Mobile Services and API Management

애저 모바일 서비스 기초 강좌

http://www.microsoftvirtualacademy.com/training-courses/azure-mobile-services-and-api-management


Universal Windows App Development with Cortana and the Speech SDK

코타나, 스피치 SDK로 만드는 윈도우 앱 강좌

http://www.microsoftvirtualacademy.com/training-courses/universal-windows-app-development-with-cortana-and-the-speech-sdk




3. ComCamp 2015

앱 개발 속도와 성능 향상(ppt와 소스 폴더)

http://1drv.ms/1EQYR0Y


유니버셜 앱의 가능성(ppt)

http://designn.me/2015/02/22/comcamp-%eb%b0%9c%ed%91%9c%ec%9e%90%eb%a3%8c-%ec%9c%a0%eb%8b%88%eb%b2%84%ec%85%9c-%ec%95%b1%ec%9d%98-%ea%b0%80%eb%8a%a5%ec%84%b12015-1-31/
 

 

4. 개발자 센터

개발자 등록 한번으로 평생 회원,

기본적인 앱 디자인 템플릿은 디자인 메뉴를 들어가 보시면 많이 있어요

 

 

http://dev.windows.com/ko-kr


Windows app dev skills 정리 중

http://1drv.ms/1CCyIWz

 

5. 개발자 지원

 

Dev Center Benefits

가입 후 등록한 앱을 알려주면 심사해서 각 단계별로 여러가지 자원을 제공

https://devcenterbenefits.windows.com/#!/

 

DVLUP

가입을 하면 앱 개발과 관련된 미션을 주고 각 미션을 해결하면, 뱃지와 포인트를 줘서..개발에 대한 목표를 설정하도록 하는 사이트

https://www.dvlup.com/

 

 

6. json2csharp

json 데이터를 넣어주고 Generate 버튼을 누르면 c# 클래스를 만들어주는 사이트

http://json2csharp.com/

 

 

7. Adobe Color

앱 디자인 리소스

https://color.adobe.com/ko/explore/newest/?time=all

 

 

8. Font Squirrel

공짜 폰트 제공 사이트, 앱 디자인 리소스

http://www.fontsquirrel.com/

 

 

9. 아이콘

NounProject

앱 아이콘 사이트, 앱 디자인 리소스를 제공하며, 사용시 라이센스를 표시해야 합니다. 라이센스를 표시 하지 않으려면, 구입하시면 됩니다.

http://thenounproject.com/

 

Syncfusion Metro Studio

사이트 가입 후 다운로드 받으셔서 설치 하신 후 제공 받은 키를 입력하시면 됩니다.

아이콘은 이미지나 Path형태로 사용할 수 있으며, 문자 케릭터도 사용할 수 있습니다. 추천합니다.

Metro Studio 버전 3이 나왔습니다!! 강추

http://www.syncfusion.com/downloads/metrostudio


syncfusion에 가입하시면 무료 다운로드가 가능합니다. 이미 가입하신분은 로그인 하시면 다운 받으실 수 있습니다.(이 외에도 많은 무료 e-book이 존재 합니다.)

http://www.syncfusion.com/resources/techportal/ebooks/windows8.1

윈도우 유니버셜 앱 개발 무료 e-book입니다.(영문)
레벨은 초보단계이며,  처음에 개념에 대해서 정리를 한번 해주고 시작하고 있습니다. 책 내용은 쉽게 정리가 되어 있는 듯합니다.


The XAML Project

아이콘을 Path 형태로 제공합니다.

http://www.thexamlproject.com/

 

10. 페이스북 그룹

 

윈도우 앱 개발(UWP, Universal)

https://www.facebook.com/groups/w10app/

 

윈도우 & 윈도우폰 앱 개발 모임

https://www.facebook.com/groups/vapps/

 

 

12. 디자인 > 이미지 출력

로컬 이미지를 화면에 출력하기 위한 가장 좋은 방법에 대한 블로그 포스트

Creating beautiful views for local files

http://blogs.msdn.com/b/windowsappdev/archive/2012/07/23/creating-beautiful-views-for-local-files.aspx


 

일러스트에서 작업한 내용을 xaml 코드로 변경시키기 위해 Express Design 프로그램을 사용합니다.

Express Design을 다운로드 받는 링크 입니다.

http://www.microsoft.com/expression/eng/

 

13. MSDN 뉴스레터 가입

매월 발행되는 MSDN 잡지가 pdf 형태로 배달이 됩니다. 최신 기술에 대한 기사를 만나세요
http://msdn.microsoft.com/ko-KR/newsletter.aspx

 

 

 

14. 개발 지원 툴

 

Windows Phone IsoStoreSpy (a cool WP8.1 + WP8 Isolated Storage Explorer)

윈도우 폰의 저장소에 접근해서 파일 업/다운로드를 할 수 있도록 지원해주는 툴

https://isostorespy.codeplex.com/releases

 

 

DB Browser for SQLite

SQLite를 이용해서 만들어진 database 파일의 내용을 보고, 편집하는 용도로 사용되는 툴

https://github.com/sqlitebrowser/sqlitebrowser/releases

 

New: Azure Storage Explorer 6 Preview 3 (August 2014)

Azure Storage를 탐색할 수 있는 탐색기 입니다.

http://azurestorageexplorer.codeplex.com/

 

15. 블로그 자체 컨텐츠

MVVM pattern


MVVM Pattern - 동영상 & 소스
http://kaki104.tistory.com/entry/MVVM-Pattern-동영상-소스


Using MVVM Pattern
http://kaki104.tistory.com/entry/kaki104-is-using-that-MVVM-pattern


Using MVVM Pattern in Portable Library
http://kaki104.tistory.com/entry/Using-MVVM-Pattern-in-Portable-Library



KTour 프로젝트 소개 및 0.7 소스

2년전에 진행했던 전사 육성 프로젝트..큰 성과는 없었지만..쿨럭 이 외에도 찾아보면 좀 있습니다;;
http://kaki104.tistory.com/entry/KTour-프로젝트-소개-및-07-소스

 

 

디자인 템플릿

http://kaki104.tistory.com/334

 

 


 

Posted by kaki104

댓글을 달아 주세요

어제 post man으로 테스트했던 내용을 1시간 30분만에 universal sample에 상쾌하게 코딩을 완료했다. 물론 정리가 완전하게 된 코딩은 아니다. 정리하기전 코딩으로 포스트 한다. 하지만, 난 정말 PCL로 정리된 소스가 필요해!!! 라고 생각 한다면, 앱 개발 페이스 북 그룹에 가입하면 Git에서 관리하는 소스 코드를 볼 수 있을 것이다.

비록, 비공개 그룹이기는 하지만, 가입에 특별한 제한은 없으니 관심있는 분은 가입하도록 한다.

 

윈도우 & 윈도우폰 앱 개발 모임

https://www.facebook.com/groups/vapps/

간단히 소개 하자면, 냄새 풀풀나는 남자 개발자들이 대부분 득실거린다. 모임 후기 링크를 추가로 올릴까 생각하고 찾아봤더니, 이상한 사람이 서있는 사진이 있어서 포기한다. ㅡㅡ;;

 

전체 소스는 맨 아래 추가하였다.

 

"모임 후기 링크 올리지?"

 

"싫다"

"인터넷에 이상하게 나온 내 사진이 돌아다니는 것을 원치 않는다."

 

"Source가 이상한데, Target이 좋을리 없잔아?"

 

"......"

 

 

1. 기본 소스

UniversalSample 소스

 

2. BitlyHelper.cs

 

중요한 부분을 먼저 설명하겠다.

 

        /// <summary>
        /// Bitly 인증
        /// </summary>
        /// <param name="id"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        public async Task<bool> BitlyAuthAsync(string id, string password)
        {
            var con = string.Format("grant_type=password&username={0}&password={1}", id, password);
            var content = new StringContent(con, Encoding.UTF8, "application/x-www-form-urlencoded");

            var hc = new HttpClient();
            hc.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", "ZDdiZTg1ZTYyZGZhNjE2YjllY2UwNWU5YjJkOTYzODQwNzI0ODEyMTo1OTE2MjIxNjViZDhkMmU3MDI4YmMwYjBjN2VmYTQ2OWVkY2NjZTYy");
            var result = await hc.PostAsync("https://api-ssl.bitly.com/oauth/access_token", content);
            if (result == null) return false;

            var jobj = JsonConvert.DeserializeObject<JObject>(await result.Content.ReadAsStringAsync());
            if (jobj == null) return false;

            _accessToken = jobj["access_token"].ToString();

            return true;
        }

 

HttpClient를 이용해서 PostAsync 메소드를 이용하기 위해서는 HttpContent가 필요한데 그 중에 하나가 StringContent이다.

con에는 사용자 아이디와 비밀번호를 파라메터로 받아서 넣어준다.

 

var con = string.Format("grant_type=password&username={0}&password={1}", id, password);

var content = new StringContent(con, Encoding.UTF8, "application/x-www-form-urlencoded");

 

 

hc헤더에 Authorization을 넣기 위해서는 new AuthenticationHeaderValue를 이용해서 생성한 후 사용한다. 그런데, 여기서 ZDdiZ...이 부분을 어떻게 만드는지 설명을 하고 넘어가야 하지 않을까 한다.

 

hc.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", "ZDdiZTg1ZTYyZGZhNjE2YjllY2UwNWU5YjJkOTYzODQwNzI0ODEyMTo1OTE2MjIxNjViZDhkMmU3MDI4YmMwYjBjN2VmYTQ2OWVkY2NjZTYy");

 

 

이전 포스트를 보면 Client ID와 Client Secret가 있었던것을 기억할 것이다. 기억이 나지 않으면 http://kaki104.tistory.com/248 링크를 눌러서 다시 본다.

 

이 두개를 Client ID + ":" + Client Secret를 해서 하나의 문자열로 구성한 후 http://www.base64encode.org/ 사이트로 이동 후 상단에 문자열을 붙여 넣고, Encode버튼을 눌러주면 만들어지는 문자열이다.

API Document에는

Where Authorization: is set to "Basic " + base64encode(client_id + ":" + client_secret). An example response might look like

위와 같이 표시되어있다. 변환된 문자열이 위와 다르기는 하지만 위에 것도 인증이 되는 것을 확인했다.

 

 

"광고닷!"

 

"스크린샷을 만들다 보니 본이 아니게 들어갔다."

 

"포토샵으로 잘라내면 돼잖아?"

 

"귀찮다"

 

ㅡ,.ㅡ 퍽퍽펑!!

 

 

반환된 결과를 JObject로 변환 후 access_token을 찾아서 _accessToken에 넣는다. JObject로 변환해 놓으면 특정 값을 찾을 때 편리하다. 해...보니 OAuth 2.0도 별거 없는것 같다. @,.@

 

var jobj = JsonConvert.DeserializeObject<JObject>(await result.Content.ReadAsStringAsync());
if (jobj == null) return false;

_accessToken = jobj["access_token"].ToString();

 

 

GetShorternUrl 메소드에 대한 설명은 생략한다. 그냥 보면 쉽게 알 수 있을 것이라 생각한다.

 

전체 소스

 

using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace UniversalSample
{
    public class BitlyHelper
    {
        /// <summary>
        /// Access Token
        /// </summary>
        internal string _accessToken ;

        private static BitlyHelper _instance;

        /// <summary>
        /// BitlyHelper Instance
        /// </summary>
        public static BitlyHelper Instance
        {
            get { return _instance = _instance ?? new BitlyHelper(); }
        }

        /// <summary>
        /// Bitly 인증
        /// </summary>
        /// <param name="id"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        public async Task<bool> BitlyAuthAsync(string id, string password)
        {
            var con = string.Format("grant_type=password&username={0}&password={1}", id, password);
            var content = new StringContent(con, Encoding.UTF8, "application/x-www-form-urlencoded");

            var hc = new HttpClient();
            hc.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", "ZDdiZTg1ZTYyZGZhNjE2YjllY2UwNWU5YjJkOTYzODQwNzI0ODEyMTo1OTE2MjIxNjViZDhkMmU3MDI4YmMwYjBjN2VmYTQ2OWVkY2NjZTYy");
            //YjA3YjIyYWFlYTVjMWQ2NDJkZTM3ZThlMmIzZjI0OGQ2Y2UyMmVmODoyYmQyZThjODdhMjEwNDM5YmY4NmFlOGQ3ODkyODQ4YWQxNzRmYmEz
            //ZDdiZTg1ZTYyZGZhNjE2YjllY2UwNWU5YjJkOTYzODQwNzI0ODEyMTo1OTE2MjIxNjViZDhkMmU3MDI4YmMwYjBjN2VmYTQ2OWVkY2NjZTYy
            var result = await hc.PostAsync("https://api-ssl.bitly.com/oauth/access_token", content);
            if (result == null) return false;

            var jobj = JsonConvert.DeserializeObject<JObject>(await result.Content.ReadAsStringAsync());
            if (jobj == null) return false;

            _accessToken = jobj["access_token"].ToString();

            return true;
        }

        /// <summary>
        /// Shorten
        /// </summary>
        /// <param name="longUrl"></param>
        /// <returns></returns>
        public async Task<string> GetShortenUrl(string longUrl)
        {
            var returnValue = string.Empty;

            var hc = new HttpClient();
            var uri = string.Format("https://api-ssl.bitly.com/v3/shorten?access_token={0}&longUrl={1}", _accessToken, longUrl);
                //longUrl의 Encode해서 보내야한다

                var encodedUrl = Uri.EscapeDataString(longUrl);
                var uri = string.Format("
https://api-ssl.bitly.com/v3/shorten?access_token={0}&longUrl={1}", _accessToken, encodedUrl);

            var result = await hc.GetStringAsync(uri);
            if (result == null) return returnValue;

            var jobj = JsonConvert.DeserializeObject<JObject>(result);
            if (jobj == null) return returnValue;

            var data = jobj["data"];
            if (data == null) return returnValue;

            returnValue = data["url"].ToString();

            return returnValue;
        }
    }
}

3. MainPageVM.cs

 

추가한 부분에 대한 소스만 올리겠다. 전체 소스는 하단의 전체 소스를 참고한다. 그리고 이곳에 올리지는 않치만 LongUrl 프로퍼티 채인지 이벤트 처리하는 부분을 생성자에 추가해 놓았다. LongUrl프로퍼티가 변경되면 커맨드의 활성/비활성 여부를 변경해 주어야 하기 때문이다.

 

        /// <summary>
        /// Bitly Auth 여부
        /// </summary>
        public bool IsBitlyAuth
        {
            get { return _isBitlyAuth; }
            set { SetProperty(ref _isBitlyAuth, value); }
        }

        /// <summary>
        /// Long Url
        /// </summary>
        public string LongUrl
        {
            get { return _longUrl; }
            set { SetProperty(ref _longUrl, value); }
        }

        /// <summary>
        /// Shorten Url
        /// </summary>
        public string ShortUrl
        {
            get { return _shortUrl; }
            set { SetProperty(ref _shortUrl, value); }
        }

        /// <summary>
        /// BitlyAuth Command
        /// </summary>
        public RelayCommand BitlyAuthCommand
        {
            get
            {
                return _bitlyAuthCommand = _bitlyAuthCommand ?? new RelayCommand(async args =>
                {
                    IsBitlyAuth = await BitlyHelper.Instance.BitlyAuthAsync("kaki104", "zkzlroqkfxptmxm");
                    CheckCommands();
                }, () => !IsBitlyAuth);
            }
        }

        /// <summary>
        /// GetShortenUrl Command
        /// </summary>
        public RelayCommand GetShortenUrlCommand
        {
            get
            {
                return _getShortenUrlCommand = _getShortenUrlCommand ?? new RelayCommand(async args =>
                    {
                        ShortUrl = await BitlyHelper.Instance.GetShortenUrl(LongUrl);
                    }, () => IsBitlyAuth && string.IsNullOrEmpty(LongUrl) == false);
            }
        }

        /// <summary>
        /// 커맨드 활성/비활성 확인
        /// </summary>
        private void CheckCommands()
        {
            BitlyAuthCommand.RaiseCanExecuteChanged();
            GetShortenUrlCommand.RaiseCanExecuteChanged();
        }

 

StackOverflow에서는 글 작성할 때 앞에서 4칸을 띄우면 소스로 인식해서 택스트 박스에 이쁘게 들어가는데, 이넘의 블로그는 그런 기능이 없다. 그나마 다행인 것은 <>를 html로 인식해서 않보이게 하는 것이 없다는 것이다.

 

4. MainPage.xaml

 

추가된 부분에 대한 소스만 올린다.

 

            <HubSection MinWidth="400" Header="Bitly" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch">
                <DataTemplate>
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="40"/>
                            <RowDefinition Height="40"/>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition Height="40"/>
                        </Grid.RowDefinitions>
                        <Button Content="Bitly Auth" Command="{Binding BitlyAuthCommand, Mode=OneWay}" Width="120"/>
                        <Button Grid.Row="1" Content="Shorten Url" Command="{Binding GetShortenUrlCommand, Mode=OneWay}" Width="120"/>
                        <TextBox Grid.Row="2" TextWrapping="Wrap" Text="{Binding LongUrl, Mode=TwoWay}"/>
                        <TextBox Grid.Row="3" Text="{Binding ShortUrl, Mode=OneWay}" IsReadOnly="True"/>

                    </Grid>
                </DataTemplate>
            </HubSection>

 

5. 실행화면

 

음 이미지 추가할 때 매번 느끼는 짜증이 하나 있는데 이미지 사이즈가 커서 오른쪽 끝에 내용이 않보이기 때문에 이미지 너비를 일부러 작게 줄이는데, 사이즈를 입력하고 바로 아래 이미지를 클릭하면 작게 바꾸었던 이미지가 다시 원래 크기로 돌아가는 버그가 있다. 이게 한두장 줄일때는 그냥 그런가 하고 넘어가는데, 매번 이런 식이라... 짬뽕난다. 된장!쌈장!꼬추장!

 

Bitly Auth 버튼 클릭 

 

인증이 완료되면 인증 버튼이 비활성화 된다.

 

url을 입력하고 tab키를 누르면 Shorten url 버튼이 활성화 된다.

 

Shorten Url 버튼을 클릭하면 짧은 url이 하단에 출려된다. 선택 후 마우스 오른쪽 버튼을 눌러서 복사하기를 선택 한다.

 

브라우저 주소창에 붙여 넣기를 하고 엔터를 입력한다.

 

원하는 결과 페이지 출력

 

"위에 이미지 링크 눌러보니 OneDrive 로그인 해야한다고 하는데?"

 

"그렇다"

 

"일부러 그런거야?"

 

"하다보니 그렇게 됐다"

 

"로그인 않해도 볼 수 있는 링크로 바꿔주지?"

 

"귀찮다"

 

"...." 폭폭폭폭 펑~

 

 

"윈폰 화면도 수정 한거야?

 

"직접 수정할 수 있는 기회를 주기 위해 않했다"

 

"귀찮아서 않한거지?"

 

"...음...그렇다"

 

ㅡ,,ㅡ 슈우욱~콰앙앙!!!!

 

 

6. 소스

 

UniversalSample_20140801.zip

 

소스에서 사용한 아이디/비밀번호/Client Id/Client Secret은 자신의 것으로 변경해서 사용하도록 한다. 포스트 등록 후 변경한다.

 

Posted by kaki104

댓글을 달아 주세요