티스토리 뷰

반응형

2023.04.07 - [WPF .NET] - 이미지 사용하기 - WPF Pack URI Part1

일반적으로 애플리케이션을 개발할 때 여러개의 라이브러리 프로젝트들을 포함해서 개발하게 됩니다.

이번 포스팅에서는 이미지를 클래스라이브러리에 넣어 두었을 때의 경로를 어떻게 사용하는지 간단하게 알아 보겠습니다.

1. PackUriSample.Module 추가

Prism library를 사용하는 이유 중에 하나는 Module이라는 클래스 라이브러리를 만들어서 관리를 할 수 있기 때문입니다.

모듈을 사용하는 방법에 대해서는 다른 포스팅에서 다루도록 하겠습니다.

2. 아이스크림 화면 추가

IceCreamView.xaml

4개의 아이스 크림 이미지를 추가하고, 이전 포스팅과 같이 Build Action, Copy to Output Directory를 설정 합니다.

여기서 이전과 다른 부분 중에 하나는 경로앞에 모듈 주소가 붙는다는 것입니다.

이전 : Source="/Assets/Images/cake3.jpg"

현재 : Source="/PackUriSample.Module;component/Assets/Images/icecream3.jpg"

메인 실행 파일과 레퍼런스된 dll에 존재하는 이미지를 참조하기 위해서는 해당 dll의 이름과 component라는 단어가 추가되어야 합니다.

<UserControl
    x:Class="PackUriSample.Module.Views.IceCreamView"
    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:local="clr-namespace:PackUriSample.Module.Views"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:prism="http://prismlibrary.com/"
    d:DesignHeight="300"
    d:DesignWidth="300"
    prism:ViewModelLocator.AutoWireViewModel="True"
    mc:Ignorable="d">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Image
            Grid.Row="0"
            Grid.Column="0"
            Source="/PackUriSample.Module;component/Assets/Images/icecream1.jpg" />
        <Image
            Grid.Row="0"
            Grid.Column="1"
            Source="/PackUriSample.Module;component/Assets/Images/icecream2.jpg" />
        <Image
            Grid.Row="1"
            Grid.Column="0"
            Source="/PackUriSample.Module;component/Assets/Images/icecream3.jpg" />
        <Image
            Grid.Row="1"
            Grid.Column="1"
            Source="/PackUriSample.Module;component/Assets/Images/icecream4.jpg" />
        <Image Grid.Row="0" Grid.Column="2" />
        <Image Grid.Row="1" Grid.Column="2" />
    </Grid>

</UserControl>

디자인 타임

런타임

3. 나머지 아이스크림 출력

이전에 사용했던 방법과 동일하게 출력합니다.

IceCreamViewModel.cs

2번 이미지는 스트림에서 불러와서 보여주고, 1, 4번 이미지는 pack://siteoforigin을 이용해서 출력했습니다.

public class IceCreamViewModel : BindableBase
{
    private Uri _iceCream1;
    public Uri IceCream1
    {
        get => _iceCream1;
        set => SetProperty(ref _iceCream1, value);
    }
    private ImageSource _iceCream2;
    public ImageSource IceCream2
    {
        get => _iceCream2;
        set => SetProperty(ref _iceCream2, value);
    }
    private ImageSource _iceCream4;
    public ImageSource IceCream4
    {
        get => _iceCream4;
        set => SetProperty(ref _iceCream4, value);
    }
    public IceCreamViewModel()
    {
        LoadIceCream2Image();
        LoadIceCream4Image();
        LoadIceCream1Image();
    }

    private void LoadIceCream1Image()
    {
        IceCream1 = new Uri("pack://siteoforigin:,,,/Assets/Images/icecream1.jpg");
    }

    private void LoadIceCream4Image()
    {
        BitmapImage bi = new();
        bi.BeginInit();
        bi.UriSource = new Uri("pack://siteoforigin:,,,/Assets/Images/icecream4.jpg");
        bi.EndInit();
        IceCream4 = bi;
    }

    private void LoadIceCream2Image()
    {
        System.Reflection.Assembly assembly = GetType().Assembly;
        string imageName = $"{assembly.GetName().Name}.Assets.Images.icecream2.jpg";
        using System.IO.Stream stream = assembly.GetManifestResourceStream(imageName);
        if (stream == null)
        {
            return;
        }
        BitmapImage bi = new();
        bi.BeginInit();
        bi.StreamSource = stream;
        bi.EndInit();
        IceCream2 = bi;
    }
}

IceCreamView.xaml

<UserControl
    x:Class="PackUriSample.Module.Views.IceCreamView"
    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:local="clr-namespace:PackUriSample.Module.Views"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:prism="http://prismlibrary.com/"
    d:DesignHeight="300"
    d:DesignWidth="300"
    prism:ViewModelLocator.AutoWireViewModel="True"
    mc:Ignorable="d">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Image
            Grid.Row="0"
            Grid.Column="0"
            Source="{Binding IceCream1}" />
        <Image
            Grid.Row="0"
            Grid.Column="1"
            Source="{Binding IceCream2}" />
        <Image
            Grid.Row="1"
            Grid.Column="0"
            Source="/PackUriSample.Module;component/Assets/Images/icecream3.jpg" />
        <Image
            Grid.Row="1"
            Grid.Column="1"
            Source="{Binding IceCream4}" />
        <Image Grid.Row="0" Grid.Column="2" />
        <Image Grid.Row="1" Grid.Column="2" />
    </Grid>

</UserControl>

4. 어떤 방식을 사용하는 것이 좋을까?

아이스크림1, 2번을 출력하는 방법인 Copy to Output Directory를 Copy Always를 선택하고, pack://siteoforigin:,,,/Assets/Images/icecreamX.jpg 절대 주소를 사용하는 방법이 편해 보이기는 합니다. 

그러나, 이 방법은 단점이 있습니다.

  • 이미지가 외부에 노출이 된다.
  • 사용자가 임의로 이미지를 변경할 수도 삭제할 수도 있음
  • nuget package 같은 것으로 만들어서 배포를 해야 하는 경우 사용 불가

이런 단점이 있기 때문에, 가능하면 Embedded resource나 Resource를 이용하는 것이 적절한데, Embedded resource의 경우에는 XAML에서 직접 접근해서 사용하는 것이 불가능하여, 코드로 스트림을 불러와서 이미지로 변환하는 작업이 필요합니다.

보안상의 이유로 암호화를 한 파일을 복호화해서 사용하는 경우가 아니라면 매우 불편할 수 있겠죠.

그래서, 최종적으로는 Resource를 선택하는 것이 가장 일반적일 것 같습니다.

5. 다른 모듈에 있는 이미지 불러오기

이번에는 화면이 있는 프로젝트가 아니라 참조가 걸려있는 프로젝트에서 이미지를 가져오는 경우에 대해서 알아 보겠습니다.

CakeView.xaml

icecream3.jpg는 xaml에서 uri를 이용해서 직접 호출해서 불러올 수 있습니다. 이 때 모듈 이름과 component를 추가로 붙여서 호출하면 됩니다.

<Image
    Grid.Row="1"
    Grid.Column="2"
    Source="/PackUriSample.Module;component/Assets/Images/icecream3.jpg" />

CakeViewModel.cs

icecream2.jpg는 Embedded resource이기 때문에 스트림으로 불러와야 하는데, 스트림을 불러오려면 어셈블리를 알아야하고, 아래 방법으로 참조된 클래스 라이브러리의 어셈블리를 반환 받아서 사용할 수 있습니다.

 

private void LoadIceCream2Image()
{
    System.Reflection.Assembly assembly = System.Reflection.Assembly.GetAssembly(typeof(PackUriSampleModule));
    string imageName = $"{assembly.GetName().Name}.Assets.Images.icecream2.jpg";
    using System.IO.Stream stream = assembly.GetManifestResourceStream(imageName);
    if (stream == null)
    {
        return;
    }
    BitmapImage bi = new();
    bi.BeginInit();
    bi.StreamSource = stream;
    bi.EndInit();
    IceCream2 = bi;
}

 

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

런타임 화면

6. 소스

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

 

GitHub - kaki104/WpfTest

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

github.com

 

반응형
댓글