방법은 아래 링크에 자세히 나와있다.

https://docs.microsoft.com/ko-kr/xamarin/xamarin-forms/platform/wpf

 

주의할 점

1. 프레임워크 대상 버전이 4.7 이상이어야한다.

2. MainWindow.xaml 내용의 처음을 Window -> wpf:FormsApplicationPage 으로 변경해야한다.

3. MainWindow.xaml.cs 에서 상속을 FormsApplicationPage 으로 변경해야한다.

4. 링크에도 나와있듯이 WPF 프로젝트에 설치한 Xamarin.Forms 의 버전과 다른 프로젝트들과 버전을 맞춰야한다.

 

아래는 기본 자마린 솔루션에서 WPF 프로젝트만 추가한 내용이다.

XamarinStudy.z01

XamarinStudy.z02

XamarinStudy.zip

 

아래는 실행한 WPF 결과

 

SMSConvey 어플을 좀 업그레이드 하려고 하는데 갑자기 아래와 같은 에러가 났다.

Detected problems with app native libraries(please consult log for details): libmonosgen-64bit-2.0.so: unauthorized access to "/system/lib64/libsqlite.so"

오레오 업데이트 하고 나서 에러가 발생된것 같은데

찾아보니 7.0 부터 발생한 내용이라고 하던데.. 일단 내가 7.0 일때 테스트 할때는 저런 에러가 발생되지 않았다.

SQLite 사용시 발생되는 에러로 해결방법은

Nuget 패키지 관리자에서 "sqlite-net" 을 제거하고 "sqlite-net-pcl" 을 설치하면 된다.

 

**추가 (아래 방식으로 완전히 해결됨)

아래와 같이 package 를 설치하고

 

아래 두파일을 삭제합니다.

 

 

참고

https://forums.xamarin.com/discussion/78234/android-libmonosgen-library-sqlite-problems-with-app-native-libraries

앱상에서 권한을 준항목들이

오레오 버전 부터는 몇몇 특정 권한들이 권한이 빠지고 다시 사용자에게 권한허용을 받아야합니다.

만약 오레오 이전 버전에 잘 동작하던게 오레오 버전에서 동작하지 않는다면

권한으로 인해 먼가 동작하지 않는지 의심해 봐야합니다.

아래는 Xamarin.Android 에서 권한을 체크하고 권한이 없는 항목에 대해서

권한 허용을 요청하는 코드입니다.

(제가 문제가 되었던 권한는 android.permission.READ_SMS,  android.permission.WRITE_EXTERNAL_STORAGE,android.permission.SEND_SMS 입니다.)

// 권한이 없어 권한허용을 물어볼 항목들
List<string> permissions = new List<string>();

// 권한이 있는지 확인할 항목들
List<string> checkPermissions = new List<string>();
checkPermissions.Add(Manifest.Permission.AccessNetworkState);
checkPermissions.Add(Manifest.Permission.Internet);
checkPermissions.Add(Manifest.Permission.WriteSms);
checkPermissions.Add(Manifest.Permission.BroadcastSms);
checkPermissions.Add(Manifest.Permission.BroadcastWapPush);
checkPermissions.Add(Manifest.Permission.ReceiveBootCompleted);
checkPermissions.Add(Manifest.Permission.ReceiveMms);
checkPermissions.Add(Manifest.Permission.ReceiveSms);
checkPermissions.Add(Manifest.Permission.SendSms);
checkPermissions.Add(Manifest.Permission.WriteExternalStorage);
checkPermissions.Add(Manifest.Permission.ReadSms);

// 권한이 있는지 확인하고 없다면 체크할 목록에 추가합니다.
foreach (var checkPermission in checkPermissions)
{
    if (ContextCompat.CheckSelfPermission(this, checkPermission) != (int)Permission.Granted)
    {
        permissions.Add(checkPermission);
    }
}

// 권한없는 항목에 대해서 추가 허용을 할지 물어보는 팝업을 띄웁니다.
ActivityCompat.RequestPermissions(this, permissions.ToArray(), 1);

위 코드를 추가하면 앱 실행시 아래처럼 허용을 묻는 팝업이 뜹니다.

 

없던 권한이 3가지 인데 알아서 권한을 묶을수 있는것 들은 묶어서 팝업이 뜨는것 같네요

다시 말하지만 기존에 AndroidManifest.xml 에 권한이 포함되있더라도

오레오버전부터는 문제가 되는 권한들이 있으므로 위 작업이 필요합니다.

 

참고

https://developer.android.com/about/versions/oreo/android-8.0-changes?hl=ko

 

Xamarin Forms 으로 모바일 개발을 하다보면

xaml 에 대한 화면을 미리 보고 싶을때가 있다. (아니 미리보는게 편하다.)

다행히 Xamarin.Forms Previewer 가 있어 사용해 봤는데 너무 느려서

사용을 안해왔다;; 코딩을 바꾸는데도 버벅인다;;

차라리 디바이스에 계속 배포해서 확인하는게 더 빠르다.

그런데 가끔 유투브 동영상을 보면 고릴라 플레이어로 디자인을 미리 보기 하면서 코딩하는걸 봐왔다..

그런데 고릴라 플레이어 셋팅하는게 그리 간단하지 않아 보여

계속 미루다가 드디어 오늘 셋팅을 완료했다.

그래서 과정을 정리해본다.

 

일단 아래 블로그는 mac 환경에서 환경 셋팅을 한 내용이다.

http://picory.com/entry/Forns-XAML-Previewer-Gorilla-Player

난 Window10 으로 시도했다.

 

1. 고릴라 플레이어 사이트에서 계정을 만든다.

아래링크로 들어가 REGISTER NOW 를 클릭한다.

https://grialkit.com/gorilla-player/

계정을 만든다. 암호는 대문자가 포함되어야한다. (특수문자는 없어도 됨)

 

계정을 만들었으면 계정 메일로 아래와 같이 메일이 오고 링크를 클릭하여 메일 인증을 한다.

메일 인증 완료!

 

2. 고릴라 플레이어(Gorilla Player)를 다운로드 한다.

아래 링크로 들어가서

https://grialkit.com/gorilla-download/

Windows 버전을 다운로드 하고 설치를 한다.

가끔사이트가 먹통이 될때가 있어 설치파일을 첨부한다.

설치 과정은 아래와 같다. 특별한 건 없다.

 

3. 고릴라 플레이어를 실행한다.

고릴라 플레이어 실행 과정이다.

아래 그림에서는 당연히 동의를....

설치할걸 체크해야하는데 기본적으로 알아서 잘 체크가 되어있다.

체크 추가 없이 다음.. (Install addin for Visual Studio, Install Player + Sample)

설치 진행중...

설치 완료!!

아래 처럼 고릴라 아이콘이 시계표시줄에 표시되어있으면 일단 정상적으로 설치 및 실행이 된것이다.

 

4. 시뮬레이터 셋팅

실행(Continue)을 하면 아래와 같은 화면이 나온다.

친절하게 설명이 잘되어있다.

아래 부분이 조금 헷깔리는데 구글 플레이에서 고릴라플레이어앱을 다운받거나 직접 소스를 열어서 설치를 할수 있다.

시뮬레이터에 설치할것이므로 소스를 열어서 직접 설치를해야한다.

아래 화면에서 빨간색 버튼을 클릭한다. 그러면  Player.sln 솔루션 이 열린다.

아래와 같이 솔루션이 열리면 구동하고자 하는 시뮬레이터를 선택한 후 Run 한다.

그럼 아래 처럼 시뮬레이터에 고릴라 플레이어앱이 설치가 된다.

설치된 앱을 구동해 보자.

앱을 구동하면 Connecting 을 기다리고 있다.

 

5. xaml 파일 보기

이제 xaml 파일을 열어 디자인이 보여지는지 확인해보자.

Visual Studio 를 열고 도구>Gorilla Player 에서 아래 그림과 같이 Disconnect From Gorilla  으로 되어있는지 확인한다.

만약 안되어있다면 Connect From Gorilla 를 실행해야된다.

이제 xaml 파일을 하나 선택하고 마우스 우측 버튼의 메뉴에서

Stick Gorilla to this XAML 를 클릭한다.

그럼 아래 처럼 시뮬레이터에서 먼가 연결이 진행된다.

엇 에러가 발생되었다.

xaml 에 에러가 있거나 하면 아래 처럼 에러가 표시된다.

마스터 디테일 페이지(MasterDetailPage) 인 경우는 아직 표현하지 못하는것 같다.

아래처럼 xaml 페이지가 정상적으로 시뮬레이터에 표시가 된다.

xaml 의 내용이 시뮬레이터에 보여지고 있다면 아래처럼 우측 상단에 고릴라 아이콘이 나타난다.

xaml 파일을 변경하면 실시간으로 시뮬레이터에 반영된다. (아래 동영상참고)

* 참고

도움말 : https://github.com/UXDivers/Gorilla-Player-Support

* 추가

혹 비쥬얼 스튜디오에 addin 이 제대로 되지 않았다면 직접 addin 쪽만 아래 링크나 첨부파일로 설치를 진행하면된다.

https://marketplace.visualstudio.com/items?itemName=UXDivers.GorillaPlayerVisualStudioddin

UXDivers.Artina.Player.VSAddin.vsix

 

 

Async programming is all the rage in mobile app development for good reasons. Using async methods for long running tasks, like downloading data, helps keep your user interface responsive, while not using async methods, or the improper use of async/await, can cause your app’s UI to stop responding to user input until the long running task completes. This can result in a poor user experience, which can then lead to poor reviews on the app stores, which is never good for business.

Today we’ll take a look at the use of async and how to utilize it to prevent jerky and unexpected behaviors in a ListView.

What is async/await?

The async and await keywords were introduced in .NET 4.5 to make calling async methods easier and to make your async code more easily readable. The async/await API is syntactic sugar that uses the TPL (Task Parallel Library) behind the scenes. If you wanted to start a new task and have code run on the UI thread after the task completes prior .NET 4.5, your code would have looked something like this:

That’s not very pretty. Using async/await, the above becomes:

The above code gets compiled behind the scenes to the same TPL code as it does in the first example, so as noted, this is just syntactic sugar, and how sweet it is!

Using Async: Pitfalls

In reading about using async/await, you may have seen the phrase “async all the way” thrown around, but what does that really mean? Simply put, it means that any method that calls an async method (i.e. a method that has the async keyword in its signature) should use the await keyword when calling the async method. Not using the await keyword when calling an async method can result in exceptions that are thrown being swallowed by the runtime, which can cause issues that are difficult to track down. Using the await keyword requires that the calling method also use the async keyword in its signature. For example:

This poses a problem if you want to call an async method using the await keyword when you can’t use the async modifier on the calling method, for instance if the calling method is a method whose signature can’t use the async keyword or is a constructor or a method that the OS calls, such as GetView in an Android ArrayAdapter or GetCell in an iOS UITableViewDataSource. For example:

 

As you may know, an async method has to return either void, Task, or Task<T>, and returning void should only be used when making an event handler async. In the case of the GetView method noted above, you need to return an Android View, which can’t be changed to return Task<View> as the OS method that calls it obviously does not use the await keyword and so can’t handle a Task<T> being returned. Thus you can’t add the async keyword to the above method and therefore can’t use the await keyword when calling an async method from the above method.

To get around this, one might be tempted, as I have been in the past, to just call a method from GetView (or similar method where the signature can’t be changed regardless of the platform) as an intermediate method, and then call the async method from the intermediate method:

 

The problem here is that IntermediateMethod is now an async method and thus should be awaited just like the MyMethodAsync method needed to be. So, you have gained nothing here, as IntermediateMethod is now async and should be awaited. In addition, the GetView method will continue running all of the code after calling IntermediateMethod(), which may or may not be desirable. If the code following the call to IntermediateMethod() depends on the results of the IntermediateMethod(), then it isn’t desirable. In such a scenario, you may be tempted to use the Wait() method call (or Result property) on the async task, e.g.:

 

Calling Wait() on the async method causes the calling thread to pause until the async method completes. If this is the UI thread, as would be the case here, then your UI will hang while the async task runs. This isn’t good, especially in an ArrayAdapter that is supplying the data for the rows of a ListView. The user will not be able to interact with the list view until the data for all of the rows has been downloaded, and scrolling will likely be jerky and/or completely non-responsive, which isn’t a good user experience. There’s also a Result property you can call on the async task. This would be used if your async task was returning data by using Task<T> as the return type of the async method. This would also cause the calling thread to wait for the result of the async task:

 

In fact doing the above may cause your UI to hang completely and for the ListView never to be populated, which is a non-starter. It may also just be jerky:

JerkyListView

In general, you should avoid using Wait() and Result, especially on the UI thread. In the iOS and Android sample projects linked at the end of this blog, you can look in ViewControllerJerky and MainActivityJerky respectively to see this behavior. Those files are not set to compile in the sample projects.

Using Async All the Way

So how do I get “async all the way” in this scenario?

One way around the above problems is to revert to the old TPL upon which async/await is based. You’re going to use TPL directly, but only once to start the chain of async method calls (and to start a new thread right away). Somewhere down the line the TPL will be used directly again, as you need to use TPL to start a new thread. You can’t start a new thread using only the async/await keywords, so some method down the chain will have to launch the new thread with TPL (or another mechanism). The async method that launches a new thread will be a framework method, like a .NET HttpClient async method in many, if not most, cases. If not using async framework methods, then some method of yours down the chain will have to launch a new thread and return Task or Task<T>.

Let’s start with an example using GetView in an Android project (though the same concept will work for any platform, i.e. Xamarin.iOS, Xamarin.Forms, etc.) Let’s say I have a ListView that I want to populate with text downloaded from the web dynamically (more likely one would download the whole list of strings first and then populate the list rows with the already downloaded content, but I’m downloading the strings row by row here for demonstration purposes, plus there are occasions where one may want to do it this way anyway). I certainly don’t want to block the UI thread waiting for the multiple downloads; rather, I would like the user to be able to start working with the ListView, scroll around, and have the text appear in each ListView cell as the text gets downloaded. I also want to make sure that if a cell scrolls out of view, that when it is reused it will cancel loading the text that is in the process of being downloaded and start loading new text for that row instead. We do this with TPL and cancellation tokens. Comments in the code should explain what’s being done.

 

In a nutshell, the above method checks to see if this is a reused cell and, if so, we cancel the existing async text download if still incomplete. It then loads placeholder text into the cell, launches the async task to download the correct text for the row, and returns the view with placeholder text right away, thereby populating the ListView. This keeps the UI responsive and shows something in the cell while the launched task does its work of getting the correct text from the web. As the text gets downloaded, you’ll see the placeholders change to the downloaded text one-by-one (not necessarily in order due to differing download times). I added a random delay to the async task to simulate this behavior since I’m making such a simple, quick request.

Here’s the implementation of GetTextAsync(...):

 

Note that I can decorate the lambda passed into Task.Run() with the async keyword, thus allowing me to await the call to my async method, and thereby achieving “async all the way.” No more Jerky ListView!

SmoothListView

See it in action

If you want to see the above in action for Xamarin.iOS, Xamarin.Android, and Xamarin.Forms, check it out on my GitHub repo. The iOS version is very similar to the above, the only difference being in how I attach the CancellationTokenSource to the cell since there is no Tag property as there is in an Android View. Xamarin.Forms, however, does not have a direct equivalent to GetView or GetCell that I’m aware of, so I simulate the same behavior by launching an async task from the main App class constructor to get the text for each row.

Happy async coding!

 

원본 : https://blog.xamarin.com/getting-started-with-async-await/

이미지 URL 주소를 가지고 화면에 표시하고자 할 때 사용한다. 

       private Bitmap GetImageBitmapFromUrl(string url)
        {
            Bitmap imageBitmap = null;

            using (var webClient = new WebClient())
            {
                var imageBytes = webClient.DownloadData(url);
                if (imageBytes != null && imageBytes.Length > 0)
                {
                    imageBitmap = BitmapFactory.DecodeByteArray(imageBytes, 0, imageBytes.Length);
                }
            }

            return imageBitmap;
        }

 

사용은 아래와 같다.

imageView.SetImageBitmap(GetImageBitmapFromUrl(urlString));

 

추가로 아래과 같은 방법이 있는데

제대로 동작하지 않는다.;

                    // 2. FAIL
                    //URL url = new URL(urlString);
                    //Bitmap bitmap = BitmapFactory.DecodeStream(url.OpenStream());
                    //imageView.SetImageBitmap(bitmap);

                    // 3. FAIL
                    //imageView.SetImageURI(Android.Net.Uri.Parse(urlString));

 

 

Grid 를 기본 제공 되는걸 쓰는게 좋겠지만 기능과 Styling 등을 하려면 시간이 많이 소요됩니다.

그렇기 때문에 좋은 컴포넌트가 있다면 쓰는걸 선호하는 편입니다.

Grid 쪽을 찾다보니 Syncfusion 이 눈에 들어왔습니다.

https://www.syncfusion.com/products/xamarin

SfDataGrid 만을 보고 들어왔는데 정말 많은 컨트롤들이 Xamarin 에서 사용가능하도록 되어있는걸 확인 할수 있었습니다.

여기 있는 컨트롤 가지고도 개발하기엔 충분해 보이네요

단 가격이 문제입니다. 그런데 다행히도

Free Community License 라고 해서

개인개발자이거나 연매출이 1백만달러 이하고 5명이하인 회사에는 무료로 사용가능하다고 합니다.

다음에 여기 컨트롤들을 좀 가져다가 써본걸 포스팅 해봐야겠네요

차트부터 우선 궁금합니다.

이제 본론으로 들어가서 SfDataGrid 를 살펴보겠습니다.

우선 사용하기 위해선 Nuget 에서 다운을 해야하는데요

NuGet.org 가 아닌 다른 곳에서 가져와야합니다.

솔루션용 NuGet 패키지 관리로 들어가서

상단의 패키지 소스 항목 옆의 톱니바퀴(설정)를 클릭합니다.

아래와 같은 창이 나오고 우측 상단에 + 을 누르고 추가된 패키지 소스 내용을 변경합니다.

이름은 아무거나 (예: syncfusion package)

소스는 http://nuget.syncfusion.com/nuget_xamarin/nuget/getsyncfusionpackages/xamarin/

으로 설정하고 업데이트 버튼을 한번 클릭해 주고 확인하고 나갑니다.

위 작업이 완료되면 패키지 소스에 내가 추가한 소스를 선택하고 sfdatagrid 로 검색하면 아래 처럼 검색이 됩니다.

솔루션용이라 우측에 각 프로젝트를 선택하도록 되어있는데 모두 선택하고 설치를 진행합니다.

확인...

무료라 고맙다. 동의함.

이제 각 프로젝트에 참조가 추가된걸 확인할수 있습니다.

아직 준비가 끝난게 아닙니다.

Android 는 기본적으로 수행이 가능하나

iOS, UWP 코딩에 초기화하는 내용을 넣어주어야합니다. (OxyPlot 차트 했을 때와 비슷)

iOS (AppDelegate.cs)

using System;
using System.Collections.Generic;
using System.Linq;

using Foundation;
using UIKit;

namespace XamarinFormsStudy.iOS
{
    // The UIApplicationDelegate for the application. This class is responsible for launching the
    // User Interface of the application, as well as listening (and optionally responding) to
    // application events from iOS.
    [Register("AppDelegate")]
    public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
    {
        //
        // This method is invoked when the application has loaded and is ready to run. In this
        // method you should instantiate the window, load the UI into it and then make the window
        // visible.
        //
        // You have 17 seconds to return from this method, or iOS will terminate your application.
        //
        public override bool FinishedLaunching(UIApplication app, NSDictionary options)
        {
            global::Xamarin.Forms.Forms.Init();
            OxyPlot.Xamarin.Forms.Platform.iOS.PlotViewRenderer.Init();
            Syncfusion.SfDataGrid.XForms.iOS.SfDataGridRenderer.Init();

            LoadApplication(new App());

            return base.FinishedLaunching(app, options);
        }
    }
}

UWP (App.xaml.cs)

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

namespace XamarinFormsStudy.UWP
{
    /// <summary>
    /// Provides application-specific behavior to supplement the default Application class.
    /// </summary>
    sealed partial class App : Application
    {
        /// <summary>
        /// Initializes the singleton application object.  This is the first line of authored code
        /// executed, and as such is the logical equivalent of main() or WinMain().
        /// </summary>
        public App()
        {
            this.InitializeComponent();
            this.Suspending += OnSuspending;
        }

        /// <summary>
        /// Invoked when the application is launched normally by the end user.  Other entry points
        /// will be used such as when the application is launched to open a specific file.
        /// </summary>
        /// <param name="e">Details about the launch request and process.</param>
        protected override void OnLaunched(LaunchActivatedEventArgs e)
        {

#if DEBUG
            if (System.Diagnostics.Debugger.IsAttached)
            {
                this.DebugSettings.EnableFrameRateCounter = true;
            }
#endif

            Frame rootFrame = Window.Current.Content as Frame;

            // Do not repeat app initialization when the Window already has content,
            // just ensure that the window is active
            if (rootFrame == null)
            {
                // Create a Frame to act as the navigation context and navigate to the first page
                rootFrame = new Frame();

                rootFrame.NavigationFailed += OnNavigationFailed;

                Xamarin.Forms.Forms.Init(e);
                OxyPlot.Xamarin.Forms.Platform.UWP.PlotViewRenderer.Init();
                Syncfusion.SfDataGrid.XForms.UWP.SfDataGridRenderer.Init();

                if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
                {
                    //TODO: Load state from previously suspended application
                }

                // Place the frame in the current Window
                Window.Current.Content = rootFrame;
            }

            if (rootFrame.Content == null)
            {
                // When the navigation stack isn't restored navigate to the first page,
                // configuring the new page by passing required information as a navigation
                // parameter
                rootFrame.Navigate(typeof(MainPage), e.Arguments);
            }
            // Ensure the current window is active
            Window.Current.Activate();
        }

        /// <summary>
        /// Invoked when Navigation to a certain page fails
        /// </summary>
        /// <param name="sender">The Frame which failed navigation</param>
        /// <param name="e">Details about the navigation failure</param>
        void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
        {
            throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
        }

        /// <summary>
        /// Invoked when application execution is being suspended.  Application state is saved
        /// without knowing whether the application will be terminated or resumed with the contents
        /// of memory still intact.
        /// </summary>
        /// <param name="sender">The source of the suspend request.</param>
        /// <param name="e">Details about the suspend request.</param>
        private void OnSuspending(object sender, SuspendingEventArgs e)
        {
            var deferral = e.SuspendingOperation.GetDeferral();
            //TODO: Save application state and stop any background activity
            deferral.Complete();
        }
    }
}

이제 준비 작업은 완료되었습니다.

 

신규 ContentPage 를 만들어 SfDataGridPage.xaml 를 생성합니다.

SfDataGridPage.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:syncfusion="clr-namespace:Syncfusion.SfDataGrid.XForms;assembly=Syncfusion.SfDataGrid.XForms"
             x:Class="XamarinFormsStudy.SfDataGridPage">
    <ContentPage.Content>
        <StackLayout>
            <syncfusion:SfDataGrid x:Name="dataGrid" />
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

SfDataGridPage.xaml.cs

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace XamarinFormsStudy
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class SfDataGridPage : ContentPage
    {
        private ObservableCollection<OrderInfo> orderInfo;

        public SfDataGridPage()
        {
            InitializeComponent();

            // 바인딩될 데이터를 추가합니다.
            orderInfo = new ObservableCollection<OrderInfo>();
            orderInfo.Add(new OrderInfo(1001, "Maria Anders", "Germany", "ALFKI", "Berlin"));
            orderInfo.Add(new OrderInfo(1002, "Ana Trujilo", "Mexico", "ANATR", "México D.F."));
            orderInfo.Add(new OrderInfo(1003, "Ant Fuller", "Mexico", "ANTON", "México D.F."));
            orderInfo.Add(new OrderInfo(1004, "Thomas Hardy", "UK", "AROUT", "London"));
            orderInfo.Add(new OrderInfo(1005, "Tim Adams", "Sweden", "BERGS", "Luleå"));
            orderInfo.Add(new OrderInfo(1006, "Hanna Moos", "Germany", "BLAUS", "Mannheim"));
            orderInfo.Add(new OrderInfo(1007, "Andrew Fuller", "France", "BLONP", "Strasbourg"));
            orderInfo.Add(new OrderInfo(1008, "Martin King", "Spain", "BOLID", "Madrid"));
            orderInfo.Add(new OrderInfo(1009, "Lenny Lin", "France", "BONAP", "Marseille"));
            orderInfo.Add(new OrderInfo(1010, "John Carter", "Canada", "BOTTM", "Tsawassen"));
            orderInfo.Add(new OrderInfo(1011, "Lauro King", "UK", "AROUT", "London"));
            orderInfo.Add(new OrderInfo(1012, "Anne Wilson", "Germany", "BLAUS", "Mannheim"));
            orderInfo.Add(new OrderInfo(1013, "Alfki Kyle", "France", "BLONP", "Strasbourg"));
            orderInfo.Add(new OrderInfo(1014, "Gina Irene", "UK", "AROUT", "London"));

            // 컬럼 Sorting 활성화 여부
            this.dataGrid.AllowSorting = true;

            // 더블 클릭시 이벤트
            this.dataGrid.GridDoubleTapped += DataGrid_GridDoubleTapped;

            // 데이터 로드시 이벤트
            this.dataGrid.GridLoaded += DataGrid_GridLoaded;

            // 편집여부
            this.dataGrid.AllowEditing = true;

            // 고정 컬럼/열
            this.dataGrid.FrozenRowsCount = 2;

            // 컬럼 이동 여부
            this.dataGrid.AllowDraggingColumn = true;

            // 데이터를 바인딩합니다.
            this.dataGrid.ItemsSource = orderInfo;

        }

        /// <summary>
        /// 데이터 로드시 이벤트입니다.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DataGrid_GridLoaded(object sender, Syncfusion.SfDataGrid.XForms.GridLoadedEventArgs e)
        {
            ActivityIndicator indicator = new ActivityIndicator();
            indicator.IsRunning = true;
            indicator.IsVisible = true;
            indicator.BackgroundColor = Color.Gray;
            this.dataGrid.Children.Add(indicator);
            Task.Delay(2000).Wait();
            indicator.IsRunning = false;
            indicator.IsVisible = false;
        }

        /// <summary>
        /// 더블 클릭시 이벤트입니다.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DataGrid_GridDoubleTapped(object sender, Syncfusion.SfDataGrid.XForms.GridDoubleTappedEventsArgs e)
        {
            OrderInfo selectData = e.RowData as OrderInfo;
            DisplayAlert("Select Value", selectData.CustomerID, "OK");
        }

        /// <summary>
        /// 바인딩할 Test Class 입니다.
        /// </summary>
        class OrderInfo : INotifyPropertyChanged
        {
            private int orderID;
            private string customerID;
            private string customer;
            private string shipCity;
            private string shipCountry;

            public int OrderID
            {
                get { return orderID; }
                set { this.orderID = value; }
            }

            public string CustomerID
            {
                get { return customerID; }
                set { this.customerID = value; }
            }

            public string ShipCountry
            {
                get { return shipCountry; }
                set { this.shipCountry = value; }
            }

            public string Customer
            {
                get { return this.customer; }
                set { this.customer = value; }
            }

            public string ShipCity
            {
                get { return shipCity; }
                set { this.shipCity = value; }
            }

            public OrderInfo(int orderId, string customerId, string country, string customer, string shipCity)
            {
                this.OrderID = orderId;
                this.CustomerID = customerId;
                this.Customer = customer;
                this.ShipCountry = country;
                this.ShipCity = shipCity;
            }

            #region INotifyPropertyChanged implementation

            public event PropertyChangedEventHandler PropertyChanged;

            private void RaisePropertyChanged(String Name)
            {
                if (PropertyChanged != null)
                    this.PropertyChanged(this, new PropertyChangedEventArgs(Name));
            }

            #endregion
        }
    }
}

결과

 

위 사용한 기능 이외에도 다양한 기능들이 있습니다.

참고 링크로 확인해 주세요^^

 

참고

http://nuget.syncfusion.com/nuget_xamarin/nuget/getsyncfusionpackages/xamarin/

https://help.syncfusion.com/xamarin/sfdatagrid/getting-started

GitHub > https://github.com/kjundev/XamarinForms

이번 시간에는 DataBase 관련 첫번째 시간으로 SQLite 를 알아보도록 하겠습니다.

보통 서버와 연결하지 않고 로컬로만 어떤 데이터를 다룰 때는 SQLite 가 가장 편한 것 같습니다.

이번에 소개할건 PlugIn.SQLite 입니다. (Nuget 에서 받을수 있습니다.)

 

프로젝트를 열고 솔루션의 NutGet 패키지 관리로 들어갑니다.

PlugIn.SQLite 로 검색하면 아래처럼 하나가 나옵니다.

오른쪽에 모두 체크하고 설치를 클릭합니다.

확인을 하게되면

각각 프로젝트 별로 (UWP 제외) SQLite 폴더가 생성되고 내부에 코딩이 자동으로 들어가 있게됩니다.

각각 프로젝트에 맞게 namespace 등은 변경해 주어야합니다.

[ Android ]

코드 내용 (프로젝트에 맞게 변경이 완료된 내용입니다.)

using System.IO;
using SQLite;
using Xamarin.Forms;
using XamarinFormsStudy.Android;

[assembly: Dependency(typeof(SQLite_Android))]

namespace XamarinFormsStudy.Android
{
    public class SQLite_Android : ISQLite
    {
        public SQLite_Android() { }
        public SQLiteConnection GetConnection()
        {
            var sqliteFilename = "MySQLiteDB.db3";
            string documentsPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal); // Documents folder
            var path = Path.Combine(documentsPath, sqliteFilename);
            // Create the connection
            var conn = new SQLiteConnection(path);
            // Return the database connection
            return conn;
        }
    }
}

[ iOS ]

코드내용 (프로젝트에 맞게 변경이 완료된 내용입니다.)

using System;
using System.IO;
using SQLite;
using Xamarin.Forms;
using XamarinFormsStudy.iOS;

[assembly: Dependency(typeof(SQLite_iOS))]

namespace XamarinFormsStudy.iOS
{
    public class SQLite_iOS : ISQLite
    {

        public SQLite_iOS()
        {
        }

        public SQLiteConnection GetConnection()
        {
            var sqliteFilename = "MySQLiteDB.db3";
            string documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal); // Documents folder
            string libraryPath = Path.Combine(documentsPath, "..", "Library"); // Library folder
            var path = Path.Combine(libraryPath, sqliteFilename);
            // Create the connection
            var conn = new SQLiteConnection(path);
            // Return the database connection
            return conn;
        }
    }
}

[ UWP ]

UWP 는 참조는 추가 되나 관련 파일이 자동으로 추가되지 않습니다.

수동으로 추가해 주어야합니다.

이식가능 프로젝트에 보면 'SQLite-AppSample' 폴더에 readme.txt 파일이 있는데 여기에 UWP 에 넣어야할 코딩이 나와있습니다.

UWP 프로젝트에서 클래스를 추가하고 위 코딩을 넣습니다.

당연히 프로젝트에 맞에 수정을 해야합니다.

코드 내용 (프로젝트에 맞게 변경이 완료된 내용입니다.)

using System.IO;
using Windows.Storage;
using SQLite;
using Xamarin.Forms;
using XamarinFormsStudy.UWP;

[assembly: Dependency(typeof(SQLite_Uwp))]

namespace XamarinFormsStudy.UWP
{
    public class SQLite_Uwp : ISQLite
    {
        public SQLite_Uwp()
        {
        }

        public SQLiteConnection GetConnection()
        {
            var sqliteFilename = "MySQLiteDB.db3";
            string documentsPath = ApplicationData.Current.LocalFolder.Path;
            var path = Path.Combine(documentsPath, sqliteFilename);
            // Create the connection
            var conn = new SQLiteConnection(path);
            // Return the database connection
            return conn;
        }
    }
}

 

[ 이식가능 프로젝트(PCL) ]

이식가능에 추가된 SQLite 관련된 파일중 TodoItem 은 테스트할 데이터에 대한 테이블의 형태가 정의되어있습니다.

using SQLite;

namespace XamarinFormsStudy
{
    public class TodoItem
    {

        // 키 정보이며 하나씩 증가합니다.
        [PrimaryKey, AutoIncrement]
        public int ID { get; set; }

        public string Text { get; set; }

        public bool Done { get; set; }

        public override string ToString()
        {
            return string.Format("Done : {0}, Text : {1}", Done, Text);
        }
    }
}

그리고 SQLiteSamplePage 에서는 CRUD 관련 코딩 예시가 나와있습니다.

또한 ContentPage 도 제공해 주어 바로 실행하여 확인도 가능합니다.

App.xaml 파일에서 아래 처럼 호출되도록 하고 실행해 보면

MainPage = new SQLiteSamplePage().GetSampleContentPage();

아래 와 같은 결과가 나옵니다.

데이터는 입력항목과 스위치 버튼의 true,false 값이 Add 하면 저장되고 Refresh 하게되면 저장된 값이 아래로 나열됩니다.

 

 

참고

https://www.youtube.com/watch?v=nrXmA-0NoOE&index=26&list=PLpbcUe4chE7-5t2mlamz6yB0qzAfO5Yln

GitHub > https://github.com/knagjun/XamarinForms

 

 

    private void EnableWifi()
   
{
     
string networkSSID = "bbox-xxx";
     
string networkPass = "mypass";

     
WifiConfiguration wifiConfig = new WifiConfiguration();
      wifiConfig
.Ssid = string.Format("\"{0}\"", networkSSID);
      wifiConfig
.PreSharedKey = string.Format("\"{0}\"", networkPass);

     
WifiManager wifiManager = (WifiManager)Application.Context.GetSystemService(Context.WifiService);

     
// Use ID
     
int netId = wifiManager.AddNetwork(wifiConfig); wifiManager.Disconnect();
      wifiManager
.EnableNetwork(netId, true);
      wifiManager
.Reconnect();
   
}

 

 

참고

https://stackoverflow.com/questions/8818290/how-do-i-connect-to-a-specific-wi-fi-network-in-android-programmatically

https://gist.github.com/Cheesebaron/5844638

'C#.NET > Xamarin' 카테고리의 다른 글

(Xamarin Forms) 1.ContentPage  (0) 2017.06.29
(Xamarin Forms) StartTimer  (0) 2017.06.22
(Xamarin) WiFi 접속하기  (0) 2017.06.18
(Xamarin.Android) splash screen 넣기  (0) 2017.06.17
(Xamarin.Android) AdMob 광고 넣기 (2)  (0) 2017.06.15
(Xamarin.Android) AdMob 광고 넣기 (1)  (0) 2017.06.15

SMSCpnvey 가 집에있는 베가넘버6 에서도 에러가 발생되어

컴퓨터와 연결후 디버깅 해보았다..

그러니 에러가 아래 처럼 나타났다.

Android.Views.InflateException: Binary XML file line #1: Error inflating class Toolbar

정확히 알수 없어 exception 으로 빠지게하여 자세히 보니


{Android.Views.InflateException: Binary XML file line #1: Error inflating class Toolbar ---> Java.Lang.ClassNotFoundException: Didn't find class "android.view.Toolbar" on path: DexPathList[[zip file "/data/app/kr.kjun.SMSConvey-2.apk"],nativeLibraryDirectories=[/data/app-lib/kr.kjun.SMSConvey-2, /vendor/lib, /system/lib]]
   --- End of inner exception stack trace ---
  at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in <3fd174ff54b146228c505f23cf75ce71>:0
  at Java.Interop.JniEnvironment+InstanceMethods.CallNonvirtualVoidMethod (Java.Interop.JniObjectReference instance, Java.Interop.JniObjectReference type, Java.Interop.JniMethodInfo method, Java.Interop.JniArgumentValue* args) [0x00089] in <bd30a18775d94dc8b6263aecd1ca9077>:0
  at Java.Interop.JniPeerMembers+JniInstanceMethods.InvokeVirtualVoidMethod (System.String encodedMember, Java.Interop.IJavaPeerable self, Java.Interop.JniArgumentValue* parameters) [0x0005d] in <bd30a18775d94dc8b6263aecd1ca9077>:0
  at Android.App.Activity.SetContentView (System.Int32 layoutResID) [0x00022] in <9ab9faae1b4b4f0da28e7c4ac61e2c78>:0
  at SMSConvey.MainActivity.OnCreate (Android.OS.Bundle bundle) [0x00011] in D:\[01]Source\K\K\SMSConvey\MainActivity.cs:40
  --- End of managed Android.Views.InflateException stack trace ---
android.view.InflateException: Binary XML file line #1: Error inflating class Toolbar
 at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:707)
 at android.view.LayoutInflater.parseInclude(LayoutInflater.java:816)
 at android.view.LayoutInflater.rInflate(LayoutInflater.java:745)
 at android.view.LayoutInflater.inflate(LayoutInflater.java:492)
 at android.view.LayoutInflater.inflate(LayoutInflater.java:397)
 at android.view.LayoutInflater.inflate(LayoutInflater.java:353)
 at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:328)
 at android.app.Activity.setContentView(Activity.java:1934)
 at md5328597d03b579986891d158e9e4b98f4.MainActivity.n_onCreate(Native Method)
 at md5328597d03b579986891d158e9e4b98f4.MainActivity.onCreate(MainActivity.java:33)
 at android.app.Activity.performCreate(Activity.java:5249)
 at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1092)
 at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2189)
 at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2275)
 at android.app.ActivityThread.access$800(ActivityThread.java:142)
 at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1220)
 at android.os.Handler.dispatchMessage(Handler.java:102)
 at android.os.Looper.loop(Looper.java:136)
 at android.app.ActivityThread.main(ActivityThread.java:5091)
 at java.lang.reflect.Method.invokeNative(Native Method)
 at java.lang.reflect.Method.invoke(Method.java:515)
 at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:792)
 at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:608)
 at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.ClassNotFoundException: Didn't find class "android.view.Toolbar" on path: DexPathList[[zip file "/data/app/kr.kjun.SMSConvey-2.apk"],nativeLibraryDirectories=[/data/app-lib/kr.kjun.SMSConvey-2, /vendor/lib, /system/lib]]
 at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:497)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:457)
 at android.view.LayoutInflater.createView(LayoutInflater.java:559)
 at android.view.LayoutInflater.onCreateView(LayoutInflater.java:652)
 at com.android.internal.policy.impl.PhoneLayoutInflater.onCreateView(PhoneLayoutInflater.java:66)
 at android.view.LayoutInflater.onCreateView(LayoutInflater.java:669)
 at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:694)
 ... 23 more
}

위처럼 에러가 ...

중요한건 Didn't find class "android.view.Toolbar 이다.

구글링을 하니 내가 사용한 Toolbar 라는것이 안드로이드 4. 대 버전에서는 지원을 하지 않는다고한다.

5. 대 버전부터 지원이 된다는...;;

킷켓은 안된다.. 롤리팝 부터 지원이 된다고 한다.;;;

아 이것도 모르고 설치 최소 버전을 4 점대로 해놔서 킷켓 사용자들에게 에러가 발생되었다.

최소 버전을 롤리팝으로 하고 다시 배포를 해야하는건지.. ActionBar 를 바꿔야 하는건지.

일단 최소버전 부터 바꿔 에러가 발생되는 일이 없도록 해야겠다.^^

 

ref

https://stackoverflow.com/questions/35883622/how-to-add-toolbar-to-android-app-without-support-library

 

+ Recent posts

티스토리 툴바