티스토리 뷰

반응형

Wpf로 애플리케이션을 개발할 때 고통스러운 기억 중의 하나가 이미지 파일을 화면에 출력하는 것입니다.

다들 저랑 비슷한 기억을 가지고 계신거죠?

이 포스팅으로 완전히 마스터를 하려고 합니다. 다음에 또 괴로운 시간을 보내고 싶지 않네요 ㅎㅎ

 

1. WPF의 Pack URI

메인이 되는 글의 위의 링크를 눌러서 참고하시면 됩니다.

WPF는 URI를 이용해서, 여러가지 상황의 파일들을 식별하고 로드할 수 있습니다.

이 포스트에서는 이미지 파일을 프로젝트에 포함시키고, 사용하는 방법에 대해서 집중적으로 다루도록 하겠습니다.

2. Build Action

이미지 파일을 프로젝트에 추가하고, 파일을 선택하면, 프로퍼티 창에 Build Action이라는 항목이 있습니다. 자세한 사항은 여기를 참고하시기 바랍니다.

이미지 파일을 등록 후 선택할 수 있는 옵션은 몇개를 정리했습니다.

  • None
    • 이 파일은 어떤 경우에도 Build 되지 않습니다.
    • 실행파일 혹은 DLL에 포함되지 않습니다
  • Embedded Resource
    • 이 파일은 어셈블리에 포함된 리소스로 컴파일됩니다.
    • DLL에 포함됩니다.
    • System.Reflection.Assembly.GetManifestResourceStream을 호출해서 불러올 수 있습니다.
  • Resource
    • 이 파일은 어셈블리에 포함된 리소스로 컴파일됩니다.
    • DLL에 포함됩니다.
    • Pack URI를 이용해서 불러올 수 있습니다.

3. Copy to Output Directory

Output Directory에 파일을 항상 복사, 없으면 복사, 복사 안함의 옵션을 선택할 수 있습니다.

  • 일반적으로 실행 파일의 하위 디렉토리가 생성되고, 파일이 복사됩니다.
  • Build Action이 None인 경우에 사용합니다.
  • Pack URI를 이용해서 불러올 수 있습니다.

4. 테스트1

4개의 케이크 이미지를 각 각 다음과 같이 설정했습니다.

  Build Action Copy to Output Directory
cake1 None Do not copy
cake2 Embedded resource Do not copy
cake3 Resource Do not copy
cake4 None Copy always

빌드 후 결과 파일 확인

루트 폴더의 파일들을 확인하면, 이미지 파일은 보이지 않습니다.

 

프로그램에서 이미지를 보기 위해서 아래와 같이 작업을 진행 합니다.

MainWindow.xaml

<Window
    x:Class="PackUriSample.Views.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:prism="http://prismlibrary.com/"
    Title="{Binding Title}"
    Width="525"
    Height="350"
    prism:ViewModelLocator.AutoWireViewModel="True">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Image Grid.Row="0" Grid.Column="0" Source="/Assets/Images/cake1.jpg" />
        <Image Grid.Row="0" Grid.Column="1" Source="/Assets/Images/cake2.jpg" />
        <Image Grid.Row="1" Grid.Column="0" Source="/Assets/Images/cake3.jpg" />
        <Image Grid.Row="1" Grid.Column="1" Source="/Assets/Images/cake4.jpg" />
    </Grid>
</Window>

디자인 타임 화면

모든 케이크 이미지가 잘 보입니다.

런타임 화면

  • cake1
    • 이미지를 불러 올 수 없습니다.
    • 실행 파일이 있는 곳에 이미지가 없기 때문입니다.
  • cake2
    • 이미지를 불러 올 수 없습니다.
    • PackUriSample.dll 파일에 이미지가 포함되어 있지만, 직접 Uri를 통한 접근이 불가능 하기 때문입니다.
  • cake3
    • 이미지를 불러 올 수 있습니다.
    • PackUriSample.dll 파일에 이미지가 포함되어 있고, Uri를 통한 접근이 가능하기 때문입니다.
  • cake4
    • 이미지를 불러올 수 없습니다.
    • 이미지 파일은 Assets\Imasge\ 폴더 아래 들어가 있으나, 기존 경로로는 접근이 않되고 새로운 경로를 입력해야 접근이 가능합니다.

Cake2 이미지 불러오기

Cake2라는 프로퍼티를 추가합니다.

private ImageSource _cake2;
/// <summary>
/// 케이크2
/// </summary>
public ImageSource Cake2
{
    get { return _cake2; }
    set { SetProperty(ref _cake2, value); }
}

GetManifestResourceStream을 이용해서 스트림을 받은 후 BitmapImage로 만들어서 바인딩 프로퍼티에 입력합니다.

imageName은 PackUriSample.Assets.Images.cake2.jpg
stream은 메모리 해제를 위해 using 문을 사용
이미지가 스트림 형태로 저장되기 때문에, 직접 접근 방식으로 사용할 수 없습니다.
/// <summary>
/// 케이크 2이미지 로드
/// </summary>
private void LoadCake2Images()
{
    System.Reflection.Assembly assembly = GetType().Assembly;
    string imageName = $"{assembly.GetName().Name}.Assets.Images.cake2.jpg";
    using System.IO.Stream stream = assembly.GetManifestResourceStream(imageName);
    if (stream == null)
    {
        return;
    }
    BitmapImage bi = new();
    bi.BeginInit();
    bi.StreamSource = stream;
    bi.EndInit();
    Cake2 = bi;
}

MainWindow.xaml 수정

<Image
    Grid.Row="0"
    Grid.Column="1"
    Source="{Binding Cake2}" />

런타임 화면

Cake4 이미지 불러오기

Cake4라는 프로퍼티를 추가합니다.

private ImageSource _cake4;
/// <summary>
/// 케이크4
/// </summary>
public ImageSource Cake4
{
    get => _cake4;
    set => SetProperty(ref _cake4, value);
}

Pack Uri를 이용해서 이미지를 불러옵니다.

아래 주소에서 siteoforigin은 실행 파일이 존재하는 경로를 의미합니다.

siteoforigin은 절대 경로로만 위치를 지정해야 합니다. (아래와 같이 pack://siteof...이 주소로 시작해야한다는 의미입니다.)

/// <summary>
/// 케이크4 이미지 로드
/// </summary>
private void LoadCake4Image()
{
    BitmapImage bi = new();
    bi.BeginInit();
    bi.UriSource = new Uri("pack://siteoforigin:,,,/Assets/Images/cake4.jpg");
    bi.EndInit();
    Cake4 = bi;
}

MainWindow.xaml 수정

<Image
    Grid.Row="1"
    Grid.Column="1"
    Source="{Binding Cake4}" />

런타임 화면

Cake1 이미지 불러오기

지금 상태에서는 불가능 하기 때문에, Copy to Output Directory를 수정해서 Copy always로 변경합니다.

Cake1 프로퍼티 추가, 이 프로퍼티는 ImageSource가 아니라 Uri입니다.

private Uri _cake1;
/// <summary>
/// 케이크1
/// </summary>
public Uri Cake1
{
    get { return _cake1; }
    set { SetProperty(ref _cake1, value); }
}

Pack URI를 이용해서 위치를 지정합니다.

여기서는 간단하게 Uri 주소만 Image 컨트롤의 Source 프로퍼티에 바인딩을 해서, 처리하는 방법을 알아 보았습니다.

/// <summary>
/// 케이크1 이미지 로드
/// </summary>
private void LoadCake1Image()
{
    Cake1 = new Uri("pack://siteoforigin:,,,/Assets/Images/cake1.jpg");
}

MainWindow.xaml 수정

<Image
    Grid.Row="0"
    Grid.Column="0"
    Source="{Binding Cake1}" />

런타임 화면

맛있어 보이는 케이크 4개...먹으면 살찔 것 같네요;;;

소스

디자인 타임에서도 보이는 방법이 있지만, 설명하기가 복잡해서.. 넘어가겠습니다~

이번 포스팅은 기본적인 내용만 다루었습니다. 본체는 다음 Part에서...

WpfTest/PackUriSample at master · kaki104/WpfTest (github.com)

 

GitHub - kaki104/WpfTest

Contribute to kaki104/WpfTest development by creating an account on GitHub.

github.com

 

반응형
댓글