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 생성
![](https://blog.kakaocdn.net/dn/r1hnH/btsKVrME37n/AqSvtGA1HeNAgAZFGCru6k/img.png)
![](https://blog.kakaocdn.net/dn/bnje56/btsKWO1PBzP/51DWhP9L9O04gQXVBK3PUK/img.png)
ViewModel 폴더에 WeatherVM.cs 파일을 생성합니다.
03. INotifyPropertyChanged
03.1 INotifyPropertyChanged 인터페이스 상속
![](https://blog.kakaocdn.net/dn/bVzTTa/btsKXq7k6HN/aZhURSrJmeXwkYFGhorLH1/img.png)
![](https://blog.kakaocdn.net/dn/dAPRuI/btsKXZVTjve/zWDTCxepxQcupu81lliTIK/img.png)
INotifyPropertyChanged 인터페이스의 PropertyChanged 이벤트 핸들러를 구현합니다.
03.2 도시 코드 검색 query 구현
![](https://blog.kakaocdn.net/dn/bQFmis/btsK0iuJP4w/xk0QwtRXllSwCVKu1WIzI0/img.png)
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
![](https://blog.kakaocdn.net/dn/cw8Q6C/btsKVaxz9LK/qxTC5soDqg57jhckDqlwbK/img.png)
![](https://blog.kakaocdn.net/dn/Sbmr4/btsKWb3qMvc/QV65jEXPCeOGpFeqowqKw1/img.png)
*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
![](https://blog.kakaocdn.net/dn/bnoYbg/btsKVIUv5wk/Wy0HfEFjdNFnhDhxwYHhJK/img.png)
<Grid DataContext="{StaticResource vm}">
<WeatherVM 프로퍼티> 와 <XAML 컨트롤> 을 Data Binding 하는데 사용하려면,
<Grid> 이하 컨트롤 범위의 DataContext(호출, 응답 환경정보)를 WeatherVM으로 지정합니다.
StaticResource로 XAML 최초 로드시 한 번 반영되도록 합니다.
04.4 데이터 바인딩
![](https://blog.kakaocdn.net/dn/OYqa0/btsKW3cthGg/5qOcrL82F3yOBWUsrxxds0/img.png)
TextBox와 WeatherVM의 Query 프로퍼티와 바인딩합니다.
TextBox의 입력값이 Query 프로퍼티로 할당됩니다.
<!-- VM(WeatherVM) Query 프로퍼티와 바인딩 -->
<TextBox Text="{Binding Query, Mode=TwoWay}"/>
![](https://blog.kakaocdn.net/dn/DfT7T/btsKVcWvM7Z/fvrulbKj1mmLYqCth28MQ1/img.png)
TextBlock에도 Query를 바인딩합니다.
04.6 데이터 바인딩 동작 구조 (1)
![](https://blog.kakaocdn.net/dn/8Ar8b/btsKWoalVJh/xmTVcdD0KxGT2Dfo0ipoE0/img.png)
동작 구조는, ❶ TextBox > ❷ ViewModel 로직 > ❸ TextBlock 까지 반영된다.
*버튼 클릭, 탭 등 TextBox의 포커스를 잃으면, TextBox에 입력된 값이 Query 속성으로 업데이트 된다.
04.7 데이터 바인딩 동작 구조 (2)
![](https://blog.kakaocdn.net/dn/chQLez/btsK0m45AyJ/XkOK8KwkHKPDkynUIZ1kHk/img.png)
(1)TextBox에서 입력값이 ViewModel의 Query 프로퍼티로 할당되고,
(2)WeatherVM의 Query 프로퍼티 변경이 감지되면,
(3)변경된 프로퍼티와 바인딩 된 모든 XAML 컨트롤에 반영합니다.
04.8 데이터 바인딩 동작 구조 (3)
![](https://blog.kakaocdn.net/dn/OLgtQ/btsKWRwqEVY/VGKKfioFZWY6FLvjJD2T7k/img.png)
![](https://blog.kakaocdn.net/dn/bUWNOD/btsKZAC0mkZ/Az0B5jZV4fdUpWBBeyI8I0/img.png)
![](https://blog.kakaocdn.net/dn/AfU8J/btsKZQljs66/RTxKfbTYNPrAsMU3DkMVI1/img.png)
05. MVVM (1) - VM 프로퍼티(CurrentCondition) 바인딩
05.1 목표
날씨 API 에서 업데이트 될 UI 속성을 Binding 합니다.
05.2 DataContext 재설정
![](https://blog.kakaocdn.net/dn/b75MaX/btsKUlsvHub/dt92GqyxbPj7FeuTWkvLwk/img.png)
하단 그리드에 DataContext를 CurrentConditions 으로 등록합니다.
DataContext 범위가 하단 그리드의 하위 컨트롤 범위로 재지정됩니다.
DataContext="{Binding CurrentConditions}"
05.3 WeatherVM.cs 클래스에, CurrentConditions 프로퍼티 구현
![](https://blog.kakaocdn.net/dn/wMOTq/btsKZzxghkm/puY816h9jLjfaVB5Pn3SKk/img.png)
ViewModel의 currentCondition 객체를 XMAL 데이터 바인딩에 사용합니다.
![](https://blog.kakaocdn.net/dn/xXnIA/btsK1BtaQsN/9bZBC5xG2pEXBDo4KCdek0/img.png)
Model의 CurrentCondition 클래스는 WeatherText, Temperature 필드를 가지고 있습니다.
![](https://blog.kakaocdn.net/dn/cox2fB/btsKZB20iBR/kAHYCi3uCtNPk7nV84rYK0/img.png)
View의 TextBlock을 ViewModel의 currentCondition 객체의 WeatherText 프로퍼티와 바인딩합니다.
<!-- VM(WeatherVM) CurrentConditions 프로퍼티와 바인딩 -->
<TextBlock Text="{Binding WeatherText, Mode=TwoWay}"
![](https://blog.kakaocdn.net/dn/OkzGy/btsK0LXLYPs/I7qhvMysab9qJ1ifNF6A7K/img.png)
View의 TextBlock을 ViewModel의 currentCondition 객체의 WeatherText 프로퍼티와 바인딩합니다.
Text="{Binding Temperature.Metric.Value,
StringFormat={}{0}°C}"
05.4 데이터 바인딩 동작 구조 (1)
![](https://blog.kakaocdn.net/dn/TYbCM/btsK1JdAFbq/qbVosFCR8FeHS1fKvHjs10/img.png)
06. MVVM(2) - VM 프로퍼티(City) 바인딩
06.1 목표
날씨 API 에서 업데이트 될 UI 속성을 Binding 합니다.
06.2 WeatherVM.cs 클래스에, selectedCity 객체를 생성
![](https://blog.kakaocdn.net/dn/HTvbz/btsKZN3eYOh/I6awx98Ks1MguaHwfSkyH1/img.png)
![](https://blog.kakaocdn.net/dn/cWKz80/btsKX1e3zoY/1LkEBKaq2pKs6f0lK7t1nk/img.png)
Model의 City 클래스는 LocalizedName 필드를 가지고 있습니다.
06.3 DataContext 재설정
![](https://blog.kakaocdn.net/dn/Lhcba/btsK1dMZON6/U7ziBUIkxcA7sVXTyL7DQ1/img.png)
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
![](https://blog.kakaocdn.net/dn/c2xaDq/btsKU5vnk1t/sm0M2kZektsAuxboftybYk/img.png)
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에 출력된다.
![](https://blog.kakaocdn.net/dn/ogxQK/btsKWLC5Plg/YkfCkowLgaLPAVJ9DGx7Mk/img.png)
바인딩된 프로퍼티가 적용되는지 확인 할 수있다.
07.3 데이터 바인딩 동작구조
(1)프로퍼티로 할당되고,
(2)WeatherVM의 프로퍼티 변경이 감지되면,
(3)변경된 프로퍼티와 바인딩 된 모든 XAML 컨트롤에 반영합니다.
07.4 Design Mode Binding
*디자인 모드에서만, 바인딩 적용하도록 설정한다. 프로그램 실행시 생성자에서 초기화하지 않는다.
![](https://blog.kakaocdn.net/dn/b9WRxq/btsKXi8sEII/l12F0WAt9wTS44cIKC4tK0/img.png)
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.