지난번에 이어서 매우 간단한 CRUD 하는 방법을 설명하려고 한다. 최대한 간단하게 하기 위해서 타이틀리 커플드(tightly coupled)로 코딩을 하는 걸로 정했다. 나중에 문의가 들어온다면 MVVM을 적용한 루즐리 커플드(loosely coupled) 방식으로 변경하도록 하겠다.


1. 디자인 변경
화면의 상단에 공간을 좀 주고, 그 곳에 버튼을 3개 배치했다. 버튼의 이름은 Add, Update, Remove이다.


디자인을 변경하면 거의 50%는 한 셈이다. 이제 각 각의 버튼에 약간의 코딩을 하도록 하겠다.


2. CRUD 코딩하기
버튼을 더블클릭해서 이벤트를 만들어보자. Button_Click이란 이름의 이벤트가 하나 만들어지면 그걸 나머지 2개의 버튼에도 같이 붙여버린다. 그리고 코딩을 살짝한다.

using SL4_RIA_Sample.Web;


private void Button_Click(object sender, RoutedEventArgs e)
{
    Button btn = (Button)sender;
    //Order는 DTO(Data Transfer Object)로 WCF RIA에서 자동으로 만들어준 것이다. 이걸 사용하려면

    //SL4_RIA_Sample.Web를 써주기만 하면 된다.           

    Order order;

    switch (btn.Content.ToString())
    {
        case "Add":

            //새로운 레코드를 추가하기 위해서는 그냥 새로운 order를 생성해서 추가한 후에 SubmitChanges()만 호출하면 된다.
            order = new Order();
            orderDomainDataSource.DataView.Add(order);
            break;
        case "Update":

            //SubmitChanges()가 실행되면 Insert, Update, Delete가 모두 적용된다.

            //RejectChanges()는 지금까지 수정된 사항을 모두 취소한다.
            orderDomainDataSource.DomainContext.SubmitChanges();
            break;
        case "Remove":

            //삭제는 단지 DataView에서 삭제하려는 Order를 지우기만 하면 된다.
            if (orderDomainDataSource.DataView.CurrentItem != null)
            {
                order = orderDomainDataSource.DataView.CurrentItem as Order;
                orderDomainDataSource.DataView.Remove(order);
            }
            break;
    }
}

3. 변경된 내용 조회
리플로 문의 주신 내용인데.. 데이터베이스가 내가 변경하지 않고 다른 사람이나 프로시저에서 변경된 경우에 새로 조회를 해도 변경된 내용이 바로 반영이 앙된다는 것인데.. 이것은 LoadBehavior에 대한 처리를 해주면 쉽게 해결 할 수 있다.

WCF RIA Control은 LoadBehavior를 기본적으로 KeepCurrent로 지정되어있다.

LoadBehavior는 데이터를 로드했을때 현재 내가 가지고 있는 데이터와 새로 불러온 데이터를 어떻게 처리 할지를 지정하는 것으로 KeepCurrent(현재 가지고 있는 데이터 유지), MergeIntoCurrent(현재 가지고 있는 데이터를 새로 가지고 온 데이터와 머지), RefreshCurrent(현재 데이터를 새로운 데이터로 몽땅 교체)의 3가지 방법을 제공한다.

이 LoadBehavior를 지정하기 위해서는 몇가지 방법이 있는데, 현재 소스에는 DomainDataSource라는 컨트롤을 사용하고 있고 그 컨트롤에서 지정 방법은 LoadingData 이벤트를 추가하고,

private void orderDomainDataSource_LoadingData(object sender, LoadingDataEventArgs e)
{
    e.LoadBehavior = System.ServiceModel.DomainServices.Client.LoadBehavior.RefreshCurrent;
}

위와 같이 코딩을 해주면 된다.

4. 이번에는 여기 까지만 올리겠다.
추가로 요청이 올라오면 더 자세한 강좌를 올리도록 하겠다.


블로그 이미지

kaki104

/// Microsoft MVP - Windows Development 2014 ~ 2019 5ring /// twitter : @kaki104, facebook : https://www.facebook.com/kaki104 https://www.facebook.com/groups/w10app/

실버라이트 4에 대한 기본적인 사항을 먼저 한번 쭈욱 정리를 하고 실버라이트 5로 넘어가야 할 것 같아서, 일단 초 간단 예제를 만들었다. 이 예제에 사용된 기술은 딱 2가지이다. 실버라이트4와 WCF RIA Service,

WCF RIA Service 설치는 http://www.silverlight.net/getstarted/riaservices/
이 페이지에 가면 3개의 링크가 있고, 3개를 모두 다운로드 받은 후 설치하면 된다.
WCF RIA가 올해 초까지만 해도 참 좋았는데..으흠..머 아직도 좋다..하하;;


1. 전체 레이아웃
1-1. 실버라이트 프로젝을 만든다.
1-2. 서버에 엔티티 프레임웍을 붙인다.
1-3. 데이터베이스를 연결한다.
1-4. 테이블에 기초한 엔티티를 만든다.
1-5. 컴파일 한다
1-6. WCF RIA(Domain Service)를 추가한다.
1-7. 컴파일 한다.
1-8. MainPage.xaml을 열어서 만들어진 컨텍스트를 드래그 드롭한다.
1-9. 실행한다.

무지 간단하다. 아마 한 10분이면 똑같이 만들 수 있을 것이다. 하지만 기능은 막강하다
물론 진짜 프로젝트에서 사용할려면 여러가지 추가적인 작업들이 들어가지만 그래도 좋다.
일단 만들어보고 더 필요한 부분이 있으면 요청이 있는 부분에 대해서만 추가 작업을 해서 올리겠다.


2. 실버라이트 프로젝을 만든다.
만드는 부분은 생략한다.

이름은 간단하게 정했다. 중요한 부분은 아래에 있는 Enable WCF RIA Services를 꼭 체크 해야한다는 것이다.


3. 서버에 엔티티 프레임웍을 붙인다.
서버 프로젝에서 Add -> New Item을 선택해서 Entity Data Model을 추가한다.

이름은 간단하게 기본 이름을 사용했다.


4. 데이터베이스를 연결한다.
기존 데이터베이스에 연결을 해서 모델을 만들 수 있는 기능을 선택한다.

여기서는 Compact 데이터베이스 파일을 사용한다. Compact 4.0을 설치하면 셈플DB인 Northwid.sdf 파일이 존재한다.
꼭 이 파일이 아니더라도 기존에 가지고 있던 데이터베이스 파일도 사용 가능하다.

여기서 Northwid.sdf파일을 선택하고 Next~

선택한 데이터베이스에서 테이블, 뷰, 프로시저 목록을 보여주고 자동으로 모델을 만들 수 있다. 여기서는 공짜니까 모든 테이블을 선택한다.
추가적으로 Pluralize or sigularize generated object names라는 것도 체크해준다. 그리고 finish

위의 그림이 자동으로 모델을 만들어 준 그림이다. 일단 여기까지 되었으면 꼭 F6키를 눌러서 컴파일을 해준다.


5. WCF RIA Service(Domain Service)를 추가한다

웹 프로젝트에서 Add -> New Item -> domain이란 이름으로 검색하면 Domain Service라는 것이 나온다. 이것이 바로 WCF RIA Service이다.

Add버튼을 누르면

위와 같은 화면이 나온다. 역시 공짜니까 다 선택한다. 오른쪽 체크 박스는 CRUD를 하겠냐는 것이다. 나중에 예제를 위해서라도 그냥 체크 하자 중요한 부분은 Generate associated classes for metadata에 체크를 해준다는 것이다.


여기까지 했을 때 완성된 솔루션 익스플로러의 모습이다.

또 다시 F6키를 눌러서 빌드를 하자


6. MainPage.xaml을 열어보자.
그리고 왼쪽으로 보면 Data Sources라는 것이 보이게 된다. (뜨는데 시간이 좀 걸린다)
이것이 WCF RIA Service를 사용 했을 때만 나오는 것으로 서버에서 만들어진 Context를 그냥 바로 드래그 드롭으로 사용 할 수 있도록 만들어 주는 것이다.

위의 내용 중에서 Order라는 것을 드래그 드롭으로 MainPage.xaml 위에 올려 놓고 위치를 조정해서 이쁘게 만든다.

그냥 올려다 놓기만해두 모든 필드의 내용들이 자동으로 데이터 그리드로 만들어진다. 얼마나 멋진 모습인지 흐흐

이제 마지막으로 실행 해보자 F5키를 눌러서 내용이 뜨는지를 보자

맨 처음에는 데이터가 뜨는데 시간이 약간 걸린다. 생각보다 Order테이블에 데이터가 좀 있다. 어차피 비동기 방식으로 가지고 오는 것이기 때문에 약간 기다리면 다 가지고 온다.


7. 초간단 프로젝트를 하나 만들었다.
하지만 여기서 조금만 더 손보면 바로 멋진 프로그램이 된다.
추가적으로 궁금한 사항, 추가 기능 등등을 많이 적어 주면 하나씩 기능을 보강해 보도록 하겠다.

블로그 이미지

kaki104

/// Microsoft MVP - Windows Development 2014 ~ 2019 5ring /// twitter : @kaki104, facebook : https://www.facebook.com/kaki104 https://www.facebook.com/groups/w10app/

실버라이트로 프로그램을 만들 때 가끔 나오는 오류인데..구글에서 찾아 봤을 때 거의 해결 방법을 찾을 수 없다.

[Arg_COMException]
Arguments:
Debugging resource strings are unavailable. Often the key and arguments provide sufficient information to diagnose the problem. See http://go.microsoft.com/fwlink/?linkid=106663&Version=5.0.60401.00&File=mscorlib.dll&Key=Arg_COMException

   at MS.Internal.XcpImports.CheckHResult(UInt32 hr)
   at MS.Internal.XcpImports.SetValue(IManagedPeerBase obj, DependencyProperty property, DependencyObject doh)
   at MS.Internal.XcpImports.SetValue(IManagedPeerBase doh, DependencyProperty property, Object obj)
   at System.Windows.DependencyObject.SetObjectValueToCore(DependencyProperty dp, Object value)
   at System.Windows.DependencyObject.SetEffectiveValue(DependencyProperty property, EffectiveValueEntry& newEntry, Object newValue)
   at System.Windows.DependencyObject.UpdateEffectiveValue(DependencyProperty property, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, ValueOperation operation)
   at System.Windows.DependencyObject.SetValueInternal(DependencyProperty dp, Object value, Boolean allowReadOnlySet, Boolean isBindingInStyleSetter)
   at System.Windows.Controls.Control.set_DefaultStyleKey(Object value)
   at System.Windows.Controls.TextBox..ctor()
   at MS.Internal.CoreTypes.GetCoreWrapper(UInt32 typeId)
   at MS.Internal.ManagedPeerTable.EnsureManagedPeer(IntPtr unmanagedPointer, Int32 typeIndex, Type type, Boolean preserveManagedObjectReference)
   at MS.Internal.XcpImports.ConvertDO(IntPtr doPointer, Int32 typeIndex, Boolean releaseObjectReference)
   at MS.Internal.XcpImports.ConvertType(CValue outVal, Int32 typeIndex, Boolean releaseObjectReference, Boolean deleteBuffer, IManagedPeerBase fromObject)
   at MS.Internal.XcpImports.ConvertCValueForManagedWithType(Type propertyType, CValue& outVal, Int32 outDOType, Boolean releaseObjectReference, Boolean deleteBuffer, IManagedPeerBase fromObject)
   at MS.Internal.XcpImports.GetValue(IManagedPeerBase managedPeer, DependencyProperty property)
   at System.Windows.RoutedEventArgs.get_OriginalSource()
   at Telerik.Windows.Controls.RadWindow.OnLostFocus(RoutedEventArgs e)
   at System.Windows.Controls.Control.OnLostFocus(Control ctrl, EventArgs e)
   at MS.Internal.JoltHelper.FireEvent(IntPtr unmanagedObj, IntPtr unmanagedObjArgs, Int32 argsTypeIndex, Int32 actualArgsTypeIndex, String eventName, UInt32 flags)

문제점(the point at issue)
바인딩 데이터를 자동으로 처리하면서 이상한(?) 데이터를 넣을려고 하는 경우 발생할 확율 높음

해결책(a solution: perhaps)
바인딩 되는 데이터를 object형으로 Type을 바꾸어 주고 테스트를 해본다.
binding property change to object type.

내 경우에는 int? type 데이터를 20개 정도 바인딩으로 사용하고 있었는데, 지속적으로 위의 에러가 발생해서..엄청난 괴로움을 당하고 있었는데..object로 변형을 한 후에는 오류를 발생 시킬려고 일부러 이것 저것 눌러도 잘 발생을 하지 않는다.(그래도 100% 발생하지 않는 것은 아님)

'Silverlight > ETC' 카테고리의 다른 글

Prism 4.0 Tips  (0) 2012.06.13
Telerik Report Tips  (2) 2012.06.08
Rx, Linq Tips  (0) 2012.05.04
Silverlight Standard Tips  (0) 2012.04.27
Twitter Client Lecture(강좌 목록) by Silverlight 5  (0) 2012.01.05
[Arg_COMException] error solution: perhaps  (4) 2012.01.04
블로그 이미지

kaki104

/// Microsoft MVP - Windows Development 2014 ~ 2019 5ring /// twitter : @kaki104, facebook : https://www.facebook.com/kaki104 https://www.facebook.com/groups/w10app/

1. 만들기 시작~
File -> New Project -> Silverlight for Windows Phone -> Windows Phone Application
Name : BusInfo
Target Windows Phone OS Version : Windows Phone OS 7.1

초기화면이 출력되면 바로 실행해본다(Ctrl + F5) 윈도우 폰 에물레이터가 실행되고 응용프로그램이 배포되어서 실행된다.

MY APPLICATION을 원하는 이름으로 바꾼다.
page name : 버스 목록 조회로 변경
글씨가 너무 커서 '회'자가 짤리니.. FontSize를 변경해준다. -> 64
다시 실행해서 확인 한다.


2. 버스 목록 조회하기
노선번호 목록 조회
http://api.bus.go.kr/contents/sub02/getBusRouteList.html
위의 서비스를 이용해서 조회를 해야한다. 여기서 인증키가 꼭 필요한데.. 인증키는 강좌 1번을 참고 하기 바란다.


3. 필요한 폴더 생성
BusInfo 프로젝트 선택 -> 마우스 오른쪽 클릭 -> Add -> New Folder
Converters, Functions, Icons, Models, ViewModels, Views
6개의 폴더를 생성해 놓는다.

4. MainPage.xaml에 컨트롤을 배열해보자
xaml 파일에 디자인을 할때, 바인딩을 할때는 블랜드를 이용한다.(VS2010에서 프로젝트를 열어 놓은 상태로 작업한다)
Microsoft Expression Blend 4 실행 -> File -> Open Project/Solution -> BusInfo.sln 선택 Open


5. 구역 나누기
Objects and Timeline -> ContentPanel 선택
MainPage.xaml에 마우스를 가져다가 대면 노란색 라인이 보인다.

위의 위치에서 클릭하면 구역이 하나 만들어진다. 소스를 확인해보자

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
 <Grid.RowDefinitions>
 <RowDefinition Height="0.081*"/> <!--1번구역-->
 <RowDefinition Height="0.919*"/> <!--2번구역-->
 </Grid.RowDefinitions>
</Grid>


나누어진 1번 구역에는
노선번호 _____________ [조회] 이런 내용을 올려 놓을 것이다.
1번 구역은 높이가 변경될 필요가 없으니 높이를 고정 시키자


<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
 <Grid.RowDefinitions>
 <RowDefinition Height="72"/>
 <RowDefinition/>
 </Grid.RowDefinitions>
</Grid>

 

노선번호 -> TextBlock
_________ -> TextBox
[조회] -> Button
3개의 컨트롤을 1번 구역에 올려 놓아야하는데 2가지 방식중에 하나를 선택 할 수 있다.
가) 1번구역에 Grid를 올려 놓고 컬럼을 만들어서 사용하는 방법
나) 1번구역에 StackPanel을 올려 놓고 Orientation을 Horizontal로 만들어서 사용하는 방법
여기서는 나)의 방법을 사용해서 배치를 한다.(편해서..)


<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
 <Grid.RowDefinitions>
  <RowDefinition Height="72"/>
  <RowDefinition/>
 </Grid.RowDefinitions>
 <StackPanel Orientation="Horizontal">
  <TextBlock TextWrapping="Wrap" Text="노선번호" VerticalAlignment="Center"/>
  <TextBox TextWrapping="Wrap" Text="TextBox" Width="275"/>
  <Button Content="조회"/>
 </StackPanel>
</Grid>

이정도면 1구역에서 해야할 일은 다 한 것 같다. 다음 2구역으로 넘어가보자
2구역에는 ListBox컨트롤 하나만 올려 놓으면 된다.

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
 <Grid.RowDefinitions>
  <RowDefinition Height="72"/>
  <RowDefinition/>
 </Grid.RowDefinitions>
 <StackPanel Orientation="Horizontal">
  <TextBlock TextWrapping="Wrap" Text="노선번호" VerticalAlignment="Center"/>
  <TextBox TextWrapping="Wrap" Text="TextBox" Width="275"/>
  <Button Content="조회"/>
 </StackPanel>
 <ListBox Grid.Row="1"/>
</Grid>


화면상으로 올려 놓은 것인지 아닌지 확실히 보이지는 안치만..올라가있는 것이다.
화면 디자인은 1차 완료~ 저장 하고 VS2010으로 돌아가서 실행해보자. 이때 VS2010에 xaml이 열려있다면..
외부에서 파일이 변경되어서 다시 불러들여야 한다는 메시지가 출력된다. 그렇게 하라구 해준다.
그리고, 다시 실행 해 주면 방금 수정된 내용이 화면에 출력된다.


6. MainPageViewModel.cs 만들기
조회버튼의 클릭 이벤트를 강제로 만들어서 사용할 수도 있지만..여기서는 MVVM 패턴을 적용해서 프로그램을 하기 때문에..
ViewModel을 만들어 주어야 한다. (MVVM 패턴을 사용하지 않으면 바인딩 기능을 이용할 수 없다..그러므로 꼭 사용하도록 하자)

우린 다시 VS2010으로 돌아와서
ViewModels폴더를 선택하고 Add -> Class -> name : MainPageViewModel.cs 파일을 추가하자

ViewModel의 중요기능 중에 프로퍼티 체인지 이벤트가 있다. 이 것을 사용 하기 위해서는 INotifyPropertyChanged 을 상속 받아야 한다. 상속 받은 후 구현을 하면 아래와 같은 소스가 만들어진다.

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ComponentModel;

namespace BusInfo.ViewModels
{
    public class MainPageViewModel : INotifyPropertyChanged
    {
        #region PropertyChange
        public event PropertyChangedEventHandler PropertyChanged;

        private void FirePropertyChange(string PropertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
            }
        }
        #endregion

    }
}

프로퍼티 체인지 이벤트를 구현하는 것은 바인딩을 원하는 모든 클래스에서 이루어져야 한다. 그렇치 않으면 데이터가 변경이 되더라도 화면에 그 내용이 표시 되지 않는 사태가 발생하기 때문이다.

7. MainPageViewModel.cs 다시 작업

뷰 모델의 기본적인 구성을 살펴 보면
가) 프로퍼티 체인지 이벤트 구현
나) 조회 조건 프로퍼티
다) 뷰모델에서 사용할 프로퍼티 or 변수
-> 뷰와 바인딩을 해야하는 넘들은 프로퍼티로, 아니면 일반 변수로 만들어도 됨
-> 구분하기 귀찮으면 모두 프로퍼티로 만들어두 문제는 없음
-> 프로퍼티도 바인딩 된 후에 변경되었을 때 화면에 바로 반영해 주어야하는 경우에는 FirePropertyChange를 사용해야하고, 그냥 바인딩만 할려고 하면 프로퍼티로만 만들어 놓으면 된다.
라) 생성자
마) 뷰모델 프로퍼티 체인지 이벤트 구현
바) 커맨드 구현
위의 구성은 내 나름대로 정의를 한 것으로 프로그램 하는데 참고 사항으로 사용하면 될 것이다.

** 조회 조건 추가 - 일반 프로퍼티
/// <summary>
/// 검색할 버스 노선 번호
/// </summary>
public string SBusNum { get; set; }

//웹클라이언트
WebClient wc;

음..다음에 인증키를 넣어야하는데..인증키는 따로 모델을 만들어서 사용하기로 하자.

8. ServiceKey.cs 작업
Models폴더를 선택하고 -> Add -> Class -> name : ServiceKey.cs 추가

using System;

namespace BusInfo.Models
{
    public class ServiceKey
    {
        /// <summary>
        /// 버스정보용 인증키
        /// </summary>
        public string BusRouteInfo { get; private set; }

        public ServiceKey()
        {
            string tempKey;
            //버스루트정보용 키
            tempKey = string.Format("여기에 본인이 받은 인증키를 넣는다.");
            //URL인코딩 작업을 해서 넣어 두어야함
            BusRouteInfo = Uri.EscapeUriString(tempKey);
        }
    }
}

본인이 받은 인증키를 " 여기에 본인이 받은 인증키를 넣는다." 여기에 입력한다.
중요한 부분은 인증키 자체를 Encoding을 해주어야 한다는 부분이다.

그리고, 다시 MainPageViewModel.cs로 이동해서 인증키 클래스를 추가한다.

9. MainPageViewModel.cs 다시 빽

/// <summary>
/// 인증 키
/// </summary>
ServiceKey SKey;

자 이제 목표는 일단 호출을 날리고 결과가 들어오는 것을 확인하는 것이다.
그럼, 기본 생성자에서 wc와 SKey를 생성하고, wc.DownloadStringCompleted 이벤트도 구현하고, 마지막으로 DLL도 하나 추가한다.
References 선택 -> Add Reference -> System.Xml.Linq 추가    //XElement 사용하기 위해

/// <summary>
/// 생성자
/// </summary>
public MainPageViewModel()
{
    //웹클라이언트 생성
    wc = new WebClient();
    wc.DownloadStringCompleted += new DownloadStringCompletedEventHandler(wc_DownloadStringCompleted);
    //서비스키 생성
    SKey = new ServiceKey();
}

/// <summary>
/// 웹클라이언트 다운로드스트링 컴플릿트 이벤트 구현
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
    if (e.Error != null)
        return;
    //xml을 xElement라는 객체로 바로 파싱해서 사용한다.
    XElement xml = XElement.Parse(e.Result);

}

여기까지 하면 wc를 이용해서 요청을 날리면 결과를 xml이란 곳에서 확인이 가능하다.
그럼 요청을 날려보자
F6키를 눌러서 빌드를 해보자 에러가 없으면 다음으로 이동~


10. 사용자 인터렉션(User Interactions) 처리
ViewModel에서는 ICommand를 하나 만들고, View에서는 InvokeCommandAction을 이용해서 ViewModel에 있는 명령을 실행하게 된다. 처음으로 조회를 할 서비스에 대해서 알아보자
노선번호 목록 조회
http://api.bus.go.kr/contents/sub02/getBusRouteList.html
2개의 요청 변수를 가지는 것을 확인 할 수 있는데, 한 개는 인증키고, 또 하나는 검색할 노선 번호이다.

다시 하나의 DLL을 추가해보자
Add Reference -> Microsoft.Expression.Interactions
위의 DLL은 ActionCommand를 사용하기위해 필요하다. 커맨드를 추가한다.

private ICommand getBusRouteListCommand;
/// <summary>
/// 노선 목록 조회 커맨드
/// </summary>
public ICommand GetBusRouteListCommand
{
    get
    {
        if (getBusRouteListCommand == null)
        {
            getBusRouteListCommand = new ActionCommand(() =>
            {
                if (SBusNum.Length <= 1)
                {
                    MessageBox.Show("2글자 이상 입력해야 합니다.");
                    return;
                }
                //REST 형식 OpenAPI 호출
                var uri = new Uri("http://ws.bus.go.kr/api/rest/busRouteInfo/getBusRouteList?ServiceKey=" + SKey.BusRouteInfo + "&strSrch=" + SBusNum);
                //입력데이터 인코딩
                SBusNum = Uri.EscapeUriString(SBusNum);
                //비동기 호출, 호출 구분자로 GetBusRouteList를 사용함
                wc.DownloadStringAsync(uri, "GetBusRouteList");
            });
        }
        return getBusRouteListCommand;
    }
}

F6을 눌러서 빌드를 해준다.(빌드 작업을 해줘야 블랜드에서 인식이 가능하다.)
여기까지 작업이 완료되었으면 이제 다시 블랜드로 넘어가서 뷰와 뷰모델과 연결하는 작업을 해주도록 하자.

11. 뷰와 뷰모델 연결하기


Objects and Timeline에서 PhoneApplicationPage를 선택한다.-> 화면 오른쪽 하단에 있는 Common Properties -> DataContext -> New 클릭


MainPageViewModel을 선택하고 OK를 누른다.

위의 작업은 뷰에 뷰모델을 DataContext에 붙이는 작업이다. 이렇게 DataContext에 뷰모델을 넣어 주어야 블랜드에서 뷰모델의 프로퍼티와 커맨드를 바인딩 할 수 있다.

화면에 ㅡ 모양을 클릭해서 화면과 소스를 동시에 보는 mode로 변경한다.
소스에서 방금 추가된 부분을 찾아간다.


뷰에서 뷰모델을 붙여 놓으면 디자인 타임에서도 생성을 시켜서 작업을 할 수 있다. 하지만, 여기서는 뷰에서 뷰모델을 인스턴스 시키지 않을 것이다. 그래서 이 부분에 있는 소스에서 phone:PhoneApplicationPage. 부분을 d:로 변경한다. d:는 디자인 타임에서만 사용하는 넘이란 뜻이다.

<d:DataContext>
 <BusInfo_ViewModels:MainPageViewModel/>
</d:DataContext>

이렇게 변경하면 디자인 타임 때는 바인딩이나 모든 것이 가능하지만, 런타임 때에는 아무런 동작을 하지 않는다.

12. TextBox와 뷰모델의 프로퍼티를 바인딩하기
확인을 위해서 TextBox를 클릭하고 Text 프로퍼티를 찾아보자..그곳에 TextBox라는 글씨가 있고 오른쪽에 하얀색 네모박스가 있는데 그 박스를 클릭하면
팝업 메뉴가 나온다. 메뉴에서 Data Binding을 선택한다.


Data Context란 이름에 뷰모델의 프로퍼티와 커맨드가 보이게 된다. 여기서 우리는 텍스트박스와 SBusNum이란 뷰모델의 프로퍼티를 TwoWay로 바인딩을 할 수 있다.

* Binding은 거의 2가지를 사용하는데
OneWay는 뷰에서 데이터를 변경할 필요가 없는 읽기 전용으로 사용할 경우이고,
TwoWay는 뷰에서 데이터를 변경해야 하는 경우에 사용하면 된다.

우리는 뷰에서 버스 번호를 입력 해서 프로퍼티를 변경해야 하기 때문에 TwoWay로 바인딩을 해준다.
OK눌러서 바인딩을 완료 한다.
그러면 Text 프로퍼티가 노란색 띠를 가지도록 변경되고, 오른쪽에 하얀 네모 박스도 동일하게 변경된다.
이렇게하면 뷰에있는 텍스트 박스와 뷰모델에 있는 프로퍼티의 바인딩이 성공적으로 완료가 된 것이다.

13. InvokeCommandAction 연결하기


Assets텝 선택 -> Behaviors 선택 -> InvokeCommandAction을 선택
선택한 내용을 드래그 해서 Button까지 끌어다가 놓으면 Button에 InvokeCommandAction이 추가된다.


위와 같은 화면이 나오면 된다.
추가된 커맨드 엑션에 세부적인 설정을 해준다. 오른쪽 상단에 Command 프로퍼티 오른쪽에 디스크 모양 아이콘을 클릭한다


그러면 Create Data Binding 팝업이 출력되면서 커맨드를 선택할 수 있다. 여기서 방금 만들어 놓았던 GetBusRouteListCommand를 선택하고 OK를 클릭한다.
이제 조회 버튼과 뷰모델에 있는 GetBusRouteListCommand는 바인딩 되었다.

<phone:PhoneApplicationPage
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:BusInfo_ViewModels="clr-namespace:BusInfo.ViewModels"
    xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
    x:Class="BusInfo.MainPage"
    mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait" Orientation="Portrait"
    shell:SystemTray.IsVisible="True">
 
    <!--Sample code showing usage of ApplicationBar-->
    <!--<phone:PhoneApplicationPage.ApplicationBar>
        <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
            <shell:ApplicationBarIconButton IconUri="/Images/appbar_button1.png" Text="Button 1"/>
            <shell:ApplicationBarIconButton IconUri="/Images/appbar_button2.png" Text="Button 2"/>
            <shell:ApplicationBar.MenuItems>
                <shell:ApplicationBarMenuItem Text="MenuItem 1"/>
                <shell:ApplicationBarMenuItem Text="MenuItem 2"/>
            </shell:ApplicationBar.MenuItems>
        </shell:ApplicationBar>
    </phone:PhoneApplicationPage.ApplicationBar>-->

 <d:DataContext>
  <BusInfo_ViewModels:MainPageViewModel/>
 </d:DataContext>

    <!--LayoutRoot is the root grid where all page content is placed-->
    <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <!--TitlePanel contains the name of the application and page title-->
        <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
            <TextBlock x:Name="ApplicationTitle" Text="kaki104 App" Style="{StaticResource PhoneTextNormalStyle}"/>
            <TextBlock x:Name="PageTitle" Text="버스 목록 조회" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}" FontSize="64" />
        </StackPanel>

        <!--ContentPanel - place additional content here-->
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
         <Grid.RowDefinitions>
          <RowDefinition Height="72"/>
          <RowDefinition/>
         </Grid.RowDefinitions>
         <StackPanel Orientation="Horizontal">
          <TextBlock TextWrapping="Wrap" Text="노선번호" VerticalAlignment="Center"/>
          <TextBox TextWrapping="Wrap" Text="{Binding SBusNum, Mode=TwoWay}" Width="275"/>
          <Button Content="조회">
           <i:Interaction.Triggers>
            <i:EventTrigger EventName="Click">
             <i:InvokeCommandAction Command="{Binding GetBusRouteListCommand, Mode=OneWay}"/>
            </i:EventTrigger>
           </i:Interaction.Triggers>
          </Button>
         </StackPanel>
         <ListBox Grid.Row="1"/>
        </Grid>
    </Grid>

</phone:PhoneApplicationPage>

조회 기능까지 추가한 뷰의 소스이다.

14. MainPage.xaml.cs에서 마무리 작업 하기
다시 VS2010으로 돌아오고, 실행해 보자.
오류도 없고 화면도 출력된다. 그런데 조회 버튼을 클릭해도 아무런 반응이 없다. 커맨드가 실행이 되는지 확인 해볼려면
...
getBusRouteListCommand = new ActionCommand(() =>
  { //요기에 브레이크를 걸어 놓는다.
      if (SBusNum.Length <= 1)
...
브레이크를 걸고 실행해도 아무런 반응이 없을 것이다. 좀 전에 이야기를 했듯이..우린 DataContext라는 곳에 뷰모델을 연결해 놓았지만..그건 디자인 타임에서만 적용되기 때문이다.
그래서 런타임에서 뷰모델을 뷰의 DataContext에 넣어주는 코딩을 MainPage.xaml.cs에 추가하도록 하자


using System.ComponentModel;
using BusInfo.ViewModels;
using Microsoft.Phone.Controls;

namespace BusInfo
{
    public partial class MainPage : PhoneApplicationPage
    {
        /// <summary>
        /// 뷰모델
        /// </summary>
        public MainPageViewModel ViewModel { get; set; }

        // Constructor
        public MainPage()
        {
            InitializeComponent();

            //디자인 타임이 아닌 경우 실행
            if (!DesignerProperties.IsInDesignTool)
            {
                //뷰모델 인스턴스
                ViewModel = new MainPageViewModel();
                //DataContext에 입력
                this.DataContext = ViewModel;
            }
        }
    }
}

** 뷰모델을 왜 이렇게 만들어서 붙일까??
처음에 그냥 뷰모델을 블랜드에서 만들고 그대로 사용해도 사실 문제는 없다. 다만, 이 프로그램에서는 뷰모델을 다른 뷰에서도 사용하기 위해서 위와 같은 방법을 사용한 것이다.
원래 원했던 내용은 MEF를 사용해서 깔끔하게 같이 사용하고 싶었는데 불행하게도 WP에서는 MEF를 지원하지 않는다..그래서 약간 다른 방법으로 처리를 했고, 더 자세한 사항은 다음 화면을 추가했을 때 자세하게 알아 보도록 하자

다시 실행하고, 조회 노선 번호를 입력 한 후에 조회 버튼을 클릭해 보자.
이제 브레이크가 걸리면 작업이 성공적인 것이다. 안 된다면..다시 찬찬히 따라 해 보도록 한다.

OpenAPI 조회 명령이 성공적으로 완료가 되면 wc_DownloadStringCompleted를 타게 되고, 어떤 결과가 리턴 되는지를 확인 한 후에 Model을 만들도록 한다.

15. MainPageViewModel.cs

/// <summary>
/// 웹클라이언트 다운로드스트링 컴플릿트 이벤트 구현
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
    if (e.Error != null)
        return;
    //xml을 xElement라는 객체로 바로 파싱해서 사용한다.
    XElement xml = XElement.Parse(e.Result);
    switch (e.UserState as string)  //여기에 브레이크
    {
    }
}

브레이크가 걸린곳에서 xml에 어떤 내용이 들어오는지 확인 해보면

<ServiceResult>
  <comMsgHeader />
  <msgHeader>
    <headerCd>0</headerCd>
    <headerMsg>정상적으로 처리되었습니다.</headerMsg>
    <itemCount>5</itemCount>
  </msgHeader>
  <msgBody>
    <itemList>
      <busRouteId>90000062</busRouteId>
      <busRouteNm>3100안산</busRouteNm>
      <edStationNm>강남역</edStationNm>
      <routeType>8</routeType>
      <stStationNm>신안산대학교</stStationNm>
      <term>60</term>
    </itemList>
    <itemList>
      <busRouteId>90000134</busRouteId>
      <busRouteNm>3100의정부</busRouteNm>
      <edStationNm>양재역</edStationNm>
      <routeType>8</routeType>
      <stStationNm>대진대.학생회관</stStationNm>
      <term>30</term>
    </itemList>
    <itemList>
      <busRouteId>90000326</busRouteId>
      <busRouteNm>31파주</busRouteNm>
      <edStationNm>불광동시외버스터미널</edStationNm>
      <routeType>8</routeType>
      <stStationNm>갈곡리(회차지점)</stStationNm>
      <term>25</term>
    </itemList>
    <itemList>
      <busRouteId>90000352</busRouteId>
      <busRouteNm>3100김포</busRouteNm>
      <edStationNm>신촌역2호선</edStationNm>
      <routeType>8</routeType>
      <stStationNm>화도터미널</stStationNm>
      <term>60</term>
    </itemList>
    <itemList>
      <busRouteId>90000176</busRouteId>
      <busRouteNm>3101안산</busRouteNm>
      <edStationNm>강남역</edStationNm>
      <routeType>8</routeType>
      <stStationNm>신안산대학교</stStationNm>
      <term>80</term>
    </itemList>
  </msgBody>
</ServiceResult>

이런 결과가 리턴 된 것을 알 수 있다.
그럼 위의 결과를 기준으로  Models 폴더에 BusRouteModel.cs라는 모델을 만들어 보기 바란다.


MainPageViewModel.cs 최종소스

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ComponentModel;
using BusInfo.Models;
using System.Xml.Linq;
using Microsoft.Expression.Interactivity.Core;


namespace BusInfo.ViewModels
{
    public class MainPageViewModel : INotifyPropertyChanged
    {
        #region PropertyChange
        public event PropertyChangedEventHandler PropertyChanged;

        private void FirePropertyChange(string PropertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
            }
        }
        #endregion

        /// <summary>
        /// 검색할 버스 노선 번호
        /// </summary>
        public string SBusNum { get; set; }

        /// <summary>
        /// 웹 클라이언트
        /// </summary>
        WebClient wc;

        /// <summary>
        /// 인증 키
        /// </summary>
        ServiceKey SKey;

        /// <summary>
        /// 생성자
        /// </summary>
        public MainPageViewModel()
        {
            //웹클라이언트 생성
            wc = new WebClient();
            wc.DownloadStringCompleted += new DownloadStringCompletedEventHandler(wc_DownloadStringCompleted);
            //서비스키 생성
            SKey = new ServiceKey();
        }

        /// <summary>
        /// 웹클라이언트 다운로드스트링 컴플릿트 이벤트 구현
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            if (e.Error != null)
                return;
            //xml을 xElement라는 객체로 바로 파싱해서 사용한다.
            XElement xml = XElement.Parse(e.Result);
            switch (e.UserState as string)
            {
            }
        }

        private ICommand getBusRouteListCommand;
        /// <summary>
        /// 노선 목록 조회 커맨드
        /// </summary>
        public ICommand GetBusRouteListCommand
        {
            get
            {
                if (getBusRouteListCommand == null)
                {
                    getBusRouteListCommand = new ActionCommand(() =>
                    {
                        if (SBusNum.Length <= 1)
                        {
                            MessageBox.Show("2글자 이상 입력해야 합니다.");
                            return;
                        }
                        //REST 형식 OpenAPI 호출
                        var uri = new Uri("http://ws.bus.go.kr/api/rest/busRouteInfo/getBusRouteList?ServiceKey=" + SKey.BusRouteInfo + "&strSrch=" + SBusNum);
                        //입력데이터 인코딩
                        SBusNum = Uri.EscapeUriString(SBusNum);
                        //비동기 호출, 호출 구분자로 GetBusRouteList를 사용함
                        wc.DownloadStringAsync(uri, "GetBusRouteList");
                    });
                }
                return getBusRouteListCommand;
            }
        }

    }
}

16. 이번회의 강좌는 여기까지만 하도록 하겠다.

조회를 날리고 결과를 가지고 왔으니 이제 그 결과를 가공해서 화면에 출력하는 일만 남은 것 같다.
쓰고 보니 스크롤의 압박이 상당히 심할 것 같은데..중간에 끊을 수가 없었다는 것을 알아 주기를 바란다.이 글이 윈폰 엡 개발하는데 만은 도움이 되었으면 좋겠고..리플도 만이 남겨주면 좋겠다.

 

Windows Phone 7 Mango 버스 정보 검색 App 만들기 3

'Windows Phone 8 > Korea Bus Information' 카테고리의 다른 글

Seoul Bus Info Search App Dev 6  (0) 2012.01.07
Seoul Bus Info Search App Dev 5  (0) 2012.01.07
Seoul Bus Info Search App Dev 4  (0) 2012.01.07
Seoul Bus Info Search App Dev 3  (0) 2012.01.04
Seoul Bus Info Search App Dev 2  (0) 2012.01.04
Seoul Bus Info Search App Dev 1  (0) 2012.01.04
블로그 이미지

kaki104

/// Microsoft MVP - Windows Development 2014 ~ 2019 5ring /// twitter : @kaki104, facebook : https://www.facebook.com/kaki104 https://www.facebook.com/groups/w10app/

1. App을 만들기 위해서 ..
알아 두어야 할 부분을 정리를 해 보았다.

2. 어떻게 데이터를 가지고 와서 표시를 해주는 것인가?
2-1. 서버에 데이터 요청을 하고 결과를 받아오기 : WebClient
Open API을 이용하기 위해서 우리는 WebClient를 사용 할 것이다.
이것에 대한 자세한 설명은 책이나 MSDN들을 참고 하면 기본 적인 사용 방법은 찾을 수 있을 것이다.
여기서는 추가적으로 인증키 처리부분에 대해서만 이야기를 한다.

2-2. 받아온 데이터 처리하기 : XElement, LINQ, ObservableCollection, Model
2-2-1. XElement
XML데이터를 Parse를 하여 프로그램에서 쉽게 접근이 가능한 형태로 변형해준다.
XElement 클래스
http://msdn.microsoft.com/ko-kr/library/system.xml.linq.xelement.aspx

2-2-2. LINQ
통합 언어 쿼리라고 하는데, 데이터를 찾고, 합치고, 조작하는 것들을 쿼리를 이용해서 쉽고 간편하게 처리하는데 사용
LINQ(통합 언어 쿼리)
http://msdn.microsoft.com/ko-kr/library/bb397926.aspx

2-2-3. ObservableCollection
일반적인 배열이나 리스트 형태가 아닌 컬렉션 형태의 데이터 구조에 옵저버 기능이 추가된 클래스이다.
ObservableCollection(Of T) 클래스
http://msdn.microsoft.com/ko-kr/library/ms668604.aspx

2-2-4. Model
여기서의 모델은 반환된 XML데이터를 가지고 만들 데이터를 말한다.

2-3. 처리된 데이터 보여주기 : ListBox, DataTemplete, Binding, Converter
2-3-1. Listbox
WP에는 DataGrid가 없다. 그래서 대부분의 목록형 테이터는 ListBox를 이용해서 출력한다.

2-3-2. DataTemplete
ListBox의 기본형태는 하나의 데이터만 목록으로 출력해 주는 것이다. 여러 가지 항목을 동시에 출력을
하기 위해서는 DataTemplete을 이용해서 틀을 만들고, ItemTemplete에 바인딩을 해 주어야한다.
DataTemplate 클래스
http://msdn.microsoft.com/ko-kr/library/system.windows.datatemplate.aspx

2-3-3. Binding
하나의 프로퍼티(속성)를 다른 속성과 연결 시켜주는 작업을 바인딩이라고 한다.
Silverlight 프로그램에서는 View와 ViewModel이 바인딩으로 연결되어 ViewModel의 속성이 변경되면
그 변경된 내용이 바로 View에 표현이 되어진다.(MVVM 패턴을 사용하는 경우만..)
Binding 클래스
http://msdn.microsoft.com/ko-kr/library/system.windows.data.binding.aspx

2-3-4. Converter
데이터를 다른 형태로 변형해야 할때 사용되는 클래스로, code값을 name으로 바꾸어서 표시하는데
주로 사용된다.
Data Conversion in Silverlight Data Binding
http://www.silverlightshow.net/items/Data-Conversion-in-Silverlight-2-Data-Binding.aspx

2-4. 사용자의 동작에 반응하기 : ICommand, ActionCommand, InvokeCommandAction
2-4-1. ICommand
MVVM 패턴에서 ViewModel에서 사용자의 명령을 정의 하는데 사용됨
ICommand 인터페이스
http://msdn.microsoft.com/ko-kr/library/system.windows.input.icommand(v=vs.95).aspx

2-4-2. ActionCommand
ICommand를 구현하는 방법중에 하나로 최대 1개의 파라메터를 가질 수 있는 커맨드이고,
커맨드 내용을 구현하는 것이 편하다는 장점이 있음
ActionCommand 클래스
http://msdn.microsoft.com/ko-kr/library/microsoft.expression.interactivity.core.actioncommand(v=expression.40).aspx

2-4-3. InvokeCommandAction
View에서 사용자의 동작에 반응하는 ICommand를 호출 하기 위해 사용
InvokeCommandAction
http://msdn.microsoft.com/ko-kr/library/ff724018(v=expression.40).aspx

2-5. 기타 : IsolatedStorage, Navigation Service, MVVM Pattern
2-5-1. IsolateStorage 
격리 저장소 , 윈도우 폰 내부에 저장 공간을 이야기하는 것으로 이곳에 필요한 데이터를 일시, 영구적으로 저장, 조회를 할 수 있다.

2-5-2. Navigation Service
네비게이션 서비스, 메인 페이지와 서브 페이지를 왔다 갔다 하는 것을 이야기한다.

2-5-3. MVVM Pattern
Model-View-ViewModel의 약자로 프로그램 개발 패턴을 이야기한다.

3. 위의 내용들을 모두 다 꼭 알고 있어야하는 것은 아니고 참고적인 내용들만 살짝 알면 된다.
그리고, 몰라두 하다보면 알 수 있으니 고민하지 말고 3번 강좌로 넘어가보도록 하자

Windows Phone 7 Mango 버스 정보 검색 App 만들기 2

'Windows Phone 8 > Korea Bus Information' 카테고리의 다른 글

Seoul Bus Info Search App Dev 6  (0) 2012.01.07
Seoul Bus Info Search App Dev 5  (0) 2012.01.07
Seoul Bus Info Search App Dev 4  (0) 2012.01.07
Seoul Bus Info Search App Dev 3  (0) 2012.01.04
Seoul Bus Info Search App Dev 2  (0) 2012.01.04
Seoul Bus Info Search App Dev 1  (0) 2012.01.04
블로그 이미지

kaki104

/// Microsoft MVP - Windows Development 2014 ~ 2019 5ring /// twitter : @kaki104, facebook : https://www.facebook.com/kaki104 https://www.facebook.com/groups/w10app/

티스토리 툴바