>

fredag 31 maj 2013

Trim funktionen ger inte samma resultat som tidigare

Microsoft ändrar tyvärr på vissa saker i nya versioner av frameworket, funktionerna Trim, TrimStart och TrimEnd är en sådan. I framework 3.5 SP1 och tidigare använde funktionerna en lista med vilka tecken som skulle tas bort medans i framework 4 och 4.5 används internt funktionen IsWhiteSpace från Char. Detta resulterar i att de senare versionen inte tar bort tecknen “zero width space” (200B) och “zero width no-break space” (FEFF). Båda är icke synliga men gör stor skillnad i en sträng jämförelse där alla tecken jämförs.

Lika så i framework 3.5 SP1 och tidigare fanns tre tecken som inte togs bort: “mongolian vowel separartor” (180E), “narrow no-break space” (202F) och “medium mathematical space” (205F).

Får du problem med detta i uppgraderad kod föreslår jag lösningen där du först kör trim följt av en borttagning av de två tidigare exkluderade tecknen:

myString.Trim().Trim(new[] { '\uFEFF', '\u200B' });

källa: http://msdn.microsoft.com/en-us/library/t97s7bs3.aspx

onsdag 8 maj 2013

Referenced assembly does not have a strong name

Vill du signera ditt program har du ett beroende på att alla referenser behöver vara signerade eller så kallade strong named. Visual Studio kommer att tala om vilka assemblys som inte är signerade genom ett meddelande i Error List: ’Referenced assembly x does not have a strong name’.
För dina egna assemblys är detta inget problem då du bara kan signera dem och kompilera om dem. För tredjeparts komponenter blir det lite svårare men det finns några kommandon du kan köra för att skapa nya signerade dll:er

Börja med att öppna ’Visual Studio Commando Promt’/’Developer Commando Promt’. Navigera sedan till platsen för dll filen (du måste inte men du slipper skriva hela sökvägen). Du behöver nu göra om filen till ett okompilerat format med hjälp av nedanstående kommando, förutsatt att filen heter filen.dll. Kommandot kommer att skapa flera filer, när du är klar med sista kommandot kan du ta bort alla dessa filer.
ildasm /all /output=filen.il filen.dll

Sedan behöver du en nyckel för själva signeringen, har du inte kan du skapa en med kommandot:
sn –k keyName.snk

Du avslutar med att skapa den signerade dll:en. Notera att nu används ilasm inte ildasm.
ilasm /dll filen.il /key=keyName.snk /output=filen.signed.dll

Du kan nu ta bort referensen till den gamla filen och lägga till den nya istället i ditt projekt.

måndag 6 maj 2013

Hitta dina Binding-fel

Alla som jobbar med WPF har många gånger skapat en binding som vid testkörning inte fungerar och sedan tillbringat en del tid med att gissa och testa olika varianter. När en binding är fel blir det nämligen inget exception fel som kastas som det blir med andra fel utan det bara sväljs av runtimen. Dock har felet fångats och ett meddelande har faktiskt skrivits. Om du tittar i Output fönstret nästa gång du får en binding som inte fungerar som den skall ser du att du har en text om vad som inte fungerar. Dock kommer det ut så mycket annat i detta fönster att det kan vara svårt att hitta just ditt fel. Önskvärt skulle ju vara att få detta på ett annat ställe eller ännu bättre få exception. Här har du två olika alternativ.

Få binding fel i en fil

I (System.Diagnostics.PresentationTraceSources) DataBindingSource.Listeners kollektionen kan du lägga in lyssnare som skall fånga upp databinding fel, redan nu ligger output-fönstret I denna kollektion. Men du kan också själv lägga till flera. TextWriterTraceListner är en av dem som du kan använda, då fångar du felen i en fil. I filen App.xaml.cs lägger du till följande funktion till klassen App.

static App()
{
  var bindingErrorLoggingFile = "din sökväg till en fil";
  System.Diagnostics.PresentationTraceSources.
   DataBindingSource.Listeners.Add(new
  TextWriterTraceListener(bindingErrorLoggingFile));
}

Få ett exception-fel

Om du istället vill få ett exception fel behöver du skriva en klass som går att lägga till listeners kollektionen och som när den får ett fel kastar exception. För att göra detta skapa en klass som ärver från TraceListner och som implementerar de funktioner som den basklassen kräver.

public class ExceptionTraceListner: TraceListener
{
  public override void Write(string message) {}
  public override void WriteLine(string message) {}
}

Sedan behövs en Exception klass som du kan kasta, visst duger någon av dem som finns men om du skriver en egen kan du utveckla den som du vill samt välja att fånga bara den i en try-catch.

public class BindingErrorException: Exception 
{
  public BindingErrorException(string message):base(message){}
}

Du kan nu välja att kasta detta exception när du fångar upp ett felmeddelande med din nya TraceListner (ExceptionTraceListner klassen). Jag rekommenderar även att lägga in ett DEBUG direktiv så att du inte får med detta beteende ut i produktion.

public override void WriteLine(string message)
{
#if DEBUG
  throw new BindingErrorException(message);
#endif
}

Du behöver nu bara lägga till lyssnaren till DataBindingSources Listners kollektion. I filen App.xaml.cs (App.xaml.vb) lägg till följande funktion till klassen App

static App()
{
  System.Diagnostics.PresentationTraceSources.
    DataBindingSource.Listeners.Add(new ExceptionTraceListner());
}


tisdag 23 april 2013

Ikoner för ditt projekt

I de flesta projekt behövs någon typ av ikoner, oftast till någon knapp eller liknande. En av Visual Studios bäst bevarade hemlighet är att det följer med ett helt bibliotek med ikoner att använda i ditt projekt när du installerar Visual Studio. I alla fall innan Visual Studio 2012. Nu återfinns denna resurs online för nedladdning och innehåller då det gamla biblioteket samt ett nytt med Modern UI anpassade ikoner.

base_bubble Flag 0356_NewComment_32
(källa: Visual Studio Image Library)

Vill du använda biblioteket som medföljer installationen hittar du det zippat under installationsmappen för Visual Studio, sökvägen kan vara: C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\VS2008ImageLibrary\1033\VS2008ImageLibrary.zip
Men det beror ju såklart på vilken version och vart du installerat det.

Bubble_16xLG FlagThread-NotFlagged,NotHot,NotSelected_10391_16x Addacomment_8818
(källa: Visual Studio Image Library)

Vill du ha det nya biblioteket laddar du ner det här:
Visual Studio Image Library




onsdag 3 april 2013

Problem med att serialisera properties

Om du har serialiserat en klass med hjälp av en IFormatter så kommer du få problem med att göra om automatic properties till vanliga properties. Dessa kommer inte att serialiseras tillbaka automatiskt.
Det finns flera sätt att serialisera och det kanske kan vara svårt att veta att det är just en IFormatter som har används men en ledtråd är att du var tvungen att sätta attributet Serializable på klassen för att få det att fungera. Eller om du själv styrt över koden som serialiserat så har du använt en BinaryFormatter eller någon annan klass som implementerar IFormatter.

Själva problemet är alltså att ha en klass liknande:

[Serializable]
public class Person
{
    public string Name { get; set; }
}

Som du sedan försöker skriva för att kunna lägga in mer logik, tex så som validering eller PropertyChange event:
[Serializable]
public class Person

{
    private string _name;

    public string Name

    {
        get { return _name; }
        set { _name = value; }
    }
}

Vid deserialisering av data som serialiserats från den första klassen och sedan skall deserialiseras tillbaka med den uppdaterade klassen, kommer inte att lyckas fylla Name propertien. Varför?
Jo, En IFormatter serialiserar ALLA fält i en klass dvs. alla klassvariabler. I den första klassen har du ju egentligen inga sådana, bara properties. Så kallade automatic properties. Finessen med automatic propeties är att slippa skriva den enkla koden med ett fält för att sedan bara mappa ut den via en property. .Net behöver dock fortfarande en variabel att lagra värdet i. Så kompilatorn kommer generera fram fälten. Eftersom sedan IFormatter:n går fält för fält och matchar namn så kommer din omgjorda property inte att fungerar trots samma namn eftersom du själv måste tillverka ett fält för förvaringen av värdet.
Den lätta lösningen på detta skulle då kunna vara att använda samma namn som .NETs automatiska framgenererade namn. Det namnet är "<Name>k__BackingField" Vilket tyvärr innehåller för oss otillåtna tecken för variabelnamn. Det du får göra för att få det att fungera är att anpassa serialiseringen, dvs du skall bestämma vad som skall spara undan och vad som läses upp samt i vilka fält. För att göra detta skall du lägga på interfacet ISerializing på klassen och implementera funktionen, GetObject, samt skriva en konstruktor med samma inparametrar. Det du inte får glömma bort är att om du tidigare inte hade någon konstruktor kommer du "förstöra" den automatgenerade default konstruktorn och får nu skriva den själv.

using System.Runtime.Serialization;

    [Serializable]
    public class Person: ISerializable
    {
        private string _name;

        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        public Person() { }

        public Person(SerializationInfo info, StreamingContext context)
        {
            //deserialization code here
        }

        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            //serialization code here
        }
    }

IFormatter:n kommer nu inte att samla in data från alla fält utan kommer istället att anropa funktionen GetObject. Vid desarialisering kommer den nya konstruktorn att användas. I SerializationInfo objektet tar ut vad som sparats från en lista där varje värde har en så kallad nyckel. Vi behöver också ta hänsyn till om det sparade data kommer från den första versionen av klassen eller den andra. Finns inte värdet vi vill ha från SerilaiszationInfo objektet kastas ett exception så för att hantera båda versionerna av klassen så får vi göra “try-try again”. För att optimera lite så skall du se till att lägga uppslaget av den nyckeln som är mest troligt att finnas först eftersom ett exception alltid slöar ner koden. Så för vår nya klass skulle det se ut som följer:
 
public Person(SerializationInfo info, StreamingContext context)
{
    try
    {
        _name = info.GetString("name");
    }
    catch (SerializationException)
    {
        _name = info.GetString("<Name>k__BackingField");
    }
}

public void GetObjectData(SerializationInfo info, StreamingContext context)
{
    info.AddValue("name", _name);
}

Glöm inte heller att ta med eventuellt andra fält som du vill ha serialiserade. Är det basklasser inblandade måste dessa fält också skrivas med, de hämtas inte automatiskt. Om det var en automatic property i en basklass som blev omskriven så har det genererade fältet ett lite annorlunda namn i formen av “BasklassNamn+<PropertyNamn>k_BakingField”. Dvs om vi hade haft en automatic property med namnet Id i basklassen PersonBase hade den hettat “PersonBase+<Id>k_BakingField”.
Skulle det vara några problem med att få till fältnamnen rätt eller att du bara funderar på vad som egentligen finns i SerializationInfo objektet så kan du alltid debugga. Det som är intressant hittar du under ‘Non-Public members’ delen och MemberNames och MemberValues.

serializationProperties


onsdag 6 mars 2013

Varför kan jag inte debugga in i properties?

När du försöker stega in (step into) i properties kan du få följande meddelande:

debugProperty

Eller så hoppar bara Visual Studio helt enkelt över raden utan att gå in och visa dig koden inuti propertyn om du tidigare svarat ‘No’ på ovanstående dialogruta.
För att få Visual Studio att stega in i kan du sätta en breakpoint inne i propertyn eller om du vill stega in i flera kan du ställa om en inställning. Gå in på menyvalet Tools och välj Options. Välj sedan i vänstra listan Debugging och kryssa ur “Step over properties and operators (Managed only)”.

debugPropertyOptions