Prüfung
- Pro Teil 1h
- Pro Teil 10 A4-Seiten Zusammenfassung
- Stoff aus Vorlesung und Übung (Alle Übungen machen!)
- Zusammenfassung: Konzepte mit Quellcode-Beispielen
- Musterprüfung auf Skripteserver
- Teil Android
- Kein XML selber schreiben, aber lesen können
- Teil WPF (Infos in Vorlesung 14)
- XAML
- Attribute / Property Syntax
- Markup Extensions
- Routed Events
- Value Converters
- Visual vs Logical Tree
- C# Konzepte
- Properties
- Events, Delegates, Event Handlers
- GUI-Entwurf
- WPF Controls
- WPF Klassenhierarchie
- Brushes
- Clipping
- Layout-Controls
- Canvas, ViewBox, Borders
- Shapes
- UI Controls (Unterschied Label / TextBlock)
- UI List Controls
- Application- und Window- Klasse
- Splash Screens
- DialogFenster, Property DialogResult!
- Commands
- NuGet
- UI-Testing mit TestStack.White
- GUI-Design
- Resources
- Static vs. Dynamic Resources
- Externe Resources (Package URI)
- Styles
- Control Templates
- Trigger
- 2D Transformationen
- GUI Design Principles / Aktuelle Trends
- Umgang mit Daten (Jeweils nur konzeptionell kennen, nicht Code auswendig)
- Markup Extensions
- BindingBase, MultiBinding
- DataContext vs. Source vs. RelativeSource
- Data Binding Syntax
- IValueConverter
- INotifyPropertyChanged
- ObservableCollection
- ObjectDataProvider
- ItemsControl
- DataGrid und CollectionViewSource
- Benutzerinteraktion
- Events und EventArgs
- Input Events
- Background Threads
- Dispatcher.Invoke
- WPF Architektur
- i18n - Embedded Resources
- MVVM
- XAML
Teil Android
Vorlesung 1 - Einführung Android
-
Miniprojekt Abgabe Woche 7 und Woche 14
-
Android Basics
- Java 7 auf Android
- Activities sind ~"Screens"
- Apps werden automatisch geschlossen
- Allgemein wird der Lifecylce stark vom System gesteuert
- Eine Activity sollte eine einzelne Aufgabe realisieren
- Activity kann sich in verschiedenen Zuständen befinden: Wird gestartet, ist aktiv, wird in den Hintergrund gehen, etc.
- Die einzelnen Methoden werden überschrieben (z.B.
onCreate()) - Start einer Activity:
onCreate(),onStart(),onResume(), erst dann ist sie interagierbar - Wird eine Activity überdeckt, wird sie pausiert
onPause(). Kommt sie wieder in den Vordergrund, wird nuronResume()aufgerufen onDestroy()könnte auch direkt anderen Zuständen aufgerufen werden!- Bei Konfigurationsänderungen wird die Activity neu gestartet (zerstört und neu aufgebaut). Also auch z.B. beim Drehen des Screens!
- Daten in
onPause()sichern - Activities werden in einem Stack verwaltet (muss nicht von gleicher App sein)
- Der Back-Button poped normalerweise die oberste Activity auf dem Stack
- Eine Gruppe von Activities (= Activity Stack) heissen "Task"
- Eine geöffnete App ist ein Task, bzw. ist ein Eintrag im "Overview Screen" ein Task
- Systemsicht
- Pro APK wird ein Prozess mit einem Thread gestartet
- Jede APK wird unter eigenem Linux User installiert
- APKs sind quasi JARs (= Zip-Files)
- Intents
- Alle Intents werden über das System verwaltet
- Expliziter Intent: Eine bestimmte Klasse ansprechen
- Impliziter Intent: z.B. "Absicht, Bild aufzunehmen"
- Explizite Intents normalerweise für interne Activities, implizite für generische Aktionen
- Views
- Alles, was der Benutzer sieht
- Jede Activity hat eine View
- GUI kann deklarativ mit XMl oder imperativ mit Java Code erstellt werden
Vorlesung 2 - Grundlagen GUI
- Eine View ist immer eine Rechteckige Fläche, für die die View verantwortlich ist
- Widgets sind fertige Komponenten (buttons, images, checkboxes, ...)
- ViewGroup ist eine Unterklasse von View
- Layouts können ineinander verschachtelt werden (auch unterschiedliche)
match_parent: Nimm den ganzen Platz einwrap_content: Nur so viel Platz wie nötig- Linear-Layout: Wenn kein Gewicht angegeben wird, wird möglichst wenig Platz verwendet. Mit Gewicht entsprechend dem Werten (mehr Gewicht -> mehr Platz)
- Neu gibt es
ConstraintLayout, das auf den GUI-Builder optimiert wurde. Ist allerdings noch in Alpha - Die
RKlasse enthält Konstanten für alle XML-Files im res-Ordner (wird vom Compiler generiert) und bildet dessen Ordnerstruktur ab @+ist die Definition einer Ressource,@ein Verweis daraufmipmap: Launcher-Icon der App- Strings mit
getString(R.string.string_name)abrufen dimens.xmlenthält Dimensionen für Layouts, z.B.16dpund werden über einen Namen aufgerufendp: Density-independent-pixels: Unabhängig von Screen-dpi. Der Basis-Faktor wird von 160dpi berechnet ("mdpi")- Für verschiedene Screen-Grössen, Sprachen, Versionen, etc. werden verschiedene XML-Files angelegt
- Die App hat nach den Lifecycle-Aufrufen keine Kontrolle mehr. Das System sendet Events (ausgelöst durch User oder z.B. Sensoren), die dann behandelt werden (Event-Listener)
- Auch Widgets können Events auslösen (->
TextWatcher)
Vorlesung 3 - Strukturierung und Navigation
Navigations-Design
- Für einen ersten App-Entwurf ein Domain-Modell erstellen
- "Screen Map" - Beziehungen zwischen Screens erstellen
- Screens gruppieren, z.B. mehrere Screens mit Tabs (Panes) trennen
- Navigation: Parent-Child-Beziehung (Hierarchisch) oder "lateral Navigation" (zwischen zwei Kindern)
- Beispiel HSR-App: Home -> Cafeteria ist hierarchisch, einzelne Wochentage lateral
- Back-Button macht "zeitliche Navigation" (vorheriges Kind oder Parent)
- Button oben links sollte immer zum Parent zurück gehen
- Für eine Gesamtübersicht Wireframes / Storyboards erstellen
Fragments
- Es kann nur immer eine Activity gleichzeitig aktiv sein.
- Fragment hat eigenen Lifecycle
- Ein Fragment kann in mehrere Activities eingebunden werden und eine Activity kann mehrere Fragments beinhalten
- Kann zur Laufzeit in Activity eingebunden (
onAttach()) und wieder entfernt werden (onDetach()) - Fragments können fix eingebunden werden, direkt als
<fragment>Tag im XML der Activity (mitnamedie Klasse angeben). Der Code der Activity ändert sich dabei nicht - Oder dynamisch: Normalerweise mit Frame-Layout
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); FragmentManager fragmentManager = getFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); MainActivityFragment fragment = new MainActivityFragment(); fragmentTransaction.add(R.id.fragment_container, fragment); fragmentTransaction.commit(); } }
- Im Unterschied zur Activity wird hier das Fragment selbst instanziert
- Fragment sollte unabhängig von der Activity sein
- Zur Kommunikation zwischen Fragment und Activity definiert das Fragment ein Interface, dass die Activity implementiert
Master-Detail Navigation
- Ein Pattern, z.B. eine Liste mit Mails -> einzelne Mail
- z.B. hat das Phone-Design nur ein einzelnes Fragment pro Activity, das Tablet-layout zeigt beide Layouts auf der gleichen Activity an
- Wenn Activity einen Einstiegspunkt in die App sein kann, muss es eine Activity sein, kein Fragment
Menüs
Programmatisch:
public boolean onCreateOptionsMenu(Menu menu) { menu.add(0, START_MENU_ITEM, 0, "Start"); menu.add(0, SUBMIT_MENU_ITEM, 0, "Submit"); return true; } public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case START_MENU_ITEM: // handle start return true; case SUBMIT_MENU_ITEM: // handle submit return true; } return super.onOptionsItemSelected(item); }
- Deklarativ: als menu-File in XML
- Einbinden mit
getMenuInflater().inflate(id, Menu)in deronCreateOptionsMenu()Methode - Mit dem "PreferenceScreen" kann ein Settings-Menü gebaut werden
- Auch das Fragment kann ein Menü steuern
- Nach Android 5.0 ist die "ActionBar" deprecated, neu ist die "Toolbar"
- Navigation Drawer ("Hamburger Menu") hat schlechte usability
Vorlesung 4 - Listen und Persistenz
Listen
- ListView
<ListView android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/listView"/>
- Folie 12: "Klient" ist ListView und "Dienst" unsere Klasse
- Die ListView nutzt einen Adapter über ein Interface, der die Klassen an das Interface anpasst
- Adapter muss wissen, wieviele Elemente es gibt (
getCount()) und ein bestimmtes Element zurückgeben (getView()) - Layout der jeweiligen Einträge sind in eigenem Layout definiert
getView()in eigenem Adapter überschreiben
public View getView(int position, View convertView, ViewGroup parent) { final Module module = modulList.get(position); if (convertView == null) { LayoutInflater layoutInflater = (...) getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = layoutInflater.inflate(R.layout.rowlayout, null); } // Get views with findViewByID, display data and set listeners return convertView; }
- Demo: https://github.com/HSR-MGE/W04-CustomArrayAdapterDemo
- An einer View kann ein Tag angehängt werden (beliebiges
Object) - Für Performance-Optimierung:
findViewByID()nur beim ersten Mal aufrufen und im Tag speichern
if (convertView == null) { ... TextView textView = (TextView) convertView.findViewById(R.id.textView); CheckBox checkBox = (CheckBox) convertView.findViewById(R.id.checkBox); Pair<TextView, CheckBox> views = new Pair<>(textView, checkBox); convertView.setTag(views); } Pair<TextView, CheckBox> views = (Pair<TextView, CheckBox>) convertView.getTag(); TextView textView = views.first; CheckBox checkBox = views.second;
- In
RecyclerViewist das bereits eingebaut- In
onBindViewHolder()sind die UI-Elemente schon drin im ViewHolder und müssen nur noch abgefüllt werden - Variante mit ListView quasi aufgeteilt in zwei Methoden
- In
Persistenz
onSaveInstanceState()speichert per default alle Views mit einer ID im Bundle gespeichert- Wird aber nicht immer ausgeführt (z.B. über Back-Button verlassen)
onCreate()erhält das Bundle vononSaveInstanceState()- Konsequenz: Daten immer in
onPause()sichern - Shared Preferences (nur bool, float, int, long, String, Set
)
SharedPreferences settings = getSharedPreferences(PREFS_NAME, MODE_PRIVATE); SharedPreferences.Editor editor = settings.edit(); editor.putBoolean("disabled", false); boolean isDisabled = settings.getBoolean("disabled", false); editor.commit();
- SQLite Helper trackt die Version. Wenn z.B. das Schema geändert wird, kann bei einem Update der App eine neue Version angegeben werden, um die Daten zu migrieren (in
onUpgrade)
Hintergrundaktionen
- Mit
Runnabledie Methoderun()überschreiben - Einen neuen Threat starten
public void onClick(View v) { Runnable runnable = new Runnable() { @Override public void run() { final Bitmap bitmap = download("http://slow.hsr.ch/hsr_cat.bmp"); Runnable command = new Runnable() { @Override public void run() { imageView.setImageBitmap(bitmap); } }; imageView.post(command); } }; Thread thread = new Thread(runnable); thread.start(); }
- Views dürfen nur aus dem Main-Thread verändert werden, darum
imageView.post(). Dies setzt einen neuen Task in die Event-Queue - OK für einfache Tasks, besser mit AsyncTask
onPreExecute(): Vorbereitung im UI-ThreaddoInBackground(): In eigemen Thread ausgeführtonPostExecute(): Resultat setzen, wieder im GUI-Thread
class DownloadBitmapTask extends AsyncTask<String, Void, Bitmap> { @Override protected void onPreExecute() { super.onPreExecute(); } @Override protected Bitmap doInBackground(String... params) { return download(params[0]); } @Override protected void onPostExecute(Bitmap bitmap) { imageView.setImageBitmap(bitmap); } } new DownloadBitmapTask().execute("http://slow.hsr.ch/hsr_cat.bmp");
Vorlesung 5 - Material Design
- Material Design styleguide: https://material.google.com/
- Alle Elemente sollten auf einem Grid von 8dp angeordnet werden
- -> Abstand immer Vielfaches von 8
- Farbkombinationen: https://material.google.com/style/color.html#color-color-palette
Vorlesung 6 - Patterns & Serivces
UI Patterns
- Multitier Architecture
- Aufteilung in (typisch) 3 Layer: Presentation, Domain, Data
- Presentation ist verantwortlich für die Darstellung, hat auf Domain Zugriff
- Domain enthält Business Logik und Domain Klassen
- Data implementiert die Speicherung der Daten und stellt sie der Domain zur Verfügung
- Keine Zyklen erlaubt
- Observer-Pattern verwenden
Services
- Muss im Manifest deklariert werden
- Einmaliger Task -> started Service
- Läuft im Hintergrund und wird nicht gestoppt, auch wenn die App pausiert / gestoppt wird
- Läuft im gleichen Thread wie das UI!
- Starten über einen Intent
startService(intent) onStartCommand()überschreiben, um Task auszuführen- mit
stopSelf()im Service stoppen - IntentService kommuniziert über Intents, wird dann im
onHandleIntent()abgearbeitet - Stellt einen Worker Thread zur Verfügung
- Problem: Wie kann der Service die Activity benachrichtigen? -> Broadcasts oder "pending Intent"
- Client-Server-Kommunikation -> bound service
- Auch über einen Intent gestartet
- Gibt Interface, über den kommuniziert werden kann
- AsyncTask: Aufgabe von Main-Thread entkoppeln. Kombinieren mit Services, um GUI-Thread nicht zu blockieren
- Beide Möglichkeiten brauchen dieselbe Service-Klasse
Broadcast Receiver
- Das System versendet Meldungen als Intents
- z.B low battery, Power connected, boot completed, etc.
- Registrierung
- Statisch im Manifest mit einem intent-Filter
- Dynamisch über einen
LocalBroadcastManager
- Eigene Broadcasts versenden mit
sendBroadcast(intent) - Es können auch Broadcasts innerhalb der App versendet werden
Vorlesung 7 - Weiterführende Themen
Sensoren
- Unterstützung von Gerät zu Gerät verschieden
- Qualität der Daten sehr unterschiedlich
- Sensordaten unterschiedlich zu interpretieren: https://developer.android.com/reference/android/hardware/SensorEvent.html#values
- Delay gibt an, wie häufig Daten abgefragt werden (braucht entsprechend mehr oder weniger Strom)
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); lightSensor = sensorManager.getSensorList(Sensor.TYPE_LIGHT).get(0); // Prüfen, ob Sensor existiert! ... // onResume(): sensorManager.registerListener(this, lightSensor, SensorManager.SENSOR_DELAY_NORMAL); // Im onPause listener wieder abmelden ... @Override public void onSensorChanged(SensorEvent event) { textView.setText(String.format("Helligkeit: %.0f", event.values[0])); }
Dependency Injection
- Problem: Klasse ist von einer anderen direkt abhängig und instanziert diese (z.B. wird eine Server-Adresse gesetzt)
- Schlecht testbar mit einem Fake-Server
- Lösungsansatz: Klasse braucht ein Interface, dass dann z.B. von einem richtigen Service und einem "Fake"-Service implementiert wird
- Implementation
- Instanzierung im Konstruktor
- Builder-Pattern (z.B. wie AlertDialog)
- Dagger 2 Framework
- Bringt zentrale Konfiguration und einfachere Testbarkeit, aber ist mehr Schreibaufwand
- View Injection
- Mit 3rd-Party-Library "Butterknife"
- Macht binds anhand von annotierten attributen und methoden
Data Binding
- Idee: im XML direkt auf Objekte zugreifen, damit es sich von selbst aktualisiert ("XML ist der Observer")
layout-Tag als Root-Element, spezifisches Layout darin verschachtelt- Im layout gibt es einen
<data>Block mit Variablen-Namen und Typen - Zugriff im Layout mit
@(<expression>) - Es wird eine Klasse generiert, z.B.
ActivityMainBinding, über den das Data-Binding gemacht werden kann - Klasse bietet Setter-Methoden für die Variablen
- Auch Listener können direkt im layout gebindet werden
- Um Views automatisch zu aktualisieren,
ObservableField<T>verwenden - Aufpassen, dass nicht zuviel Logik ins XML kommt
Teil WPF
Vorlesung 8 - Einführung WPF
- XAML
- Der "x"-Namespace wird sepparat importiert
- Ist wie der "android:" Namespace in Android
- Pixel sind immer device-independent! D.h. sie sehen auf jeder Auflösung gleich gross aus
- 1 Pixel ist immer 1/96 Inch
- Problem: Aliasing, weil kanten nicht mehr direkt auf Pixel liegen müssen. Kann mit Properties verhindert werden
- Elemente werden zu Klassen übersetzt, dessen Attribute zu Properties der Klasse
- XAML ist also nur eine Hilfe, Objekte zu instanziieren. Das gleiche könnte man in Code machen
- Es gibt auch eine Property Element Syntax, um komplexere Properties zu definieren, z.B. verschachtelte Elemente
- Logical Tree: Baum, der man selbst per XAML / Code definiert hat
- Visual Tree: Baum, der zur Laufzeit angezeigt wird (also z.B. auch Borders)
- Events werden dem Logical Tree herunter gegeben (von Window aus) und vom Element hinauf (Bubbling)
Preview-Events sind Tunneling, also von "oben"
Vorlesung 9 - UI Entwurf
- Zusatzinfos in Appendix-Folien (auch Prüfungsstoff!)
- Unter
Controlsind intereraktive Elemente,FrameworkElementbeherbergt alle, aber auch nicht interaktive Elemente wie Bilder - Im Gegensatz zu Android keine
matchParento.ä, sondern wird über alignment gelöst, z.B.stretch - Width / Height gilt immer mit dem Rahmen (wie bei CSS
box-sizing: border-box) - Farbangaben werden vom Compiler in einen Brush umgewandelt
- Ein Grid ohne Zellen und Spalten (= 1 Zelle) kann verwendet werden, um darin Elemente anzuordnen. Nacheinander stehende Elemente werden gestacked
- Im XAML mit
local:auf den aktuellen Namespace zugreifen, z.B. auf eigenen Klassen - Dialogfenster werden geschlossen, wenn das Property
DialogResultgesetzt wird (als Nebeneffekt!)
Vorlesung 10 - GUI-Design
Resources
- XAML-Objekte, die in Layout verwendet werden können
- z.B. Brush, Color, Style, String
- Jede Ressource mit einem Key
- Werden in dll rein kompiliert
- In Application.xaml Datei unter
<Application.Resources>- Mit
x:KeyAttribut eine ID festlegen - Im layout mit
{StaticResource <resourceID>}ansprechen
- Mit
- Key wird zuerst in Element und Parent-Nodes gesucht, dann bei
Window.Resources, am Schluss beiApplication.Resources - Static Resource: Binding geschieht Compile-Time
- Dynamische Resource: Mit
{DynamicResource <resourceID>}, lässt auch dynamisch geladene Resourcen zu, binding zur Laufzeit. Z.B. um Themes zur Laufzeit zu wechseln - Im Code zugreifen mit
object FindResource(key)und auf entsprechenden Type casten - Eigenes Resource Dictionary in eigenem File erstellen
- Root-Tag
<ResourceDictionary> - Externe Res-Dicts einbinden in Tag
<ResourceDictionary.MergedDictionaries> - Resource ist verfügbar, sobald sie im XAML definiert wurde (oder aus externem File verlinkt)
- Root-Tag
Styles
- Für ein Steuerelement einen Style festlegen mit mehreren Eigenschaften
- Style mit
<Style x:Key="id">in Resource-Dictionary definieren - als statiche Resource beim Steuerelement dem
Style-Property zuweisen Button.Propertyist nur eine Hilfe für den Compiler. Der Style könnte auch auf ein anderen Control-Typ angewendet werden- Mit
TargetTypekann im Style das Steuerelement angegeben werden. Wenn der Key weggelassen wird, ist er standardmässig auf alle Controls dieses Typs angewendet - Styles können vererbt werden
- Mit
BasedOnProperty im Style-Tag "Parent" definieren
- Mit
- Mit Templates kann das komplette Aussehen eines Controls verändert werden
- Im
Template-Property des Styles festgelegt - Mit
TemplateBindingdie Default-Werte abrufen
- Im
- Mit
Style.Triggersz.B. MouseOver-Verhalten festlegen- Visual State Manager übersteuert z.T. Style-Triggers
- Lösung: Eigenes Control Template schreiben
Vorlesung 11 - Data Binding
- Für grosse Projekte eigene Styling-Libraries erstellen
- MVVM: Events werden am Besten via Command an das ViewModel übergeben, dafür muss nicht die ganze Runtime (15MB bei WPF) geladen werden
Binding
TargetNullValue: Wenn gebundenes Objekt Null ist, wird dies verwendetStringFormatmit "{}" beginnen, um die Klammern zu "escapen"ElementName: Wert eines anderen XAML-Objekt übernehmen- Normalerweise Quelle der Daten mit
DataContextangeben, dann sind alle Bindings Properties dieser Quelle- DataContext gilt auch für Child-Element
- Üblicherweise wird im Code-Behind der Datacontext für ein Window gesetzt -> ViewModel
- Für eine Liste wird
ObservableCollection<T>verwendet - DataContext kann auch auf einzelnes Element zugewiesen werden (unüblich)
- DataContext auf Window-Ebene ist starke Bindung ViewModel <-> Window. Besser mit Dependency Injection
- RelativeSource: Datenquelle ist relativ zum visual tree. So kann z.T auch komplett ohne Code-behind gearbeitet werden (für einfache Fälle)
Modeist Richtung des Binding: OneWay: Lesend, TwoWay: lesend und schreibend- Ist pro Element-Typ unterschiedlich per default, aber muss selten angepasst werden
INotifiyPropertyChangedist das wichtigste Interface bei Data Binding- Hat nur eine Methode
PropertyChanged()und gibt das Property mit, dass geändert wurde
- Hat nur eine Methode
- WPF registriert sich selbst als Eventhandler auf die Klassen, die
INotifyPropertyChangedimplementieren- Dadurch wird die View automatisch aktualisiert
Vorlesung 11 - Details der Benutzerinteraktion
- WPF stellt für alles mögliche Events zur Verfügung (text changed, window closed, text selected, ...)
- Für eigene Event-handler: Entweder Super-Klasse überschreiben oder dem Event eine neue Funktion zuweisen
WPF App Lifecycle
- Startpunkt bei .NET ist die
Main()Methode - Bei mehreren Main-Methoden in den Build-Settings festlegen
- WPF generiert eine partial Class
AppmitMain()im "obj" Ordner- Ruft
run()auf, das die EventsStartupundActivatedauslöst (Activatedheisst Fenster kommt in den Vordergrund)
- Ruft
- Window
Closing: Bevor das Fenster geschlossen wird (kann abgebrochen werden)
Routed Events
- Sind XAML UI Events
- z.B. Mouse-Event, Keyboard-Event, ...
- Werden abwärts und aufwärts (bubbling) durch den Visual Tree gesendet
- Wenn
Handledauf true gesetzt wird, wird das Event abgebrochen, asonsten werden sie weiter gegeben - Wenn Event vor dem "ankommen" (Tunneling) behandelt werden soll,
PreviewEvents verwenden - Eventhandler direkt im XAML festlegen:
<Button Name="myButton" MouseDown="myButton_OnMouseDown"> - Nicht alle Events durchlaufen standardmässig die komplette Bubbling oder Tunneling Phase (
TextInputbei TextBox z.B. bricht das Event nach der Verarbeitung ab)
Vorlesung 13 - I18n / MVVM
Background Threads
Task.Run(() => { // Background code Dispatcher.Invoke(() => { // Läuft im UI Thread // UI benachrichtigen, dass man fertig ist }) });
Dispatcherist eine Property innerhalb desTask.Run()Contexts
Internationalization
- "Internationalisierung": App vorbereiten für verschiedene Sprachen
- "Lokalisierung": App übersetzen
- Mit .NET Embedded Resources
System.Globalization.CultureInfoObjekte enthalten infos über Sprache, Formate, etc.- Im Projekt ein
Resources.resxablegen - Abruf mit
Properties.Resources.STRING_ID- oder
Properties.Resources.ResourceManager.GetString("STRING_ID") Resources= Name des Resources.resx File
- oder
- Neue Sprache: z.B.
Resources.en-US.rexerstellen
-
WPF-Spezifisch
- In
csprojdie DefaultUICulturefestlegen - In
AssemblyInfo.csZeile auskommentieren und auf Default-Sprache setzen - Zugriff im XML:
xml <Window xmlns:resx="clr-namesapce:I18n.Properties" ...> <TextBlock Test="{x: Static resx:Resouces.STRING_ID}" /> </Window>
- In
-
Parameter in Strings mit
{0},{1}definieren undString.format()verwenden- Oder mit Binding im XML und String als StringFormat mitgeben
MVVM
- ViewModel greift auf Model zu, Databinding auf View
- Model sendet
PropertChangedEvents zu ViewModel - TODO: Bild Schichten MVVM Folie 24
- ViewModel:
- Nachteil Variante 1: Man muss ganzes
Gadgetsaustauschen, wenn es ein POCO ist
- Nachteil Variante 1: Man muss ganzes
public class GadgetVm:BindableBase // vgl. Slides zu INotifyPropertyChanged (Variante 3) { private string _inventoryNumber; public string InventoryNumber { get { return _inventoryNumber; } set { SetProperty(ref _inventoryNumber, value, nameof(InventoryNumber)); } } }
- Jedes einzelne Property implementieren
- Dann mit z.B. AutoMapper mappen
Mapper.Initialize(cfg => cfg.CreateMap<Gadget, GadgetVm>()); // Annahme: gadget sei eine Variable des Typs Gadget var vm = Mapper.Map<GadgetVm>(gadget);
- Kopiert alle Properties vom Gadget ins ViewModel und umgekehrt
Commands
- View soll nichts von ViewModel kennen, aber Kommandos absetzen können
- Command-Klass erstellen, von
ICommandableiten canExecute()undExecute()implementieren- Im ViewModel ein Property des Commands einfügen
- Nachteil: Kein Access auf private Member des ViewModels
- Andere Variante
- Command als innere Klasse des ViewModels
- Im Command eine Istanz auf das ViewModel mitgeben
- Besser: In einem
RelayCommanddas Command kapseln und diesem Funktionen mitgeben - Command binden
<Button Content="Open" Command="{Binding OpenGadgetViewCommand}" CommandParameter="{Binding SelectedGadget}" />
Vorlesung 14 - XAMARIN / Review
XAMARIN
- Bridging Technolgies: Hilfs-Libraries, um Projekte von z.B. Java / Obj. C konvertierten zu Universal Windows Platzform Kompatibilität
- Silo-Ansatz: App drei Mal mit unterschiedlichen Technologien native entwickeln. Fast 3x Aufwand!
- Xamarin-Ansatz: Backend mit C# einmal schreiben, GUI für iOS, Android und Windows sepparat erstellen
- Mit Xamarin.Forms kann auch ein Shared UI erstellt werden
- Platformspezifisch nur noch Tweaks