티스토리 뷰

반응형

버스 정보 검색 엡 만들기 강좌를 빨리 마무리를 하기 위해 속도를 올리고 있는데… 따라 하기는 잘 진행이 되고 있는 지 궁금하다.
이렇게 이야기를 해도 반응은 거의 없으니..후딱 시작 해야겠다.

1. StationDataTemplate 만들기
처음에 BusRouteDataTemplate 만드는 방법을 참고해서 StationDataTemplate 만들어 보자.


완성된 템플릿

<DataTemplate x:Key="StationDataTemplate">
 <Border BorderThickness="0" Width="450">
  <Grid >
   <Grid.ColumnDefinitions>
    <ColumnDefinition Width="0.8*"/>
    <ColumnDefinition Width="0.2*"/>
   </Grid.ColumnDefinitions>
   <Grid.RowDefinitions>
    <RowDefinition Height="0.5*"/>
    <RowDefinition Height="0.5*"/>
   </Grid.RowDefinitions>
   <StackPanel Orientation="Horizontal">
    <TextBlock TextWrapping="Wrap" Text="◎    순번 : "/>
    <TextBlock TextWrapping="Wrap" Text="{Binding Seq}"/>
    <TextBlock TextWrapping="Wrap" Text="◎ 정류장번호 : " Margin="10,0,0,0"/>
    <TextBlock TextWrapping="Wrap" Text="{Binding StationNo}"/>
   </StackPanel>
   <StackPanel Orientation="Horizontal" Grid.Row="1">
    <TextBlock TextWrapping="Wrap" Text="◎ 정류장 : "/>
    <TextBlock TextWrapping="Wrap" Text="{Binding StationNm}"/>
   </StackPanel>
   <TextBlock Grid.Column="1" TextWrapping="Wrap" Text="운행정보"/>
   <TextBlock Grid.Column="1" TextWrapping="Wrap" Text="차량번호" Grid.Row="1" FontSize="13.333" VerticalAlignment="Center"/>
  </Grid>
 </Border>
</DataTemplate>

실행해서 결과를 확인한다.


2. 중복 조회를 하지 못하게 조회 버튼 막기
WebClient를 이용해서 조회를 한번 실행 후 결과를 기다리는 동안 다시 조회를 실행하지 못하도록 BoolToBoolRevConvert를 하나 만들어서 처리하자.

using System;
using System.Windows.Data;

namespace BusInfo.Converters
{
    public class BoolToBoolRevConvert : IValueConverter
    {
        /// <summary>
        /// IsBusy 프로퍼티 데이터를 반대로 반환하는 컨버터
        /// IsBusy가 True이면 조회 중이기 때문에 조회 버튼의 IsEnabled를 False로 변경해 주어야 하고
        /// IsBusy가 False이면 조회를 할 수 있기 때문에 버튼의 Isenabled를 True로 변경해 주는 작업이 필요
        /// 이런 작업을 하기 위해 만든 컨버터
        /// </summary>
        /// <param name="value"></param>
        /// <param name="targetType"></param>
        /// <param name="parameter"></param>
        /// <param name="culture"></param>
        /// <returns></returns>
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            bool isbusy = System.Convert.ToBoolean(value);
            bool returnValue = false;

            if (isbusy == true)
                returnValue = false;
            else
                returnValue = true;

            return returnValue;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

MainPageViewModel.cs에서 IsBusy 프로퍼티를 추가한다.

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

bool isBusy;
/// <summary>
/// 웹 클라이언트 조회 중 여부
/// </summary>
public bool IsBusy
{
    get { return isBusy; }
    private set
    {
        isBusy = value;
        FirePropertyChange("IsBusy");
    }
}

GetBusRouteListCommand에서

//비동기 호출, 호출 구분자로 GetBusRouteList를 사용함
wc.DownloadStringAsync(uri, "GetBusRouteList");

IsBusy = wc.IsBusy; //여기 하나 추가

void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)에서
IsBusy = wc.IsBusy; //맨 첫 번째 줄에 하나 추가

F6을 눌러 빌드 해준다. 이제 Blend로 이동한다.

 
조회 버튼 클릭 -> IsEnabled 프로퍼티 오른쪽에 네모 선택 -> Data Binding 선택
IsBusy 프로퍼티 선택 -> Value Converter 오른쪽에 […] 선택 -> BoolToBoolRevConvert 선택 -> OK

여기까지 확인 되었으면 OK 실행해서 결과를 확인하자.

조회 글씨가 회색으로 변한 것을 볼 수 있다.(조회가 완료되기 전까지는 조회 버튼과 작별이다)

3. 뷰 모델 저장하는 위치 수정
MainPageViewModel.cs의 생성자에 넣어 놓았던,

//격리 저장소에 뷰모델을 저장
StaticFunctions.SaveToIS("MainPageViewModel", this);

이 부분에 오류가 발생한다. 위치를 변경한다.

private ICommand loadCommand;
/// <summary>
/// 엡이 로드가 될때 실행
/// </summary>
public ICommand LoadCommand
{
    get
    {
        if (loadCommand == null)
        {
            loadCommand = new ActionCommand(() =>
            {
                //격리 저장소에 뷰모델을 저장
                StaticFunctions.SaveToIS("MainPageViewModel", this);
            });
        }
        return loadCommand;
    }
}

그리고 Blend에서 InvokeCommandAction을 PhoneApplicationPage에 추가 하고
EventName : Loaded -> Command : LoadCommand로 선택 해준다.
이렇게 하면 폼이 로드 될 때 한번 실행된다.

4. 1번에서 DataTemplate을 이용해서 경유정류소 목록을 조회해 봤는데..약간 부족한 점은 도대체 무슨 어떤 버스의 정류소 목록인지를 알 수가 없는 것과 운행정보, 차량 번호를 표시하지 못하는 것이다. 그 부분은 다음 강좌에서 처리하도록 하겠다.
추가로 약간의 수정 사항이 있는데 그것은 뷰 모델의 전체 소스를 참고 하기 바란다.

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Input;
using System.Xml.Linq;
using BusInfo.Functions;
using BusInfo.Models;
using Microsoft.Expression.Interactivity.Core;
using Microsoft.Phone.Controls;

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;

        bool isBusy;
        /// <summary>
        /// 웹 클라이언트 조회 중 여부
        /// </summary>
        public bool IsBusy
        {
            get { return isBusy; }
            private set
            {
                isBusy = value;
                FirePropertyChange("IsBusy");
            }
        }

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

        ObservableCollection<BusRouteModel> busRouteCollection;
        /// <summary>
        /// 버스 목록 컬렉션 - 프로퍼티
        /// </summary>
        public ObservableCollection<BusRouteModel> BusRouteCollection
        {
            get { return busRouteCollection; }
            set
            {
                busRouteCollection = value;
                FirePropertyChange("BusRouteCollection");
            }
        }

        ObservableCollection<StationByRouteModel> stationCollection;
        /// <summary>
        /// 노선별 경유 정류소 컬렉션 - 프로퍼티
        /// </summary>
        public ObservableCollection<StationByRouteModel> StationCollection
        {
            get { return stationCollection; }
            set
            {
                stationCollection = value;
                FirePropertyChange("StationCollection");
            }
        }

        //메인 프레임
        PhoneApplicationFrame root;

        /// <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)
        {

            IsBusy = wc.IsBusy;

            if (e.Error != null)
                return;
            //xml을 xElement라는 객체로 바로 파싱해서 사용한다.
            XElement xml = XElement.Parse(e.Result);
            //어떤 서비스의 결과인지에 따라서 각각 다른 처리를 한다.
            switch (e.UserState as string)
            {
                case "GetBusRouteList":
                    //버스 노선 목록 파싱
                    var rows = from item in xml.Descendants("itemList")
                               select new BusRouteModel
                               {
                                   BusRouteId = Convert.ToInt32(item.Element("busRouteId").Value),
                                   BusRouteNm = item.Element("busRouteNm").Value,
                                   StartStationNm = item.Element("stStationNm").Value,
                                   EndStationNm = item.Element("edStationNm").Value,
                                   RouteType = Convert.ToInt32(item.Element("routeType").Value),
                                   Term = Convert.ToInt32(item.Element("term").Value)
                               };
                    BusRouteCollection = new ObservableCollection<BusRouteModel>(rows);
                    break;
                case "GetStaionByRoute":
                    //정류소 목록 파싱
                    var stations = from item in xml.Descendants("itemList")
                                   select new StationByRouteModel
                                   {
                                       BusRouteId = Convert.ToInt32(item.Element("busRouteId").Value),
                                       BusRouteNm = item.Element("busRouteNm").Value,
                                       Seq = Convert.ToInt32(item.Element("seq").Value),
                                       Section = Convert.ToInt32(item.Element("section").Value),
                                       Station = Convert.ToInt32(item.Element("station").Value),
                                       StationNm = item.Element("stationNm").Value,
                                       GpsX = Convert.ToDouble(item.Element("gpsX").Value),
                                       GpsY = Convert.ToDouble(item.Element("gpsY").Value),
                                       FullSectDist = Convert.ToSingle(item.Element("fullSectDist").Value),
                                       StationNo = Convert.ToInt32(item.Element("stationNo").Value),
                                       RouteType = Convert.ToInt32(item.Element("routeType").Value),
                                       BeginTm = item.Element("beginTm").Value,
                                       LastTm = item.Element("lastTm").Value,
                                       TrnstnId = Convert.ToInt32(item.Element("trnstnid").Value)
                                   };
                    StationCollection = new ObservableCollection<StationByRouteModel>(stations);
                    break;
            }

            if (1 == 1)
            {
            }
        }

        private ICommand getBusRouteListCommand;
        /// <summary>
        /// 노선 목록 조회 커맨드
        /// </summary>
        public ICommand GetBusRouteListCommand
        {
            get
            {
                if (getBusRouteListCommand == null)
                {
                    getBusRouteListCommand = new ActionCommand(() =>
                    {
                        if (SBusNum == null || 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");

                        IsBusy = wc.IsBusy;
                    });
                }
                return getBusRouteListCommand;
            }
        }

        private ICommand selectionChangedCommand;
        /// <summary>
        /// 노선 목록에서 선택된 아이템이 변경된 경우 실행되는 커맨드
        /// </summary>
        public ICommand SelectionChangedCommand
        {
            get
            {
                if (selectionChangedCommand == null)
                {
                    selectionChangedCommand = new ActionCommand(item =>
                    {
                        //커맨드 파라메터로 전달 받은 오브젝트를 형변환
                        BusRouteModel route = item as BusRouteModel;
                        //형변환을 성공적으로 처리했다면
                        if (route != null)
                        {
                            //정류소 조회
                            var uri = new Uri("http://ws.bus.go.kr/api/rest/busRouteInfo/getStaionByRoute?ServiceKey=" + SKey.BusRouteInfo + "&busRouteId=" + route.BusRouteId);
                            wc.DownloadStringAsync(uri, "GetStaionByRoute");

                            //던지고 바로 정류소 목록 조회 화면으로 네비게이션
                            root.Navigate(new Uri("/Views/StationByRouteView.xaml", UriKind.Relative));
                        }
                    });
                }
                return selectionChangedCommand;
            }
        }

        private ICommand loadCommand;
        /// <summary>
        /// MainPage.xaml이 로드가 될때 실행
        /// </summary>
        public ICommand LoadCommand
        {
            get
            {
                if (loadCommand == null)
                {
                    loadCommand = new ActionCommand(() =>
                    {
                        //메인 프레임 입력 - 네비게이션하는데 필요
                        if (root == null)
                        {
                            //생성자에서는 이 데이터를 가지고 올 수가 없음..
                            root = App.Current.RootVisual as PhoneApplicationFrame;
                        }
                        //격리 저장소에 뷰모델을 저장
                        StaticFunctions.SaveToIS("MainPageViewModel", this);
                    });
                }
                return loadCommand;
            }
        }
    }
}

5. 버스 정보를 제공하는..

서버가 응답 속도가 평소에 비해 무지하게 느린 것이 날씨가 추워서 성능 저하가 된 것 같다.
앞으로 2회 강좌로 마무리를 할 수 있도록 노력하겠다.

 

 

 

 

 

반응형

'Previous Platforms > KBI' 카테고리의 다른 글

Seoul Bus Info Search App Dev 9  (0) 2012.01.12
My BusInfo app deploy  (0) 2012.01.08
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
댓글