본문 바로가기

C#

C# 윈폼(Windows Forms) 웹(Blazor + Ant Design) 메뉴 예쁘게 만들기

반응형

개발 환경 요약

  • 개발 도구: Visual Studio 2022
  • 프로그래밍 언어: C#
  • 프레임워크: .Net 8.0
  • 애플리케이션 유형: Windows Forms와 Blazor를 결합한 애플리케이션
  • UI 디자인: Ant Design 사용

 

 

Visual Studio 환경 설정

  • Visual Studio Installer 실행 및 수정
 
  • 워크로드 선택 및 수정: .Net 데스크톱 개발 선택 수정
 

Windws Forms 애플리케이션 프로젝트 생성

  • Visual Studio 2022 실행 > 새 프로젝트 만들기
 
  • Windows Forms 앱 > 다음
 
  • 프로젝트 이름 > 위치 > 다음
 
  • 프레임워크 선택 > 만들기

 

 

NuGet 패키지 설치

  • Microsoft.AspNetCore.Components.WebView.WindowsForms: Windows Forms에서 Blazor 컴포넌트를 호스팅할 수 있게 해주는 라이브러리
  • AntDesign.ProLayout: Blazor 애플리케이션의 UI를 개선 및 편의성을 위해 사용하는 라이브러리
  • Newtonsoft.Json: JSON 데이터를 처리하기 위한 라이브러리
최상위 패키지(Top-level Package)와 전이적 패키지(Transitive Package)
  • 최상위 패키지는 프로젝트 개발자가 직접 설치하거나 명시적으로 추가한 패키지를 의미
  • 전이적 패키지는 최상위 패키지가 의존하는 다른 패키지들에 의해 간접적으로 필요로 하는 패키지

 

프로젝트 Sdk 변경

  • 솔루션 탐색기 > WinFormsBlazorAntDesign(프로젝트명 클릭)
  • 프로젝트명을 클릭만 해도 열리는데 안열릴 시 > 프로젝트명 우클릭 > 프로젝트 파일 편집
  • <Project Sdk="Microsoft.NET.Sdk"> 기존 코드를 <Project Sdk="Microsoft.NET.Sdk.Razor"> 이렇게 변경
  • Microsoft.NET.Sdk: 이 요소는 프로젝트가 .NET 표준 라이브러리를 사용하는 일반적인 .NET 애플리케이션을 위한 것임을 나타냄
  • Microsoft.NET.Sdk.Razor: 이 요소는 프로젝트가 Razor를 사용하여 동적 웹 페이지를 생성하는 ASP.NET Core 웹 애플리케이션을 위한 것임을 나타냄(Razor는 C# 또는 HTML을 혼합 사용)

 

 

Visual Studio 재시작(Visual Studio 종료 > 시작)

  • Project Sdk를 변경했는데 재시작을 안하면 웹 관련 요소들을 못가져오는 경우가 있음

 

Form1.cs [디자인] 수정

  • 솔루션 탐색기 > Form1.cs 더블 클릭
  • 보기 > 도구 상자
  • Form1안에서 우클릭 > 속성 > Size: 1100, 500 으로 변경(변경 안해도 문제는 없음)
  • 도구 상자 > BlazorWebView 있는지 확인
  • BlazorWebView Form1에 드래그앤드랍
  • WebView2 우클릭 > 속성
  • Dock: Fill 속성 변경, 배치 방법을 지정하는 속성이며 컨트롤이 부모 컨테이너의 전체 영역을 채우도록 설정
  • 솔루션 탐색기 > Form1 우클릭 > 코드 보기 또는 Form1 클릭 > F7
  • Form1.cs 코드 작성
using Microsoft.AspNetCore.Components.WebView.WindowsForms;
using Microsoft.Extensions.DependencyInjection;

namespace WinFormsBlazorAntDesign
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            // ServiceCollection 인스턴스를 생성하여 의존성 주입(DI)을 위한 서비스들을 등록하기 위해 사용
            var services = new ServiceCollection();
            // Windows Forms 애플리케이션에서 Blazor 컴포넌트를 호스팅할 수 있게 해주는 BlazorWebView 컨트롤을 사용할 수 있게 서비스 컬렉션에 추가
            services.AddWindowsFormsBlazorWebView();
            // Ant Design 컴포넌트 라이브러리를 서비스 컬렉션에 추가
            services.AddAntDesign();
            // Blazor 애플리케이션의 진입점으로 사용
            blazorWebView1.HostPage = "wwwroot\\index.html";
            // ServiceCollection에 등록된 서비스들로부터 서비스 제공자(ServiceProvider)를 생성하고, 이를 blazorWebView1 컨트롤의 서비스 제공자로 설정
            blazorWebView1.Services = services.BuildServiceProvider();
            // Counter 컴포넌트를 blazorWebView1 컨트롤의 루트 컴포넌트로 추가하고, 이 컴포넌트가 #app 요소 내에서 렌더링되도록 설정
            // #app 요소는 index.html 코드에 <div id="app"></div> 요소 내에서 렌더링되도록 설정
            blazorWebView1.RootComponents.Add<Main>("#app");
        }
    }
}
 

 

 

폴더 및 파일 생성 및 코드 작성

  • wwwroot: index.html
  • Datas: menu.json
  • Pages: A.razor, B.razor, C.razor
  • Shared: MainLayout.razor
  • _Imports.razor
  • Main.razor

 

  • index.html
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>윈폼 웹</title>

    <base href="/" />

    <link href="_content/AntDesign/css/ant-design-blazor.css" rel="stylesheet" />
    <link href="_content/AntDesign.ProLayout/css/ant-design-pro-layout-blazor.css" rel="stylesheet" />
</head>

<body>
    <div id="app"></div>

    <script src="_framework/blazor.webview.js"></script>
    <script src="_content/AntDesign/js/ant-design-blazor.js"></script>
</body>
</html>

 

  • _Imports.razor
@using AntDesign
@using AntDesign.ProLayout
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Routing
@using WinFormsBlazorAntDesign
@using WinFormsBlazorAntDesign.Shared
 
  • Mian.razor
    • <Router> 컴포넌트는 Blazor 앱의 라우팅 시스템을 설정합니다. 사용자가 앱 내에서 다른 페이지로 이동할 때 URL을 기반으로 해당 페이지 컴포넌트를 찾아 렌더링하는 역할
    • AppAssembly="@typeof(Program).Assembly: 라우터가 페이지 컴포넌트를 찾을 어셈블리를 지정합니다. 일반적으로 프로그램의 진입점을 포함하는 어셈블리를 사용
    • <Found Context="routeData">: URL이 앱의 라우트 설정과 일치할 경우 실행되며, routeData를 통해 해당 페이지로 라우트 정보를 전달하고, <RouteView>를 사용하여 해당 컴포넌트를 렌더링, DefaultLayout="@typeof(MainLayout)"는 라우트된 페이지에 기본적으로 적용될 레이아웃을 지정
    • <NotFound>: URL이 어떤 라우트 설정과도 일치하지 않을 경우 실행되며, <LayoutView>를 사용하여 MainLayout을 기본 레이아웃으로 사용하고, 사용자에게 404 오류 메시지를 표시
<Router AppAssembly="@typeof(Program).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
    </Found>
    <NotFound>
        <LayoutView Layout="@typeof(MainLayout)">
            <Result Status="404" />
        </LayoutView>
    </NotFound>
</Router>

<AntContainer />
 
  • MainLayout.razor
    • @using AntDesign.ProLayout: ProLayout 컴포넌트를 사용하기 위해 Ant Design ProLayout 라이브러리의 네임스페이스를 참조
    • @using Newtonsoft.Json: JSON 데이터 처리를 위해 Newtonsoft.Json 라이브러리를 참조
    • @inherits LayoutComponentBase: 현재 컴포넌트가 Blazor의 레이아웃 컴포넌트로 동작하게 하기 위해 LayoutComponentBase를 상속받고, 이를 통해 @Body 렌더링 지점에서 자식 컨텐츠를 표시할 수 있습니다.
    • BasicLayout: ProLayout 라이브러리의 기본 레이아웃 컴포넌트이고, 이를 통해 애플리케이션의 주요 레이아웃 구조를 정의
    • <ChildContent>: 실제 페이지 컨텐츠를 포함할 영역이고, @Body는 Blazor 레이아웃 시스템에서 현재 페이지의 주요 컨텐츠를 렌더링하는 데 사용되는 지시어
@using AntDesign.ProLayout
@using Newtonsoft.Json
@inherits LayoutComponentBase

<AntDesign.ProLayout.BasicLayout MenuData="_menuData" Title="윈폼 웹">
    <ChildContent>
        @Body
    </ChildContent>
</AntDesign.ProLayout.BasicLayout>

@code
{
    private MenuDataItem[]? _menuData = { };

    protected override async Task OnInitializedAsync()
    {
        await base.OnInitializedAsync();

        var text = File.ReadAllText("Datas/menu.json");

        _menuData = JsonConvert.DeserializeObject<MenuDataItem[]>(text);
    }
}

<style>
    .ant-layout {
        background: white;
    }

    main {
        background-color: white;
    }

    footer {
        display: none;
    }

    .ant-pro-sider-menu {
        font-weight: 600;
    }
</style>
 
  • menu.json
[
  {
    "path": "/",
    "name": "메뉴A",
    "key": "A",
    "icon": "file"
  },
  {
    "path": "/b",
    "name": "메뉴B",
    "key": "B",
    "icon": "cloud"
  },
  {
    "path": "/c",
    "name": "메뉴C",
    "key": "C",
    "icon": "robot"
  }
]
 
  • A.razor: "/a" 아니고 "/" 이렇게 페이지 설정함
@page "/"

<h3>A</h3>

@code {

}
 
  • B.razor
@page "/b"

<h3>B</h3>

@code {

}
 
  • C.razor
@page "/c"

<h3>C</h3>

@code {

}
 

 

빌드 & 실행 결과

 

반응형