Model
namespace G24W1502WPFRest;
// 서버로부터 받은 JSON 예제
//{
// "id": 516,
// "title": "PUBG: BATTLEGROUNDS",
// "thumbnail": "<https://www.freetogame.com/g/516/thumbnail.jpg>",
// "short_description": "Get into the action in one of the longest running battle royale games PUBG Battlegrounds.",
// "game_url": "<https://www.freetogame.com/open/pubg>",
// "genre": "Shooter",
// "platform": "PC (Windows)",
// "publisher": "KRAFTON, Inc.",
// "developer": "KRAFTON, Inc.",
// "release_date": "2022-01-12",
// "freetogame_profile_url": "<https://www.freetogame.com/pubg>"
//},
// Kotlin의 data class에 해당
public record class Game(
int id,
string title,
string thumbnail,
string short_description,
string game_url,
string genre,
string platform,
string publisher,
string developer,
string release_date,
string freetogame_profile_url
);
////이렇게 만들어도 됨
//public class Game {
// public int id;
// public string title { get; set; }
// public string release_date { get; set; }
// public string genre { get; set; }
// public string publisher { get; set; }
// public string developer { get; set; }
// public string short_description { get; set; }
// public string platform { get; set; }
// public string thumbnail { get; set; }
// public string game_url { get; set; }
// public string freetogame_profile_url { get; set; }
//}
⭐ViewModel
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Text.Json;
using System.Windows;
namespace G24W1502WPFRest;
public class GameViewModel : INotifyPropertyChanged {
private ObservableCollection<Game> _games
= new ObservableCollection<Game>();
public ObservableCollection<Game> Games => _games;
private Game? _selectedGame = null;
public Game? SelectedGame {
get => _selectedGame;
set {
if (value == null || _selectedGame == value)
return;
_selectedGame = value;
OnPropertyChanged(nameof(SelectedGame));
OnPropertyChanged(nameof(IsSelected));
}
}
public Visibility IsSelected => (_selectedGame == null)
? Visibility.Hidden
: Visibility.Visible;
//---------------------------------------------
// 참고
// <https://learn.microsoft.com/en-us/dotnet/fundamentals/networking/http/httpclient>
// REST API 접속을 위한 변수
private static HttpClient sharedClient = new() {
BaseAddress = new Uri("<https://www.freetogame.com/api/>"),
};
public async Task GetGames() {
try {
// <https://www.freetogame.com/api/games?platform=pc> 에서 JSON 받아옴
HttpResponseMessage response
= await sharedClient.GetAsync("games?platform=pc");
response.EnsureSuccessStatusCode();
string result = await response.Content.ReadAsStringAsync();
// 디버깅을 위해 출력 창에 결과 출력
//System.Diagnostics.Debug.WriteLine(result);
if (string.IsNullOrEmpty(result)) return;
// 서버에서 받은 JSON을 List로 변환
List<Game>? resultGames
= JsonSerializer.Deserialize<List<Game>>(result);
if (resultGames == null) return;
// ObservableCollection에 추가
foreach (Game game in resultGames) {
_games.Add(game);
}
}
catch (HttpRequestException e) {
// HTTP 요청 관련 예외 처리
System.Diagnostics.Debug.WriteLine($"Request error: {e.Message}");
}
catch (JsonException e) {
// JSON 파싱 관련 예외 처리
System.Diagnostics.Debug.WriteLine($"JSON parse error: {e.Message}");
}
catch (Exception e) {
// 그 외의 예외 처리
System.Diagnostics.Debug.WriteLine($"Unexpected error: {e.Message}");
}
}
//---------------------------------------------
public event PropertyChangedEventHandler? PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propName = "") {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
}
View
<Window x:Class="G24W1502WPFRest.MainWindow"
xmlns="<http://schemas.microsoft.com/winfx/2006/xaml/presentation>"
xmlns:x="<http://schemas.microsoft.com/winfx/2006/xaml>"
xmlns:d="<http://schemas.microsoft.com/expression/blend/2008>"
xmlns:mc="<http://schemas.openxmlformats.org/markup-compatibility/2006>"
xmlns:local="clr-namespace:G24W1502WPFRest"
mc:Ignorable="d"
Title="무료 게임 리스트" Height="450" Width="600">
<Border Padding="8">
<DockPanel>
<DataGrid
x:Name="GameGrid"
DockPanel.Dock="Left"
AutoGenerateColumns="False"
Width="Auto"
SelectionMode="Single"
ItemsSource="{ Binding Games }"
SelectedItem="{ Binding SelectedGame }">
<DataGrid.Columns>
<DataGridTemplateColumn
Header="이미지"
Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image
Source="{ Binding thumbnail }"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn
Header="게임 제목"
Width="150">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock
Text="{ Binding title }"
Padding="8 0"
VerticalAlignment="Center"
TextWrapping="Wrap"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<Border
Padding="8"
Background="LightGray">
<StackPanel
Visibility="{ Binding IsSelected }">
<Image
Source="{ Binding SelectedGame.thumbnail }"/>
<TextBlock
Text="{ Binding SelectedGame.title}"
FontSize="30" FontWeight="Bold"
Background="DarkBlue" Foreground="White"
Padding="8"
TextWrapping="Wrap"
TextAlignment="Center"/>
<TextBlock
Text="{ Binding SelectedGame.short_description}"
Margin="0 8"
TextWrapping="Wrap"
TextAlignment="Justify"/>
<StackPanel
Orientation="Horizontal"
Margin="0 8">
<TextBlock
Text="장르: "
FontSize="16"/>
<TextBlock
Text="{ Binding SelectedGame.genre}"
FontSize="16"/>
</StackPanel>
<UniformGrid
Rows="1">
<Button
Content="게임 프로필"
Padding="8"
Margin="0 8 4 8"
Click="StartWebBrowser"
Tag="{ Binding SelectedGame.freetogame_profile_url}"/>
<Button
Content="게임 홈페이지"
Padding="8"
Margin="4 8 0 8"
Click="StartWebBrowser"
Tag="{ Binding SelectedGame.game_url}"/>
</UniformGrid>
</StackPanel>
</Border>
</DockPanel>
</Border>
</Window>
xaml.cs
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
namespace G24W1502WPFRest;
public partial class MainWindow : Window
{
private GameViewModel vm = new GameViewModel();
public MainWindow()
{
InitializeComponent();
DataContext = vm;
//GameGrid.ItemsSource = vm.Games;
InitializeGames();
}
private async void InitializeGames() {
await vm.GetGames();
}
public void StartWebBrowser(object sender, RoutedEventArgs e) {
string url = (string)((Button)sender).Tag;
Process.Start(
new ProcessStartInfo("cmd", $"/c start {url}") {
CreateNoWindow = true
}
);
}
}
여기에 Layout 추가
StackPanel
<StackPanel Orientation="Vertical" Margin="10"> //Horizontal 왼쪽에서 오른쪽
<Button Content="버튼 1" Margin="5"/> //Vertical 위에서 아래
<Button Content="버튼 2" Margin="5"/>
<Button Content="버튼 3" Margin="5"/>
</StackPanel>
TextBlock
<TextBlock Text="안녕하세요, WPF!"
FontSize="24"
FontWeight="Bold"
Foreground="DarkBlue"
TextAlignment="Center"
Margin="10"/>
UniformGrid
<UniformGrid Rows="2" Columns="3" Margin="10"> // 2행 3열로 만들어짐
<Button Content="1"/>
<Button Content="2"/>
<Button Content="3"/>
<Button Content="4"/>
<Button Content="5"/>
<Button Content="6"/>
</UniformGrid>
Border