Dock App Launcher tutorijal [C#] [WPF]

Dock App Launcher tutorijal [C#] [WPF]

offline
  • Pridružio: 14 Feb 2008
  • Poruke: 12391

Index ::
0 - Plan
1 - Kreiranje projekta
1.1 - Uvlačenje resursa
1.2 - Kreiranje Image kontrole
1.3 - Podešavanje transparentnosti prozora
2 - Programiranje
2.1 - Pozicioniranje Launchera
2.2 - Kreiranje Icon klase
2.3 - Event Handleri
2.4 - Pisanje Drop handlera
2.5 - Context Meni
2.6 - MouseEnter i MouseLeave
2.7 - Brisanje ikonica

#0 Plan :

Napisati launcher koji će imati najosnovnije funkcije :

- Dinamično dodavanje novih kontrola na prozor prevlačenjem ikonice
- Prikazivanje ikonice koja je prevučena iz samog fajla
- Brisanje ikonice po izboru
- Pokretanje aplikacije
- Osnovne animacije kodom

Za sada, pa ćemo videti da li ćemo dalje nastaviti sa ozbiljnim animacijama i slično.

WIP rezultat :


Ispod ovog posta se nalazi prva revizija u kojoj su napravljene prave animacije i repozicioniranje ikonica, izgleda ovako :


Potrebno :
Visual Studio WPF projekat
GFX pozadine launchera
Što se znanja tiče - tutorijal će ići korak po korak kroz sve pa je potrebno samo pažljivo čitati

Trenutni tech plan :
- Načiniti prozor transparentnim
- Povezati i prepoznati osnovne evente koji će nam biti potrebni
- Napisati handlere koji će biti korišćeni od strane svake nove kontrole

- Prilikom drop eventa na launcher :
-- Stvoriti novu Image kontrolu
-- Izvući ikonicu iz prevučene staze
-- Konvertovati ikonicu u upotrebljiv oblik
-- Podesiti postavke privremene Image kontrole i pozicionirati je
-- Dodati kontrolu u listu

- Prilikom MouseUp eventa na ikonici :
-- Proveriti da li je desni klik upotrebljen
-- Keširati kliknutu kontrolu ako jeste

- Prilikom klika na Remove context menija :
-- Obrisati keširanu kontrolu sa prozora
-- Obrisati keširanu kontrolu iz liste kontrola

- Programerske animacije
-- Prilikom mouse enter eventa samo povećati veličine
-- Smanjiti prilikom mouse leave

- Kreiranje Context menija i povezivanje istog sa ikonicama

#1 Kreiranje i osnovno podešavanje projekta

#Kreiranje projekta

1. Otvorite Visual Studio
2. File > New > Project
3. C# > WPF Application > OK

Pripremite neku grafičku podlogu za launcher. Može biti bilo kog izgleda. Kada je oblik u pitanju, mi ćemo u ovom prvom tutorijalu stvarati ikonicu odmah pored poslednje stvorene ikonice uz mali offset, pa preporučujem nešto pravougaonasto ...

U mom slučaju je to ova crna podloga sa GIF-a.
Takođe, u ovom prvom tutorijalu ćemo koristiti hardkoriane vrednosti širine i visine pa imajte to u vidu.

#Uvlačenje resursa

1. Project > *imeaplikacije_Properties
2. Resources > Add Existing Resource
3. Izaberite pozadinu launchera koju ćete koristiti



4. Solution Explorer > Klik na novi resurs fajl > Properties kartica > Build Action > Resource



#Kreiranje Image kontrole

1 Kliknite na prozor > Toolbox > Dvoklik na Image
2. Dok je slika selektovana > Properties
> Common :
- Source : Izaberite resurs koji ste uvukli, u mom slučaju GFX Launcher
- Stretch : Fill
- Stretch Direction : Both
> Layout :
- Width : upišite # piksela širine vaše slike
- Height : upišite # piksela visine vaše slike



#Podešavanje transparentnosti prozora

1 Document Outline kartica > Klik na Window > Properties
> Brush :
1. Kliknite na Background
2. Podesite Alpha polje na 0



> Appearance :
Čekirajte AllowsTransparency

> Layout :
Width - podesite da odgovara vašoj pozadini launchera



Pokrenite aplikaciju. Ukoliko ste sve dobro uradili, neće se videti ništa osim celokupnog grafičkog elementa koji smo uvukli ranije i koji će nam služiti kao pozadina.

#2 Programiranje

#2.1 Pozicioniranje launchera

Želim da mi launcher bude uvek centriran pri vrhu ekrana pa ćemo to prvo skloniti s puta.

Možemo lako centrirati bilo koji objekat na ekranu ukoliko imamo podatke o širini i visini ekrana.

Da bi centrirali launcher, moramo prvo podeliti širinu ekrana sa 2.
To će nam dati centralnu tačku (liniju, kako god) tj. sredinu ekrana.
No, tu tačku ne možemo da upotrebimo kao početnu poziciju launchera jer bi se dogodilo ovo :



Sredina ekrana bi postala početna tačka launchera koji poštuje isti sistem kao i ekran.

Međutim, ukoliko oduzmemo od tog broja polovinu širine launchera i to iskoristimo kao početnu tačku, dobićemo centriran element.





1. Klik na Window > Properties > Event Handlers ikonica > Loaded - dvoklik



// Kreiranje eventa : neću ubuduće ponavljati ovaj prvi korak već ću reći "Kreirati event handler"...

Sada se otvorio drugi deo prozora sa C# kodom i u njemu je kreiran novi event handler za Loaded event.

Iskoristićemo napisano iznad da centriramo ceo prozor :

   private void Window_Loaded(object sender, RoutedEventArgs e)         {             this.Left = (System.Windows.SystemParameters.PrimaryScreenWidth / 2) - this.Width / 2;             this.Top = 0;         }          

Uzeti u obzir : Koristim ključnu reč this i to se odnosi na sam prozor. Dakle uzimam širinu prozora jer je kod mene podešena pravilno. Ukoliko kod vas nije, umesto this koristite naziv Image kontrole koju ste okačili na formu ranije.

Pokrenite aplikaciju i videćete da je prozor centriran kao što smo predvideli iznad i stoji pri vrhu jer je "Top" podešen na 0.

Sve izmene koje napravimo u aplikaciji se mogu videti u XAML pregledu :
<Window x:Class="WpfApplication1.MainWindow"         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"         Title="MainWindow" Height="350" Width="1484" Background="Transparent" AllowsTransparency="True" WindowStyle="None" Loaded="Window_Loaded">     <Grid>         <Image HorizontalAlignment="Left" Height="50" VerticalAlignment="Top" Width="1479" Source="Resources/GFXLauncher.png" Stretch="Fill"/>     </Grid> </Window>

Možete čak i njega da koristite kako bi povezali kontrole, stvorili nešto i slično.

#2.2 Kreiranje Icon klase

Da bi izbegli probleme sa sledećim delovima tutorijala, obrišite sve vaše namespace-ove i prekopirajte ove.

using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Interop; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes;

Kada prevučem ikonicu na launcher želim da sačuvam nekoliko informacija :
Stazu - da bih mogao da pokrenem proces
Naziv - u slučaju da želim da prikažem naziv nekada
Ikonicu i
Image kontrolu

Napraviću klasu koja će sadržati sve promenljive odgovarajućih tipova.

   public class IconObjectClass     {         public string iconPath,                       iconName;         public Icon iconIcon;         public System.Windows.Controls.Image imgControl;     }      

Dakle svaki put kada dodam novi predmet na launcher, napraviću kopiju ove klase i sačuvati je. Ta kopija predstavlja taj predmet i moći ćemo da radimo sa njom šta nam je volja.
Na prozoru nećemo ništa imati osim grafičkog elementa i sve ćemo dinamično stvarati.

Klasa je spremna, sada ćemo da napravimo listu koju ćemo ubuduće koristiti.

Unutar MainWindow klase upišite :

 List<IconObjectClass> iconObjects = new List<IconObjectClass>();

#2.3 Event Handleri

Koristićemo svega par handlera.
Želimo da znamo kada korisnik "baci" nešto na launcher --> Drop event
Želimo da znamo kada je korisnik mišem prešao preko ikonice --> Mouse Enter
... kada je kursor nije više na ikonici --> Mouse Leave
... kada klikne na ikonicu --> Mouse Up

Imaćemo svega par handlera koje ćemo dodeliti svakoj novoj ikonici kada je kreiramo.
Hajde da ih napravimo.

Unutar MainWindow klase napravite novi void :

Korisnik je prevukao nešto na formu :
private void Image_Drop(object sender, DragEventArgs e) {}

Kliknuo nekim tasterom :
private void Image_MouseUp(object sender, MouseButtonEventArgs e) {}

Prešao kursorom preko ikonice :
private void Icon_MouseEnter(object sender, MouseEventArgs e){}

Pomerio kursor sa ikonice :
private void Icon_MouseLeave(object sender, MouseEventArgs e){}

Sada, dakle, kod izgleda ovako :

using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace WpfApplication1 {     public class IconObjectClass     {         public string iconPath;         public string iconName;         public Icon iconIcon;         public System.Windows.Controls.Image imgControl;     }           public partial class MainWindow : Window     {         List<IconObjectClass> iconObjects = new List<IconObjectClass>();         public MainWindow()         {             InitializeComponent();         }         private void Window_Loaded(object sender, RoutedEventArgs e)         {             this.Left = (System.Windows.SystemParameters.PrimaryScreenWidth / 2) - this.Width / 2;             this.Top = 0;         }         private void Image_Drop(object sender, DragEventArgs e) { }         private void Image_MouseUp(object sender, MouseButtonEventArgs e) { }         private void Icon_MouseEnter(object sender, MouseEventArgs e) { }         private void Icon_MouseLeave(object sender, MouseEventArgs e) { }     } }

Do sada smo uradili :
Podesili prozor
Pozicionirali ga
Napravili klasu koja će držati informacije o ikonici
Napisali handlere koje ćemo kasnije dodeliti

Kako ćemo ikonice dinamično kreirati kasnije, ne možemo sada nigde da ih dodelimo dok ne dođe pravi trenutak za to, međutim imamo već pozadinu launchera na koju ćemo prevlačiti ikonice.

1. Klik na pozadinu launchera > Properties > Event Handlers
2. Pronađite Drop polje > Upišite "Image_Drop" bez navodnika
3. Pritisnite enter
Ukoliko niste pogrešno napisali naziv, otvoriće se prozor sa kodom i neće biti dodata nikakva linija.




Time smo i povezali event handler za drop na sliku.
Sada ostaje samo da ispišemo kod i obradimo sve.

#2.4 Pisanje Drop eventa

Ovo je ujedno i najkompleksniji deo cele ove aplikacije.
Hajmo redom !

Prvo ćemo proveriti da li je išta prebačeno na prozor.
Zatim ćemo u nizu sačuvati staze do svakog prevučenog fajla.
Kreiraćemo novu Image kontrolu i podesićemo kritične vrednosti
Dodelićemo handlere novoj Image kontroli
Sve to ćemo staviti u privremenu klasu
Klasu ćemo dodati u Listu.
Na kraju, dodaćemo samu Image kontrolu na prozor.

Dakle, kod pišemo u "Image_Drop" handleru
    private void Image_Drop(object sender, DragEventArgs e)         {             if (e.Data.GetDataPresent(DataFormats.FileDrop))             {                 int cnt = 0; // <- brojač jer ćemo provući kroz petlju unose                 // Niz koji će sadržati staze svih prevučenih fajlova                 string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);                 // Petlja koja će proći kroz taj isti niz                 foreach (string s in files)                 {                     // Takođe želimo da "pokupimo" ikonicu svakog fajla                     Icon tmpIc = System.Drawing.Icon.ExtractAssociatedIcon(files[cnt]);                     // Kreiramo novu Image kontrolu                     System.Windows.Controls.Image tmpObj = new System.Windows.Controls.Image()                     {                         // podešavamo parametre                         Width = 32,                         Height = 32,                         HorizontalAlignment = HorizontalAlignment.Left,                         VerticalAlignment = VerticalAlignment.Top,                         // Source                         // ContextMenu                     };                 }             }         }

Sledeći deo koda će pozicionirati našu ikonicu na pravo mesto.
Pozicioniranje je takođe jednostavno.
Svaku novu ikonicu želim da stavim odmah pored prethodne ikonice sa malim odstupanjem (offset).

Ovo je takođe jednostavno uraditi.
Imamo dve ikonice :
Ikonica A:
Širina 10 px
X pozicija 5 px

Želimo da pomerimo ikonicu B odmah do ikonice A.
Ikonica B :
Širina 10px
X pozicija = IkonicaA.x (5px) + IkonicaA.širina(10px) = 15px

Dakle kada na poziciju ikonice A dodamo širinu ikonice A, dobijemo "desnu ivicu" ikonice A i to je početak ikonice B.

Međutim da bi pozicionirali ikonicu na ovaj način moramo imati bar jednu u našoj listi.
Zbog toga ćemo prvo proveriti da li naša Lista ima bar jedan unos, ukoliko ima, uzećemo poziciju i širinu te ikonice kako bi izračunali poziciju sledeće.

Ukoliko ipak nema nikakve ikonice, moraćemo sami da unesemo vrednosti prve ikonice.

Kako god okrenuli, moraćemo da koristimo tip Thickness kako bi dodelili nove vrednosti našoj ikonici.
U nastavku koda pišemo :

 if (iconObjects.Count >= 1)                     {// Dakle, naša lista ima bar jedan unos                         // Računamo poziciju X naše ikonice B.x = A.x + A.width                         double tmpLeft = iconObjects[iconObjects.Count - 1].imgControl.Margin.Left +                                             iconObjects[iconObjects.Count - 1].imgControl.Width;                         Thickness _margin = iconObjects[iconObjects.Count - 1].imgControl.Margin;                         _margin.Left = tmpLeft + 5; // 5 je offset koji smo spomenuli                         _margin.Top = 0;                         tmpObj.Margin = _margin;                     }                     else                     { // U slučaju da nema nikakve ikonice na launcheru                         Thickness _margin = new Thickness() { Left = 60, Top = 0 }; // Hardkodiramo vrednosti prve ikonice                         tmpObj.Margin = _margin;                     }

Sada ćemo dodati handlere našoj Image kontroli :
  // Handleri                     tmpObj.MouseUp += Image_MouseUp;                     tmpObj.MouseEnter += Icon_MouseEnter;                     tmpObj.MouseLeave += Icon_MouseLeave;

Konačno, pravimo privremenu promenljivu klase da spakujemo podatke za Listu :

 IconObjectClass tmpIconObjectClass = new IconObjectClass()                     {                         imgControl = tmpObj,                         iconPath = files[cnt],                         iconName = System.IO.Path.GetFileNameWithoutExtension(files[cnt]),                         iconIcon = tmpIc                     };

I prosleđujemo je listi, dok prozoru prosleđujemo kontrolu :

defaultGrid.Children.Add(tmpObj);                     iconObjects.Add(tmpIconObjectClass);

Međutim fali nam jedna stvarčica !
Ikonicu kao ikonicu ne možemo koristiti na ovaj način, već moramo da je konvertujemo u ImageSource.

Napravite novu funkciju koja će odraditi ovu konverziju za nas :
 public static ImageSource ConvertToImageSource(Icon icon)         {             return Imaging.CreateBitmapSourceFromHIcon(icon.Handle, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());         }

Vratite se nazad u handler za Drop i dopišite sledeću liniju koda :
Source = ConvertToImageSource(tmpIc);

Konačni kod handlera sada izgleda ovako :
 private void Image_Drop(object sender, DragEventArgs e)         {             if (e.Data.GetDataPresent(DataFormats.FileDrop))             {                 int cnt = 0; // <- brojač jer ćemo provući kroz petlju unose                 // Niz koji će sadržati staze svih prevučenih fajlova                 string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);                 // Petlja koja će proći kroz taj isti niz                 foreach (string s in files)                 {                     // Takođe želimo da "pokupimo" ikonicu svakog fajla                     Icon tmpIc = System.Drawing.Icon.ExtractAssociatedIcon(files[cnt]);                     // Kreiramo novu Image kontrolu                     System.Windows.Controls.Image tmpObj = new System.Windows.Controls.Image()                     {                         // podešavamo parametre                         Width = 32,                         Height = 32,                         HorizontalAlignment = HorizontalAlignment.Left,                         VerticalAlignment = VerticalAlignment.Top,                         Source = ConvertToImageSource(tmpIc)                         // ContextMenu                     };                     if (iconObjects.Count >= 1)                     {// Dakle, naša lista ima bar jedan unos                         // Računamo poziciju X naše ikonice B.x = A.x + A.width                         double tmpLeft = iconObjects[iconObjects.Count - 1].imgControl.Margin.Left +                                             iconObjects[iconObjects.Count - 1].imgControl.Width;                         Thickness _margin = iconObjects[iconObjects.Count - 1].imgControl.Margin;                         _margin.Left = tmpLeft + 5; // 5 je offset koji smo spomenuli                         _margin.Top = 0;                         tmpObj.Margin = _margin;                     }                     else                     { // U slučaju da nema nikakve ikonice na launcheru                         Thickness _margin = new Thickness() { Left = 60, Top = 0 }; // Hardkodiramo vrednosti prve ikonice                         tmpObj.Margin = _margin;                     }                     // Handleri                     tmpObj.MouseUp += Image_MouseUp;                     tmpObj.MouseEnter += Icon_MouseEnter;                     tmpObj.MouseLeave += Icon_MouseLeave;                     IconObjectClass tmpIconObjectClass = new IconObjectClass()                     {                         imgControl = tmpObj,                         iconPath = files[cnt],                         iconName = System.IO.Path.GetFileNameWithoutExtension(files[cnt]),                         iconIcon = tmpIc                     };                     defaultGrid.Children.Add(tmpObj);                     iconObjects.Add(tmpIconObjectClass);                 }             }         }

Dobićete jednu grešku ukoliko sada pokušate da pokrenete ovaj kod - defaultGrid nije definisan.
Zaboravio sam da napišem ranije da moramo da imenujemo grid!
Nazad na prozor ponovo i selektujte grid, te ga preimenujte u "defaultGrid".



Propustio sam još jednu stvar - dok ste na prozoru selektujte grafički element launchera i u properties kartici otvorite "Common" karticu, zatim kliknite na strelicu koja će proširiti karticu i čekirajte "AllowDrop" :




Ukoliko već sada pokrenete aplikaciju i pokušate da prevučete par ikonica, videćete da se one pravilno pozicioniraju jedna pored druge !

Ostalo je još da pokrijemo par kontrola, dosta manje koda, da osposobimo ostatak aplikacije.

#2.5 Context Meni
Želim da obrišem ikonicu po želji i to želim da uradim tako što ću desnim klikom prikazati meni i izabrati "Remove".

Meni je malo čudan u odnosu na Windows Forms i moraćemo da ga napravimo u XAML-u kao resurs prozora te kasnije pozovemo kodom kad treba!

1. Otvorite Dizajnerski deo IDE-a i kliknite na "XAML" dugme
2. Dodajte sledeći kod kao "Child" Window-a :
  <Window.Resources>         <ContextMenu x:Key="ItemContextMenu">             <MenuItem Header="Remove" Click="MenuItem_Click"></MenuItem>         </ContextMenu>     </Window.Resources>

Ili ovako :


Svakako ne želite da propustite kreiranje novog event handlera za MenuItem.

OK, sada imamo context meni u resursima. Moramo da ga pozovemo kodom.
Deklarišite ga odmah ispod liste :
ContextMenu iconContextMenu;

Moramo da se vratimo nazad i izmenimo deo koda u Drop handleru takođe.
Linija koda koju trebate izmeniti je iskomentarisana : "// ContextMenu".
Obrišite tu liniju i zamenite je :
 ContextMenu = iconContextMenu

Zatim upišite sledeću liniju koda u Loaded eventu :
iconContextMenu = (ContextMenu)this.Resources["ItemContextMenu"];

Koji nakon dodatka izgleda ovako :
 private void Window_Loaded(object sender, RoutedEventArgs e)         {             this.Left = (System.Windows.SystemParameters.PrimaryScreenWidth / 2) - this.Width / 2;             this.Top = 0;             iconContextMenu = (ContextMenu)this.Resources["ItemContextMenu"];         }

#2.6 Mouse Enter i Mouse Leave

Za sada ćemo napraviti najjednostavniju "animaciju". Kada kursor pipne bilo koju ikonicu, povećaćemo veličinu ikonice. Kada izađe, smanjićemo je.
Kasnije ćemo možda u nekoj reviziji videti kako rade animacije u WPF-u.

Prvo ću napraviti jednu funkciju koja će da mi vrati Image kontrolu koja je ispod kursora.

System.Windows.Controls.Image GetIcon(RoutedEventArgs s)         {             return (System.Windows.Controls.Image)s.Source;         }

Zatim ću napisati sledeći kod u MouseEnter eventu :

 private void Icon_MouseEnter(object sender, MouseEventArgs e)         {             System.Windows.Controls.Image thisObject = GetIcon(e);             thisObject.Width += 20;             thisObject.Height += 20;             Grid.SetZIndex(thisObject, 99);         }

Prvo "kupimo" kontrolu ispod kursora, zatim je povećavamo za 20 piksela i na kraju pomeramo Z index te kontrole na 99.
Z index možete zamisliti kao sortirajući lejer. Što je veći indeks to je objekt "bliži" ekranu, što su drugi objekti niži oni se renderuju ispod objekta sa višim brojem.

Zašto pomeramo indeks ? Zbog toga što želimo da ikonica koja bude već "ispliva" na površinu u odnosu na druge ikonice. U suprotnom bi bila povećana ali ispod drugih ikonica.

Slično ćemo uraditi sa MouseLeave eventom :
private void Icon_MouseLeave(object sender, MouseEventArgs e)         {             System.Windows.Controls.Image thisObject = GetIcon(e);             thisObject.Width -=20;             thisObject.Height -=20;             Grid.SetZIndex(thisObject,0);         }

Pokrenite aplikaciju, dodajte nekoliko ikonica i testirajte "efekat" :



Ukoliko kliknete desnim klikom na ikonicu, otvoriće se meni !

OK, ostaje još samo da obrišemo određenu ikonicu iz launchera.

#2.7 - Brisanje ikonica

Prvo ću napraviti funkciju koja će brisati ikonicu iz Liste i sa prozora.

   private void RemoveIcon(System.Windows.Controls.Image Control)         {             // Prosleđujemo Image kontrolu ovoj funkciji             defaultGrid.Children.Remove(Control); // uklanjamo je sa prozora             // Props to Daex;; pronalazimo unos u Listi koji ima istu kontrolu koju tražimo             IconObjectClass ikona = iconObjects.Find(x => x.imgControl == Control);             iconObjects.Remove(ikona); // brišemo taj unos iz liste         }

Zatim ću deklarisati :
ystem.Windows.Controls.Image lastClicked;

U handleru za Mouse Up ćemo prvo proveriti da li je kliknuto desnim klikom, ukoliko jeste, keširaćemo kontrolu. Ukoliko nije, želimo da pokrenemo proces.
  private void Image_MouseUp(object sender, MouseButtonEventArgs e)         {             if (e.ChangedButton == MouseButton.Right)             { // Ukoliko je desnim klikom kliknuto                 lastClicked = (System.Windows.Controls.Image)e.Source; // čuvamo kontrolu             }             else             { // ukoliko je pak prvi klik, pokrećemo proces                 // Props to Daex;; pronalazimo odgovarajući unos iz liste koji odgovara ovoj kontroli                 IconObjectClass ikona = iconObjects.Find(x => x.imgControl == e.Source);                 Process.Start(ikona.iconPath); // pokrećemo proces prema stazi iz liste             }         }

Pokrenite ponovo aplikaciju i videćete da sve radi kao što treba.

+ Source

I to je to za ovaj prvi tutorijal.
Potrudiću se da odradim animacije i svašta nešto od fancy efekata za sledeću reviziju, ako stignem...
WPF je poprilično zabavan ali isto tako nelogičan za mene jer dolazim iz WForms okruženja, dva dana sam proveo guglajući osnove koje su nelogične u odnosu na WForms.



Registruj se da bi učestvovao u diskusiji. Registrovanim korisnicima se NE prikazuju reklame unutar poruka.
offline
  • Pridružio: 14 Feb 2008
  • Poruke: 12391

Revizija 1

Indeks :
0 Izmene
1 Kreiranje Canvasa i drugih kontrola
1.2 Kreiranje drugih kontrola
2 Podešavanje kontrola
3.Data Binding
3.1 Povezivanje preko IDEa
3.2 Povezivanje preko XAML-a
4. Programiranje
4.1 Animiranje Canvasa kodom
4.2 Funkcija za animiranje ikonica
4.3 Funkcija za repozicioniranje ikonica

#0 Izmene
- Dodat deo aplikacije sa opcijama
- Animiranje prikaza tog dela
- Animiranje stanja ikonica (mouse over, mouse leave, click) upotrebom animation sistema

WIP Pregled :


Trenutni tech plan :
- Kreiranje canvasa za opcije
-- Podešavanje kontrola na njemu
-- Bindovanje kontrola

- Pisanje animacije za otvaranje i zatavaranje prozora
-- Kreiranje animacije kodom
-- Kreiranje animacije u XAML-u

- Povezivanje opcija sa prethodno hardkodiranim vrednostima
-- Pisanje funkcije za novo crtanje svih ikonica sa novim vrednostima

- Animacije ikonica
-- Pisanje funkcije koja će sadržati sve animacije u programu i koju ćemo pozvati prilikom eventa a koja će primeniti animaciju na prosleđeni objekat

#1 Kreiranje Canvasa i drugih kontrola

U prethodnom tutorijalu smo hardkodirali vrednosti poput veličine ikonica, offseta između njih, animacija itd. Želim da oslobodim te vrednosti pa ćemo napraviti mali set opcija koji će se prikazati kada ih pozovemo iz context menija na samom launcheru.

Prvi deo ove sekcije je "lickanje" izgleda i podešavanje kontrola po želji.

#1.1 Kreiranje Canvasa
Prvo što ćemo uraditi jeste kreiranje Canvas kontrole. Ona je jedan od mnogih tipova "Panela" u WPF-u i omogućiće nam da grupišemo sve kontrole na njoj i da ih po želji pomeramo ukoliko za to ima potrebe.

WPF ima mnogo Panel-like kontrola koje rade mnogo različitih sortiranja za vas inače...
Preporučujem da bacite pogled na ovo ako vas baš interesuje :

https://msdn.microsoft.com/en-us/library/ms754152%28v=vs.110%29.aspx
http://www.codeproject.com/Articles/140613/WPF-Tut.....yout-Trans

Elem :

1. Dizajner > Selektujte grid > Toolbox > Canvas > Dupli klik



Raširite Canvas po želji da odgovara vašem projektu.

2. Dok je selektovan Canvas > Properties kartica > Brush > Solid Color ili Gradient > Podesite boju > Properties Kartica > Name > canvas1




Ukoliko ste ispratili dva koraka iznad, sada imate kontrolu canvas1 koja se jasno vidi na prozoru.
Ukoliko ipak ne vidite canvas, uverite se da je Canvas "child" Grida i da je prozor (Window) dovoljno širok ili dugačak...

#1.2 Kreiranje drugih kontrola

Sada ćemo kreirati druge kontrole koje će nam trebati :
Slider x2 i textbox x2- Za veličinu ikonica i offset između istih
3x combobox - Za izbor teme, mouse over efekta i mouse click efekta
Slider - za podešavanje transparentnosti launchera
Label - Za tekstove i informacije

Iz Toolboxa birajte odgovarajuće kontrole i prevucite ih na prozor. Uverite se da je svaka od navedenih kontrola "Child" canvas1 kontrole. Ukoliko kontrola nije Child, jednostavno je prevucite na Canvas u Document Outline kartici.

Trenutno canvas izgleda ovako :



Vi naravno možete da pravite ono što vam odgovara ali ako želite da pratite tutorijal, savetujem da prekopirate sve kontrole koje ja imam na prozoru.

Svaka od kontrola ima u Properties prozoru mnogo opcija za izgled pa možete i tu da "svratite" kako bi ulepšali iste. Ja sam dobio ovakav izgled samo menjajući boje u Brush sekciji Properties kartice.

#2 Podešavanje kontrola

Pretpostaviću da ste do ovog trenutka napravili kanvas i na njemu napravili kontrole koje sam spomenuo iznad.

Prvo ću preimenovati neke od kontrola jer će nam trebati ime u kodu kasnije.

Kliknite na svaku od kontrola koju spomenem i promenite parametar "Name" :
Klizač za providnost launchera - opacitySlider
Klizač za veličinu ikonice - iconSizeSlider
Klizač za razmak između ikonica - iconOffsetSlider

Strelicama sam obeležio kontrole i mesto gde menjate naziv istih.



Ostalo je još samo da podesimo neke vrednosti.
Na primer, iako se Opacity vidi u Properties kartici kao 0-100, vrednost je zapravo 0-1.
Stoga moramo da izmenimo Dock Opacity klizač (opacitySlider).

1. Kliknite na opacitySlider > Properties > Common :
Large Change : 0.1
Maximum : 1
Minimum : 0
Small Change : 0.1
Tick Frequency : 0.1



Ispod tog klizača se nalazi jedan Label. On će nam govoriti kolika je vrednosti klizača.

2. Klik na taj label > Properties kartica > ContentStringFormat :
Upišite u polje : {0:N2}%



To će nam dodatno formatirati unos kasnije.

Ideja je za sada da ovaj combo box na sredini, ispod natpisa "Theme" sadrži resurse u vidu GFX elementa koji su predefinisani i koji će menjati pozadinu launchera.
To ćemo možda kasnije zameniti nekom kontrolom i generisati oblike kodom, međutim za sada nam treba samo prikaz svih tema koje se nalaze u resursima.

Ukoliko ste pratili prvi tutorijal znate da samo napravili GFX element i sačuvali ga u resursima aplikacije.

Sada ćemo dodati nove unose u kolekciju Theme combo boxa.

1. Klik na Theme Combo Box > Properties > Common > Items > ... (dugme)
2. (Novi prozor) Combo box > Other Type > Search box > System.Windows.Controls.Image >
Selektujte kontrolu > OK
3. U combo boxu će se pojaviti unos "Image", selektujte ga i kliknite na Add.
4. Selektujte novi unos na Items listi, desno će se pojaviti properties > Common :
Source : Izaberite grafički element
Stretch : Fill
StretchDirection : Both

Layout >
Width : 504
Height : 67

Vrednosti odgovaraju mom combo boxu i u suštini će ispuniti combo box grafičkim elementom tako d





5. Kada potvrdite sa OK, kliknite ponovo na Combo Box i u Common kartici promenite vrednost polja "Selected Index" sa -1 na 0

#3. Data Binding

Sada bi u Windows Formsu morali da napišemo event kod koji bi menjao polje svaki put kada bi pomerili klizač, na primer. Međutim to nije slučaj u WPF-u.

Iako to možemo da uradimo - nećemo, već ćemo iskoristiti prednost Bindinga.

DataBinding nam omogućava da povežemo dva elementa, bilo da su ta dva elementa na UI-u ili duboko u logici koda.
Jednom povezani, elementi će uvek prepoznati izmene i ažurirati sebe, bez potrebe da se napiše kod koji obrađuje event.

Mi ćemo sada povezati klizač za providnost launchera sa grafičkim elementom. Povezaćemo vrednost klizača na vrednost opacity osobine tog elementa. Svaki put kada korisnik pomeri klizač, Opacity polje će se ažurirati u skladu sa klizačem !

Zatim ćemo povezati preostala dva klizača sa textbox kontrolama.

Binding možemo uraditi na par načina.
Ja ću pokazati kako se može uraditi iz XAML-a i samog Editora.

#3.1 Povezivanje opacity vrednosti na value vrednost slidera preko interfejsa

1. Kliknite na grafički element launchera
2. Properties kartica > Opacity > Kockica pored polja za unos > Create Data Binding
3. Element Name > opacitySlider > Value > OK



Pokrenite aplikaciju i pomerite opacity klizač. Videćete da se vrednost transparentnosti grafičkog elementa menja u skladu sa vrednošću klizača.


#3.2 Povezivanje vrednosti klizača za veličinu ikonice na tekst vrednost tekst polja preko XAML-a

Drugi način da povežemo dve kontrole je da napišemo kod u XAML-u.

1. Kliknite na txtIconSize kontrolu > Otvorite XAML pregled
2. U definiciji text kontrole, dopišite :
Text="{Binding Value, ElementName=iconSizeSlider}



Pokrenite aplikaciju i pomerite klizač - videćete da je vrednost klizača povezana.

Međutim ovo je ipak kozmetički prikaz korisniku, mi ćemo morati drugačije da iskoristimo ove podatke.

Bilo kojom metodom, povežite ostale vrednosti klizača sa odgovarajućim kontrolama na isti način.

- Selektujete kontrolu gde želite da prikažete vrednost
- Povežete vrednost po izboru
Evo kako izgleda forma, strelicama sam označio polja i slajdere koje trebate povezati :



Treba naglasiti da je naravno moguće i kodom podesiti Binding, više o tome : https://msdn.microsoft.com/en-us/library/ms742863%28v=vs.110%29.aspx

U slučaju da ste negde zapeli, evo gotovog XAML koda za nove kontrole koje sam dodao sa svim podešavanjima primenjenim do sada :

+ XAML

Ninja izmena :
Zaboravio sam da vam kažem da vežete još dve vrednosti :

Vežite Height i Width vrednosti grafičkog elementa na value vrednost slidera za Width i Height



1. Klik na grafički element launchera > Properties > Margin >
4. Programiranje

Sada kada imamo sve kontrole spremne, počećemo sa programerskim delom posla.

Pre svega želimo da prikažemo korisniku ovaj canvas svaki put kada izabere "Options" iz context menija. Takođe želimo da animiramo prikaz Canvasa.

Prvo ćemo dodati novi unos u Context Meniju kog smo napravili u prvom tutorijalu.

1. XAML Pregled > Window.Resources > Context Menu > Ispod linije koja definiše prvi unos u meniju kopiramo :



Dakle, samo smo dodali unos u Context Meni koji je već povezan sa prozorom i napravićemo handler koji će pozvati funkciju za prikaz opcija.

2. Prikaz C# koda > Napravite novi handler :

 private void ShowOptions_Click(object sender, RoutedEventArgs e)         {             ShowOptionsAnim();         }

// VS je mogao sam da vam napravi handler ukoliko ste, dok je selektovan naziv eventa u XAML-u, pritisnuli F12

3. Odmah ispod njega, napravite novu funkciju sa nazivom "ShowOptionsAnim"

void ShowOptionsAnim() { }

#4.1 Animiranje Canvasa kodom


ShowOptionsAnim će kreirati animacije, povezati ih na Canvas, izmeniti svojstva ukoliko je to potrebno i pustiti ih.

Kada je sama animacija u pitanju, želim da se Canvas proširi sa svoje visine na određenu visinu i da vremenom postaje "vidljiv".

Za ovo će nam trebati dve različite double animacije.
Njih možemo ovako deklarisati :

Doubleanimation foldout = new DoubleAnimation();

Animaciji moramo da definišemo nekoliko kritičnih parametara :

From
To
Duration

Dakle, od kog broja/koje vrednosti animacija počinje, dokle i koliko će trajati.

To možemo da uradimo ovako :
foldout.From = 2; foldout.To = 5; foldout.Duration = new Duration(TimeSpan.FromSeconds(.25))
Ovako :

DoubleAnimation foldout = NewAnimation(2, 5, Duration(TimeSpan.FromSeconds(.25)));

Ili ovako :
DoubleAnimation foldout = NewAnimation() { From = 2, To = 5, Duration = Duration(TimeSpan.FromSeconds(.25)) }
Kako god preferirate i kako god vam je čitljivije...

Za upotrebu animacija će nam trebati namespace : System.Windows.Media.Animation, možete ga dodati ručno ili preko VS-a :




Dakle trebaće mi animacija koja će proširiti visinu canvasa od trenutne vrednosti Canvasa do fiksne vrednosti.

Definisaću animaciju foldout koja će proširiti vrednost od Canvas1.height vrednosti do fiksne vrednosti :
 DoubleAnimation foldout = new DoubleAnimation(canvas1.Height, 258, new Duration(TimeSpan.FromSeconds(.25)));

Trebaće mi i animacija za opacity vrednost :
DoubleAnimation fadeIn = new DoubleAnimation(0, 1, new Duration(TimeSpan.FromSeconds(.50)));

Na kraju, zbog toga što mi kontrole nisu vezane za canvas po pitanju prikaza, moraću da napravim još jednu animaciju koja će biti zaslužna za sam prozor :
DoubleAnimation winFoldout = new DoubleAnimation(mainWindow.Height, 258, new Duration(TimeSpan.FromSeconds(.25)));

Animacije ćemo dodati u novi Storyboard koji će nam omogućiti da lako kontrolišemo više animacija, prvo ću definisati novi Storyboard pa ću dodati prethodne animacije u isti :

 Storyboard storyBoard = new Storyboard();             storyBoard.Children.Add(foldout);             storyBoard.Children.Add(fadeIn);             storyBoard.Children.Add(winFoldout);

Sada moram da podesim ciljane vrednosti i ciljanu kontrolu naših animacija.
Ovo mogu da uradim preko Storyboard klase uz pomoć funkcija SetTargetName, koja će pripojiti animaciju nekoj kontroli i SetTargetProperty koja će precizirati tip vrednosti koju ciljamo.

Storyboard.SetTargetName(foldout, canvas1.Name);             Storyboard.SetTargetName(fadeIn, canvas1.Name);             Storyboard.SetTargetProperty(fadeIn, new PropertyPath(OpacityProperty));             Storyboard.SetTargetProperty(foldout, new PropertyPath(HeightProperty));             Storyboard.SetTargetName(winFoldout, mainWindow.Name);             Storyboard.SetTargetProperty(winFoldout, new PropertyPath(HeightProperty));

Jednom kada podesimo ciljani property i kontrolu, možemo da pozovemo animiaciju, međutim ja to još neću uraditi.

Kako će se ovaj prozor prikazivati i skrivati, želim da napravim "kontra animaciju" koja će sakriti prozor sa opcijama ukoliko je prikazan, na isti način na koji smo ga prikazali.

Za ovo će mi trebati jedan bool koji ćemo koristiti kao okidač svaki put kada pustimo animaciju.

Definišite bool na početku klase
bool optionsShown = false;

i vratite se nazad u ShowOptionsAnim funkciju, te nastavite odmah ispod poslednje linije.

:
 if (!optionsShown)             {                 foldout.To = 258;                 winFoldout.To = 258;                 fadeIn.From = 0;                 fadeIn.To = 1;                 storyBoard.Begin(this);                 optionsShown = true;             }             else             {                 foldout.To = 0;                 winFoldout.To = 60;                 fadeIn.From = 1;                 fadeIn.To = 0;                 storyBoard.Begin(this);                 optionsShown = false;             }

Taj deo koda proverava vrednost optionsShown booleana i u skladu sa tome menja vrednosti animacija, pusta animaciju i menja vrednost boola za sledeću upotrebu.
Pokrenite prozor i videćete da animacija radi.

Ceo kod za ovu metodu :

    void ShowOptionsAnim()         {             DoubleAnimation foldout = new DoubleAnimation(canvas1.Height, 258, new Duration(TimeSpan.FromSeconds(.25)));             DoubleAnimation fadeIn = new DoubleAnimation(0, 1, new Duration(TimeSpan.FromSeconds(.50)));             DoubleAnimation winFoldout = new DoubleAnimation(mainWindow.Height, 258, new Duration(TimeSpan.FromSeconds(.25)));                       Storyboard storyBoard = new Storyboard();             storyBoard.Children.Add(foldout);             storyBoard.Children.Add(fadeIn);             storyBoard.Children.Add(winFoldout);             Storyboard.SetTargetName(foldout, canvas1.Name);             Storyboard.SetTargetName(fadeIn, canvas1.Name);             Storyboard.SetTargetName(winFoldout, mainWindow.Name);             Storyboard.SetTargetProperty(fadeIn, new PropertyPath(OpacityProperty));             Storyboard.SetTargetProperty(foldout, new PropertyPath(HeightProperty));             Storyboard.SetTargetProperty(winFoldout, new PropertyPath(HeightProperty));             if (!optionsShown)             {                 foldout.To = 258;                 winFoldout.To = 258;                 fadeIn.From = 0;                 fadeIn.To = 1;                 storyBoard.Begin(this);                 optionsShown = true;             }             else             {                 foldout.To = 0;                 winFoldout.To = 60;                 fadeIn.From = 1;                 fadeIn.To = 0;                 storyBoard.Begin(this);                 optionsShown = false;             }          }

Pre nego što krenemo na ozbiljnije poslove, sredićemo još dve sitnice :
0. Ukoliko već niste, imenujte glavni prozor aplikacije iz Document Outline kartice na "mainWindow"

1. Klik na topOffsetSlider > Properties > Events > Value changed dupli klik , da napravite handler za value changed

2. Upišite kod :
 mainWindow.Top = dockTopOffsetSlider.Value;

3. Napravite handler i za leftOffsetSlider te kopirajte kod :
 mainWindow.Left = dockLeftOffsetSlider.Value;

Oba handlera izgledaju ovako :
 private void dockTopOffsetSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)         {             mainWindow.Top = dockTopOffsetSlider.Value;         }         private void dockLeftOffsetSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)         {             mainWindow.Left = dockLeftOffsetSlider.Value;         }


Većina interfejsa sada radi.
Klizači za širinu i visinu pravilno pomeraju grafički element.
Klizač za transparentnost pravilno radi.
Klizači za pomeranje prozora rade.
Rade pravilno i animacije opcija.

Sada imamo još dva posla :
1. Da napišemo funkciju koja će da primeni animacije na ikonicu koja se nalazi pod kursorom
2. Da napišemo funkciju koja će repozicionirati sve ikonice prema novim vrednostima sa klizača

#4.2 Funkcija za primenu animacije na ikonicu

Prvi tutorijal je imao kodiranu "animaciju" koja je samo povećavala ikonicu.

Kreiraću dve promenljive :
 int iconMouseOverAnimID,             iconClickAnimID;

One će čuvati index selektovanog itema u izborniku animacija.

Napravite novi SelectionChanged handler za ComboBox :

private void cbAnimUpdate(object sender, SelectionChangedEventArgs e)         {             ComboBox thisObj = (ComboBox)e.Source;             if (thisObj.Name == "cbMouseOverAnim")             {                 iconMouseOverAnimID = thisObj.SelectedIndex;             }             else if (thisObj.Name == "cbClickAnim")             {                 iconClickAnimID = thisObj.SelectedIndex;             }         }

Bitno je da vam se izbornici zovu : "cbMouseOverAnim" i "cbClickAnim".

Sada želim da omogućim korisniku da izabere animaciju koja će se primeniti dinamično na ikonicu koja se nalazi ispod kursora.

Za ovo ćemo napisati posebnu funkciju 0 PlayIconAnimation koja će primiti tri parametra :
int animID - ID prema kom ćemo pozvati određenu animaciju
int animState - ID prema kom ćemo menjati razna stanja animacije - npr. možemo imati posebnu animaciju za mouse in, mouse leave itd. u sklopu jednog animID-a
RoutedEventArgs e - Samo ćemo proslediti informaciju sa MouseEnter i MouseLeave handlera

  void PlayIconsAnimation(int animID, int animState, RoutedEventArgs e)          {          }

Prvo ću pokupiti image kontrolu preko GetIcon funkcije koju smo napisali u prošlom tutorijalu.
Zatim ću registrovati ime objekta privremeno, kako bi mogli da ga koristimo za primenu animacije i deklarisaću novi Storyboard kako bi ga koristili kasnije.

 System.Windows.Controls.Image senderObj = GetIcon(e);             senderObj.Name = "tmpAnimObject";             RegisterName(senderObj.Name, senderObj);             Storyboard sb = new Storyboard();

Zatim ćemo proveriti šta je prosleđeno i prema tome ćemo pisati različite animacije.
 switch (animID)             {                 case 0: // Zoom In                     break;             }

Prva animacija - Zoom In je radije jednostavna.
Kada miš pređe preko ikonice želim da je povećam, kada izađe da je smanjim.
Koristićemo sličnu animaciju onoj koju smo napisali za Opcije :

#4.2.1 Zoom In
 case 0 :    // Zoom In                     DoubleAnimation increaseSizeW = new DoubleAnimation()                     {                         From = senderObj.Width,                         To = 48,                         Duration = new Duration(TimeSpan.FromSeconds(.25)),                     };                     DoubleAnimation increaseSizeH = new DoubleAnimation()                     {                         From = senderObj.Height,                         To = 48,                         Duration = new Duration(TimeSpan.FromSeconds(.25)),                     };                     if (animState == 2)                     {                         increaseSizeH.To = 32;                         increaseSizeW.To = 32;                     }                                     sb.Children.Add(increaseSizeW);                     sb.Children.Add(increaseSizeH);                     Storyboard.SetTargetName(increaseSizeW, "tmpAnimObject");                     Storyboard.SetTargetName(increaseSizeH, "tmpAnimObject");                     Storyboard.SetTargetProperty(increaseSizeH, new PropertyPath(HeightProperty));                     Storyboard.SetTargetProperty(increaseSizeW, new PropertyPath(WidthProperty));                     sb.Begin(this);                        break;


Dakle, dve animacije za širinu i visinu unutar jednog Storyboarda.
Proveravamo animState takođe i u slučaju da je animState 2, menjamo vrednosti obe animacije kako bi dobili okrenutu animaciju.

#4.2.2 Swing

Swing animacija je drugačija. Koristićemo DoubleAnimation opet da bi pomerili vrednost ali ovog puta moramo da pristupimo RenderTransform-u kako bi dobili podatak o uglu Image kontrole.
Da bi uopšte pristupili toj vrednosti, moramo da dodelimo RotateTransform samoj kontroli.

case 1: // Swing anim                     if (animState == 1) {                         var rt2 = new RotateTransform();                         senderObj.RenderTransform = rt2;                         RotateTransform tempSP = senderObj.RenderTransform as RotateTransform;

Sada možemo da pristupimo vrednosti ugla i napravimo animacije prema njoj :

  DoubleAnimation rotation = new DoubleAnimation()                          {                                                            From = tempSP.Angle,                              To = 30,                              Duration = new Duration(TimeSpan.FromSeconds(.50))                          };                          DoubleAnimation rotation1 = new DoubleAnimation()                          {                              From = 30,                              To = 0,                              Duration = new Duration(TimeSpan.FromSeconds(.50)),                                                    };

Međutim sada nećemo koristiti storyboard, već ćemo pokrenuti animaciju na samom property-u
No pre nego što to uradim, želim da napravim handler za kraj animacije.
Ova animacija će pustiti prvi deo koji će je rotirati za 30 stepeni, čim se ta animacija završi pokrenućemo kontra animaciju. Za ovo nam treba handler koji će se aktivirati kada se prva animacija završi.

Želim da mi kod ostane u ovoj funkciji pa ću koristiti malu prečicu da definišem handler :

rotation.Completed += (o, s) =>                          {                              rotation1.From = tempSP.Angle;                              senderObj.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, rotation1);                          };


Na kraju, pozvaću animaciju :
  senderObj.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, rotation);                     }                     break;

#4.2.3 Swing360
Swing 360 se zasniva na istom kodu samo što su stepeni različiti :
  case 2: // Swing 360                                           if (animState == 1) {                         var rt3 = new RotateTransform();                         senderObj.RenderTransform = rt3;                         RotateTransform tempSP = senderObj.RenderTransform as RotateTransform;                          DoubleAnimation rotation360 = new DoubleAnimation()                          {                              From = tempSP.Angle,                              To = 360,                              Duration = new Duration(TimeSpan.FromSeconds(3)),                          };                                                    senderObj.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, rotation360);                                             }                     else if (animState == 2)                     {                                                RotateTransform tempSP = senderObj.RenderTransform as RotateTransform;                                                DoubleAnimation rotation360 = new DoubleAnimation()                        {                            From = tempSP.Angle,                            To = 360,                            Duration = new Duration(TimeSpan.FromSeconds(3))                        };                             senderObj.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, rotation360);                                             }                     break;

#4.2.4 Glow
Na kraju, Glow animacija će zapravo dodeliti effect samoj slici - Drop Shadow.

Prvo ću definisati DropShadowEffect, zatim ću promeniti njegove vrednosti - poput boje, debljine senke i slično. Na kraju ću ga dodati kontroli. Kada kursor izađe sa ikonice samo ćemo obrisati sve efekte sa kontrole.

 case 3: // Glow                     if(animState == 1){                         System.Windows.Media.Effects.DropShadowEffect tmpEffectShadow = new System.Windows.Media.Effects.DropShadowEffect();                         System.Windows.Media.Color tmpColor = new System.Windows.Media.Color();                         tmpColor.R = 255;                         tmpColor.G = 255;                         tmpColor.B = 255;                         tmpEffectShadow.ShadowDepth = 2;                         tmpEffectShadow.Color = tmpColor;                         senderObj.Effect = tmpEffectShadow;                     }                     else if (animState ==2)                     {                         senderObj.Effect = null;                     }                     break;

Na kraju funkcije ću ukloniti naziv privremene kontrole i obrisati registrovano ime :
senderObj.Name = null;             UnregisterName("tmpAnimObject");

Cela funkcija :

 void PlayIconsAnimation(int animID, int animState, RoutedEventArgs e)         {             System.Windows.Controls.Image senderObj = GetIcon(e);             senderObj.Name = "tmpAnimObject";             RegisterName(senderObj.Name, senderObj);             Storyboard sb = new Storyboard();                 switch (animID)             {                 #region ZoomIn                 case 0 :    // Zoom In                     DoubleAnimation increaseSizeW = new DoubleAnimation()                     {                         From = senderObj.Width,                         To = 48,                         Duration = new Duration(TimeSpan.FromSeconds(.25)),                     };                     DoubleAnimation increaseSizeH = new DoubleAnimation()                     {                         From = senderObj.Height,                         To = 48,                         Duration = new Duration(TimeSpan.FromSeconds(.25)),                     };                     if (animState == 2)                     {                         increaseSizeH.To = 32;                         increaseSizeW.To = 32;                     }                                     sb.Children.Add(increaseSizeW);                     sb.Children.Add(increaseSizeH);                     Storyboard.SetTargetName(increaseSizeW, "tmpAnimObject");                     Storyboard.SetTargetName(increaseSizeH, "tmpAnimObject");                     Storyboard.SetTargetProperty(increaseSizeH, new PropertyPath(HeightProperty));                     Storyboard.SetTargetProperty(increaseSizeW, new PropertyPath(WidthProperty));                     sb.Begin(this);                                                               break;                 #endregion                 #region Swing                 case 1: // Swing anim                     if (animState == 1) {                         var rt2 = new RotateTransform();                         senderObj.RenderTransform = rt2;                         RotateTransform tempSP = senderObj.RenderTransform as RotateTransform;                          DoubleAnimation rotation = new DoubleAnimation()                          {                                                            From = tempSP.Angle,                              To = 30,                              Duration = new Duration(TimeSpan.FromSeconds(.50))                          };                          DoubleAnimation rotation1 = new DoubleAnimation()                          {                              From = 30,                              To = 0,                              Duration = new Duration(TimeSpan.FromSeconds(.50)),                                                    };                                                    rotation.Completed += (o, s) =>                          {                              rotation1.From = tempSP.Angle;                              senderObj.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, rotation1);                          };                          senderObj.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, rotation);                     }                     break;                 #endregion                 #region Swing360                 case 2: // Swing 360                                           if (animState == 1) {                         var rt3 = new RotateTransform();                         senderObj.RenderTransform = rt3;                         RotateTransform tempSP = senderObj.RenderTransform as RotateTransform;                          DoubleAnimation rotation360 = new DoubleAnimation()                          {                              From = tempSP.Angle,                              To = 360,                              Duration = new Duration(TimeSpan.FromSeconds(3)),                          };                                                    senderObj.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, rotation360);                                             }                     else if (animState == 2)                     {                                                RotateTransform tempSP = senderObj.RenderTransform as RotateTransform;                                                DoubleAnimation rotation360 = new DoubleAnimation()                        {                            From = tempSP.Angle,                            To = 360,                            Duration = new Duration(TimeSpan.FromSeconds(3))                        };                             senderObj.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, rotation360);                                             }                     break;                 #endregion                 #region Glow                 case 3: // Glow                     if(animState == 1){                         System.Windows.Media.Effects.DropShadowEffect tmpEffectShadow = new System.Windows.Media.Effects.DropShadowEffect();                         System.Windows.Media.Color tmpColor = new System.Windows.Media.Color();                         tmpColor.R = 255;                         tmpColor.G = 255;                         tmpColor.B = 255;                         tmpEffectShadow.ShadowDepth = 2;                         tmpEffectShadow.Color = tmpColor;                         senderObj.Effect = tmpEffectShadow;                     }                     else if (animState ==2)                     {                         senderObj.Effect = null;                     }                     break;                 case 4:                     if (animState == 1)                     {                     }                     else                     {                     }                     break;             }                 #endregion             senderObj.Name = null;             UnregisterName("tmpAnimObject");         }

Na kraju, izmenićemo MouseOver i MouseLeave + iz prvog posta

tako da odgovaraju novom "sistemu" :
 private void Icon_MouseEnter(object sender, MouseEventArgs e)         {             PlayIconsAnimation(iconMouseOverAnimID, 1, e);             System.Windows.Controls.Image thisObject = GetIcon(e);             Grid.SetZIndex(thisObject, 99);         }         private void Icon_MouseLeave(object sender, MouseEventArgs e)         {             System.Windows.Controls.Image thisObject = GetIcon(e);             Grid.SetZIndex(thisObject, 0);             PlayIconsAnimation(iconMouseOverAnimID, 2, e);         }  private void Image_MouseUp(object sender, MouseButtonEventArgs e)             {                           if (e.ChangedButton == MouseButton.Right)                 {                     lastClicked = (System.Windows.Controls.Image)e.Source;                              }                 else                 {                  PlayIconsAnimation(iconClickAnimID, 0, e);                  IconObjectClass ikona = iconObjects.Find(x => x.imgControl == e.Source);                  Process.Start(ikona.iconPath);                 }             }

#4.3 Funkcija za repozicioniranje ikonica

Ovo je ujedno i poslednja funkcija za ovu reviziju.
Ova funkcija će samo da repozicionira ikonice u odnosu na nove vrednosti veličine i offseta.
Primaće dva parametra : veličinu ikonicu, offset.
Proći će kroz listu naše klase i repozicionirati ikonice.

 private void RepositionIcons(int iconSize, int iconOffset)         {             int counter = 0;             foreach (var s in iconObjects)             {                 s.imgControl.Width = iconSize;                 s.imgControl.Height = iconSize;                  if (counter != 0 && iconObjects.Count >=1)                  {                      s.imgControl.Margin = new Thickness()                      {                          Left = iconObjects[counter-1].imgControl.Margin.Left + iconObjects[counter-1].imgControl.Width + iconOffset,// offset                          Top = 0,                      };                  }                  else                  {                      iconObjects[0].imgControl.Margin = new Thickness() { Left = 60, Top = 0 };                  }                 counter++;             }         }

Dakle, primenjujemo novu veličinu na svaku Image kontrolu, zatim računamo novu poziciju svake ikonice tako što na x poziciju prethodne dodajemo njenu širinu i offset, i pozicioniramo tu ikonicu.
Izuzetak je prva ikonica koja ima hardkodiranu vrednost - 60.

Funkciju želim da pozovem svaki put kada se vrednost klizača za veličinu ikonice ili piksela offseta, pa ću tako napraviti novi handler :
private void iconSizeSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)         {             if (mainWindow.IsLoaded)                 RepositionIcons(Convert.ToInt32(iconSizeSlider.Value), Convert.ToInt32(iconOffsetSlider.Value));                     }

i povezati ga sa dva klizača levo.

Testirajte aplikaciju - sve radi kako treba.



Ko je trenutno na forumu
 

Ukupno su 1079 korisnika na forumu :: 49 registrovanih, 7 sakrivenih i 1023 gosta   ::   [ Administrator ] [ Supermoderator ] [ Moderator ] :: Detaljnije

Najviše korisnika na forumu ikad bilo je 3466 - dana 01 Jun 2021 17:07

Korisnici koji su trenutno na forumu:
Korisnici trenutno na forumu: 9k38, _Rade, babaroga, bigfoot, Bojadin Strumski, bojank, bojankrstc, bojcistv, Centauro, dane007, dankisha, Dannyboy, Dimitrise93, djolew, FOX, Goran 0000, goxin, janbo, Karla, Kibice, Krvava Devetka, loon123, Lošmi, Marko Marković, mercedesamg, milenko crazy north, milutin134, mocnijogurt, moldway, mrav pesadinac, Neutral-M, ozzy, Parker, radoznao, randja26, repac, Rogan33, royst33, sasa87, Simon simonović, slonic_tonic, stegonosa, styg, Trpe Grozni, uruk, vathra, wulfy, ZetaMan, 1107