Dette er anden del af min Vista Media Center SDK Guide. Første del omhandler at sætte Visual Studio op med et projekt så det kan debugges direkte i Vista Media Center.
Into the belly of the beast: MCML Part One!
Hvis du har fulgt første del af min guide korrekt (og jeg har skrevet den korrekt) så skulle du nu have et projekt der kan compiles og køres i vista media center. Når du gør det får du forhåbentlig denne skærm:
Folkene bag SDK’et har været så flinke at give os nogle sample filer at lege med.
Dem vil vi så gerne lægge til siden så vi kan tage tingene fra bunden af
Første trin er hvordan vi bruger vores egne MCML filer. MCML kan loades direkte fra en url/uri eller gemmes i projektets resources. Vi vil gøre som de fine herrer hos Microsoft og lægge vores MCML filer i Resources.
Lav en nyt mcml dokument under Markup med Add->New Item…->Mcml File, kald det Forside.mcml. Nu skal filen tilføjes til Resources. Det kan gøres ved at åbne projektets properties og vælge Resources eller dobbelt klikke Resources.resx. Med resources åben, træk Forside.mcml fra Solution Exploreren over i resources vinduet. Vinduet vælger nu at vise dig hvilke Files der er tilføjet til resources incl. Forside.mcml.
Nu sætter vi lige vores add-in til at starte med vores forside istedet for Menu.mcml. Det gøres i Application.cs under GoToMenu():
public void GoToMenu()
{
Dictionary<string, object> properties = new Dictionary<string, object>();
properties["Application"] = this;
if (session != null)
{
///////////////////////////////////////////////////Menu //stod der før
session.GoToPage("resx://MCTest2/MCTest2.Resources/Forside", properties);
}
else
{
Debug.WriteLine("GoToMenu");
}
}
Med den ændring kan projektet compiles og køres:
Resources i MCML
Der er flere forskellige måder at referere til resources i MCML. (Med Content menes billeder, strenge, mcml filer mm.) Man kan som vi lige har gjort gemme resources i Resources.resx, det er smart for MCML filer og billeder, som addin’et skal bruge. Man kan derud over bruge:
- .res filer, som jeg ikke ved så meget om
- http, ved at bruge en ganske normal url
- file, file://DriveLetter:\FolderName\IdentifierName, for eksempel
file://E:\MyButton.mcml
Når man bruger .resx, som her er adressen man bruger resx://AssemblyName/ResourceContainerName/IdentifierName. Den lille finurlighed man skal holde øje med er ResourceContainerName, som inkluderer namespace som prefix (det rodede jeg en del med).
Du kan læse mere om dette i Windows Media Center SDK under Using Resources in MCML.
MCML
Åben nu Forside.mcml igen. Som du kan se er MCML blot et XML dokument. Den nemmeste måde at se på et mcml dokument er at se det, som et projekt for sig selv. Komplet med assembly referencer, klasser, objekter, lokale variabler, events og metoder. Metaforet passer selvfølgelig ikke 100% på men som du vil se er der mange paralleler.
<Mcml>
Top elementet indeholder referencer til andre Mcml dokumenter og .NET assemblies (Yep, man kan direkte fra MCML instanciere objekter og kalde metoder fra .NET). Den første attribut xmlns=”http://schemas.microsoft.com/2006/mcml” definerer blot at dette er et mcml dokument og at der findes et xml skema, der skal overholdes. Det er rigtig nice fordi så kan Visual Studio bruge Intellisense
Efter det har vi en reference til System: xmlns:cor=”assembly://MSCorLib/System” dette definerer cor som et synonym for namespacet System i assemblien MSCorLib. Hvilket vil sige at hvis vi skal bruge en streng vil vi skulle skrive cor:String som type.
Lad os på dette tidspunkt tilføje en reference til vores projekt, så vi kan tilgå det kode vi laver senere. tilføj en ny attribut
<Mcml xmlns="http://schemas.microsoft.com/2006/mcml"
xmlns:cor="assembly://MSCorLib/System"
xmlns:a="assembly://MCTest/MCTest" >
Hvor MCTest skal være navnet på dit projekt.
<UI>
UI svarer til at lave en klasse i C#. Faktisk svarer det lidt til at lave en User Control, som kendt fra Windows Forms. Name attributten er påkrævet, ligesom det er påkrævet at navngive sine klasser. Som med klasser kan man nedarve UI elementer med BaseUI attributten, men det går jeg ikke lige ind i her.
<Properties>
Properties er ligesom public properties i C#. Det er variabler der er mulige at sætte fra andre UI elementer. I det her eksempel har vi MyColor som en property. Dvs. at hvis jeg ville instanciere Forside fra en anden UI med en anden farve i MyColor ville jeg gøre sådan:
<f:Forside MyColor="Black" />
Givet at jeg har lavet en reference til Forside.mcml i <Mcml>
<Mcml xmlns="http://schemas.microsoft.com/2006/mcml"
xmlns:cor="assembly://MSCorLib/System"
xmlns:a="assembly://MCTest/MCTest"
xmlns:f="resx://MCTest/MCTest.Resources/Forside">
Hvis jeg ville have at man skulle selv definere MyColor istedet for at den har en default værdi, så ville jeg skrive:
<Color Name="MyColor" Color="$Required"/>
$Required er et keyword i Properties.
Application.cs sender i GoToMenu() en reference til sig selv i en property kaldet Application, hvis vi vil have adgang til den fra Forside skal vi have defineret den property:
<a:Application Name="Application" Application="$Required"/>
Linjen definerer en property af typen Application fra namespacet a giver propertien et navn, som den kan refereres via i dette UI element, og at værdien af den nye property skal gives som input til UI elementet når det bliver brugt.
<Locals>
Locals er som det lyder, lokale variable, dette kan være alle former for objekter og de er kun tilgængelige i dette <UI>. Locals er som instans variable så hver instans af en UI har sin egne Locals.
<Rules>
Rules er hvor ting bliver sjovere og hvor klasse metaforet falder lidt fra hinanden. Her kan man lave regler for hvad der skal ske når events bliver affyret. Man kan definere hvad der skal ske når objekter’s properties bliver ændret, dog kun hvis man har lavet de properties korrekt
Rules er lidt mere avanceret og kun sjovt hvis vi har nogle meningsfyldte rules at lave, så indtil videre springer vi lige dette over. Jeg vil dog lige hurtigt forklare <Default>. Default reglen sætter værdien af Target til værdien i Value hvis ingen andre regler definerer hvad Target skal være. I eksemplet:
<Default Target="[MyText.Content]" Value="[MyString]"/>
Her bliver Mytext.Content sat til strengen MyString som er defineret i Locals. Hvis der ikke er andre regler der sætter MyText.Content til noget andet. Med det menes at vi kunne have lavet en Condition rule der hvis et bestemt krav er overholdt sætter MyText.Content til noget andet. Hvis det krav på et tidspunkt ikke længere er opfyldt vil MyText.Content få værdien MyString igen, pga Default reglen.
Når man bruger variable som parametre i attributter skal der klammer rundt om.
<Content>
Content er hvor selve interfacet skal defineres. Her bruges predefinerede og egne UI elementer til at beskrive hvordan dette UI element skal se ud. Der kan kun være et element under <Content> men heldigvis kan det element være et sammensat element som <Panel>, <ColorFill> eller <Clip>. I Forside.mcml er der kun et <Text> element i Content, Text er indbygget og er stortset en label, sæt den et sted med en font og farve så skriver den noget tekst.
En knap
Vi vil starte med at lave en simpel knap, så når vi trykker på den vises en dialog boks. Vi tager selvfølgelig udgangspunkt i Forside.mcml.
Knappen vi laver skal have et fint lille ikon og noget tilhørerende tekst, og så må den egentlig gerne være centreret på skærmen.
Først skal vi have et <Panel>, <ColorFill> eller <Clip> ind fordi vi skal bruge mere end det ene <Text>. Så det første vi gør er at putte <Text> ind i en <ColorFill>. Vi bruger en color fill for så er det åbentlyst hvor stor knappen er
<ColorFill Content="Gray">
<Layout>
<FlowLayout ItemAlignment="Center" Orientation="Horizontal"/>
</Layout>
<Children>
<Text Name="MyText" Color="[MyColor]" Font="Calibri, 24"/>
</Children>
</ColorFill>
Alle container elementerne som colorfill har 2 vigtige under elementer, det er layout og children. Layout bestemmer hvordan containerens children skal positioneres ifht hinanden, hvis man ikke bruger noget layout vil alle bare blive tegnet ovenpå hinanden. Den jeg har valgt her er FlowLayout, som tegner alle elementer i en række enten horizontalt eller vertikalt. I dette tilfælde horizontalt og ItemAlignment siger hvordan elementerne skal positioneres på det andet led, her er center valgt for så er elementer centreret ifht det største element.
Nu vil vi gerne have et ikon på vores knap, dette gøres ved at lægge et billede i projektets resources, det er under Project-> Properties-> Resources -> Vælg Images (Ctrl+2). Her kan man trækket et billede over i eller bruge Add Resource -> Existing File. Du kan evt bruge dette billede(*hint til fremtidige guides*):
Med billedet i resouces kan vi tilføje det til vores color panel. Lige før Text elementet skriv:
<Graphic Content="resx://MCTest/MCTest.Resources/harddisk" />
Hvis du starter debugging nu vil der være en “knap” i øverste venstre hjørne. Men vi vil egentlig gerne have den centreret, så vi lægger ColorFill ind i et Panel:
<Content>
<Panel Layout="Center">
<Children>
<ColorFill Content="Gray" Name="Baggrund">
...
</ColorFill>
</Children>
</Panel>
</Content>
Dette panel bruger en Inline Layout parameter, som jeg ikke vil komme ind på nu, blot at dette Center layout lægger alle underelementer direkte i midten af Panelet (oven på hinanden) så den er bedst at bruge hvis man kun har et element under <Children>. Hvis du har gjort sådan som jeg har forestillet mig du ville vil du nu have denne skærm hvis du starter debugging:
Nu har vi en dødkedelig knap man faktisk ikke kan noget med. For at få lidt gang i denne knap skal vi til at lave nogle rules.
Først og fremmest vil vi gerne have knappen til at kunne få fokus. Under <Rules> tilføjer vi:
<Default Target="[Input.KeyInteractive]" Value="true" />
Som betyder sæt denne UI til at være interaktiv! Input er en indbygget variabel. Når knappen kan få focus kan vi gøre så den kan klikkes på, til det skal vi bruge en Input handler, nemlig ClickHandler. Sådan en oprettes under <Locals>:
<ClickHandler Name="Clicker"/>
Desuden skal der oprettes en Command, som blot er et objekt der forstår konceptet af at blive Invoked. (Til tider bruges interfacet ICommand istedet så man kan sende en command som parameter under Properties, men det kommer vi tilbage til). Nu skal vi bare bruge en Command under <Locals>:
<Command Name="Kommando" />
Nå man klikker med vores Clicker skal Kommando Invokes. Det beskriver man ved at binde Kommando til Clicker.Command under <Rules>:
<Binding Target="[Clicker.Command]" Source="[Kommando]" />
Dette er stortset synonym for Clicker.Command = Kommando;. Nu kan vi faktisk klikke på vores knap. Men da Kommando stadig ikke gør noget sker der heller ikke noget. For at gøre det lidt sjovere vil vi kalde en funktion fra vores C# kode i Application.cs, der er der allerede en funktion:
public void DialogTest(string strClickedText)
Som vi vil kalde. Det gør man også med en Rule:
<Changed Source="[Kommando.Invoked]">
<Actions>
<Invoke Target="[Application.DialogTest]"
strClickedText="Uhh, gør det igen!" />
</Actions>
</Changed>
Denne siger: Hvis Kommand.Invoke ændrer sig (eller i dette tilfælde, da Invoked faktisk er et event, bliver affyret) så udfør alle opgaver under <Actions>. <Invoke> kalder en funktion på et eller andet objekt. Da Application er defineret i vores Properties kan vi kalde DialogTest på den direkte ved navn. De parametre DialogTest tager skal tilføjes som attributter til <Invoke> disse er case-sensitive!, i dette tilfælde er det strClickedText. som selvfølgelig er den tekst der skal stå i dialogboksen.
Nu skulle det være muligt at klikke knappen og få følgende fine besked:
Till next time
Det var det for denne del af guiden. I del 3 vil jeg begynde at lave en Filbrowser, hvor der skal laves lister og disse skal bindes til filbrowser C# kode.
Sidst får du lige her min kilde kode for Forside.mcml.
felizk Programming, Tips, Tutorial, Vista Media Center