2013. 5. 27. 10:34 Windows8/Windows Store App

현재 뮤직 플레이어를 만들고 있는데 한가지 문제점이 생겼다.

 

노래를 재생 하기 위해서는 MediaElement 인스턴스가 필요하다.

일단 MediaElement 에 노래 Urifile 을 넣어주고 Play() 메서드만 호출해 주면 재생하는건 아무 문제가 없다.

 

근데 문제는??

 

Element 가 모든 페이지 에서 공유가 되어야 한다는 것이다. 참고로 디자인 패턴에선 이걸 (싱글턴 패턴) singleton pattern 이라고 한다.

 

예를 하나 들어보자.

 

MainPage , MusicPlayingPage, 그리고 App bar 가 하나 있다고 치자.

 

MainPage 에는 MusicPlayingPage 로 이동하는 버튼이 있고 MusicPlayingPage 로 이동하면 노래가 자동으로 재생이 된다. 즉 MediaElement 를 사용하게 된다.

 

하지만 App bar 에서도 노래를 재생하거나 멈출 수 있다. 현재 재생되고 있는 노래를 멈추려면 아까 MusicPlayingPage 에서 재생된 그 MediaElement 에 접근이 가능해야 한다.

 

하지만 MusicPlayingPageApp bar 는 서로 다른 영역이다. 이렇게 서로 다른 두 영역에서 한가지 resource 를 사용할 수 있는 방법은 무엇이 있을까?!

 

일단 두가지 방법을 알아 내었다.

 

 

1. MediaElement 를 static 변수로 사용한다.

 

누구나 생각해 볼 수 있는 방법이다.. Windows store App 은 보통 처음 시작되면 App.xaml.csOnLaunched 가 호출되게 된다.

App 이 시작될 때 처음 호출되는 부분이므로 이곳에다 static 변수를 선언해 놓으면 App 이 종료될 때 까지는 계속 사용할 수가 있다.

 

나는 다음과 같이 만들어 보았다.

 

[App.caml.cs]

 

    public sealed partial class MainPage : Page
    {
        public static MediaPlayer player = null;
        public MainPage()
        {
            this.InitializeComponent();
            player = new MediaPlayer();
        }

        /// <summary>
        /// Invoked when this page is about to be displayed in a Frame.
        /// </summary>
        /// <param name="e">Event data that describes how this page was reached.  The Parameter
        /// property is typically used to configure the page.</param>
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            RootFrame.Navigate(typeof(RootPage), "");
        }
    }

 

[MediaPlayer.cs]

 

namespace MediaTest
{
    public class MediaPlayer
    {
        private MediaElement _media = null;
        public MediaElement Media
        {
            get;
            set;
        }

        public MediaPlayer()
        {
            Media = new MediaElement();
            Media.AudioCategory = Windows.UI.Xaml.Media.AudioCategory.BackgroundCapableMedia;
            //Media.AutoPlay = true;
        }

        public void Play()
        {
            Media.Play();
        }

        public void Stop()
        {
            Media.Stop();
        }

        public async void setMedia(StorageFile file)
        {
            var stream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
            Media.SetSource(stream, file.ContentType);
        }
    }
}

 

아까 말한대로 App.xaml.csMediaPlayer 라는 클래스의 인스턴스를 static 으로 생성해 줬다. MediaPlayer 는 그냥 예제용으로 만든 클래스 이다. MediaPlayer 클래스에는 실제로 MediaElement 가 위치해 있는 것이다.

 

그럼 이 MediaElement 를 사용해 보도록 하자.

 

[MusicPlayingPage.xaml.cs]

 

        private async void Button_Click_1(object sender, RoutedEventArgs e)
        {
            StorageFolder storageFolder = KnownFolders.DocumentsLibrary;
            StorageFile file = await storageFolder.CreateFileAsync("Music.mp3", CreationCollisionOption.OpenIfExists);
            MainPage.player.setMedia(file);
            MainPage.player.Play();
 
         }

 

MusicPlayingPage 에서는 간단히 버튼이 눌리면 음악 파일을 읽어와 stream 방식으로 재생해주고 있다.

노란색으로 하이라이트 된 부분을 보면 아까 static 으로 선언했던 player 변수를 사용하고 있다.

static 으로 선언이 되었기 때문에 어느 Page 에서든 접근이 가능하다.

 

App bar 에서 사용할 때도 저렇게 사용해 주면 된다.

 

근데 이렇게 하는건 뭔가 찜찜하다.. 잘못된 방법은 아니지만 뭔가 찜찜하다.. 그래서 MSDN을 찾아보니 아래와 같은 방법이 제시되어 있었다.

 

 

2. App.xaml 에 변수를 선언

 

기가막힌 방법이다. App.xaml 는 이미 static 하게 사용하도록 만들어져 있기 때문에 이곳에 선언을 해 두면 어디서든 사용이 가능하다.

 

 

<Application
    x:Class="MediaTest.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:MediaTest">


    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>

                <!--
                    Styles that define common aspects of the platform look and feel
                    Required by Visual Studio project and item templates
                 -->

                <ResourceDictionary Source="Common/StandardStyles.xaml"/>
            </ResourceDictionary.MergedDictionaries>
            <MediaElement x:Name="localMediaElement"/>
        </ResourceDictionary>
    </Application.Resources>
</Application>

 

노란색 하이라이트를 보면 MediaElement를 다른 resource 들과 동일하게 선언해 놓은 것을 볼 수 있다.

이렇게 하면 소스코드와 마찬가지로 App.xaml 페이지가 호출 될 때 MediaElement 인스턴스가 생성이 된다. 사실 정확한건 아니다. 이건 내 직감일 뿐이다...

 

이제 선언을 했으니 저걸 불러다 사용해 보자.

 

 

        private async void Button_Click_1(object sender, RoutedEventArgs e)
        {
            StorageFolder storageFolder = KnownFolders.DocumentsLibrary;
            StorageFile file = await storageFolder.CreateFileAsync("01 봄바람.mp3", CreationCollisionOption.OpenIfExists);
 
            MediaElement media = (MediaElement)App.Current.Resources["localMediaElement"];
            var stream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
            media.SetSource(stream, file.ContentType);
            media.Play();
        }

 

노란색 하이라이트만 보자. 아까 등록한 resource 를 불러다쓰려면 저렇게 하면 된다. 다시 말하지만 현재 저 resourcestatic 하게 인스턴스 화 되어 있는 상태이다. 때문에 다른 페이지에서 저렇게 가져온다고 해서 새로 인스턴스가 생성되거나 하지 않는다. 레퍼런스만 가져오는 것이다.

 

근데 보면 저거 꼭 "App.xaml 에 선언해줘야 되는건가?" 라는 궁금증이 든다.

 

그래서 static resource 파일인 CommonStandardStyles.xaml 에도 선언을 해 줘봤다.

 

잘된다!

 

그럼 다시.

 

우리는 보통 StandardStyles 에다 필요한 선언을 하지 않는다. Microsoft 에서 Common 폴더 내에 있는건 왠만하면 건들지 말라고 권장하고 있기 때문이다. 그래서 보통 ResourceDictonary를 새로 하나 생성해서 사용하곤 한다.

 

그럼 새로 생성한 ResourceDictonary 에 변수를 선언해 줘도 static 하게 동작할까?

 

잘된다!!

 

하지만 새로 생성한 ResourceDictonaryApp.xaml 에서 StandardStyle.xamlmerge 해줘야 한다는 점.

 

[App.xaml]

 

<Application
    x:Class="MediaTest.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:MediaTest">


    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>

                <!--
                    Styles that define common aspects of the platform look and feel
                    Required by Visual Studio project and item templates
                 -->

                <ResourceDictionary Source="Common/StandardStyles.xaml"/>
                <ResourceDictionary Source="CustomStyle.xaml"/>
            </ResourceDictionary.MergedDictionaries>
            <!--<MediaElement x:Name="localMediaElement"/>-->
        </ResourceDictionary>
    </Application.Resources>
</Application>

 

이 두가지 방법을 쓰면 싱글턴 패턴을 쓰는데 무리가 없을 것 같다.

 

좀 더 전문적인 글을 보기 위해선 아래 링크를 참고하자.

 

http://msdn.microsoft.com/en-us/library/ff650316.aspx

posted by townone