tag:blogger.com,1999:blog-21433882576009213782023-11-16T14:46:59.101+01:00xp-developmentAlles zu C#, WPF, MVVM, Windows, .NET und anderen Themen.Jan Schuberthttp://www.blogger.com/profile/01762805030145046416noreply@blogger.comBlogger43125tag:blogger.com,1999:blog-2143388257600921378.post-36510948347283092372019-04-05T20:17:00.001+02:002019-04-05T20:17:34.912+02:00Raspberry Pi als Home Server Teil 4Beim Parsen bzw. Zusammenstellen des Response-Streams bin ich auf ein Problem gestoßen. Es war auf einmal gar nicht mehr so leicht, die beiden Response Bytes an die richtige Stelle im Stream zu schreiben. Bei gültigen Responses war dies noch eine leichte Übung. Was aber macht man, wenn das angeforderte Command nicht unterstützt wird und man so nicht unterscheiden kann, ob es sich um ein Connection-Required Command oder um ein Connection-Not-Required Command handelt? Durch diesen Unterschied würde sich bei der aktuellen Definition die Response-Bytes um einige Stellen verschieben.<br />
<br />
Diese Schwachstelle im Protokoll werde ich ausmerzen, indem ich bei Connection-Required Commands die Response-Bytes vor den Connection Identifier setze.<br />
<br />
Eine weitere Anpassung gibt es bei allen enumerierbaren Datentypen. Diese bekommen die Anzahl der Bytes im Stream als ushort vorgesetzt. Somit vereinfacht sich das Parsen der Request und Response Streams noch einmal.<br />
<br />
Die gesamte Dokumentation ist hier zu finden:<br />
<a href="https://jan-schubert.github.io/HomeAutomation/" target="_blank">https://jan-schubert.github.io/HomeAutomation/</a><br />
<br />
<br />
<a href="http://xp-development.blogspot.com/2018/11/raspberry-pi-als-home-server.html" target="_blank">Teil 1 der Serie: Raspberry Pi als Home Server</a><br />
<a href="http://xp-development.blogspot.com/2018/12/raspberry-pi-als-home-server-teil-2.html" target="_blank">Teil 2 der Serie: Raspberry Pi als Home Server</a><br />
<a href="http://xp-development.blogspot.com/2019/01/raspberry-pi-als-home-server-teil-3.html" target="_blank">Teil 3 der Serie: Raspberry Pi als Home Server</a>Jan Schuberthttp://www.blogger.com/profile/01762805030145046416noreply@blogger.com2tag:blogger.com,1999:blog-2143388257600921378.post-17600944754270801842019-01-09T20:10:00.000+01:002019-01-09T20:10:15.852+01:00Raspberry Pi als Home Server Teil 3Um mit dem Raspberry kommunizieren zu können, habe ich erstmal ein einfaches binäres Protokoll definiert.<br />
Folgende Vorraussetzungen gelten:<br />
<ul>
<li>Die Kommunikation läuft über TCP/IP.</li>
<li>Alle Ganzzahlen werden dabei im Little-Endian Format übertragen.</li>
<li>Strings werden als Unicode übertragen.</li>
</ul>
Server (Raspberry) Anforderungen:<br />
<ul>
<li>Alle Connections nach 5 Minuten ohne Kommunikation beenden.</li>
<li>Bei allen gestarteten Transaktionen wird nach 15 Sekunden ohne Kommunikation ein Rollback ausgeführt.</li>
</ul>
Das Protokoll ist aufgeteilt in Commands, die eine Anmeldung am Server benötigen und eben in die die keine brauchen.<br />
<br />
Zu den Commands, die keine Anmeldung benötigen gehört bspw. das Connect Command. Folgend der Bytestream für dieses Command.<br />
<br />
<pre>00 01 00 00 00 00 00 00 00 AF FE</pre>
<ul>
<li>Das erste Byte 00 steht für die Protokollversion und wird vorerst immer 00 sein. Dies habe ich eingeführt damit ich später, wenn ich merke das ich Anpassungen bzw. Erweiterungen im Protokoll benötige, dieses einfach unter einer neuen Versionsnummer erweitern kann. Dann müsste lediglich der Client und der Server verschiedene Protokollversionen unterstützen.</li>
<li>Die nächsten vier Bytes 01 00 00 00 stehen für den Request Type und geben an, was eigentlich gemacht werden soll.</li>
<li>Die nächsten zwei Bytes (ushort) sind ein Counter, der bei jeder Anfrage hochgezählt wird und in der Antwort vom Server zurückgeben wird. Ob dieser Counter wirklich benötigt wird, ist mir noch nicht ganz klar. Es gibt Vor- und Nachteile, die ich noch gegeneinander abwägen muss. Ggf. wird der Counter in der endgültigen Version des Protokolls 00 nicht mehr vorhanden sein.</li>
<li>Die nächsten zwei Bytes (ushort) sind die Länge der Datenbytes in diesem Beispiel 00 00 und somit gibt es auch keine Daten.</li>
<li>Die letzten beiden Bytes ist die CRC Checksumme AF FE. Diese wird in meinen Beispielen immer mit AF FE angegeben, in der eigentlichen Implementierung dann aber natürlich berechnet.</li>
</ul>
<div>
Der dazugehörige Response hat lediglich den Unterschied, dass nach dem Counter 2 Bytes als Response Codes dienen im folgenden Beispiel 00 00:</div>
<br />
<pre>00 01 00 00 00 00 00 00 00 00 00 AF FE</pre>
<br />
Es gibt Common Response Codes und Command spezifische. Folgend die Common Response Codes bei denen das erste Byte immer FF ist:<br />
<ul>
<li>wrong crc FF 01</li>
<li>not connected FF 02</li>
<li>transaction required FF 03</li>
<li>unknown command FF 04</li>
<li>corrupt data FF 05</li>
<li>not supported protocol version FF 06</li>
</ul>
<br />
Die gesamte Dokumentation ist hier zu finden:<br />
<a href="https://jan-schubert.github.io/HomeAutomation/" target="_blank">https://jan-schubert.github.io/HomeAutomation/</a><br />
<br />
<br />
<a href="http://xp-development.blogspot.com/2018/11/raspberry-pi-als-home-server.html" target="_blank">Teil 1 der Serie: Raspberry Pi als Home Server</a><br />
<a href="http://xp-development.blogspot.com/2018/12/raspberry-pi-als-home-server-teil-2.html" target="_blank">Teil 2 der Serie: Raspberry Pi als Home Server</a>Jan Schuberthttp://www.blogger.com/profile/01762805030145046416noreply@blogger.com0tag:blogger.com,1999:blog-2143388257600921378.post-34599551928698714642018-12-05T19:58:00.001+01:002018-12-05T19:58:18.359+01:00Raspberry Pi als Home Server Teil 2<div dir="ltr">
Nachdem ich das Grundgerüst zum Parsen (RequestParser und ResponseParser) und zum Zusammenstellen (RequestBuilder und ResponseBuilder) fertiggestellt und zeitgleich das Command für das Connect implementiert habe, habe ich damit begonnen den Server für den Raspberry zu schreiben.</div>
<div dir="ltr">
<br /></div>
<div dir="ltr">
Das Projekt Template für Visual Studio 2017 ist unter folgendem Link zu finden:</div>
<div dir="ltr">
<a href="https://marketplace.visualstudio.com/items?itemName=MicrosoftIoT.WindowsIoTCoreProjectTemplatesforVS15" target="_blank">https://marketplace.visualstudio.com/items?itemName=MicrosoftIoT.WindowsIoTCoreProjectTemplatesforVS15</a></div>
<div dir="ltr">
<br /></div>
<div dir="ltr">
Nach dem Anlegen des Projektes habe ich eine kurze sehr simple Implementierung geschrieben und im nächsten Zug versucht die App auf meinen Raspberry zu veröffentlichen. Die Zielplattform musste ich korrekterweise noch auf ARM stellen, konnte nach dem Deployen aber direkt via Remote Debugging die App debuggen.</div>
<div dir="ltr">
<br /></div>
<div dir="ltr">
Die Background Task:</div>
<pre>public sealed class StartupTask : IBackgroundTask
{
private static readonly ILogger Log = LogManager.GetCurrentClassLogger();
private BackgroundTaskDeferral _deferral;
public async void Run(IBackgroundTaskInstance taskInstance)
{
Log.Info("-----------------------------------------------------------------------------");
Log.Info($"Start server {typeof(StartupTask).GetTypeInfo().Assembly.GetName().Version}.");
_deferral = taskInstance.GetDeferral();
taskInstance.Canceled += TaskInstanceCanceled;
await new Bootstrapper().RunAsync();
}
private void TaskInstanceCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
{
Log.Info($"Stop server {typeof(StartupTask).GetTypeInfo().Assembly.GetName().Version}.");
_deferral.Complete();
}
}
</pre>
<div dir="ltr">
<br />
Release Notes für Windows IoT Raspberry Pi 3 B+ Insider Preview</div>
<div dir="ltr">
<a href="https://docs.microsoft.com/en-us/windows/iot-core/release-notes/insider/rpi3bp">https://docs.microsoft.com/en-us/windows/iot-core/release-notes/insider/rpi3bp</a><br />
<br />
<a href="http://xp-development.blogspot.com/2018/11/raspberry-pi-als-home-server.html" target="_blank">Teil 1 der Serie: Raspberry Pi als Home Server</a></div>
Jan Schuberthttp://www.blogger.com/profile/01762805030145046416noreply@blogger.com0tag:blogger.com,1999:blog-2143388257600921378.post-29570164571716626322018-11-14T20:54:00.000+01:002018-11-25T21:32:13.035+01:00Raspberry Pi als Home Server Teil 1<div>Das eigene Heim wird heutzutage immer Smarter. Dabei treffen unterschiedlichste Interessen und verschiedenste Protokolle aufeinander. Von der Lichtsteuerung über ZigBee, hin zur Rolladensteuerung über KNX, bis hin zur Sensorüberwachung mit Enocean. Alles soll miteinander interagieren können, am Besten über eine und nicht über verschiedene Apps. Zusätzlich sollte eine Steuerung auch mit Alexa, Cortana oder dem Google Assistant möglich sein.</div>
<div>
Diesem Thema möchte ich mich in den nächsten Wochen und Monaten widmen und habe damit auch schon begonnen.</div>
<div>
<br></div>
<div>
Ich habe mir einen Raspberry Pi 3 B+ bestellt und Windows 10 IoT installiert. Danach habe ich bei GitHub ein neues Projekt angelegt und begonnen ein Protokoll für die Kommunikation zwischen Raspberry und der App zu definieren. Parallel dazu habe ich ein .NET Standard Projekt erstellt und erste Klassen angelegt (ConnectRequestBuilder, ResponseParser, ...).</div>
<div>
<br></div>
<div>
Wie immer gilt: TDD.</div>
<div>
<br></div>
<div>
<a href="https://github.com/jan-schubert/HomeAutomation">https://github.com/jan-schubert/HomeAutomation</a></div>
Jan Schuberthttp://www.blogger.com/profile/01762805030145046416noreply@blogger.com0tag:blogger.com,1999:blog-2143388257600921378.post-11738745918442842172014-06-06T16:21:00.000+02:002014-06-06T16:21:43.576+02:00Lokalisierung einer Windows 8 Store AppDie Lokalisierung in Windows Store Apps funktioniert denkbar einfach. Soll Beispielsweise der Text eines TextBlock's in verschiedenen Sprachen dargestellt werden, so muss diesem Control lediglich eine <b>x:Uid</b> zugewiesen werden. Zum Beispiel folgendermaßen:<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQCXZ8zpVsqz7e4zkou8Ja7Vimy9hrXNOns9AhmyS9lFeNtYUM0Nb5cMtIv0deQn7pURlOvU4cpw5Ynrn64oNN8Bz8EhAWxoeuvzm5naL75HAj4XjtBZxe0TFAEityF3vJoH16ZXVh-m15/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> <Page
x:Class="App2.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock x:Uid="MyTextBlock" />
</Grid>
</Page>
</code></pre>
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjW1cz3RfD-SgZtEHPTlGViNWQklSZ4sZcPn6mmZCl4-mXAenBOVrcE0XtIIbUVDN1Nnmsykez_jXkhSG_AgBC2LIuGdYntECQiBWEEOLwPSGYxelcfoMZ3jd-xVJNE5hIST05YR1iNy5E/s1600/SolutionExplorer.jpg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjW1cz3RfD-SgZtEHPTlGViNWQklSZ4sZcPn6mmZCl4-mXAenBOVrcE0XtIIbUVDN1Nnmsykez_jXkhSG_AgBC2LIuGdYntECQiBWEEOLwPSGYxelcfoMZ3jd-xVJNE5hIST05YR1iNy5E/s1600/SolutionExplorer.jpg" /></a>Jetzt muss in dem Projekt ein Ordner angelegt werden, der <b>Strings</b> heißt. In diesen kommen weitere Verzeichnisse, die den Namen der gewünschten Sprache im BCP-47 Format haben. Z.B. <b>de-DE</b> oder nur <b>de</b> oder <b>en</b>. Jedes dieser Verzeichnisse bekommt eine <b>Resources.resw</b> Datei.<br />
<br />
Für unser Beispiel legen wir die Resource <b>MyTextBlock.Text</b> mit dem Wert "Hallo Welt!" im Deutschen und im Englischen das gleiche mit dem Wert "Hello World!" an. Das Pattern ist also <b>Uid.PropertyName</b> und zeigt, dass es nicht nur möglich ist, den anzuzeigenden Text, sondern auch andere Properties eines Controls zu setzen.<br />
<br />
Das war es schon, wird die Anwendung gestartet, wird der Text in der jeweiligen Standard Sprache angezeigt.Jan Schuberthttp://www.blogger.com/profile/01762805030145046416noreply@blogger.com0tag:blogger.com,1999:blog-2143388257600921378.post-23888336343822894772014-04-03T19:00:00.000+02:002014-04-03T19:01:05.463+02:00XP sagt auf WiedersehenMicrosoft hat es schon vor einiger Zeit angekündigt und nun ist es soweit! Windows XP wird in 5 Tagen und 6 Stunden den Support einstellen!<br />
<br />
Spätestens jetzt sollten auch die letzten Firmen und Privatpersonen Abschied nehmen und sich nach etwas neuem umschauen. Denn ohne Sicherheitsupdates und Hotfixes wird es auf kurz oder lang Probleme mit dem System geben.<br />
<br />
Den aktuellen Countdown gibt es direkt von Microsoft <a href="http://www.microsoft.com/de-de/windows/xp/" target="_blank">hier</a>!Jan Schuberthttp://www.blogger.com/profile/01762805030145046416noreply@blogger.com0tag:blogger.com,1999:blog-2143388257600921378.post-71797107659277411642014-03-13T19:28:00.000+01:002014-03-14T07:53:45.983+01:00Asynchrone Commands testen leicht gemachtWas war das immer für ein Krampf die Execute Methode eines Commands zu testen, das asynchonen Code ausführt. Das hat aber schon seit geraumer Zeit ein Ende. NUnit unterstützt seit der Version 2.6.2 das async await, das mit C# 5.0 der Entwicklerwelt zur Verfügung gestellt wurde.<br />
<br />
Für alle Commands, die längere Aktionen ausführen sollen, nutze ich die folgende Klasse:<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQCXZ8zpVsqz7e4zkou8Ja7Vimy9hrXNOns9AhmyS9lFeNtYUM0Nb5cMtIv0deQn7pURlOvU4cpw5Ynrn64oNN8Bz8EhAWxoeuvzm5naL75HAj4XjtBZxe0TFAEityF3vJoH16ZXVh-m15/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> public class AsyncCommand<TExecuteArg, TCanExecuteArg> : ICommand
{
public event EventHandler CanExecuteChanged
{
add
{
if (_canExecuteMethod == null)
return;
CommandManager.RequerySuggested += value;
}
remove
{
if (_canExecuteMethod == null)
return;
CommandManager.RequerySuggested -= value;
}
}
public AsyncCommand(Func<TExecuteArg, Task> executeMethod, Func<TCanExecuteArg, bool> canExecuteMethod = null)
{
if (executeMethod == null)
throw new ArgumentNullException("executeMethod");
_executeMethod = executeMethod;
_canExecuteMethod = canExecuteMethod;
}
public Task Execute(TExecuteArg arg)
{
return _executeMethod(arg);
}
public bool CanExecute(TCanExecuteArg arg)
{
return _canExecuteMethod == null || _canExecuteMethod(arg);
}
bool ICommand.CanExecute(object parameter)
{
return CanExecute((TCanExecuteArg) parameter);
}
async void ICommand.Execute(object parameter)
{
await Execute((TExecuteArg) parameter);
}
private readonly Func<TExecuteArg, Task> _executeMethod;
private readonly Func<TCanExecuteArg, bool> _canExecuteMethod;
}</code></pre>
Wie man sehen kann, ist die Execute Methode mit dem async Keyword versehen. Alles was in der Execute Action ausgeführt wird, kann also asynchron sein.<br />
<br />
Die Benutzung ist einfach, so wie man es vom async/await bzw. von den synchronen Commands her kennt. Hier ein Beispiel in dem im MainViewModel drei Sekunden nach dem Ausführen des Commands eine Nachricht gesetzt werden soll.<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQCXZ8zpVsqz7e4zkou8Ja7Vimy9hrXNOns9AhmyS9lFeNtYUM0Nb5cMtIv0deQn7pURlOvU4cpw5Ynrn64oNN8Bz8EhAWxoeuvzm5naL75HAj4XjtBZxe0TFAEityF3vJoH16ZXVh-m15/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> public class MainViewModel : ViewModelBase
{
public string Message
{
get { return _message; }
set
{
_message = value;
NotifyPropertyChanged("Message");
}
}
public AsyncCommand<object, object> LongRunningCommand { get; private set; }
public MainViewModel()
{
LongRunningCommand = new AsyncCommand<object, object>(OnLongRunningExecute);
}
private async Task OnLongRunningExecute(object args)
{
await Task.Run(() => Thread.Sleep(TimeSpan.FromSeconds(3)));
Message = "Test message";
}
private string _message;
}</code></pre>
Die OnLongRunningExecute Methode ist hier auch mit dem async Keyword versehen und in der Methode werden dann die drei Sekunden gewartet bevor die Nachricht gesetzt wird.<br />
Diese Implementierung hat den großen Vorteil, das die Anwendung nicht einfriert nachdem der Button geklickt wurde.<br />
<br />
Und nun zum eigentlichen Thema: Testen!<br />
Wie schon geschrieben, unterstützt NUnit seit der Version 2.6.2 das Ausführen von asynchronen Tests. Dementsprechend einfach ist es das Command zu testen:<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQCXZ8zpVsqz7e4zkou8Ja7Vimy9hrXNOns9AhmyS9lFeNtYUM0Nb5cMtIv0deQn7pURlOvU4cpw5Ynrn64oNN8Bz8EhAWxoeuvzm5naL75HAj4XjtBZxe0TFAEityF3vJoH16ZXVh-m15/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> [TestFixture]
public class LongRunningCommand
{
[Test]
public async void Usage()
{
var viewModel = new MainViewModel();
await viewModel.LongRunningCommand.Execute(null);
viewModel.Message.Should().Be("Test message");
}
}</code></pre>
Benutzt man eine ältere Version von NUnit, läuft der Test in weniger als einer Sekunde durch und wird beim Assert schief laufen.<br />
<br />
<a href="https://sites.google.com/site/xpdevelopmentfiles/downloads/AsyncCommandSpike.zip?attredirects=0&d=1" target="_blank">Hier der Sourcecode mit Visual Studio 2012 Solution.</a>Jan Schuberthttp://www.blogger.com/profile/01762805030145046416noreply@blogger.com0tag:blogger.com,1999:blog-2143388257600921378.post-9177303412320956152014-03-12T19:56:00.000+01:002014-03-12T19:57:00.916+01:00Window Einstellungen in UserSettings komfortabel speichernEs ist wahrscheinlich schon seit Windows 95 Standard, dass sich eine Anwendung wieder an der Stelle öffnet, an der sie das letzte mal beendet wurde.<br />
<br />
WPF spielt hier gut mit den UserSettings zusammen und es können ohne großen Aufwand alle nötigen Daten gesichert und für den nächsten Anwendungsstart wieder geladen werden.<br />
<br />
Bei der Settings.settings Datei muss drauf geachtet werden, dass der Zugriff auf public gesetzt wird. Ansonsten müssen einfach alle Eigenschaften eingetragen werden, die gesichert werden sollen.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-quoQ_IRgW1FLQylyK2bhHuK1jAbWKnKWq-KdLlgQ8EB5cfyqMJmDcM5LzYUSkGyGgbYKySDOryq5uD_9KpuUkbmRmHiyTaO_BxuR_FkLYhFw95J5w6cG1sVBTB2rQVmpQbBe4QRa4uM/s1600/Settings.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-quoQ_IRgW1FLQylyK2bhHuK1jAbWKnKWq-KdLlgQ8EB5cfyqMJmDcM5LzYUSkGyGgbYKySDOryq5uD_9KpuUkbmRmHiyTaO_BxuR_FkLYhFw95J5w6cG1sVBTB2rQVmpQbBe4QRa4uM/s1600/Settings.jpg" height="200" width="400" /></a></div>
<br />
Im XAML benötigen man noch einen Zugriff auf die Settings. Diesen Zugriff stelle ich in der App.xaml zur Verfügung.<br />
<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQCXZ8zpVsqz7e4zkou8Ja7Vimy9hrXNOns9AhmyS9lFeNtYUM0Nb5cMtIv0deQn7pURlOvU4cpw5Ynrn64oNN8Bz8EhAWxoeuvzm5naL75HAj4XjtBZxe0TFAEityF3vJoH16ZXVh-m15/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> <Application x:Class="Sample.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:properties="clr-namespace:Sample.Properties">
<Application.Resources>
<ResourceDictionary>
<properties:Settings x:Key="Settings" />
</ResourceDictionary>
</Application.Resources>
</Application></code>
</pre>
<br />
Danach können diese Eigenschaften an dem Hauptfenster der WPF Anwendung gebunden werden. In meinem Beispiel in der MainView.xaml.<br />
<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQCXZ8zpVsqz7e4zkou8Ja7Vimy9hrXNOns9AhmyS9lFeNtYUM0Nb5cMtIv0deQn7pURlOvU4cpw5Ynrn64oNN8Bz8EhAWxoeuvzm5naL75HAj4XjtBZxe0TFAEityF3vJoH16ZXVh-m15/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> <Window x:Class="Sample.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="{Binding Source={StaticResource Settings}, Path=Default.MainViewWidth, Mode=TwoWay}"
Height="{Binding Source={StaticResource Settings}, Path=Default.MainViewHeight, Mode=TwoWay}"
Top="{Binding Source={StaticResource Settings}, Path=Default.MainViewTop, Mode=TwoWay}"
Left="{Binding Source={StaticResource Settings}, Path=Default.MainViewLeft, Mode=TwoWay}"
WindowState="{Binding Source={StaticResource Settings}, Path=Default.MainViewWindowState, Mode=TwoWay}"
Title="Sample">
<Grid />
</Window></code>
</pre>
<br />
Es muss beachtet werden, dass der Mode beim Binding auf TwoWay gesetzt wird, weil die Werte sonst nur gelesen, aber nicht wieder zurück in die Settings geschrieben werden.<br />
<br />
Zum Schluss müssen die Einstellungen noch gesichert werden. Das mache ich generell beim Beenden der Anwendung. Ich mache dies allerdings nur, wenn die Anwendung ordnungsgemäß beendet wird, um ggf. einen inkonsistenten Zustand der Daten zu verhindern. Diesen Zustand wird es bei den hier gezeigten Eigenschaften zwar nie geben, allerdings können ja noch beliebig viele andere Eigenschaften in den Settings gesichert werden, bei denen die Konsistenz sichergestellt werden muss.<br />
In der App.xaml.cs habe ich demnach die OnExit Methode überschrieben und rufe dort ggf. die Save Methode der Settings Klasse auf.<br />
<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQCXZ8zpVsqz7e4zkou8Ja7Vimy9hrXNOns9AhmyS9lFeNtYUM0Nb5cMtIv0deQn7pURlOvU4cpw5Ynrn64oNN8Bz8EhAWxoeuvzm5naL75HAj4XjtBZxe0TFAEityF3vJoH16ZXVh-m15/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> public partial class App
{
protected override void OnExit(ExitEventArgs e)
{
base.OnExit(e);
if (e.ApplicationExitCode == 0)
Settings.Default.Save();
}
}</code>
</pre>
<br />
Nun sollte sich bei jedem Start der Anwendung das Fenster genau dort wieder öffnen, an der es zuletzt auch geschlossen wurde.Jan Schuberthttp://www.blogger.com/profile/01762805030145046416noreply@blogger.com0tag:blogger.com,1999:blog-2143388257600921378.post-67796981257174030032014-01-24T19:00:00.000+01:002014-01-24T19:03:01.381+01:00Lambda Expressions nichts anderes als ein Kompilerschwindel?!Lambda Ausdrücke hat wahrscheinlich jeder C# Entwickler lieben gelernt. Lang definierte Delegates gehören schon lange der Vergangenheit an. Aber was genau steckt eigentlich hinter diesen <b><i>Lambda Expressions</i></b>?<br />
<br />
Eigentlich gar nichts so sehr besonderes. Der Compiler macht am Ende ganz normale Klassen- und Methodenaufrufe draus. Wer ab und an mal seine Assemblies decompiliert ist bestimmt auch schon mal auf die <i>DisplayClass</i> Klassen gestoßen. Diese sind mit dem <a href="http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.compilergeneratedattribute(v=vs.110).aspx" target="_blank">CompilerGenerated</a> Attribut versehen und dienen genau für unseren Zweck: Lambda Ausdrücke.<br />
<br />
So wird aus folgendem Code:<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQCXZ8zpVsqz7e4zkou8Ja7Vimy9hrXNOns9AhmyS9lFeNtYUM0Nb5cMtIv0deQn7pURlOvU4cpw5Ynrn64oNN8Bz8EhAWxoeuvzm5naL75HAj4XjtBZxe0TFAEityF3vJoH16ZXVh-m15/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> namespace LambdaSpike
{
public class ClassWithLambdaExpression
{
public ClassWithLambdaExpression()
{
var i = 3;
var foo = new Action(() => Console.Write("{0}: do something..", i));
}
}
}
</code></pre>
<br />
Folgendes nachdem es der Compiler unter die Mangel genommen hat:<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQCXZ8zpVsqz7e4zkou8Ja7Vimy9hrXNOns9AhmyS9lFeNtYUM0Nb5cMtIv0deQn7pURlOvU4cpw5Ynrn64oNN8Bz8EhAWxoeuvzm5naL75HAj4XjtBZxe0TFAEityF3vJoH16ZXVh-m15/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> public class ClassWithLambdaExpression
{
[CompilerGenerated]
private sealed class c__DisplayClass1
{
public int i;
public void b__0()
{
Console.Write("{0}: do something..", this.i);
}
}
public ClassWithLambdaExpression()
{
ClassWithLambdaExpression.c__DisplayClass1 c__DisplayClass = new ClassWithLambdaExpression.c__DisplayClass1();
c__DisplayClass.i = 3;
Action foo = new Action(c__DisplayClass.b__0);
}
}</code></pre>
Jan Schuberthttp://www.blogger.com/profile/01762805030145046416noreply@blogger.com0tag:blogger.com,1999:blog-2143388257600921378.post-34505670295434582462014-01-21T20:12:00.000+01:002014-01-21T20:13:48.477+01:00Performance Schub im Visual Studio WPF Designer<div dir="ltr">
Bei der Entwicklung von WPF Anwendungen kann einen der Designer im Visual Studio aufgrund der Performance schon mal zur Weißglut bringen. Eine kleine Änderung im XAML Code hat zur Folge, dass das komplette Projekt neu kompiliert wird. Das kann je nach Projektgröße unter Umständen mehrere Sekunden dauern.<br />
<br /></div>
<div dir="ltr">
Hat man einen Style bzw. einen Theme im XAML Code definiert, so wird dieser natürlich auch bei jeder Änderung im Designer mit kompiliert. Das kann je nach größe recht lange dauern.<br />
<br /></div>
<div dir="ltr">
Abhilfe kann dabei die Auslagerung des Styles schaffen. Legt man ein eigenes Projekt für diese Styles an, so muss dieser Code nicht jedes mal auf's neue kompiliert werden. Als weiteren Vorteil sehe ich die bessere Trennung zwischen den eigentlichen Views und deren Design.</div>
Jan Schuberthttp://www.blogger.com/profile/01762805030145046416noreply@blogger.com0tag:blogger.com,1999:blog-2143388257600921378.post-50446811970888992942013-12-08T16:17:00.002+01:002013-12-09T08:45:12.924+01:00Modulare Anwendung mit Prism?Da meine nächste Anwendung wieder in WPF entwickelt werden soll und diese diesmal in viele verschiedene Module aufgeteilt wird, habe ich mich auf die Suche nach Patterns und Frameworks gemacht. Schnell bin ich wieder einmal auf <a href="https://compositewpf.codeplex.com/" target="_blank">Prism</a> gestoßen. Schon vor einiger Zeit habe ich mich mit dem Framework befasst, es damals allerdings als nicht passend für meine damalige Anwendung gehalten. Mit dem neuen Projekt sieht das nun anders aus!<br>
<br>
Verschiedene Module sollen von verschiedenen Entwicklern entwickelt werden. Wichtig dabei ist mir, dass die Abhängigkeiten zwischen den Modulen bis auf's Minimum reduziert werden. Die Anwendung soll wie immer Test Driven entwickelt werden können und außerdem soll es möglich sein, dass wenn ein Modul nicht geladen werden kann, nicht die ganze Anwendung nicht benutzt werden kann, sondern alle anderen Module zur Verfügung stehen!<br>
<br>
Die letzten Projekte wurden mit <a href="https://cinch.codeplex.com/" target="_blank">Cinch</a>, das auf <a href="https://mefedmvvm.codeplex.com/" target="_blank">MEFedMVVM</a> aufsetzt, entwickelt. Auf die vielen Features, die diese Frameworks mit sich bringen, möchte ich natürlich auch in den nächsten Projekten nicht verzichten müssen. Aber Sacha Barber hat sich (wie immer) auch darum bemüht und evaluiert, wie diese Frameworks miteinander harmonieren. Einen Beitrag dazu hat er auf <a href="http://www.codeproject.com/Articles/145175/Showcasing-Cinch-MVVM-framework-Prism-4-interopera" target="_blank">Codeproject</a> verfasst.<br>
<br>
Prism legt viel Wert auf unabhängige Module und Dependency Injection, was das Testen der Software Komponenten vereinfacht. Es können verschiedene Dependency Injection Manager, wie <a href="http://msdn.microsoft.com/en-us/library/ff649614.aspx" target="_blank">Unity</a> oder das im .NET Framework integrierte MEF genutzt werden. Die Entscheidung viel schnell und leicht, da ich ja weiterhin Cinch bzw. MEFedMVVM nutzen möchte.<br>
<br>
Prism selbst ist in der Version 4 sehr gut Dokumentiert. Ich bin derzeit fleißig am Lesen und Evaluieren, ob alle meine Anforderungen erfüllt werden. Momentan sieht es aber so aus, als ob Prism genau das richtige wäre!Jan Schuberthttp://www.blogger.com/profile/01762805030145046416noreply@blogger.com0tag:blogger.com,1999:blog-2143388257600921378.post-39651291054136581582013-12-07T13:58:00.000+01:002013-12-09T08:11:29.593+01:00Fazit für FeedlyAm 1. Juli 2013 hatte google den google reader aus dem <a href="https://chrome.google.com/webstore?hl=de&utm_source=chrome-ntp-launcher" target="_blank">Chrome Web Store</a> genommen und <a href="http://cloud.feedly.com/" target="_blank">feedly</a> übernahm die meisten User.<br>
<br>
Fazit: Die Übernahme von vorhandenen Blog Abos hatte ohne Probleme funktioniert. Kategorien können eingerichtet werden und es kann zwischen verschiedenen Themes ausgewählt werden. Außerdem und wie ich finde das wichtigste ist, dass die verschiedenesten Plattformen unterstützt werden. So kann ich nicht nur auf dem PC News lesen, sondern auch auf meinem IPhone!Jan Schuberthttp://www.blogger.com/profile/01762805030145046416noreply@blogger.com0tag:blogger.com,1999:blog-2143388257600921378.post-81042098613489445642013-12-06T21:15:00.000+01:002013-12-06T21:16:39.078+01:00Nokia verleiht Entwicklergeräte<div class="MsoNormal">
Wer gerne mal eine App entwickeln möchte, aber leider kein passendes Smartphone zur Hand hat, kann sich bei Nokia diese nun leihen. Da möchte Nokia die Entwicklerwelt mal wieder ein bisschen nach vorne bringen!<br />
<br />
<span style="color: black; mso-style-textfill-fill-alpha: 100.0%; mso-style-textfill-fill-color: black;"><a href="http://developer.nokia.com/forms/Entwicklergeraet.xhtml" target="_blank">http://developer.nokia.com/forms/Entwicklergeraet.xhtml</a></span><o:p></o:p></div>
Jan Schuberthttp://www.blogger.com/profile/01762805030145046416noreply@blogger.com0tag:blogger.com,1999:blog-2143388257600921378.post-63369814898885398942013-09-13T19:01:00.000+02:002013-09-13T19:01:54.833+02:00Das neue iOS 7 kommtEndlich ist es soweit!<br />
Das neue iOS von Apple kommt und ich bin schon wahnsinnig gespannt drauf. Es kommt in einem neuen Design mit einer ganz neuen Farbpalette und steht für iPhones ab der Version 4 zur Verfügung. Das Video zur siebten Version vom iOS kann man sich <a href="http://www.apple.com/de/ios/" target="_blank">hier</a> anschauen und verspricht so einiges.<br />
Ich zumindest freu mich jetzt schon auf den 18. September :)Jan Schuberthttp://www.blogger.com/profile/01762805030145046416noreply@blogger.com0tag:blogger.com,1999:blog-2143388257600921378.post-34308757205109373572013-09-13T18:48:00.002+02:002013-09-13T18:49:46.316+02:00Die ApplicationException im .NET FrameworkWozu wurde eigentlich die ApplicationException im .NET Framework eingeführt und wer sollte diese nutzen?<br />
Sinn und Zweck dieser Exception war urspünglich alle selbst geschriebenen Exceptions von der ApplicationException ableiten zu lassen. So konnte man in einem try-catch Block einfach unterscheiden, ob eine Ausnahme aus der eigenen Anwendung oder aus dem .NET Framework geschmissen wurde.<br />
Leider wurde dieses Konzept schnell wieder von Microsoft selbst über den Haufen geworfen, indem direkt aus dem Framework heraus schon ApplicationExceptions geworfen werden. So ist es zum Beispiel möglich beim Nutzen des Namespaces Reflection oder auch beim Laden eines FixedDocuments eine ApplicationException zu bekommen.Jan Schuberthttp://www.blogger.com/profile/01762805030145046416noreply@blogger.com0tag:blogger.com,1999:blog-2143388257600921378.post-70722009152843508862013-06-20T15:54:00.001+02:002013-12-08T16:26:49.465+01:00Mit MEFedMVVM zur WPF AnwendungIch nutze nun schon seit geraumer Zeit das Framework Cinch von Sacha Barber um meine Anwendungen zu entwickeln. Cinch selber macht sich MEFedMVVM zu nutze um die Bindung zwischen View und ViewModel zu erstellen.<br />
<br />
Mit dem Framework ist es leicht für die Design Zeit die gebundenen Klassen auszutauschen und das Testen wird durch die Schnitstellen sehr vereinfacht.<br />
<br />
Hier die Links:<br />
<ul>
<li><a href="http://mefedmvvm.codeplex.com/" target="_blank">MEFedMVVM</a></li>
<li><a href="http://cinch.codeplex.com/" target="_blank">Cinch</a></li>
</ul>
Jan Schuberthttp://www.blogger.com/profile/01762805030145046416noreply@blogger.com0tag:blogger.com,1999:blog-2143388257600921378.post-80364214531245606942013-06-18T22:06:00.001+02:002013-12-08T16:27:10.201+01:00Command Line Args mit MEFedMVVMWie bekommt man eigentlich Command Line Argumente mit MEFedMVVM in sein ViewModel? Die Argumente kommen ja wie üblich in WPF der StartUp Methode der App Klasse mit.<br />
<br />
Es gibt zwei Wege:<br />
<ol>
<li>Entweder man nutzt die Argumente der StartUp Methode und weißt diese einem Service des Containers zu.<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQCXZ8zpVsqz7e4zkou8Ja7Vimy9hrXNOns9AhmyS9lFeNtYUM0Nb5cMtIv0deQn7pURlOvU4cpw5Ynrn64oNN8Bz8EhAWxoeuvzm5naL75HAj4XjtBZxe0TFAEityF3vJoH16ZXVh-m15/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> ICommandLineArgs service = ViewModelRepository.Instance.Resolver.Container.GetExport<ICommandLineArgs>().Value;
</code></pre>
</li>
<li>Oder man greift ganz einfach auf die Methode System.Environment.GetCommandLineArgs() zu. Das hat den Vorteil, das es nicht nötig ist, in der StartUp Methode die Argumente zuzuweisen.<br />Nutzt man aber trotzdem einen eigenen Service, der intern die System.Environment.GetCommandLineArgs() nutzt, kann man diesen Service, wie gewohnt, seinem ViewModel übergeben. Für die Tests kann dann ganz einfach ein Test Service geschrieben werden, um die Argumente für die Testfälle vorzugeben.<span style="font-family: Consolas, Courier, monospace; font-size: x-small;"></span></li>
</ol>
Jan Schuberthttp://www.blogger.com/profile/01762805030145046416noreply@blogger.com0tag:blogger.com,1999:blog-2143388257600921378.post-35950691304505284942013-06-16T11:46:00.000+02:002013-06-16T11:48:49.786+02:00Lesen um zu lernenJeden Tag schreitet die Entwicklung ein Stückchen vorran. Immer auf dem aktuellen Stand zu sein, ist nicht immer leicht. Clean Code Developer <a href="http://www.clean-code-developer.de/Oranger-Grad.ashx?HL=lesen#Lesen_Lesen_Lesen_6" target="_blank">rät</a> deshalb sechs Fachbücher im Jahr zu lesen. Wenn man nun mal überlegt, wie viel Fachbücher kosten, kann das schnell mal ins Geld gehen. Nehmen wir mal an ein Buch kostet 60 Euro und sollen sechs im Jahr lesen. Das wären schon mal 360 Euro im Jahr. Dabei gibt es immer wieder Bücher, bei denen schnell auffällt, dass das Buch für einen selbst nicht geeignet ist. Das heißt man würde sich, um die Anzahl sechs auch wirklich zu lesen, nochmal ein bis zwei Bücher extra kaufen. Und so landet man schnell bei kosten zwischen 400 und 500 Euro im Jahr.<br />
<br />
<a href="http://www.safaribooksonline.com/" target="_blank">safaribooksonline.com</a> kann die Geldbörse entlasten. Für gut 240 Euro im Jahr, kann man eine viele viele Bücher online und auf mobilen Geräten lesen. Es gibt dort Fachbücher ohne ende und man kann sich zehn Bücher im Monat anschauen. Außerdem ist eine Volltextsuche über alle Bücher möglich, was einem das Suchen bei Problemen extrem vereinfacht.<br />
Für 35 Euro monatlich bekommt man sogar einen komplett Zugang, in dem so viele Bücher gelesen werden können, wie man möchte.<br />
Für die mobilen Geräte gibt es sogar die Möglichkeit die Bücher offline zu lesen!<br />
<br />
Reinschauen lohnt sich in jedem Fall. Für 15 Tage kann man kostenfrei alles ausprobieren!Jan Schuberthttp://www.blogger.com/profile/01762805030145046416noreply@blogger.com0tag:blogger.com,1999:blog-2143388257600921378.post-20364039399692345532013-06-13T21:30:00.000+02:002013-06-13T21:34:39.024+02:00Nachtrag zu: Mauszeiger im Sourcecode setzen<br />
Wie ich ja schon <a href="http://xp-development.blogspot.de/2013/06/mauszeiger-im-sourcecode-setzen.html" target="_blank">beschrieben</a> hatte, ist es möglich den Cursor aus dem Sourcecode heraus zu setzen. Allerdings ist dabei zu beachten, dass dies bedeutet, dass das setzen der Eigenschaft Cursor am WPF Control keine Auswirkung mehr hat. Erst wenn man die Eigenschaft wieder auf null setzt, wird die Cursor Eigenschaft des Controls wieder berücksichtigt.<br />
<br />
Dementsprechend sollte mein Beispiel viel eher so aussehen:<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQCXZ8zpVsqz7e4zkou8Ja7Vimy9hrXNOns9AhmyS9lFeNtYUM0Nb5cMtIv0deQn7pURlOvU4cpw5Ynrn64oNN8Bz8EhAWxoeuvzm5naL75HAj4XjtBZxe0TFAEityF3vJoH16ZXVh-m15/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> private void OnIsBusyChanged(object sender, bool isBusy)
{
if (isBusy)
Mouse.OverrideCursor = Cursors.Wait;
else
Mouse.OverrideCursor = null;
}</code></pre>
<br />
So wird die Anwendung, wenn sie in den Busy Modus versetzt wird, den Wait Cursor anzeigen. Ansonsten den Cursor, den das WPF Control vorgibt.Jan Schuberthttp://www.blogger.com/profile/01762805030145046416noreply@blogger.com0tag:blogger.com,1999:blog-2143388257600921378.post-85778854247815241502013-06-12T21:01:00.003+02:002013-06-12T21:02:43.791+02:00TFS for freeMit dem Team Foundation Server von Microsoft können Projekte geplant und verwaltet werden. Sowohl eine Sourcecode Verwaltung, als auch einen Buildagent bringt er mit sich.<br />
<br />
Auf der Seite <a href="http://tfs.visualstudio.com/" target="_blank">tfs.visualstudio.com</a> kann man diesen kostenfrei nutzen!<br />
<br />
Folgend ein paar Argumente, warum man es einfach mal versuchen sollte:<br />
<br />
<ul>
<li>Es können beliebig viele Projekte angelegt werden.</li>
<li>Bei der Sourcecode Verwaltung kann sogar zwischen der Microsoft eigenen und Git gewählt werden.</li>
<li>Agile Projektplanung wird vom TFS auch unterstützt.</li>
<li>Tests können ausgeführt werden.</li>
<li>Arbeiten in Teams bei der kostenfreien Variante mit bis zu fünf Personen.</li>
</ul>
<div>
Und alles was man braucht, ist eine Windows Live ID. Also ausprobieren lohnt sich!</div>
Jan Schuberthttp://www.blogger.com/profile/01762805030145046416noreply@blogger.com0tag:blogger.com,1999:blog-2143388257600921378.post-63755725881879111932013-06-11T18:59:00.000+02:002013-06-11T19:00:38.539+02:00Mobile Farbwahl mit dem iPhoneNachdem ich vor kurzem über den Adobe Service Kuler <a href="http://xp-development.blogspot.de/2013/06/hilfe-bei-der-farbwahl.html" target="_blank">berichtete</a>, habe ich nun auch eine mobile Version des Services entdeckt. Sobald man die App auf dem iPhone startet, wird die Kamera aktiviert und die Farbwahl beginnt.<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhj_WS3nC92FTLzvEJF6jwxHjDmKUZAdeSfZGwXGLvvtoINoYN1GQx_YxRa-p94PgYYQ7UbttebJvf5y309gHcjk2ydlCMbCunSrGBKHaLwh13g9_k7iOf2QT1seZBk8V9L6ltpXj2Jg9k/s1600/kuler-iphone.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhj_WS3nC92FTLzvEJF6jwxHjDmKUZAdeSfZGwXGLvvtoINoYN1GQx_YxRa-p94PgYYQ7UbttebJvf5y309gHcjk2ydlCMbCunSrGBKHaLwh13g9_k7iOf2QT1seZBk8V9L6ltpXj2Jg9k/s320/kuler-iphone.jpg" width="213" /></a></div>
Die fünf Farbpunkte sucht sich die App automatisch zusammen. Es ist möglich das aktuelle Bild mit einem Klick zu fixieren und die Farbpunkte beliebig zu verschieben.<br />
Ein Farbschema kann dann gespeichert und wenn gewünscht auch direkt der Community zur Bewertung freigegeben werden.<br />
<br />
Folgend der Link zur App:<br />
<br />
<ul>
<li><a href="https://itunes.apple.com/us/app/adobe-kuler/id632313714" target="_blank">iPhone Kuler App</a></li>
</ul>
Jan Schuberthttp://www.blogger.com/profile/01762805030145046416noreply@blogger.com0tag:blogger.com,1999:blog-2143388257600921378.post-54653848929382085972013-06-10T20:29:00.001+02:002013-06-10T20:29:58.215+02:00Mauszeiger im Sourcecode setzenDer Cursor kann, nicht nur wie schon <a href="http://xp-development.blogspot.de/2013/06/mauszeiger-im-xaml-setzen.html" target="_blank">beschrieben</a> im XAML gesetzt werden. Er kann auch im Sourcecode, wie zum Beispiel im ViewModel, überschrieben werden.<br />
<br />
Als klassisches Beispiel habe ich einen Event-Handler in dem ich schaue, ob die Anwendung Busy ist oder nicht und dementsprechend einen Wait Cursor oder einen Pfeil anzeigt.<br />
<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQCXZ8zpVsqz7e4zkou8Ja7Vimy9hrXNOns9AhmyS9lFeNtYUM0Nb5cMtIv0deQn7pURlOvU4cpw5Ynrn64oNN8Bz8EhAWxoeuvzm5naL75HAj4XjtBZxe0TFAEityF3vJoH16ZXVh-m15/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> private void OnIsBusyChanged(object sender, bool isBusy)
{
if (isBusy)
Mouse.OverrideCursor = Cursors.Wait;
else
Mouse.OverrideCursor = Cursors.Arrow;
}</code></pre>
Jan Schuberthttp://www.blogger.com/profile/01762805030145046416noreply@blogger.com0tag:blogger.com,1999:blog-2143388257600921378.post-86754254940057045672013-06-09T14:17:00.000+02:002013-06-09T14:18:08.464+02:00LED Control in WPFWie ich ja schon mal <a href="http://xp-development.blogspot.de/2013/05/controls-selber-bauen.html" target="_blank">berichtete</a>, ist es in WPF recht einfach ein eigenes Control zu entwickeln. Nun möchte ich das mal anhand eines Beispiels belegen.<br />
Ziel: Ein LED Control, bei dem es möglich ist eine beliebige Farbe vorzugeben und außerdem soll das Control in der Größe auch skalierbar sein, ohne das es an schärfe verliert.<br />
<br />
Als erstes legen wir eine neue Klasse mit dem Namen "Led" an. Diese bekommt eine DependencyProperty mit dem Namen Color vom Typ <a href="http://msdn.microsoft.com/en-us/library/system.windows.media.color.aspx" target="_blank">System.Windows.Media.Color</a>. Das ist dann später die Eigenschaft, die im XAML gesetzt werden muss, um der Led eine Farbe zu geben.<br />
<br />
Hier die Led.cs Datei<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQCXZ8zpVsqz7e4zkou8Ja7Vimy9hrXNOns9AhmyS9lFeNtYUM0Nb5cMtIv0deQn7pURlOvU4cpw5Ynrn64oNN8Bz8EhAWxoeuvzm5naL75HAj4XjtBZxe0TFAEityF3vJoH16ZXVh-m15/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace LedSpike
{
public class Led : Control
{
public static readonly DependencyProperty ColorProperty =
DependencyProperty.Register("Color", typeof(Color), typeof(Led), new PropertyMetadata(default(Color)));
public Color Color
{
get { return (Color)GetValue(ColorProperty); }
set { SetValue(ColorProperty, value); }
}
static Led()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Led), new FrameworkPropertyMetadata(typeof(Led)));
}
}
}
</code></pre>
<br />
Jedes Control benötigt ein ContentControl, ohne dieses kann es nicht angezeigt werden. Wie man unterschiedliche Themes anlegt, habe ich <a href="http://xp-development.blogspot.de/2013/06/wpf-themes.html" target="_blank">hier</a> beschrieben.<br />
<br />
Wir legen nun erstmal den Themes Ordner in unserem Projekt an und erstellen die Generic.xaml Datei, um das Standard Template für unser Control zu erstellen.<br />
Da das Control in verschiedenen Größen genutzt werden soll, nutzen wir eine Viewbox. Danach erstellen wir eine Ellipse und legen einen Farbverlauf fest, damit das Control auch modern aussieht. Unser Verlauf hat zwei Farben, wobei ich die eine Farbe generell mit Weiß vorbelege, um einen schimmernden Effekt zu erzeugen. Die andere Farbe habe ich an die DependencyProperty gebunden.<br />
<br />
Hier die Themes/Generic.xaml Datei:<br />
<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQCXZ8zpVsqz7e4zkou8Ja7Vimy9hrXNOns9AhmyS9lFeNtYUM0Nb5cMtIv0deQn7pURlOvU4cpw5Ynrn64oNN8Bz8EhAWxoeuvzm5naL75HAj4XjtBZxe0TFAEityF3vJoH16ZXVh-m15/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> <ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:LedSpike">
<Style TargetType="{x:Type local:Led}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:Led}">
<Viewbox>
<Ellipse Margin="10,10,10,10" Width="300" Height="300">
<Ellipse.Fill>
<RadialGradientBrush Center="0.6,0.35" GradientOrigin="0.6,0.35" RadiusY="0.67" RadiusX="0.67">
<RadialGradientBrush.RelativeTransform>
<TransformGroup>
<ScaleTransform CenterY="0.35" CenterX="0.6" ScaleY="1" ScaleX="1" />
<SkewTransform AngleY="0" AngleX="0" CenterY="0.35" CenterX="0.6" />
<RotateTransform Angle="-4.447" CenterY="0.35" CenterX="0.6" />
<TranslateTransform X="0" Y="0" />
</TransformGroup>
</RadialGradientBrush.RelativeTransform>
<GradientStop Color="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Color}" Offset="0.8" />
<GradientStop Color="White" />
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
</Viewbox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
</code></pre>
<br />
Die Benutzung ist sehr einfach. Es bedarf lediglich einen Namespace Eintrag und das Led Element im XAML.<br />
<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQCXZ8zpVsqz7e4zkou8Ja7Vimy9hrXNOns9AhmyS9lFeNtYUM0Nb5cMtIv0deQn7pURlOvU4cpw5Ynrn64oNN8Bz8EhAWxoeuvzm5naL75HAj4XjtBZxe0TFAEityF3vJoH16ZXVh-m15/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> <Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:LedSpike" x:Class="LedSpike.MainWindow"
Title="LED spike" Height="200" Width="200">
<Grid>
<local:Led Color="Red" />
</Grid>
</Window>
</code></pre>
<br />
Das Ergebnis:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5AeH_mrMnJ_GkwqWvDV7SXxb5h_UzhrxoIxEadcKOar1nAPHtUMRG1yo0R6YDOn0bPLhmTadyiVzVOFx9NtgfPWsFuaxifHtVYdQ6wj8DYzIf5pWQsjzKqbPn2Zlq45JQ3GUSZz35eTk/s1600/Led-red.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5AeH_mrMnJ_GkwqWvDV7SXxb5h_UzhrxoIxEadcKOar1nAPHtUMRG1yo0R6YDOn0bPLhmTadyiVzVOFx9NtgfPWsFuaxifHtVYdQ6wj8DYzIf5pWQsjzKqbPn2Zlq45JQ3GUSZz35eTk/s1600/Led-red.jpg" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<br />Jan Schuberthttp://www.blogger.com/profile/01762805030145046416noreply@blogger.com0tag:blogger.com,1999:blog-2143388257600921378.post-73811344053710050912013-06-08T20:23:00.000+02:002013-06-08T20:23:50.004+02:00WPF Resourcen in mehrere Dateien auslagernIn WPF hat man die Möglichkeit Styles und Templates für seine Controls in Dateien auszulagern. Die Klasse ResourceDictionary hilf einem dabei.<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQCXZ8zpVsqz7e4zkou8Ja7Vimy9hrXNOns9AhmyS9lFeNtYUM0Nb5cMtIv0deQn7pURlOvU4cpw5Ynrn64oNN8Bz8EhAWxoeuvzm5naL75HAj4XjtBZxe0TFAEityF3vJoH16ZXVh-m15/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> <ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
</ResourceDictionary> </code></pre>
Um dann mehrere Resourcen zu laden kommt die MergedDictionaries Property ins Spiel. Wenn man zum Beispiel einem Window verschiedene Resourcen zuweisen möchte, könnte das so aussehen:<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQCXZ8zpVsqz7e4zkou8Ja7Vimy9hrXNOns9AhmyS9lFeNtYUM0Nb5cMtIv0deQn7pURlOvU4cpw5Ynrn64oNN8Bz8EhAWxoeuvzm5naL75HAj4XjtBZxe0TFAEityF3vJoH16ZXVh-m15/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> <Window x:Class="WpfSpike.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Sample1.xaml" />
<ResourceDictionary Source="Sample2.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
</Window>h</code></pre>
Wie man sehen kann, wird dem ResourceDictionary über die Source Property der Pfad zur Resource übergeben.Jan Schuberthttp://www.blogger.com/profile/01762805030145046416noreply@blogger.com0tag:blogger.com,1999:blog-2143388257600921378.post-83625749958132783302013-06-07T17:44:00.001+02:002013-06-07T17:44:53.216+02:00Mauszeiger im XAML setzenJedes WPF Control bzw jedes FrameworkElement hat eine Cursor Eigenschaft. Mit der kann man jedem Control den gewünschten Cursor zuordnen.<br />
<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQCXZ8zpVsqz7e4zkou8Ja7Vimy9hrXNOns9AhmyS9lFeNtYUM0Nb5cMtIv0deQn7pURlOvU4cpw5Ynrn64oNN8Bz8EhAWxoeuvzm5naL75HAj4XjtBZxe0TFAEityF3vJoH16ZXVh-m15/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> <StackPanel>
<Label Cursor="Hand">label with hand cursor</Label>
<Label Cursor="Help">label with help cursor</Label>
</StackPanel>
</code></pre>
<br />
Das Ergebnis ist dementsprechend:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmbgpdtxtJMFhKxa5w79VwuB7OevkpG0yZtE8Nprgh9pb_kzsKte3oxG3-jR8JuDCDfj0kphOOr3DVlO9Jk2hlGnbygKopibRY5k9VMCGic_sWuQ6tGbam7M10wBbcOSCmJTYAohqp2Mw/s1600/Hand-Cursor.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmbgpdtxtJMFhKxa5w79VwuB7OevkpG0yZtE8Nprgh9pb_kzsKte3oxG3-jR8JuDCDfj0kphOOr3DVlO9Jk2hlGnbygKopibRY5k9VMCGic_sWuQ6tGbam7M10wBbcOSCmJTYAohqp2Mw/s1600/Hand-Cursor.jpg" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjqO7VrnO45BlB3cxcfelsPH4IJaR3LTUg6q_xr9P5H3atMhCKSEAFmo9d8jNtifftPyWQJ3KTfOmTthko_39aiODHCxxs-vcpps1Zi2mRCtiixEKQj9QWpxsz9Bo95GIP2A-mYz5Tbjqg/s1600/Help-Cursor.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjqO7VrnO45BlB3cxcfelsPH4IJaR3LTUg6q_xr9P5H3atMhCKSEAFmo9d8jNtifftPyWQJ3KTfOmTthko_39aiODHCxxs-vcpps1Zi2mRCtiixEKQj9QWpxsz9Bo95GIP2A-mYz5Tbjqg/s1600/Help-Cursor.jpg" /></a></div>
<br />
Die Cursor Eigenschaft vererbt sich auch weiter, so dass jedes Child Control auch davon betroffen ist.Jan Schuberthttp://www.blogger.com/profile/01762805030145046416noreply@blogger.com0