Montag, 9. November 2009

NUnit im STA - Modus

Neulich hatte ich die Anforderung, dass ein Programm Daten der Zwischenablage nutzen und modifizieren sollte. Ich habe (natürlich) angefangen einen Test zu schreiben. Leider war dies mit dem Unit-Test-Tool meiner Wahl (NUnit) nicht möglich. Der Fehler:
System.Threading.ThreadStateException: Der aktuelle Thread muss in den STA-Modus (Singlethreadapartment) gesetzt werden, bevor OLE-Aufrufe durchgeführt werden können.
Der Fehler tritt im Übrigen auch auf, wenn man versucht UserControls zu testen und sollte für WPF und WinForms gleich sein.
Die Lösung des Problems: NUnit-Tests im Single-Threaded Apartment (STA) statt in der Vorgabe (Multithreaded Apartment (MTA)) laufen lassen. Das Vorgehen: Man erstellt eine einfache app.config mit folgendem Inhalt:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <configSections>
        <sectionGroup name="NUnit">
            <section name="TestRunner" type="System.Configuration.NameValueSectionHandler"/>
        </sectionGroup>
    </configSections>
    <NUnit>
        <TestRunner>
            <!-- Valid values are STA,MTA. Others ignored. -->
            <add key="ApartmentState" value="STA" />
            <!-- See ThreadPriority enum for other valid values -->
            <add key="ThreadPriority" value="Normal" />
        </TestRunner>
    </NUnit>
</configuration>
Damit läuft NUnit im STA, und die es kann auf die Zwischenablage zugegriffen werden.
Die Lösung habe ich von hier.

Dienstag, 3. November 2009

ReportPropertyChanged vs. OnPropertyChanged in Entities

Gerade etwas länger mit folgender Fehlermeldung gerungen: System.ArgumentException: Die Eigenschaft 'Foo' weist keine gültige Entitätszuordnung für das Entitätsobjekt auf. Weitere Informationen finden Sie in der Dokumentation zu Entity Framework.
Der Fehler trat immer auf,  wenn ich Foo einen wert zuweisen wollte.

Ursache des Fehlers war, das ich meinem eigenen Post über Zusammengesetzte Properties nicht gefolgt war, sondern statt dessen auf diversen Settern noch ReportPropertyChanged("Foo") programmiert hatte.
ReportPropertyChanged ist abernicht das gleiche wie OnPropertyChanged. ReportPropertyChanged scheint noch mehr zu tun und funktioniert daher nur auf Properties aus Enities mit Entitätszuordnung.
Nachdem ich den Fehler gefunden hatte machte die Fehlermeldung auf einmal auch viel mehr sinn.
Die Lösung des Problems war also einfach nicht ReportPropertyChanged zu benutzen, sondern OnPropertyChanged.
(Und bei dieser gelegenheit noch einmal mein Vorgehen in diesem Fall zu überdenken, und mich etwas mehr an meine eigenen Posts halten.)

Sonntag, 25. Oktober 2009

BeforeSaveEvent in EntityFramework

Neulich habe ich nach so etwas wie BeforeSave gesucht für meine Entities. Folgendes war meine Lösung (In einer partial class):
public partial class MyEntities
{
    partial void OnContextCreated()
    {
        this.SavingChanges += HandleSavingChanges;
    }

    private static void HandleSavingChanges(object sender, EventArgs e)
    {
        MyEntities context = (MyEntities) sender;
        //code....
    }
}
In meiner partial-Klasse kann ich keinen Konstruktor erstellen. (Jedenfalls keinen, der schon an anderer Stelle definiert wurde...) Daher verwende ich OnContextCreated. Dies ist eine Methode, die vom erzeugten Code ausgeführt wird. Hier hänge ich einfach einen Eventhandler an SavingChanges.

Freitag, 2. Oktober 2009

"Zusammengesetzte" Properties in EntityFramework

Ich habe für EF-Objekte gerne mal "eigene" Properties in partial classes. Diese sind dann immer vonmindestens einer, gerne auch mehreren Properties der Entity abhängig.
Damit dann auch die richtigen PropertyChanged & -Changing Events erzeugt werden habe ich gerade folgendes gefunden: Alle Properties der EF-entities implementieren eine partial OnFooChanging bzw. OnFooChanged Methode, an die man sich in der partial class einfach "anhängen" kann. Etwa so:
public string MyTitle
{
   get
   {
      return this.Title;
   }
   set
   {
      this.Title = value;
   }
}

partial void OnTitleChanging(string unused)
{
   OnPropertyChanging("MyTitle");
}

partial void OnTitleChanged()
{
   OnPropertyChanged("MyTitle");
}
BTW: "Navigierbare Properties" (damit meine ich Properties in EF-Objekten, die auf andere EF-Objekte verweisen) lösen keinen PropertiyChanged Events aus. Hier findet sich ein kleiner Workaround dazu.

Mittwoch, 30. September 2009

ValidationErrors in xaml Controls

Validation auf xaml-controls findet man "überall" im Netz.. z.B. kann man das so machen:
<Binding Path="Person" 
         Mode="TwoWay">
 <Binding.ValidationRules>
  <ui:PersonValidationRule />
 </Binding.ValidationRules>
</Binding>
Nun habe ich aber keine möglichkeit auf einem Window sowas zu machen wie Window.ChildsHaveErrors(). Aus dieser Lage hilft das Folgende:
<Window [blah...]
  Validation.Error="ValidationError">

<Binding Path="Person" 
         Mode="TwoWay"
   NotifyOnValidationError="True">
    <Binding.ValidationRules>
        <ui:PersonValidationRule />
    </Binding.ValidationRules>
</Binding>
und im Code-Behind:
private int errorsInForm;
private void ValidationError(object sender, ValidationErrorEventArgs e)
{
   switch (e.Action)
   {
      case ValidationErrorEventAction.Added:
         this.errorsInForm++;
         break;
      case ValidationErrorEventAction.Removed:
         this.errorsInForm--;
         break;
   }
}
private bool WindowHasErrors
{
   get
   {
      return this.errorsInForm>0;
   }
}
  • Auf der Window-Deklaration im xaml den Validation.Error-Event setzten (Validation.Error ist übrigens eine "attached dependency property", sollte also auf fast allen Elementen funktionieren...)
  • Auf der Binding-Deklaration NotifyOnValidationError auf true setzen, damit wird der Fehler aus dem eigentlichen Element auf dem dieser entsteht hinaus "gebubbelt" (z.B. bis zum Window...)
  • Im Code-Behind, im ValidationError-EventHandler werden einfach die entstandenen Fehler gezählt (ValidationErrorEventAction.Removed ist ein "ehemaliger Fehler"...)
  • In WindowHasError wird dann nur noch geprüft, ob die aktuelle Zahl der Fehler größer ist als 0.
Meinungen dazu ?

VS2008 benötigt CLR-Typen von MS SQL 2008

Ich habe leztens alles deinstalliert, das "*SQL*2008*" im Namen hatte, und meinen SQL-Server 2005 DeveloperED wieder installiert.
Ein paar Tage später musste ich feststellen, dass DB-Verbindungen mit meinem VS2008 nicht mehr möglich waren.Der Fehler war: "Could not load file or assembly: Microsoft.SqlServer.Management.Sdk.Sfc in Visual Studio 2008 Server Explorer"


Lange Rede, kurzer Sinn: VS2008 benötigt für die DB-Verbindungen:
  • Microsoft SQL Server 2008 Management Objects (SMO) und
  • Microsoft SQL Server System CLR Types 
Beides gibts zum Download bei Microsoft.
Nachdem ich mir die Finger wund gesucht hatte um der Lösung auf die Schliche zu kommen habe ich auch diesen Post gefunden, der das gleiche Problem beschreibt.

Verschiedene Implementationen des selben Interfaces mit Unity "injecten"

PersonRepository und VorgangRepository sind zwei Implementationen eines Interfaces: IRepoistory
Um nun meinen DBAdapter, der (natürlich IDBAdapter implementiert und) beide IRepositories als parameter (natürlich in der richtigen Reihenfolge...) benötigt (Ob das eine tolle Idee war sei mal dahin gestellt.) instanziieren zu können, habe ich folgendes gemacht:
IUnityContainer container = UnityContainerProvider.UnityContainer;
container.RegisterType<IDbAdapter, DbAdapter>();
container.RegisterType<IRepository, PersonRepository>("Person");
container.RegisterType<IRepository, VorgangRepository>("Vorgang");
container.Configure<InjectedMembers>().ConfigureInjectionFor<DbAdapter>(
 new InjectionConstructor(
  new ResolvedParameter<IRepository>("Person"),
  new ResolvedParameter<IRepository>("Vorgang")
  ));
  • Configure() - Ich will die Member der Injection steuern...
  • ConfigureInjectionFor() - auf den DBAdapter instanzen
  • new InjectionConstructor() - der Konstruktor soll gesteuert werden
  • new ResolvedParameter("Person") - IRepository soll mit dem Key "Person" aufgelöst werden.
Etwas störend finde ich die Notwendigkeit dies über zwei Strings zu steuern, also wenn jemand eine bessere Idee hat.. Immer her damit.

Edit: Etwas besser ist natürlich das IRepository noch zwei mal abzuleiten (wenn dies möglich ist...) einmal als
IPersonenRepository und einmal als IVorgangsRepository.

Dienstag, 15. September 2009

Da in meiner Firma mit Outlook gearbeitet wird, habe ich auch meine Aufgaben in Outlook organisiert.
Nun habe ich bisher immer das Feature „Eine Aufgabe aus dieser Mail erstellen” vermisst.
Folgendes VBA-Makro habe ich nun dafür erstellt:
Option Explicit
Public Sub Mail2Task()
Dim olTask As Outlook.TaskItem
Dim olMail As MailItem
Dim olIns As Inspector
Dim olExp As Explorer
Set olTask = Application.CreateItem(olTaskItem)
Set olExp = Application.ActiveExplorer

If olExp.Selection.Count <> 1 Then
MsgBox "Funktioniert nur mit genau einer E-Mail"
Exit Sub
End If

If Not TypeOf olExp.Selection.Item(1) Is Outlook.MailItem Then
MsgBox "Funktioniert nur mit E-Mails !"
Exit Sub
End If

Set olMail = olExp.Selection.Item(1)
With olTask
.Subject = olMail.Subject
.Body = olMail.Subject & vbCrLf & vbCrLf
.StartDate = Now
.DueDate = DateAdd("d", 7, Date)
.ReminderSet = False
.Status = olTaskInProgress
End With
olTask.Attachments.Add olMail
Set olIns = olTask.GetInspector
olIns.Display ("True")
End Sub

Das Makro erstellt aus einer selektierten eMail eine folgende Aufgabe:
  • Betreff der Aufgabe entspricht dem Betreff der eMail
  • Text der Aufgabe enthält noch einmal den Betreff der eMail sowie die eigentliche Mail als Anhang.
  • Start der Aufgabe ist sofort
  • Termin (Ende) ist in 7 Tagen
  • Status ist „In Progress”
Anschließend wird die Aufgabe zur Bearbeitung (Termin-anpassung, Wichtigkeit, etc.) geöffnet.

Samstag, 5. September 2009

Anfängergeschwindigkeit

Ich habe über mein neues neo-layout berichtet. Nun habe gerade den schnell-schreiben-speedtest gefunden.

15 WörterZum Vergleich : Mit Qwerz waren es etwa 29 Wörter/min bei ca. 180 Zeichen — auch kein Blumentopf mit zu gewinnen...

Freitag, 4. September 2009

SVDRP mit C#

Ich habe diese Woche mein erstes Google-Code Projekt gestartet.
Das Ziel von SvdrpNet ist mehrfach:
  1. Ich wollte eine kleine GUI, um SVDRP-Anfragen zu versenden und die Ergebnisse darzustellen.
  2. Ich wollte eine GUI um die Timer meiner beiden modifizierten M740AV zu betrachten und (und dies ist der Punkt, den mir bisher keine andere Software bieten konnte:) um die Timer von einem Gerät auf ein anderes kopieren.
Schließlich habe ich mich für vier kleine Teilprojekte entschieden: Zusätzlich zu den oberen Zweien noch
  1. Ein kleines Kommandozeilentool, analog zu i.
  2. Eine „Wrapper-Lib“ für die SVDRP-Zugriffe.
Die Minimalfunktionen von iv., iii. und i. sind bereits fertig gestellt.
Das Projekt findet sich unter : http://code.google.com/p/svdrpnet/
Eine entsprechende Diskussionsgruppe habe ich unter http://groups.google.com/group/svdrpnet eingerichtet.

Neo-layout

Ich habe es getan.. In der letzten Woche hat meinen neuer Rechner endlich seinen Dienst aufgenommen. Dabei habe ich gleich alles (Konsole und X) mit dem neo2 Tastatur-Layout versehen.
Einen Tag später war ich so ziemlich am Ende und wollte das Layout schon wieder wechseln...
Statt dessen habe ich meinen Dymo bemüht und mir eine passende neo2 Tastatur geschaffen.




Aktuell macht das ganze einen eher „miesen“ Eindruck. Aber das Tippen macht wieder Spaß. Vielleicht schaffe ich es ja auch das Ganze etwas zu verbessern.. (Also weniger mit dem Dymo arbeiten, dafür mehr mit einem Farblaser...)

Freitag, 21. August 2009

Erstellen einer IList<T> ohne T

Mein Problem gerade: Erstellen einer IList<T> ohne T -- nur typeof(T) ist bekannt.
Lösung:
private static IList GetIListOfGenericType(Type t)
{
Type genericList = typeof (List<>).MakeGenericType(new[] {t});
return (IList)Activator.CreateInstance(genericList);
}

Meine Quelle: Creating generic type object dynamically using reflection

Mittwoch, 19. August 2009

Poll über den verwendeten TestRunner

Habe gerade bei Jamie Cansdale einen Poll über den verwendeten TestRunner gefunden (eigentlich von Fabio Maulo). Nach meinem Vote war ich doch überrascht über die Verteilung: aktuell knapp 40% - ReSharper user dabei....

Dienstag, 18. August 2009

Formatierungen suchen und ersetzen mit Word

Heute mal keinen Code: Ich habe gerade etwas gelernt...
Mit MS Word nach Formatierungen suchen und diese ersetzen:

In meinem Beispiel: Text in Schrifgröße 10 ersetzen durch Schriftgröße 8

In Word "Suchen und Ersetzten", die Erweiterte Ansicht aufklappen...
In "Sonderformat" Suchen nach "beliebiges Zeichen" und Ersetzen durch "Suchen nach Text" (Das ist wohl die Computer-gestützte Übersetzung von " gefundener Text"...)
Dann noch die Formatierungen einstellen - auf beiden Feldern - Fertig:


Ok, also doch noch Code dazu:
Selection.Find.ClearFormatting
Selection.Find.Font.Size = 10
Selection.Find.Replacement.ClearFormatting
Selection.Find.Replacement.Font.Size = 8
With Selection.Find
.Text = "^?"
.Replacement.Text = "^&"
.Forward = True
.Wrap = wdFindContinue
.Format = True
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
Selection.Find.Execute Replace:=wdReplaceAll

Freitag, 7. August 2009

SyntaxHighlighter

Endlich habe ich es geschafft meinen Sourcecode hier im Blog etwas besser aussehen zu lassen.
Hier habe ich eine erste Idee bekommen.
Allerdings wird dort die Version 1.5.1 des SyntaxHighlighters beschrieben. Auf http://alexgorbatchev.com/wiki/SyntaxHighlighter gibts die aktuellste Version.
Wenn man (wie ich ich) erst nach dem Ersten Post vorgeht und anschließend versucht die aktuellste Version zu verwenden geht erst einmal nichts mehr. Auf der Upgrade Seite steht dann dazu auch was..

Jedenfalls ist der Quellcode nun gut zu lesen..

Wpf ContextMenuNotEnabledBug

Gestern habe ich Probleme gehabt mit einem wpf Bug (?) – In einer Maske (mit wenig Elementen) waren die Einträge des Kontext-Menüs (dessen Items an Commands gebunden waren…) nicht aktiv. Erst Nach einem Klick auf ein Element (aber nicht auch jedes…) wurden die Einträge aktiv.. In meinen Augen ein absolut wirres Verhalten.

Nach langem Suchen habe ich eine Erklärung und ein paar Workarounds gefunden: Bei MSDN und in einem anderen Blog.

Persönlich bin eher für fool-proof (Wenn ich in einem Jahr oder in zweien wieder an den code muss denke ich nicht daran die MenuItems zu “Workarounden”….) und minimal-Invasiv.

Also so wenig wie möglich meinen Code “umbiegen”, nur damit das Kontext-Menü funktioniert. Meine Lösung ist daher:

Im code-behind des wpf-Window:

internal BenutzerVerwaltung()
{
InitializeComponent();
ContextMenuNotEnabledBugHelper.Workaround(this);
}

Dazu der ContextMenuNotEnabledBugHelper:

/// <summary>
/// Helper to Work around the ContextMenu[Not]EnabledBug of wpf.
/// see http://social.msdn.microsoft.com/forums/en-US/wpf/thread/7bd75a7c-eab4-4f3a-967b-94a9534a7455/
/// and http://www.wiredprairie.us/journal/2007/04/commandtarget_menuitem_context.html
/// regarding this...
/// </summary>
public static class ContextMenuNotEnabledBugHelper
{
public static void Workaround(Window window)
{
foreach (object child in RecurseChildren(window))
{
if (HasContextMenu(child))
{
Workaround(window, GetContextMenu(child));
}
}
}

private static bool HasContextMenu(object o)
{
return GetContextMenu(o) != null;
}

private static ContextMenu GetContextMenu(object o)
{
if (o is DependencyObject)
{
return (ContextMenu) ((DependencyObject) o).GetValue(ContextMenuService.ContextMenuProperty);
}
return null;
}

private static void Workaround(Window window, ContextMenu contextMenu)
{
foreach (object o in LogicalTreeHelper.GetChildren(contextMenu))
{
if (o is MenuItem)
{
MenuItem menuItem = (MenuItem) o;
IInputElement oldTarget = menuItem.CommandTarget;
menuItem.CommandTarget = window; // <-- this is the Workaround !
if (oldTarget != null)
{
menuItem.CommandTarget = oldTarget;
}
}
}
}

private static IEnumerable RecurseChildren(DependencyObject current)
{
foreach (object child in LogicalTreeHelper.GetChildren(current))
{
yield return child;
if (child is DependencyObject)
{
foreach (object chlidsChild in RecurseChildren((DependencyObject) child))
{
yield return chlidsChild;
}
}
}
}
}

Also: Meinungen dazu ??

Dienstag, 4. August 2009

Keyboard shortcuts could not be assigned…

Da ich (mal wieder) folgenden Fehler vom ReSharper hatte:

Some keyboard shortcuts could not be assigned because the current keyboard scheme is read only.
Change the keyboard scheme and try again.
clip_image002[5]

Nunja. Das hatte ich schon einmal -- gelöst… Wie nur ??

Das Problem ist, dass die Keyboard-Schemes unter

%ProgramFiles%\Microsoft Visual Studio 9\Common7\IDE\<xxxx>.vsk

liegen. – Und damit standardmäßig (für mich) Schreibgeschützt. Also meinen freundlichen Admin nach schreibrechten auf der Datei gefragt… und “Geht”.

Falls jemand weiß, ob sich die keybindings auch im user-profile speichern lassen, bin ich für jeden Tipp dankbar.

Mittwoch, 29. Juli 2009

Markup-Extension in WPF selber erstellen

Lange nichts geschrieben.. Heute eine Kleinigkeit zu wpf:

Markup-Extension in WPF selber erstellen:

Bsp: Ein PersonenAnzeigeControl erwartet ein Objekt vom Typ Person – auch in xaml – hier können aber nur Texte übergeben werden. (Nachname sei eindeutig….) :

Eine Klasse:

public class PersonExtension : MarkupExtension
{
public string Nachname { get; set;}

public override object ProvideValue(IServiceProvider serviceProvider)
{
//Target herausfinden…
IProvideValueTarget provideValueTarget = (IProvideValueTarget) serviceProvider.GetService(typeof (IProvideValueTarget));
DependencyProperty dependencyProperty = provideValueTarget.TargetProperty as DependencyProperty;
if (dependencyProperty == null)
{
return null;
}

//dependencyProperty ist nun das Target, dass gesetzt werden soll (hier „Person“) –
//wichtig, damit Personen nur den Richtigen Properties zugewiesen werden
return dependencyProperty.PropertyType!=typeof(Person) ? null : Person.FindByNachname(this.Nachname);
}
}

Kann wie folgt im xaml verwendet werden


<xaml>
<personenanzeigecontrol person = " { my:Person nachname=Müller } " />
</xaml>

Hinweise:
  • Die Klasse PersonExtension kann in xaml als my: PersonExtension oder als my:Person verwendet werden. Funktioniert nur, wenn es keine richtige Klasse my:Person gibt.
  • Wenn es einen Konstruktor gäbe
    • public PersonExtension(string nachname)…
    • Könnte die Extension in xaml (auch) wie folgt verwendet werden: Person=“{my:Person Müller}“

Dienstag, 2. Juni 2009

Leere VisualStudio 2008 Solution im Explorer

von Stefan bekam ich mal den Tipp wie man den Explorer dazu bringt, dass man eine leere VS2008 Solution anlegen kann (Datei - Neu - Microsoft Visual Studio 2008 Solution).
Hier gibt's das nochmal zum Nachlesen.

Da ich schon immer mal ein msi-Installer erstellen wollte, habe ich mir dies' als hinreichend kleines Ziel genommen.

Das Ergebnis kann hier begutachtet werden.

Donnerstag, 28. Mai 2009

Mein erstes Blog

Das ist es also.. Mein erstes Blog.

Viel Zeit ist immer wenig - bei mir zumindest. Mal sehen was hier so landet.

Ich war bei Stefan Lieser und Ralf Westphal im Clean Code Developer Camp und versuche mich daher als Clean Code Deveoper. Dazu gehört auch „Erfahrungen weitergeben“ (Grüner Grad), was ich hiermit beginnen möchte.

Ich zumindest bin gespannt…