15. WeatherApp - MVVM(1)
01.
01. MVVM 패턴의 전제 조건
[유형1] View 컨트롤의 변경 >> ViewModel 의 프로퍼티 변경 >> View 컨트롤 반영
[유형2] API, TCP, DB >> ViewModel 의 프로퍼티 변경 >> View 컨트롤 반영
두 유형 모두, ViewModel의 프로퍼티 변경되는 시점을 기준으로 View 컨트롤에 반영된다.
ViewModel의 프로퍼티 변경을 알려주고, 이때 동작하는 기술을 배우는 것이 MVVM 패턴을 구현하는 핵심이다.
01.2 Data Binding
OneWay: ViewModel > View
TwoWay: ViewModel > View
View > ViewModel
01.3 Data Binding 기본 구조
"XMAL 컨트롤"에 "ViewModel 의 Property"를 바인딩합니다.
"XMAL UI" <binding>> "ViewModel Property"
ViewModel Property" <binding>> " XMAL UI"
원본 속성 또는 대상 속성이 변경되면 바인딩 된 로직이나 객체에 자동 업데이트 합니다.
예시) UI 객체에 입력시 > 바인딩 된 로직의 모델(그리고 모델에 바인딩된 다른 UI까지 한번)에 반영
01.5 리소스
WPF 에서는, 모델 데이터를 리소스 형태로 가져와, XAML 컨트롤 데이터 바인딩에 사용할 수 있다.
(추가1)
StaticResource: XAML 최초 로드시 한 번 반영
DynamicResource: 동적 할당
(추가2)
local Resource: 특정 컨트롤에서 하위 컨트롤까지 범위를 가지는 리소스
window Resource: 특정 윈도우 내에서 접근 가능한 전역 리소스 (MainWindow.xaml)
application Resource: 전체 윈도우에서 접근 가능한 전역 리소스 (App.xaml)
01.6 DataContext
프로그래밍에서 Context의 의미는 호출, 응답을 위한 환경정보에 가깝다.(≒ 네임스페이스)
특정 컨트롤 범위 내에서 " 데이터 바인딩을 위한 소스 객체"에 접근 가능하도록 명시하는 속성
DataContext로 지정된 객체의 프로퍼티를, XAML 코드에서 데이터 바인딩에 사용할 수 있다.
02. ViewModel 생성
ViewModel 폴더에 WeatherVM.cs 파일을 생성합니다.
03. INotifyPropertyChanged
03.1 INotifyPropertyChanged 인터페이스 상속
INotifyPropertyChanged 인터페이스의 PropertyChanged 이벤트 핸들러를 구현합니다.
03.2 도시 코드 검색 query 구현
query 프로퍼티가 업데이트가 되면, PropertyChanged 이벤트 핸들러가 동작하도록 로직을 구현합니다.
public class WeatherVM : INotifyPropertyChanged
{
//[1] 시나리오: 도시 검색을 요청(query)한다.
private string query;
public string Query
{
get { return query; }
// [2] Property가 변경되면,
set
{
query = value;
// OnPropertyChanged 함수에서
OnPropertyChanged(nameof(Query));
}
}
// [4] 이벤트 핸들러가 동작하면,
// XAML에 바인딩 되어있는 모든 UI컨트롤에 해당 query 값이 업데이트 된다.
public event PropertyChangedEventHandler? PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
// [3] PropertyChanged 이벤트를 실행한다.
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
04. (XAML) Data Binding
04.1 빌드
XAML에 로직이 정확히 반영되기 위해서는, 빌드를 한 번 해주는 것이 좋다.
혹은, XAML에 녹색, 파란색 밑줄이 생긴다면 Visual Studio를 한 번 종료 한 뒤, 재실행 하는 것도 추천한다.
04.2
*xmlns: XML Name Space
③ WeatherApp.ViewModel을 vm으로 네임스페이스로 등록합니다.
④ WeatherWindow 리소스에서 x:Key 값인, vm 으로 접근하도록 등록합니다.
xmlns:vm="clr-namespace:WeatherApp.ViewModel"
<Window.Resources>
<vm:WeatherVM x:Key="vm"/>
</Window.Resources>
04.3 DataContext
<Grid DataContext="{StaticResource vm}">
<WeatherVM 프로퍼티> 와 <XAML 컨트롤> 을 Data Binding 하는데 사용하려면,
<Grid> 이하 컨트롤 범위의 DataContext(호출, 응답 환경정보)를 WeatherVM으로 지정합니다.
StaticResource로 XAML 최초 로드시 한 번 반영되도록 합니다.
04.4 데이터 바인딩
TextBox와 WeatherVM의 Query 프로퍼티와 바인딩합니다.
TextBox의 입력값이 Query 프로퍼티로 할당됩니다.
<!-- VM(WeatherVM) Query 프로퍼티와 바인딩 -->
<TextBox Text="{Binding Query, Mode=TwoWay}"/>
TextBlock에도 Query를 바인딩합니다.
04.6 데이터 바인딩 동작 구조 (1)
동작 구조는, ❶ TextBox > ❷ ViewModel 로직 > ❸ TextBlock 까지 반영된다.
*버튼 클릭, 탭 등 TextBox의 포커스를 잃으면, TextBox에 입력된 값이 Query 속성으로 업데이트 된다.
04.7 데이터 바인딩 동작 구조 (2)
(1)TextBox에서 입력값이 ViewModel의 Query 프로퍼티로 할당되고,
(2)WeatherVM의 Query 프로퍼티 변경이 감지되면,
(3)변경된 프로퍼티와 바인딩 된 모든 XAML 컨트롤에 반영합니다.
04.8 데이터 바인딩 동작 구조 (3)
05. MVVM (1) - VM 프로퍼티(CurrentCondition) 바인딩
05.1 목표
날씨 API 에서 업데이트 될 UI 속성을 Binding 합니다.
05.2 DataContext 재설정
하단 그리드에 DataContext를 CurrentConditions 으로 등록합니다.
DataContext 범위가 하단 그리드의 하위 컨트롤 범위로 재지정됩니다.
DataContext="{Binding CurrentConditions}"
05.3 WeatherVM.cs 클래스에, CurrentConditions 프로퍼티 구현
ViewModel의 currentCondition 객체를 XMAL 데이터 바인딩에 사용합니다.
Model의 CurrentCondition 클래스는 WeatherText, Temperature 필드를 가지고 있습니다.
View의 TextBlock을 ViewModel의 currentCondition 객체의 WeatherText 프로퍼티와 바인딩합니다.
<!-- VM(WeatherVM) CurrentConditions 프로퍼티와 바인딩 -->
<TextBlock Text="{Binding WeatherText, Mode=TwoWay}"
View의 TextBlock을 ViewModel의 currentCondition 객체의 WeatherText 프로퍼티와 바인딩합니다.
Text="{Binding Temperature.Metric.Value,
StringFormat={}{0}°C}"
05.4 데이터 바인딩 동작 구조 (1)
06. MVVM(2) - VM 프로퍼티(City) 바인딩
06.1 목표
날씨 API 에서 업데이트 될 UI 속성을 Binding 합니다.
06.2 WeatherVM.cs 클래스에, selectedCity 객체를 생성
Model의 City 클래스는 LocalizedName 필드를 가지고 있습니다.
06.3 DataContext 재설정
View의 TextBlock의 Text 속성을 ViewModel selectedCity 객체의 LocalizedName 프로퍼티와 바인딩합니다.
<!-- 부모 컨트롤 DataContext가 하위 컨트롤을 포함하기 때문에-->
<!-- DataContext를 재지정, Selectedcity 프로퍼티와 바인딩 -->
<TextBlock DataContext="{StaticResource vm}"
Text="{Binding Selectedcity, Mode=TwoWay}"
Foreground="#f4f4f8"
FontSize="20"
Margin="20,0" />
07. MVVM(3) - 테스트
07.1
WeatherVM 생성자에서,
City.LocalizedName 값과
CurrentConditions.WeatherText, CurrentConditions.Temperature.Units 값을 할당한 상태로 시작한다.
값을 할당하는 동작은, 외부 API, DB 등에서 각 프로퍼티로 업데이트 되었을때 값이 할당되는 동작과 동일하다.
07.2
ViewModel, WeatherVM 클래스의 생성자에서, CurrentConditions, SelectedCity 프로퍼티를 초기화한다.
public WeatherVM()
{
SelectedCity = new City
{
LocalizedName = "서울"
};
CurrentConditions = new CurrentConditions
{
WeatherText = "Partly cloudy",
Temperature = new Temperature
{
Metric = new Units
{
Value = 21
}
}
};
}
07.2 WeatherVM 생성자 정보가 바인딩 된 UI에 출력된다.
바인딩된 프로퍼티가 적용되는지 확인 할 수있다.
07.3 데이터 바인딩 동작구조
(1)프로퍼티로 할당되고,
(2)WeatherVM의 프로퍼티 변경이 감지되면,
(3)변경된 프로퍼티와 바인딩 된 모든 XAML 컨트롤에 반영합니다.
07.4 Design Mode Binding
*디자인 모드에서만, 바인딩 적용하도록 설정한다. 프로그램 실행시 생성자에서 초기화하지 않는다.
public WeatherVM()
{
if (DesignerProperties.GetIsInDesignMode(new System.Windows.DependencyObject()))
{
SelectedCity = new City
{
LocalizedName = "서울"
};
CurrentConditions = new CurrentConditions
{
WeatherText = "Partly cloudy",
Temperature = new Temperature
{
Metric = new Units
{
Value = 21
}
}
};
}
}
08.