Blue Orange Green Pink Purple

Archive for November, 2009

You can use the search form below to go through the content and find a specific post or page:

Nov 29

Leaner CSS for .NET

Schon vor einiger Zeit hab ich ein tolles Ruby Gem für die Webentwicklung in Kombination (und das ist eigentlich immer der Fall) mit CSS entdeckt: LESS – Leaner CSS.

Ich stand schon mal kurz davor, dass Ganze spasshalber auf .NET zu portieren — aus Zeitmangel hab ichs dann doch lassen. Anscheinend war ich aber nicht der einzige, der so eine Idee hatte :) Christopher Owen, Erik van Brakel and Daniel Hoelbling haben die Idee tatsächlich in die Tat umgesetzt und das Ruby Gem auf .NET portiert: .less{} heißt das Teil und bietet exakt die gleiche Funktionalität, wie die Ruby Version. Der Unterschied ist vorallem der, dass die .NET Version nicht “vorkompiliert” werden muss, sondern über HTTP-Handler direkt zur Laufzeit verwendet werden kann. Ob das ein Vor- oder Nachteil ist, wird sich noch herausstellen (aus Performancesicht).

Ich habs auf jeden Fall schon getestet und es funktioniert einwandfrei.

In diesem Sinne.

Nov 28

HowTo: Fluent Interface in Repositories einsetzen

Heute möchte ich mich mal mit der Idee der Fluent Interfaces beschäftigen. Mit Fluent Interfaces ist es möglich, Programmcode in “Satzform” niederzuschreiben und somit wesentlich lesbarer wird.

Wie funktioniert ein Fluent Interface?
Schauen wir uns zuerst ein einfaches Beispiel an:

var obj = new Foo();
obj.Start()
   .Execute()
   .Stop();

In dem Beispielcode wird auf ein Objekt vom Typ Foo zuerst eine Methode Start() aufgerufen. Diese Methode liefert als Rückgabewert wiederrum ein Objekt vom Typ Foo. Dadurch ist es möglich, auf die Methode wiederrum eine Methode (nämlich Execute()) des Objekts Foo aufzurufen. Da auch diese Methode wieder ein Objekt vom Typ Foo liefert, kann darauf die letzte Methode Stop() aufgerufen werden. Ziemlich easy und der Code wird um einiges lesbarer. Die Implementierung der Klasse Foo sieht so aus:

public class Foo
{
   public Foo Start()
   {
      return this;
   }

   public Foo Execute()
   {
      return this;
   }

   public Foo Stop()
   {
      return this;
   }
}

Das Ganze ist auch unter Method Chaining bekannt und wird zum Beispiel bei der beliebten Javascript Bibliothek jQuery eingesetzt. Mehr Infos zu Fluent Interfaces gibts hier http://www.bjoernrochel.de/2008/08/10/implementing-a-fluent-api-for-the-ribbon.

Fluent Interfaces für Repositories
Neben den vielen Vorteilen, die Fluent Interfaces mit sich bringen und den vielen Einsatzmöglichkeiten, die sich für Fluent Interfaces ergeben, möchte ich auf  eine detailierter eingehen, da diese bei jeder Business- und datengetriebenen Anwendung zum Einsatz kommt (sollte): Das Repository Pattern. Wer noch keine Erfahrung mit diesem Pattern hat, kann sich mal unter schlau http://martinfowler.com/eaaCatalog/repository.html und http://blog.wekeroad.com/mvc-storefront/asp-net-mvc-mvc-storefront-part-2/ machen.

Sehen wir uns einfach folgendes Beispiel an, dass auf einen LINQ2SQL Context zurückgreift:


public class PersonRepository
{
   private static DataContext _DataContext { get; set; }

   public IQueryable<Person> Find()
   {
      return from person in _DataContext.Persons
             select person;
   }

   static PersonRepository()
   {
      _DataContext = new FluentDataContext();
   }
}

Über die Extension-Methods, die mit C# 3.0 gekommen sind, kann man jetzt so ein Fluent Interface ganz einfach implementieren:


public static class PersonExtensions
{
   public static IQueryable<Person> WithName(this IQueryable<Person> items,
                                             string name)
   {
      if (items == null)
         throw new ArgumentNullException("IQueryable<Person> is null.");
      if (string.IsNullOrEmpty(name))
         throw new ArgumentNullException("Found invalid compare-string for name.");

      return items.Where(person => person.Name.CompareTo(name) == 0);
   }

   public static IQueryable<Person> WithName(this IQueryable<Person> items,
                                             DateTime birthday)
   {
      if (items == null)
         throw new ArgumentNullException("IQueryable<Person> is null.");

      return items.Where(person => person.Birthday.Equals(birthday));
   }
}

Rob Conery hat in einem Screencast diese Idee genauer beschrieben (er nennt es hier Pipes & Filters): http://blog.wekeroad.com/mvc-storefront/mvcstore-part-3/

Das Problem mit POCOs!
Ein Problem, das in meinem Projekten immer wieder auftritt, ist, dass ich solche Pipes & Filters nur dann einsetzen kann, wenn ich in meinem Model-Layer keine POCOs einsetze und bei LINQ2SQL direkt mit den automatisch generierten Model-Klassen arbeite. Nur genau das will ich nicht: Ich will mit meinen eigenen Domain-Model-Klassen arbeiten, um keine Abhängigkeiten in den restlichen Layern auf das LINQ Model zu bekommen. Um das zu erreichen, müssen wir unser Repository ändern (wobei <Person> unser eigenes Domainmodel ist und <DBPerson> das LINQ2SQL Model ist):


public IQueryable<Person> Find()
{
   return from person in _DataContext.DBPersons
          select new Person
                {
                   Id = person.Id,
                   Name = person.Name,
                   Birthday = person.Birthday
                };
}

Gut so. Jetzt mappen wir direkt im Repository auf unser Domainmodel. Das Problem, dass jetzt aber entsteht ist, das wir unsere Extension-Methods so nicht mehr verwenden können, da die nur mit dem LINQ2SQL Model funktionieren. Jetzt können wir diese Methoden auf unser Domainmodel umändern und wir hätten die Möglichkeit über Fluent Interfaces die Daten abzufragen:


var rep = new PersonRepository();
var items = rep.Find().WithName("John");

Bei diesem Codefragment erkennt man schon die Problematik, die wir nun schlußendlich haben: Wir müssen zuerst alle Daten aus der Datenquelle lesen, auf unser Domainmodel mappen und erst dann über die Extension-Methods weiter aussortieren. Bei einer SQL Tabelle, die mehrere Tausend Datensätze speichert ist dieser Workflow definitiv nicht gewünscht und auch nicht empfehlenswert.

Implicit Cast Operator
Um dieses Problem zu lösen, verwenden wir ein tolles Feature von C#: Cast Operatoren (http://msdn.microsoft.com/en-us/library/85w54y0a%28VS.80%29.aspx). Sehen wir uns folgende Klasse an:


public class FluentPerson
{
   private IQueryable<DBPerson> _Items { get; set; }

   public FluentPerson WithName(string name)
   {
      _Items = _Items.Where(person => person.Name.CompareTo(name) == 0);
      return this;
   }

   public FluentPerson WithBirthday(DateTime birthday)
   {
      _Items = _Items.Where(person => person.Birthday.Equals(birthday));
      return this;
   }

   public static implicit operator List<Person>(FluentPerson fluent)
   {
      return (from person in fluent._Items
              select new Person
                  {
                     Id = person.Id,
                     Name = person.Name,
                     Birthday = person.Birthday
                  }).ToList<Person>();
   }

   public FluentPerson(IQueryable<DBPerson> items)
   {
      _Items = items;
   }
}

Diese Klasse verwaltet eine Liste von DBPerson, die immer die aktuellen, noch verfügbaren DBPersons beinhaltet. In den einzelnen Methoden geben wir immer die Instanz der Klasse selbst zurück – somit haben wir ein Fluent Interface. Im Implicit Cast Operator erzeugen wir nun aus der Liste der LINQ2SQL Daten eine Liste unseres Domainmodels. Das tolle dabei nun, dass LINQ die Query auf die Datenbank erst dann absetzt, sobald die Daten, die gelesen werden sollen, im Programmcode auch wirklich verwendet werden – dh, in unserem Fall wird die Query nicht in den Fluent Interface Methoden (WithName() oder WithBirthday()) sondern erst im Implict Cast Operator abgesetzt (weil wir dort auch tatsächlich auf die Daten der Datenbank zugreifen). Tolle Sache. Um das Ganze noch verwenden zu können, speichert nun unser Repository eine Instanz dieser FluentPerson Klasse. Verwenden können wir das Ganze nun so:


var repository = new PersonRepository();
List<Person> persons = repository.Fluent.WithName("Joana").
                                        .WithBirtday(new DateTime(2000, 12, 24));
persons.ForEach(person => Console.WriteLine("{0}, {1}", person.Name, person.Birthday.ToShortDateString()));

Sehr sinnvoll das Ganze. Beispiel gibts wie immer hier: http://downloads.juergenoberngruber.at/blog/FluentRepositories.zip

In diesem Sinne.

Nov 22

HowTo: Behaviors/Triggers richtig einsetzen

Mit Expression Blend 3 ist ein neues Konzept gekommen, das definitiv eine großartige Erweiterung des Funktionsumfangs von WPF und Silverlight darstellt. Hat man bisher fehlende Funktionalität eines UI Controls über Umwege erstellt (zb das Ausfrühen von Commands, sobald ein RoutedEvent eines Controls auftritt), kann man das realtiv einfach und zentral verwalten: die Rede ist von den Behaviors und Triggers!

Was sind Behaviors und Triggers?
Es handelt sich um ein Konzept, gewisse Funktionalität zentral auszulagen und später auf jedes UI Control anzuhängen. Wann, was und wie das Behavior bzw. der Trigger die Dinge erledigt spielt keine Rolle bzw. entscheidet das Behavior bzw. der Trigger von selbst. Unterschieden wird zwischen drei unterschiedlichen Typen:

  • Behavior: Diese Klasse ist die am meisten verwendete. Dabei implementiert das Behavior selbst vollkommen autonom was wann und wie passieren soll. Das UI Control, dass das Behavior verwendet hat keinerlei Kontrolle darüber, was im Behavior selbst passiert.
  • TriggerAction: Diese Klasse wird direkt an einen RoutedEvent gehängt und aufgerufen, sobald dieser Event auftritt.
  • TargetedTriggerAction: Diese Klasse ist der TriggerAction sehr ähnlich — jedoch kann dabei ein anderes UI Control beim RoutedEvent verändert werden

Alle drei Klassen haben generische Gegenstücke, bei denen noch expilizit das UI Control angegeben werden kann, für das das Behavior verwendet werden kann.

Custom Behavior erstellen!
Wir wollen uns nun ein eigenes Behavior erstellen, das relativ einfach ist, aber ein, meiner Meinung nach, gängies Problem lösen soll. Es soll die Maus verändern, sobald der Benutzer über einen Control bewegt, das angeklickt werden kann. Funktionaliätit, die es im Web schon lange gibt, auf Rich Client Anwendungen aber fehlt.

Als erstes erstellen wir uns eine Klasse, die von Behavior ableitet. Behavior liegt im Assembly System.Windows.Interactivity, dass standarmäßig nicht inkludiert ist. Zu finden sollte es unter

%Progam Files%\Microsoft SDKs\Expression\Blend 3\Interactivity\Libraries\WPF (oder Silverlight)

sein. Haben wir die Klasse, können wir unterschiedliche Methoder überschreiben. Die zwei wichtigsten für unser Demo sind OnAttached() und OnDetaching(). In OnAttached registrieren wir uns Eventhandler für die Events MouseEnter und MouseLeave. Im OnDetaching entfernen wir diese Eventhandler wieder.

Betritt nun die Maus das Control, wird der Eventhandler für MouseEnter aufgerufen, in dem wir den Mauszeiger verändern. In MouseLeave setzen wir ihn wieder auf den Standard zurückgesetzt. Ganz einfach eigentlich.


public class ClickableBehavior : Behavior<FrameworkElement>
{
     protected override void OnAttached()
     {
          this.AssociatedObject.MouseEnter += new MouseEventHandler(_MouseEnter);
          this.AssociatedObject.MouseLeave += new MouseEventHandler(_MouseLeave);

          base.OnAttached();
     }

     protected override void OnDetaching()
     {
          this.AssociatedObject.MouseEnter -= new MouseEventHandler(_MouseEnter);
          this.AssociatedObject.MouseLeave -= new MouseEventHandler(_MouseLeave);

          base.OnDetaching();
     }

     /// <summary>
     /// Der Typ des Cursos, auf den das Behavior wechsel soll.
     /// </summary>
     public Cursor EnterCursor
     {
          get { return (Cursor)GetValue(EnterCursorProperty); }
          set { SetValue(EnterCursorProperty, value); }
     }
     public static readonly DependencyProperty EnterCursorProperty =
           DependencyProperty.Register("EnterCursor", typeof(Cursor),
           typeof(ClickableBehavior), new UIPropertyMetadata(Cursors.Hand));

     #region Eventhandler

     /// <summary>
     /// Wird aufgerufen, sobald die Maus das FrameworkElement betritt.
     /// </summary>
     private void _MouseEnter(object sender, MouseEventArgs e)
     {
          this.AssociatedObject.Cursor = EnterCursor;
     }

     /// <summary>
     /// Wird aufgerufen, sobald die Maus das FrameworkElement verlässt.
     /// </summary>
     private void _MouseLeave(object sender, MouseEventArgs e)
     {
          this.AssociatedObject.Cursor = null;
     }

     #endregion Eventhandler
}

Nachdem wir die Logik nun haben, können wir das Behavior jetzt verwenden. Das tun wir, indem wir im XAML Code zuerst den richtigen Namespace einbinden (System.Windows.Interactivity und unseren lokalen Namespace) und dann auf ein beliebiges Control das Behavior anfügen:


<Grid Margin="50" Background="Gold">
    <interactivity:Interaction.Behaviors>
         <local:ClickableBehavior />
    </interactivity:Interaction.Behaviors>
</Grid>

Das Demoprojekt kann wie immer hier  heruntergeladen werden: Download Demoprojekt (VS 2010 Solution).

In diesem Sinne.

Nov 22

HowTo: Globales web|app Konfigurationsfile in mehreren Projekten nützen

Will man eine Konfigurationsdatei in mehreren Projekten gemeinsam nützen, ist das von Haus aus nicht ganz so einfach bzw. wird “out of the box” von Visual Studio nicht unterstützt. Bei einem meiner aktuellen Projekte steh ich aber genau vor dieser Herausforderung. Ich hab als Ausgangsitutation folgende Projektstruktur:

  • Projekt: Services (WCF Services)
  • Projekt: Richt Client (WPF Client)
  • Projekt: Core Lib

Die beiden ersten Projekte sind voneinander unabhängig und werden von unterschiedlichen Stellen aus verwaltet. Beide verwenden aber die _Core Lib_ in der sich logischerweise einiges an Funktionalität befindet, die von den beiden anderen Projekten verwendet wird. Zum Beispiel befindet sich darin der DAL, der ja, verwendet man als Datenspeicher ein RDMS, einen Connectionstring benötigt. Genau diese Konfigurationseinstellung will man jetzt nicht bei den beiden “Hauptprojekten” replizit speichern sondern zentral verwalten können und auf die beiden Projekte automatisch verteilen. Aber wie geht das Ganze?

Meines Wissens nach gibt es von Visual Studio dafür keine “out of the box” Lösung, um genau so ein Szenario abzubilden. Es ist zwar möglich, auf die web|app.config eines anderen Projektes zuzugreifen — was aber bedeutet, das seine Konfigurationseinstellungen dezentral verwaltet werden. Und genau das will ich normalerweise in einem größeren Projekt vermeiden.

Lösung? Verwendung von Pre/Post Build Events!

Visual Studio macht es einem auf “High Level” Basis relativ einfach, den Build-Vorgang zu erweitern. Um nicht in die Tiefe gehen zu müssen, und benutzererstellte MSBuild Tasks zu erstellen, gibt es für jedes VS Projekt die sogenannten Build Events. Mit denen ist es möglich, vor und nach einem Kompiliervorgang gezielt eigene Kommandos auszuführen. Zum Beispiel ist es möglich, die alt bekannten DOS Kommandos wie copy oder mkdir auszuführen. Und genau das machen wir uns in diesem Fall zu nütze.

Über die Projekt-Einstellungen kommt man zu folgenden Screen:

VS Projekt Einstellungen

Dort kann man jetzt in die beiden Textboxen für Pre-Build und Post-Build seine eigenen Kommandos einfügen.

Ich hab für unser Demoprojekt eine Solution erstellt, die drei Projekte speichert: Client, Web und Core. Client und Web verwenden die Core Bibliothek, die wiederrum auf Einstellung von Client oder Web zugreift.

Auf Einstellungen zugreifen

Als erstes implementieren wir den Zugriff auf die (hoffentlich) vorhandenen Konfigurationseinstellungen. In der Core Bibliothek erstellen wir uns eine Klasse, die die unterschiedlichen Einstellung aus den Konfiruationseinstellungen ausliest und zurückgibt:

public static class Settings
{
    public static string Author
    {
       get { return ConfigurationManager.AppSettings["author"]; }
    }
    public static string Email
    {
       get { return ConfigurationManager.AppSettings["email"]; }
    }
    public static int Copyright
    {
       get { return Convert.ToInt32(ConfigurationManager.AppSettings["copyright"]); }
    }
}

Über die Klasse ConfigurationManager (im Assembly System.Configuration) kann man auf die AppSettings bzw. ConnectionStrings zugreifen. In diesem Beispiel gehen wir davon aus, das wir unsere Einstellungen nur in den AppSettings speichern. ConfigurationManager greift im Hintergrund auf die Einstellungen der app|web.config zu. Da wir dieses Projekt eigentlich in dem Client bzw. Web verwenden, verwendet der ConfigurationManager indirekt die Einstellungen aus diesem Projekt. Im Web-Projekt erzeugen wir uns nun AppSettings, auf die wir später im Page-Load zugreifen:

<appSettings>
   <add key="author" value="Jürgen Oberngruber" />
   <add key="email" value="blog@juergenoberngruber.at" />
   <add key="copyright" value="2009" />
</appSettings>

Im Page_Load können wir jetzt über unsere Settings Klasse indirekt auf die Settings zugreifen:

protected void Page_Load(object sender, EventArgs e)
{
    Response.Write(Core.Settings.Author);
}

Das Ganze funktioniert natürlich auch für unseren WPF Client (dort arbeiten wir mit der app.config Datei):

WPF Client

Globales Konfigurationsfile

So, das Ganze ist jetzt so, wie wir es ja eigentlich gar nicht wollen. Um ein globales Konfigurationsfile zu erstellen, erzeugen wir uns einfach auf “Solution-Ebene” ein neues *config File, dass die globalen Einstellungen, die wir später in den beiden Projekten (Web und Client) verwenden, speichert. Um dieses File jetzt für die beiden Host-Projekt zugänglich zu machen, müssen wir als erstes dem web|app.config File mitteilen, dass es eine zusätzliche Konfiguration aus einem externen File nehmen soll:

<appSettings file="GlobalSettings.config">
</appSettings>

Versuchen wir jetzt das Projekt laufen zu lassen, bekommen wir einen Error, weil es die Datei nicht finden kann — was auch klar ist, weil es das File nicht in diesem Pfad gibt. Grund dafür ist, das Visual Studio das File beim kompilieren ignoriert — wir wollen aber erreichen, dass es beim Kompilieren in den bin Ordner bzw. in das richtige Verzeichnis kopiert wird. Hier kommen nun endlich unsere Post-Build Events zum Einsatz.

Über das Kommando copy versuchen wir, das GlobalSettings.config File in den richtigen Dateipfad zu kopieren. Für das Web Projekt soll es dort hin, wo die web.config liegt. Das Post-Build Kommando sieht als so aus:

copy “$(SolutionDir)GlobalSettings.config” “$(ProjectDir)GlobalSettings.config”

Visual Studio bietet uns dabei Unterstützung an, in dem es Konstanten (Macros) zur Verfügung stellt, die später beim Kompilieren aufgelöst werden:

post_build_macros

Kompilieren wir jetzt neu, sollte die GlobalSettings.config im richtigen Ordner liegten und die file Angabe in der web.config klappt nun auch. Das gleiche funktioniert auch beim Client Projekt — dort liegt die app.config jedoch im /bin Ordner. Das tolle dabei ist jetzt auch, dass wir trotzdem in unseren einzelnen Projekten noch die “Globale Konfiguration” überschreiben bzw. neue Werte hinzufügen können (<add>, <remove> und <clear>).

Das ganze Demo Projekt kann hier herunter geladen werden: GlobalSettings_Demo (VS 2010).

Hat wer eine besser Idee —> Comments!

In diesem Sinne.

Nov 21

HowTo: Webapplications mit TeamCity builden

Arbeitet man mit Teamcity (ein sehr gutes und nettes CI Tool) kommt man bestimmt einmal in die Verlegenheit, eine Visual Studio Solution mit unterschiedlichen Projekten darin über dem CI Server zu builden. Gibt es in der Solution auch “Webapplications“, werden die auf Anhieb mal nicht funktionieren.

Warum?
Beim Builden dieser Webapplication meldet Teamcity einen Fehler, dass er ein *.targets File nicht finden kann.

Das importierte Projekt C:\Programme\MSBuild\Microsoft\VisualStudio\v9.0\WebApplications\Microsoft.WebApplication.targets wurde nicht gefunden. Vergewissern Sie sich, dass der Pfad in der <Import>-Deklaration korrekt und die Datei auf dem Datenträger vorhanden ist.

Grund dafür ist das Fehlen von zwei Dateien auf dem Build-Server, die normalerweise bei einer sauberen Visual Studio Installation in folgendem Ordner liegen sollten: $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v9.0\WebApplications\
Nachdem aber normalerweise auf dem Buildserver kein Visual Studio vorhanden ist, schlägt der Buildvorgang einer Webapplication über Teamcity fehl.

Lösung!
Die Lösung liegt ganz nahe. In dem oben angeführten Verzeichnis sollten zwei Dateien vorhanden sein:

  • Microsoft.WebApplication.Build.Tasks
  • Microsoft.WebApplication

Diese beiden Dateien einfach auf dem Buildserver, unter dem selben Verzeichnispfad wie sie lokal vorliegen, abspeichern und der Buildprozess für das Webapplications-Projekt sollte klappen.

In diesem Sinne.

Nov 21

HowTo: WPF Fenster mit eigener Titelleiste

Letztens stand ich vor der Herausforderung, bei einem Projekt ein nettes, anspruchvolles UI zu gestalten. Ein gutes UI sollte individuell sein und nicht zu sehr am Look & Feel des Betriebssystem hängen. Dank WPF ist sowas ja kein Problem mehr (zumindest mit nicht allzu großem Aufwand).

Ein WPF Fenster mit eigenen Titelleiste?
Ein Fenster einer Anwendung individuell zu gestalten, bedeutet für mich auch zugleich, eine eigene Titelleiste zu rendern und nicht die des OS zu verwenden. Mit WPF geht das relativ einfach.

1. Rahmenloses Fenster erstellen
Als erstes erstellen wir uns mal ein ein WPF Projekt in Visual Studio. Um nun aus dem “normalen” Fenster ein Fenster zu erzeugen, dass eine eigene Titelleiste render kann, müssen wir zunächst die Standardleiste von Windows entfernen. In WPF geht das ganz einfach, in dem wir die Properties

  • “WindowStyle” auf “None” und
  • “AllowsTransparency” auf “True“

setzen. Somit haben wir mal unser ersten Ziel erreicht und haben ein rahmenloses Fenster.

2. Titelleiste aus einem Usercontrol bauen
Damit wir nun eine eigene Titelleiste rendern können, ist es am sinnvollsten, das gesamte UI inklusive Logik in ein Usercontrol auszulagen.
Also in Visual Studio ein Usercontrol zum Projekt hinzufügen und darin dann das gewünschte Aussehen in XAML erzeugen. Das Ganze sieht bei mir ca. so aus (ich habe mittlerweile das Fenster wieder auf weiß umgestellt):

Nachdem wir nun das Aussehen definiert haben, müssen wir noch die Logik für die einzelnen Events hinzufügen. Ein “normales” Fenster hat folgendene Eigenschaften:

  • Minimieren
  • Maximieren
  • Schließen
  • Fenster verschieben

Um diese Funktionalität zu implementieren, müssen wir auf den einzelnen Controls Eventhandler registrieren, die wiederrum Hilfsfunktionen verwenden, die die genaue Funktionsweise implementieren.

2.1 Schließen
Sobald die Grafik zum “Beenden der Anwendung” geklickt wurde, führen wir folgenden Code aus:

private void _Exit()
{
   System.Windows.Application.Current.Shutdown();
}

2.2 Minimieren
Für das Minimieren des Fensters verwenden wir einfach die Eigenschaft “WindowState” des übergeordneten Fensters und setzen diese auf “Minimize”:

private void _Minimize()
{
   _Parent.WindowState = WindowState.Minimized;
}

2.3 Maximieren
Für das Maximieren ist es wichtig, das wir nicht, wie beim Minimieren, den WindowState des übergeordneten Fensters auf “Maximized” setzen, da wir sonst die Anwendung auf Fullscreen schalten –> und das wollen wir nicht. Stattdessen wollen wir nur den verfügbaren Platz des Screens verwenden (ohne Taskleiste, etc.). Das bekommen wir von SystemParameters.WorkArea. Abhängig von einem Flag, das wir uns intern mitspeichern, setzen wir die Abmessungen und die Position des übergeordneten Fensters. Wichtig dabei ist auch, das wir beim Maximieren die aktuelle Position des Fensterns speichern, damit wir beim zurückschalten von Maximized auf Normal die richtigen (alten, letzten) Abmessungen und die Position setzen können:

private void _Resize()
{
   if (_CurrentState == WindowState.Normal)
   {
      _PreviousPosition = new Dimensions
      {
          Left = _Parent.Left,
          Top = _Parent.Top,
          Width = _Parent.Width,
          Height = _Parent.Height
      };

      _Parent.Width = SystemParameters.WorkArea.Width;
      _Parent.Height = SystemParameters.WorkArea.Height;
      _Parent.Left = SystemParameters.WorkArea.Left;
      _Parent.Top = SystemParameters.WorkArea.Top;
      _CurrentState = WindowState.Maximized;
   }
   else
   {
      _Parent.Width = _PreviousPosition.Width;
      _Parent.Height = _PreviousPosition.Height;
      _Parent.Left = _PreviousPosition.Left;
      _Parent.Top = _PreviousPosition.Top;
      _CurrentState = WindowState.Normal;
   }
}

2.4 Fenster verschieben
Um das Fenster zu verschieben greifen wir auf Win32 zurück. Dort gibts zwei Methoden, mit denen man ein paar Win32 Messages versendet, um das Hauptfenster zu verschieben:

private void _Move()
{
   ReleaseCapture();
   SendMessage(new WindowInteropHelper(_Parent).Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0);
}

In diesem Sinne.

Nov 21

HowTo: Internetverbindung auf PC über Smartphone einrichten

Schon öfters war ich in der Situation, auf meinem Netbook eine aktive Internetverbindung zu verwenden, die ich zu diesem Zeitpunkt aber leider nicht hatte. Daher war ich auf der Suche, eine gültige UMTS bzw. GPRS Verbindung meines Smartphones zu verwenden. Über diverse Suchmaschinen war ich lange Zeit nicht fündig, bis ich hier eine Lösung gefunden habe. Eigentlich ist das Ganze relativ einfach.

Auf dem Windows Mobile Gerät gibts unter Programme ein Programm, dass sich bei meiner deutschen WM Version “Internet-Freigabe” nennt:

Öffnet man das Programm hat man 2 Möglichkeiten, eine Verbindung herzustellen:

  • Über USB oder
  • über eine Bluetooth PAN Verbindung

Eines von den Beiden auswählen und auf Verbinden klicken. Anschließend sollte auf dem PC alle notwendigen Treiber automatisch installiert werden.

Fertig. Nun sollte der PC die aktive Verbindung des Smartphones verwenden.
Hat bei mir auf meinem Windows Mobile 6 Phone problemlos funktioniert.

In diesem Sinne.

Nov 21

HowTo: Wie kann ich *.vhd Files vergrößern?

Vor einigen Wochen habe ich mir einige Windows 7 Images zu diversen Dev-zwecken erstellt. Nachdem ich die meisten Images direkt aus dem Win7 Bootmanager heraus boote, habe ich die meisten Images als vollständiges System (VS, Expression Suite, Office, …) aufgesetzt. Alles perfekt geklappt bis zum dem Zeitpunkt, als mir bei den Images der Speicherplatz ausgegangen ist. Ich bin anfangs hergegangen und hab nicht benötigte Programme vom System entfernt. Klappt auch soweit, bis der Speicherplatz wieder zu wenig wird: Also war das Ganze nur eine Lösung auf Zeit. Deshalb hab ich ein wenig gegoogelt und relativ schnell eine Lösung gefunden.

1. *.vhd mittels VHD Resizer vergrößern
Dieses kleine, aber feine Tool VHD Resizer erlaubt es einem, ein *.vhd File im Nachhin zu vergrößern (bzw. zu verkleinern). Das Tool basiert auf .NET 2.0 und benötigt für eine korrekte Ausführung ein installiertes .NET 2.0 Redist-Paket. Einmal heruntergeladen und über das *.msi Paket installiert bekommt man eine *.exe Datei, die einfach gestartet werden kann.

Nachdem das Quell- und Zielverzeichnis und die gewünschte neue Dateigröße ausgewählt wurden, kann man das vegrößern lostarten. Abhängig vom System und der Größe des Ausgangs-Images bzw. des Zielimages kann das konvertierten schon einige Zeit in Anspruch nehmen. Auf meinem System (3 GB Ram) hat das Konvertieren von 15 auf 20 gig so geschätzte 10 Minuten gedauert.

2. *.vhd mit DISKPART erweitern
Nachdem das Konvertieren erledigt ist kann man das Image einfach (entweder direkt über den Bootmanager oder eben über Virtual PC) booten. Dann die Kommandozeile mit Administrator-Rechten starten und den Befehl DISKPART ausführen:

Diskpart ist ein Tool zur Partiotionierung von HDDs über die Kommandozeile das seit Windows 2000 mitgeliefert wird. Über das Kommando

LIST VOLUME

kann man sich eine Liste aller verfügbaren Partitionen auflisten lassen. Anschließend über

SELECT VOLUME X

die richtige Partition auswählen, wobei X für die richtige Nummer der Partition steht. Über

DETAIL VOLUME

gibts dann noch detailierte Informationen zu der aktuell ausgewählten Partition:

Wie im Screen zu sehen ist, haben wir bei der aktuellen Partition eine Gesamtgröße von 20 GB, wobei ca 5,4 GB noch nicht zugewiesen sind. Um das zu erreichen, verwenden wir den Befehl

EXTEND

um den nicht zugewiesenen Speicher der Partition zuzuweisen.

Fertig. Die Partition hat nun eine Gesamtgröße von 20GB. Weitere Infos zu DISKPART gibts unter http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/diskpart.mspx?mfr=true.

In diesem Sinne.

Nov 20

Hello world!

Nachdem ich ja bereits vor einigen Monaten angekündigt habe, das ich demnächst meinen Blog starten werde, hab ich es nun endlich gschafft und mir fest vorgenommen, das auch tatsächlich durchzuziehen.

Was wirds hier zu lesen geben?
In erster Linie mal einen Haufen technischer Artikel, die großteils “Mircosoft-lastig” sein und vorallem die Themen Client- und Webentwicklung behandeln werden. Da ich mich selbst seit einigen Jahren zum Großteil auf der Microsoft .NET Schiene bewege, bildet dieses Thema natürlich die Basis aller zukünftigen Artikel des Blogs. Konkret, um es auf die aktuellen Technologien zu beziehen, werden vermutlich Themen wie WPF, Silverlight, ASP.NET MVC etc. sicherlich häufig Inhalt meiner Post sein.

Neben dieser Themenbasis möchte ich definitiv auch Themen der Software Architektur und des Software Designs für die unterschiedlichen Technologien behandeln. Alt bekannte Themen wie MVC, Dependency Injection, etc werden sollen ebenso behandelt werden als auch neuere Themen. Grob gesagt werde ich hier alles posten, was für mich Berechtigung hat und interessant ist.

In diesem Sinne.

  • Recent Posts
    • HowTo: Eigene Regeln für Microsoft StyleCop erstellen
    • HowTo: Microsoft StyleCop Integration mit Visual Studio und MSBuild
    • HowTo: Eigene Templates für Visual Studio 2008 erstellen, Teil 2
    • HowTo: Eigene Templates für Visual Studio 2008 erstellen, Teil 1
    • Performance des Cassini Webservers in Kombination mit Firefox
  • Archives
    • June 2010 (2)
    • May 2010 (3)
    • March 2010 (3)
    • February 2010 (4)
    • January 2010 (3)
    • December 2009 (1)
    • November 2009 (9)
  • Tags
    .net AOP ASP.NET ASP.NET MVC blend Bootmanager C# ci Codequality Configuration Continuous Integration css Cursor DateTime DDD Deployment dynamisch emit Exrpession Extensibility Fluent HowTo Microsoft MVVM Pattern PostSharp Reflection Repository ruby silverlight Software Design StyleCop System teamcity Templates VHD Virtual Images Visual Studio Vorlagen web Windows 7 Windows Mobile WinForms WPF XAML
  • About

    Jürgen Oberngruber is a project manager and software architect living in Wels, Austria and currently working at ecomplexx Austria, Wels. During his study at the University of Applied Sciences in Hagenberg, Austria he gained a deep knowledge in the field of software engineering using a lot of different programming languages. Since a few years he's focusing on the Microsofts .NET platform including all relevant technologies. One of his passion is to explore, to test and to evaluate new technologies and programming languages (mostly in the field of Microsofts .NET platform). Checkout more information on www.juergenoberngruber.at

  • Meta
    • Log in
    • Entries RSS
    • Comments RSS
    • WordPress.org
  • Archives
    • June 2010
    • May 2010
    • March 2010
    • February 2010
    • January 2010
    • December 2009
    • November 2009
  • Search






  • Home

© Copyright Jürgen Oberngruber's Blog. All rights reserved.
Designed by FTL WordPress Themes brought to you by DT Web Template

Back to Top