Videógaléria - Silverlight 1.1 (2. rész)

Előző cikkemben a Silverlight 1.1-gyel való ismerkedés kapcsán rögtön implementáltam is egy videolejátszót. Miután a hihetetlen boldogságból magamhoz tértem és újra gondolkozni tudtam, rájöttem, hogy bármilyen csodálatosnak is véltem az első Silverlight 1.1-en elkövetett "hello world" jellegű alkalmazásomat, máris jöttek belülről a kérdések:

  • hogyan fogom kontrolként használni
  • honnan lesznek a videók nem bedrótozottak
  • (miért jönnek mindig újabb igények Nerd)???

Oké, válaszoljuk meg csípőből: Silverlight Page helyett Control-t csinálok belőle, amiket kiteszek egy Silverlight Page-re, mégpedig úgy, hogy dinamikusan felolvasom monjuk a /video mappából a wmv fájlokat, és ebből lesz a videógalériám. Itt jön el a pillanat, amikor is realizálódik bennem, hogy tök jó, hogy én felolvasom a /video mappa tartalmát, de a Silverlight nem Surprised, mert szegény nem fér hozzá a fájlokhoz. Legalábbis azokhoz nem. Persze, persze, tud ő fájlt kezelni, de nem így ahogy én akarom. Úgysem lehet mindent egylépésben megoldani, így hát osszunk mi is:

  • első lépés: bedrótozott módon több videó kontroll megjelenítése a lapon
  • második lépés: statikus fájlok helyett dinamikus mappafelolvasás

Nyissuk meg tehát a már meglévő solution-t, és vágjunk is bele. Mivel jelenleg egyetlenegy XAML fájlunk van, a Page.xaml, ami az oldalt reprezentálja majd, így picit át kellene strukturálni az alkalmazást. Mivel úgyis kontrolt akarunk készíteni, adjunk hozzá egy új Silverlight User Control-t a projekthet, legyen Player.xaml. Mielőtt gyorsan át copypaste-elnénk a meglávő markup és cs kódot, nézzük meg, hogy mit is kapunk a Silverlight User Control-ban.

  • nincs a XAML-ben x:Class (már érezzük hogy ezzel még bajunk lesz Nerd, mert azért kódot mégicsak akarunk írni hozzá, vagy legalábbis bemásolni, hiszen már megírtuk)
  • az újonnan létrehozott Player.xaml Embedder Resource lett
  • System.Windows.Controls.Control osztályból származik public class Player : Control
  • van neki konstruktora (ahol rendkívül előrelátóan megoldották nekünk a kódfájl hozzárendelését):
        public Player()
        {
            System.IO.Stream s = this.GetType().Assembly.GetManifestResourceStream("VideoPlayer.Player.xaml");
            this.InitializeFromXaml(new System.IO.StreamReader(s).ReadToEnd());
        }

Felolvassa a hozzátartozó xaml fájl tartalmát (ezért kellett erőforrásnak definiálni ezt a xaml-t, hogy fel tudjuk olvasni a tartalmát StreamReaderrel!!!) sztringként, és inicializálja belőle a kontrollt, így lesz egy xaml leírásból Silverlight tartalom. Mostmár jöhet a copypaste, a Page.xaml markupját (a root canvas belsejét), és a mögöttes kódot másoljuk át a Player.xaml-be (figyeljünk a szükséges névterek using-olására). A Page-ben lévő Page_Loaded tartalmát másoljuk be a Player konstruktorába (készíthetnénk ide is egy loaded eseménykezelőt, amit az xaml-be is fel kellene venni a root canvas-ba Loaded-ként, de ha már úgyis van kontruktor, pont tökéletes lesz). Amit még tegyünk meg, hogy a Player.xaml gyökér Canvas-ában definiáljunk nevet, szélességet, magasságot és háttérszínt:

        <Canvas
        xmlns="http://schemas.microsoft.com/client/2007" 
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        x:Name="parentCanvas" 
        Width="320"
        Height="260"
        Background="#FF000000"
         >

Jelenleg ott tartunk, hogy ami eddig a Page-ben volt implementálva, az átkerült a Player-be, a Page pedig jelenleg üres, mindjárt kitöltjük (azaz betöljük a Player-eket Nerd). De előtte biztos ami biztos, fordítsunk. Nyugdojunk meg, az 79 hiba Surprised nem az aminek látszik. Ha jobban megfigyeljük, hogy mi is a nyűgje, akkor hamar kiderül, hogy a kontrolljaink nincsenek deklarálva. "De hát eddig működött, akkor most mi történt?" - kérdezhetnénk, miközben értetlenül bámulunk a Visual Studio Error List ablakára Crying. Semmi nem történt, és pont ez a baj. Amíg Page volt a Silverlight-os tartalom, addig lefordult a xaml (Build action=SilverlightPage!!!), és teljesen hasonlóan az aspx-es működéshez, automatiksuan generálódott egy fájl (.g), amiben pont definiálva voltak a konrtoljaink, mégpedig az általunk elkeresztelt x:Name névvel, ezért nem kellett semmi extrát elkövetnünk, mielőtt kódból használhattuk volna őket, mint bármilyen más jólfésült kontrol esetén ezt már megszoktuk. Félreértés ne essék, csoda nem történt, a kontrollokat definiálni kellett, csak nem nekünk Nerd. Azonban mivel a Silverlight User Control nem úgy fordul, legalábbis a xaml része biztosan nem (a .cs belefordul a projekt kimeneti dll-be), ezért a varázsló sem fogja helyettük definiálni a kontrollokat, nincs mit tenni, nekünk kell (igen kézzel Angry,és igen, ez bizony fájdalmas, főleg ha kellően sok, névvel hivatkozott vezérlőnk van, mint például a videoplayer-ben Hot). Definiáljunk hát egy FrameworkElement _root; változót, és egészítsük ki az inicializálást így: _root = this.InitializeFromXaml(new System.IO.StreamReader(s).ReadToEnd());, azaz a _root-on keresztül igazából megvan a Silverlight objektumhierarchiánk, és a FindName()-el minden kontrolt el tudunk érni, még a konstruktorban, mielőtt felhasználnánk őket:

        #region Define variables to controls
        MediaElement VideoPlayer;
        Canvas PlayPauseButton;
        Canvas StopButton;
        Canvas MuteButton;
        Rectangle VolumeSliderOpaque;
        Canvas VolumeSlider;
        Path MuteSymbol;
        Canvas FullScreenButton;
        Canvas Timeline;

        TextBlock PositionText, ProgressText;

        ScaleTransform PathProgressScaleTransform;

        Storyboard PlayPauseButton_FocusInAnimation, PlayPauseButton_FocusOutAnimation, PlayPauseButton_PressAnimation, PlayPauseButton_DepressAnimation;
        Storyboard PlaySymbol_ShowAnimation, PlaySymbol_HideAnimation, PauseSymbol_ShowAnimation, PauseSymbol_HideAnimation;

        Storyboard StopButton_FocusInAnimation, StopButton_FocusOutAnimation, StopButton_PressAnimation, StopButton_DepressAnimation;

        Storyboard MuteButton_FocusInAnimation, MuteButton_FocusOutAnimation, MuteButton_PressAnimation, MuteButton_DepressAnimation;

        Storyboard FullScreenButton_FocusInAnimation, FullScreenButton_FocusOutAnimation, FullScreenButton_PressAnimation, FullScreenButton_DepressAnimation;

        #endregion
        #region Get controls by FindName
            
        VideoPlayer = _root.FindName("VideoPlayer") as MediaElement;
            
        PlayPauseButton = _root.FindName("PlayPauseButton") as Canvas;
        StopButton = _root.FindName("StopButton") as Canvas;
        MuteButton = _root.FindName("MuteButton") as Canvas;
        VolumeSliderOpaque = _root.FindName("VolumeSliderOpaque") as Rectangle;
        VolumeSlider = _root.FindName("VolumeSlider") as Canvas;
        MuteSymbol = _root.FindName("MuteSymbol") as Path;
        FullScreenButton = _root.FindName("FullScreenButton") as Canvas;
        Timeline = _root.FindName("Timeline") as Canvas;
        PositionText = _root.FindName("PositionText") as TextBlock;
        ProgressText = _root.FindName("ProgressText") as TextBlock;

        PathProgressScaleTransform = _root.FindName("PathProgressScaleTransform") as ScaleTransform;

        PlayPauseButton_FocusInAnimation = _root.FindName("PlayPauseButton_FocusInAnimation") as Storyboard;
        PlayPauseButton_FocusOutAnimation = _root.FindName("PlayPauseButton_FocusOutAnimation") as Storyboard;
        PlayPauseButton_PressAnimation = _root.FindName("PlayPauseButton_PressAnimation") as Storyboard;
        PlayPauseButton_DepressAnimation = _root.FindName("PlayPauseButton_DepressAnimation") as Storyboard;
        PlaySymbol_ShowAnimation = _root.FindName("PlaySymbol_ShowAnimation") as Storyboard;
        PlaySymbol_HideAnimation = _root.FindName("PlaySymbol_HideAnimation") as Storyboard;
        PauseSymbol_ShowAnimation = _root.FindName("PauseSymbol_ShowAnimation") as Storyboard;
        PauseSymbol_HideAnimation = _root.FindName("PauseSymbol_HideAnimation") as Storyboard;

        StopButton_FocusInAnimation = _root.FindName("StopButton_FocusInAnimation") as Storyboard;
        StopButton_FocusOutAnimation = _root.FindName("StopButton_FocusOutAnimation") as Storyboard;
        StopButton_PressAnimation = _root.FindName("StopButton_PressAnimation") as Storyboard;
        StopButton_DepressAnimation = _root.FindName("StopButton_DepressAnimation") as Storyboard;
        MuteButton_FocusInAnimation = _root.FindName("MuteButton_FocusInAnimation") as Storyboard;
        MuteButton_FocusOutAnimation = _root.FindName("MuteButton_FocusOutAnimation") as Storyboard;
        MuteButton_PressAnimation = _root.FindName("MuteButton_PressAnimation") as Storyboard;
        MuteButton_DepressAnimation = _root.FindName("MuteButton_DepressAnimation") as Storyboard;
        FullScreenButton_FocusInAnimation = _root.FindName("FullScreenButton_FocusInAnimation") as Storyboard;
        FullScreenButton_FocusOutAnimation = _root.FindName("FullScreenButton_FocusOutAnimation") as Storyboard;
        FullScreenButton_PressAnimation = _root.FindName("FullScreenButton_PressAnimation") as Storyboard;
        FullScreenButton_DepressAnimation = _root.FindName("FullScreenButton_DepressAnimation") as Storyboard;

        #endregion

Ezzel megvagyunk, fordíthatunk, és végre megint nincs hiba, tökéletes Open-mouthed. Vagyis az lenne, ha csinálna bármit is, de ugye a Page.xaml üres. Tegyünk ez ellen. Változtassunk pár alap dolgot a gyökér Canvason, úgy mint Width="640" Height="480" Background="Gray". Ezzel a felületet el is készítettük, van egy szürke hátterünk, oda jönnek majd egyesével a videoplayer-ek. A Page_Loaded -ban regiszrtáljunk be egy eseménykezelőt a browser átméreteződéséhez BrowserHost.Resize += new EventHandler(BrowserHost_Resize);, és implementáljuk következőképp:

        private void BrowserHost_Resize(object sender, EventArgs e)
        {
            // Set size to host size
            Width = BrowserHost.ActualWidth;
            Height = BrowserHost.ActualHeight;

            if (!_videosLoaded && (0 != Width) && (0 != Height))
            {
                //// If Videos haven't been loaded yet and the control is initialized, load them now
                foreach (var file in new string[] { "SilverLight.wmv", "SilverLight.wmv", "SilverLight.wmv", "SilverLight.wmv", "SilverLight.wmv" })
                {
                    new Player(this, new Uri(HtmlPage.DocumentUri, file));
                }
                _videosLoaded = true;
            }
        }

Azaz a Silverlight oldalunk foglalja el a böngésző teljes méretét, és ha még nem lennének betöltve a videók (ne felejtsük el felvenni globális változónak a _videosLoaded-et), akkor töltsünk be párat. Itt most még bedrótozzuk a videók elérési útját, mind az öt videoplayer-be ugyanazt a videót töltjük be. Látható, hogy a Player objektum létrehozásakor átadunk neki két paramétert: magát a Page objektumot, illetve a wmv fájl elérési útját az oldalhoz képest relatívan megadva. Módosítsuk ezen paraméterezésnek megfelelően a Player osztályunk kontruktorát, és vegyünk fel egy változót, amiben a host oldalt tároljuk:

        private Page _parent;
        public Player(Page parent, Uri video)
        {
            //InitializeComponent from XAML
            ...

            //get controls by FindName
            ...
            //store the parent
            _parent = parent;


            // Create a Downloader to fetch the video
            ...
            downloader.Open("GET", new Uri(HtmlPage.DocumentUri, video), true);
            ...

            
            //add the control to its parent host
            _parent.Children.Add(this);
        }

Figyeljük meg, hogy hogyan kellett átírni a Downloader-ben a bedrótozott fájlnevet a konstruktorban megkapottra, illetve, hogy a Silverlight kontrol télnyleg meg is jelenjen az oldalon, illik őt hozzáadni a szülő Page kontrolhoz. Ha mist futtatjuk az alkalmazást, akkor azt kell látnunk, hogy a szürke háttér bal felső sarkában ott van öt videólejátszónk, és mind ugyanazt a wmv fájlt tartalmazza. Mielőtt mindenki kétségbeesne, aki a futtatáskor egyetlen videólejátszót lát a bal felső sarokban, emlékeztetnék mindenkit, hogy mivel mindegyik a 0,0 pozícióra van kitéve, ezért csak a legfelső látszik Wink.

Csináljunk valamit, ami láthatóvá teszi mind az öt lejátszót, és random helyezi el őket a képernyőn. Ehhez először egészítsük ki a Player.xaml fájlunkat a következővel. Az erőforrások után és a MediaElement elé szúrjunk be pár transzformációt, nyújtás, forgatás, eltolás, minegyik névvel ellátva:

 <Canvas.RenderTransform>
    <TransformGroup>
      <RotateTransform x:Name="rotateTransform" CenterX="160" CenterY="130" />
      <ScaleTransform x:Name="scaleTransform" CenterX="160" CenterY="130" />
      <TranslateTransform x:Name="translateTransform" />
    </TransformGroup>
  </Canvas.RenderTransform>

Természetesen nem feledkezünk meg a definiálni a változókat a kontroloknak, és FindName()-elni a konstruktorban:

        ScaleTransform scaleTransform;
        RotateTransform rotateTransform;
        TranslateTransform translateTransform;
 
        scaleTransform = _root.FindName("scaleTransform") as ScaleTransform;
        rotateTransform = _root.FindName("rotateTransform") as RotateTransform;
        translateTransform = _root.FindName("translateTransform") as TranslateTransform;

Írjunk pár segédfüggvényt, amik segítségével ezeket a transzformációkat könnyen használni is tudjuk:

        public void Translate(double deltaX, double deltaY)
        {
            translateTransform.X += deltaX;
            translateTransform.Y += deltaY;
        }

        public void Rotate(double deltaAngle)
        {
            rotateTransform.Angle += deltaAngle;
        }

        public void Scale(double deltaScale)
        {
            scaleTransform.ScaleX *= deltaScale;
            scaleTransform.ScaleY *= deltaScale;
        }

Ezután nincs más dolgunk, mint a a konstruktorban, még mielőtt a parent host-hoz hozzáadnánk a videoplayer-ünket, véletlenszerűen transzformáljuk el picit, forgassuk és toljuk el:

        //initial random transformations
        Translate(random.Next((int)(BrowserHost.ActualWidth - _root.Width)), random.Next((int)(BrowserHost.ActualHeight - _root.Height)));
        Rotate(random.Next(-30, 30));

Ehhez természetesen szükségünk lesz egy véletlenszám generátorra: private Random random = new Random(DateTime.Now.Millisecond);.

Futtassuk most az alkalmazást, és valami hasonló kezdő képernyőt kell látnunk (most már mindenkinek ténylegesen megvan az öt videólejátszója Hot):

VideoGallery

Első ránézésre nagyon szuper, tényleg mindegyik tud a többitől függetlenül videót lejátszani, de még nem 100%-os megoldás, pár kattintás után két kis funkcionális hiányosság rögtön előkerül:

  • teljes képernyős módba váltáskor ugyan felnagyítódik az ablak, de az elforgatása és eltolása megmarad, így csúnyán kilóg a képből
  • ha takarásban vannak egymáshoz képest a videók (márpedig úgy vannak), akkor a hátsók "elérhetetlenek" lesznek, ha a vezérlőgombjai vannak takarva

Kezdjük kipipálni őket ebben a sorrendben (természetesen a kipipálás feltétele a megvalósítás Hot). Mikor teljes képernyős módba váltunk, akkor a felnagyításon kívül vissza kell forgatni 0 fokba, másodszor, ha el van tolva, akkor vissza kell tolni 0,0 pozícióba. Ha már Silverlight, akkor adjuk meg a módját, ne csak simán állítsuk 0-ra ezeket az értékeket, hanem animáljuk őket Wink. Továbbá csináljuk úgy, hogy megjegyezzük az eredeti pozícióját, és abba állítsuk vissza majd.

Első lépésként írjuk meg XAML-ben az animációhoz szükséges StoryBoardot, ami a meglévő skálázás, nyújtás és eltolás canvas transzformációkat fogja animálni:

      <Storyboard x:Name="DoFullScreen">
        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="rotateTransform" Storyboard.TargetProperty="Angle">
          <SplineDoubleKeyFrame x:Name="DoFullScreen_rotateTransform_KeyFrameAngle" KeyTime="00:00:00.3000000" Value="0"/>
        </DoubleAnimationUsingKeyFrames>
        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="scaleTransform" Storyboard.TargetProperty="ScaleX">
          <SplineDoubleKeyFrame x:Name="DoFullScreen_scaleTransform_KeyFrameX" KeyTime="00:00:00.3000000" Value="0"/>
        </DoubleAnimationUsingKeyFrames>
         <DoubleAnimationUsingKeyFrames Storyboard.TargetName="scaleTransform" Storyboard.TargetProperty="ScaleY">
          <SplineDoubleKeyFrame x:Name="DoFullScreen_scaleTransform_KeyFrameY" KeyTime="00:00:00.3000000" Value="0"/>
        </DoubleAnimationUsingKeyFrames>
        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="translateTransform" Storyboard.TargetProperty="X">
          <SplineDoubleKeyFrame x:Name="DoFullScreen_translateTransform_KeyFrameX" KeyTime="00:00:00.3000000" Value="0"/>
        </DoubleAnimationUsingKeyFrames>
         <DoubleAnimationUsingKeyFrames Storyboard.TargetName="translateTransform" Storyboard.TargetProperty="Y">
          <SplineDoubleKeyFrame x:Name="DoFullScreen_translateTransform_KeyFrameY" KeyTime="00:00:00.3000000" Value="0"/>
        </DoubleAnimationUsingKeyFrames>
      </Storyboard>

Nagyon egyszerűen mindent meg tudtunk fogalmazni, 3 századmásodperc alatt minden transzformáció értékét 0-ra állítjuk, így biztos nem lesz se elforgatva, se eltolva a videó. Vigyázzunk viszont a nagyítással, mert a 0-szoros skálázás nem biztos hogy az, amit szeretnénk Surprised. Ezek az értékek még így nem egészen korrektek, mindjárt látni fogjuk, ezért ezeket majd kódból kiokoskodjuk. De mielőtt bármit kódolni tudnánk, ne felejtsük el deklarálni és a konsturktorban FindName()-elni a kontrollokat:

        SplineDoubleKeyFrame DoFullScreen_rotateTransform_KeyFrameAngle;
        SplineDoubleKeyFrame DoFullScreen_scaleTransform_KeyFrameX;
        SplineDoubleKeyFrame DoFullScreen_scaleTransform_KeyFrameY;
        SplineDoubleKeyFrame DoFullScreen_translateTransform_KeyFrameX;
        SplineDoubleKeyFrame DoFullScreen_translateTransform_KeyFrameY;
        Storyboard DoFullScreen;
        DoFullScreen_rotateTransform_KeyFrameAngle = _root.FindName("DoFullScreen_rotateTransform_KeyFrameAngle") as SplineDoubleKeyFrame;
        DoFullScreen_scaleTransform_KeyFrameX = _root.FindName("DoFullScreen_scaleTransform_KeyFrameX") as SplineDoubleKeyFrame;
        DoFullScreen_scaleTransform_KeyFrameY = _root.FindName("DoFullScreen_scaleTransform_KeyFrameY") as SplineDoubleKeyFrame;
        DoFullScreen_translateTransform_KeyFrameX = _root.FindName("DoFullScreen_translateTransform_KeyFrameX") as SplineDoubleKeyFrame;
        DoFullScreen_translateTransform_KeyFrameY = _root.FindName("DoFullScreen_translateTransform_KeyFrameY") as SplineDoubleKeyFrame;
        DoFullScreen = _root.FindName("DoFullScreen") as Storyboard;

Említettem, hogy a teljes képernyős mód előtti beállításokat szeretnénk megőrizni, ehhez szükségünk lesz pár segédváltozóra: eredeti méterek, elforgatás, eltolás (x,y), nyújtás:

        private double originalWidth = 320, originalHeight = 260;
        double _lastTranslateTransformX, _lastTranslateTransformY, _lastScaleTransformX, _lastScaleTransformY, _lastRotateTransformAngle;

Az előkészületek után írjuk át ezeknek megfelelően a BrowserHost.FullScreenChange eseménykezelőjét:

        void BrowserHost_FullScreenChange(object sender, EventArgs e)
        {
            if (BrowserHost.IsFullScreen)
            {
                //save original values
                _lastScaleTransformX = scaleTransform.ScaleX;
                _lastScaleTransformY = scaleTransform.ScaleY;

                _lastTranslateTransformX = translateTransform.X;
                _lastTranslateTransformY = translateTransform.Y;

                _lastRotateTransformAngle = rotateTransform.Angle;

                //set new values to rotate, skew, and translate
                DoFullScreen_rotateTransform_KeyFrameAngle.Value = 0;
                DoFullScreen_scaleTransform_KeyFrameX.Value = BrowserHost.ActualWidth / originalWidth;
                DoFullScreen_scaleTransform_KeyFrameY.Value = BrowserHost.ActualHeight / originalHeight;
                DoFullScreen_translateTransform_KeyFrameX.Value = this.originalWidth / 2 * (BrowserHost.ActualWidth / originalWidth - 1);
                DoFullScreen_translateTransform_KeyFrameY.Value = this.originalHeight / 2 * (BrowserHost.ActualHeight / originalHeight - 1);
            }
            else
            {
                //restore original values
                DoFullScreen_rotateTransform_KeyFrameAngle.Value = _lastRotateTransformAngle;
                DoFullScreen_scaleTransform_KeyFrameX.Value = _lastScaleTransformX;
                DoFullScreen_scaleTransform_KeyFrameY.Value = _lastScaleTransformY;
                DoFullScreen_translateTransform_KeyFrameX.Value = _lastTranslateTransformX;
                DoFullScreen_translateTransform_KeyFrameY.Value = _lastTranslateTransformY;
            }

            //start the animation
            DoFullScreen.Begin();
        }

Tehát amikor teljes képrenyős módba váltunk, akkor először is midnen aktuális transzformáció értékét elmentjük a _last* változókba, majd beállítjuk az új értékeket:

  • elforgatás szöge: 0 fok, tiszta sor, vízszintesen kell állnia és kész, semmi forgatás
  • skálázás: a böngészőablak méretére kell felnagyítani az eredeti méretből, a skálázás mértékét X és Y irányban a BrowserHost.Actual* / original* hányados adja meg
  • eltolás: mivel az eltolási transzformáció bázisa a videoplayer közepe, ezért a fenti kélettel kell kiszámolnunk, hogy a nagyításnak megfelelően mennyivel kell eltolni a és milyen irányba a lejátszót

A teljes képernyőre váltás ezek után már jól működik, próbáljuk is ki. Most még jobban zavaróvá válik, hogy a kiválasztott lejátszó nem kerül legfelülre, és ha például egy hátsó lejátszót teszek ki a képernyőre, akkor nem jön előre Crying. Nagyon sürgősen változtassunk ezen.

Oldjuk meg úgy, hogy ha egy videó fölé visszük ez egeret, akkor az automatikusan előtérbe kerüljön. Ehhez első körben a Player.xaml.cs konstruktorában regisztráljunk be eseménykezelőt a MouseEnter eseményhez, majd implementáljuk:

        //MouseEnter eventhandler
        this.MouseEnter += new MouseEventHandler(Player_MouseEnter);
        void Player_MouseEnter(object sender, MouseEventArgs e)
        {
            _parent.SetActiveVideo(this);
        }

Látjuk, hogy a host oldalon állítjuk be az aktív videó értékét, készítsük el oda az ezt megvalósító függvényt (Page.xaml.cs):

        internal void SetActiveVideo(Player player)
        {
            // Bring the active Video to the top
            player.SetValue<int>(Canvas.ZIndexProperty, maxZIndex);
            player.Scale(1.0000001);
            maxZIndex++;
        }

Mielőtt megnéznénk hogy mit csinálunk, szükségünk lesz a private int maxZIndex = 1; definiálására. A SetActiveVideo metódusban tehát beállítjuk a paraméterként megkapott Player objektum ZIndex tulajdonságát a maxZIndex-re, ezzel biztosítva, hogy ő legyen legfelül. Még mielőtt továbbmennénk, ejtsünk erről még pár szót. Amikor betöltődnek a videólejátszók, akkor abban sorrendben lesznek egymás fölött, ahogy betöltődnek, mivel mindegyik zIndex értéke a default, azaz 0 (mivel nincs külön beállítva). Tehát ha mi 1-től kezdve indítjuk a maxZIndex változónkat, és minden videóállításkor ezt adjuk zIndex-nek a lejátszónak, majd 1-gyel növeljük, akkor pont jók vagyunk, mindig az aktuális videó fog előtérbe kerülni. (Itt megjegyezhetitek, hogy lehetett volna intelligesebben is kezelni a maxZIndex értéket, de úgy gondolom hogy a demo kereteibe belefér, hogy ettől eltekintsek. Persze ha valaki kellően sokszor vált ily módon videót, és átpörgeti a System.Int32 értékét, (tegyük fel, hogy másodpercenként 10-szer tud videót váltani, akkor 2147483647 / 10 / 3600 / 24 / 365, azaz durván 6.8 évem van egy intelligensebb megoldásra  Hot), na de inkább ne menjünk bele.

A következő sor úgy érzem még picit magyarázatra szorul, miért kell ide ez a player.Scale(1.0000001); ? Az a helyzet, hogy hiába állítom a zIndex tulajdonságot, az magában kevés, nem fog újrarajzolást eredményezni Confused. A rendelkezésre álló lehetőségek közül (mivel nincs direkt Update, vagy Refresh, vagy hasonló jellegű metódus), a legegyszerűbb egy újrarajzolást úgy kikényszerítenem, hogy például transzformálok egyet a vezérlőn, mondjuk nagyítsuk 1.0000001 -szeresére. Ez itt megint könnyelműség, egy ilyen mértékű átméretezést végrehajtani, de úgy gondolom ezt is megengedetem jelen esetben, mert körülbelül 1000000-szor kell így is MouseEnter eseményt kiváltani egy lejátszón, hogy a mérete 1.1-szeresére nőjön Hot.

 

Megvagyunk tehát a kitűzött célunkkal, újrafelhasználható Silverlight vezérlőt faragtunk a már meglévő videólejátszó alkalmazásból. Egyelőre még mindig bedrótozott videókkal dolgozik, és ígéretemnek megfelelően ezt is ki fogjuk javítani egy intelligens megoldásra, de az már egy másik történek (és egy következő post Nerd). A forráskódot fogyasszátok egészséggel!


zip VideoPlayer_v2_(user_control).zip (521 kB)




2007.07.24. 13:58:29 | Permalink | Hozzászólások: 0 | Tárgyszavak: ,


  • TFS-t mindenkinek

    Balássy György (MS RD, ASP.NET MVP, MCTS) Korábban már írtam arról, hogy a Dev10 hullámmal megváltozott az online és az offline MSDN is. A teljes képhez hozzátartozik, hogy az MSDN előfizetések is megváltoznak. Tovább »
  • Olaszország 2007

    Dávid Zoltán Velence, Verona, Garda tó, Tenno, Arco, túrázás, kemping. Tovább »


Írja meg Ön is véleményét!


Hozzászólásokat csak regisztrált, bejelentkezett felhasználóktól tudunk elfogadni!

Hozzászólások