(-: Fibergeek's Blog :-)
Inhoud blog
  • Python 2 of 3
  • IDA-python: GetStructureIdAt
  • Geslaagd in Microsoft's 70-680 examen
  • minibio: kort vervolg
  • miniboa: een voorbeeld
    Zoeken in blog

    Categorieën
  • Code: C/C++ (2)
  • Code: Powershell (1)
  • Code: Python (8)
  • Code: WPF (2)
  • Programmeren (5)
  • 07-03-2012
    Klik hier om een link te hebben waarmee u dit artikel later terug kunt lezen.minibio: kort vervolg

    Ok, een korte vervolgblog. Als je de "on_connect" en "on_disconnect" niet override van "miniboa", dan houdt hij zelf de clients bij in een dictionary "clients" en via een functie "client_list". Ik heb mijn servertje dus aangepast om deze lijsten zelf aan te beheren. Ik vind deze versie van de code beter.

    from miniboa import TelnetServer

    class Server(TelnetServer):
     def __init__(self):
      TelnetServer.__init__(self)
     
     def shutdown(self):
      self.server_socket.close()
     
     def run(self):
      while True:
       self.poll()
     
     def poll(self):
      TelnetServer.poll(self)
      for client in self.client_list():
       if client.active and client.cmd_ready:
        self.on_command(client)
     
     def broadcast(self, msg):
      for client in self.client_list():
       client.send(msg + "n")
     
     def register_client(self, client):
      self.client_list().append(client)
     
     def unregister_client(self, client):
      self.client_list().remove(client)

    def RunServer():
     server = CreateServer()
     try:
      server.run()
     except KeyboardInterrupt:
      pass
     finally:
      server.shutdown()

    def CreateServer():
     server = Server()
     server.on_connect    = lambda client: my_on_connect   (server, client)
     server.on_disconnect = lambda client: my_on_disconnect(server, client)
     server.on_command    = lambda client: my_on_command   (server, client)
     return server

    def my_on_connect(server, client):
     server.register_client(client)

    def my_on_disconnect(server, client):
     server.unregister_client(client)

    def my_on_command(server, client):
     cmd = client.get_command()
     server.broadcast(msg=cmd)

    RunServer()

    07-03-2012, 00:00 Geschreven door Fibergeek  


    Categorie:Programmeren
    Tags:Python,miniboa,telnet
    Klik hier om een link te hebben waarmee u dit artikel later terug kunt lezen.miniboa: een voorbeeld

    Het onderstaande programma is mijn testprogramma gebasseerd op de voorbeeldprogramma's die met het "miniboa" framework worden meegeleverd. Ik ben niet helemaal tevreden maar dat ligt meer aan het "miniboa" framework zelf. Het liefst van al zou ik de CLIENTS-variable in de Server-class willen hangen, zodat één pythonprogramma meerdere servers zou kunnen draaien.

    from miniboa import TelnetServer

    CLIENTS = []

    class Server(TelnetServer):
     def __init__(self):
      TelnetServer.__init__(self)
     
     def shutdown(self):
      self.server_socket.close()
     
     def run(self):
      while True:
       self.poll()
     
     def poll(self):
      TelnetServer.poll(self)
      for client in CLIENTS:
       if client.active and client.cmd_ready:
        self.on_command(client)

    def RunServer():
     server = CreateServer()
     try:
      server.run()
     except KeyboardInterrupt:
      pass
     finally:
      server.shutdown()

    def CreateServer():
     server = Server()
     server.on_connect    = my_on_connect
     server.on_disconnect = my_on_disconnect
     server.on_command    = my_on_command
     return server

    def my_on_connect(client):
     CLIENTS.append(client)

    def my_on_disconnect(client):
     CLIENTS.remove(client)

    def my_on_command(client):
     cmd = client.get_command()
     broadcast_message(msg=cmd)

    def broadcast_message(msg):
     for client in CLIENTS:
      client.send(msg + "n")

    RunServer()

    07-03-2012, 00:00 Geschreven door Fibergeek  


    Categorie:Code: Python
    Tags:Python,miniboa,telnet
    23-02-2012
    Klik hier om een link te hebben waarmee u dit artikel later terug kunt lezen.IDA-python: OpStrsize

    Wederom code die voor zichzelf spreekt, een kleine functie om "imul X, size Y" te creëeren met IDA python:

    struc = "struct_Test"

    def ToStrucId(struc):
            if type(struc) == str:
                    struc = GetStrucIdByName(struc)
            assert struc != BADADDR
            return struc

    def OpStrsize(ea, struc):
      struc = ToStrucId(struc)
      assert GetMnem(ea) == "imul"
      assert GetOpType(ea, 2) == 5
      assert GetOperandValue(ea, 2) == GetStrucSize(struc)
      OpStroff(ea, 2, struc)
     
    OpStrsize(ScreenEA(), struc)

    23-02-2012, 10:28 Geschreven door Fibergeek  


    Categorie:Code: Python
    Tags:IDAPython
    18-02-2012
    Klik hier om een link te hebben waarmee u dit artikel later terug kunt lezen.een paar IDA-python snippets

    De volgende code zou voor zich moeten spreken, het is handig als je veel structures hebt gemaakt in een IDA-project, je kan dan de structures die je nodig hebt bovenaan plaatsen (volgorde aanpassen):...

    struc1 = "class_A"
    struc2 = "class_B"

    def ToStrucId(struc):
            if type(struc) == str:
                    struc = GetStrucIdByName(struc)
            assert struc != BADADDR
            return struc

    def MoveStrucToTop(struc):
      SetStrucIdx(ToStrucId(struc1), 0)
     
    def MoveStrucAfter(struc_to_move, struc_after):
      SetStrucIdx(ToStrucId(struc_to_move), GetStrucIdx(ToStrucId(struc_after)) + 1)

    def MoveStrucBefore(struc_to_move, struc_after):
      SetStrucIdx(ToStrucId(struc_to_move), GetStrucIdx(ToStrucId(struc_after)))

    MoveStrucToTop(struc1)
    MoveStrucAfter(struc2, struc1)

    18-02-2012, 00:00 Geschreven door Fibergeek  


    Categorie:Code: Python
    Tags:IDAPython,python
    30-01-2012
    Klik hier om een link te hebben waarmee u dit artikel later terug kunt lezen.getallen uit een lijst kiezen om een gegeven totaal te bekomen

    Dit weekend had ik voor mezelf een klein vraagstuk gemaakt.

    Na aankopen te doen bij de lokale Carrefour waren we (mijn vrouw en ik) 111,37€ armer, en hebben we daarbij 4 maaltijdcheques gebruikt van 7 euro. Nu vroeg ik me dus af; van al die bedragen op het kasticket, welke bedragen moet ik optellen om tot exact 28 euro (4 maal 7) te komen en hierbij enkel rekening houdend met voedingswaren.

    Aangezien mijn liefde voor Python, begon ik in die taal te programmeren en schreef ik het volgende programma:

    from copy import copy
    class NietGevonden(Exception):
      pass
    def probeer_te_vinden(totaal, lijst):
     if totaal == 0:
       return []
     if totaal < 0:
       raise NietGevonden
     for getal in lijst:
      try:
        mijn_lijst = copy(lijst)
       mijn_lijst.remove(getal)
       antwoord = probeer_te_vinden(totaal - getal, mijn_lijst)
       antwoord.append(getal)
       return antwoord
      except NietGevonden:
       pass
     raise NietGevonden

    Het programma werkte maar is verre van snel. Maar snel was die avond geen vereiste. De oplossing van het probleem was dat wel en daaraan voldoed dit programma.

    Dat het programma niet snel is, wist ik al tijdens de ontwikkeling ervan. Gebruik van een Exception als deel van de oplossing en de copy-operatie per recursie en per lus.

    De volgende reeks is traag:

    lijst = [2.79,5.38,11.99,3.79,2.69,0.89,1.49,3.99,1.03,1.29,0.88,1.69,2.49,2.69,
    2.79,3.25,1.49,1.65,1.49,3.64,3.28,4.24,1.99]
    probeer_te_vinden(20.50, lijst)


    Maar vandaag is een andere dag, en wil ik op zoek naar een snellere oplossing. Ik kan uiteraard proberen mijn algoritme te verbeteren maar nog beter is een bestaand algoritme opzoeken en analyseren. kwestie van het wiel niet opnieuw te moeten uitvinden.

    Via google en de zoekstring: <<<python try different numbers best combination>>> kwam ik op de volgende site: http://stackoverflow.com/questions/4632322/finding-all-possible-combinations-of-numbers-to-reach-a-given-sum

    Daar staat een Python-oplossing anders dan de mijne, maar het was niet die oplossing die mijn aandacht trok.

    def subset_sum_recursive(numbers,target,partial):
        s = sum(partial)
        #check if the partial sum is equals to target
        if s == target:
            print "sum(%s)=%s"%(partial,target)
        if s >= target:
            return # if we reach the number why bother to continue
        for i in range(len(numbers)):
            n = numbers[i]
            remaining = numbers[i+1:]
            subset_sum_recursive(remaining,target,partial + [n])

    def subset_sum(numbers,target):
        #we need an intermediate function to start the recursion.
        #the recursion start with an empty list as partial solution.
        subset_sum_recursive(numbers,target,list())

    Er staat ook een Haskell-oplossing die maar 1 regel bedraagd:

    filter ((==) 12345 . sum) $ subsequences [1,5,22,15,0,..]

    En dan de versie waarmee ik verder heb zitten spelen:

    filter ((==) 27 . sum) $ subsequences [1,5,22,16,11]

    Ik ken Haskell totaal niet (!), maar ik kwam al snel op de volgende site uit: http://tryhaskell.org/
    Daar kon ik die code even snel uitproberen zonder Haskell te moeten installeren.
    En inderdaad, het werkte:

    => [[5,22],[16,11]

    Ik weet graag hoe zaken werken, dus typte ik gewoon <<<subsequences [1,5,22,16,11]>>> in.
    Met als resultaat:

    => [[],[1],[5],[22],[16],[11],[1,5],[1,22],[1,16],[1,11],[5,22],[5,16],[5,11],[22,16],[22,11],[16,11],[1,5,22],[1,5,16],[1,5,11],[1,22,16],[1,22,11],[1,16,11],[5,22,16],[5,22,11],[5,16,11],[22,16,11],[1,5,22,16],[1,5,22,11],[1,5,16,11],[1,22,16,11],[5,22,16,11],[1,5,22,16,11]]


    Waarom heeft Python zoiets niet? Na ongeveer een half uur googlen en lezen in de Python docs, kwam ik de "powerset"-functie tegen.

    from itertools import chain, combinations
    def powerset(iterable):
        "powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
        s = list(iterable)
        return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))

    Die powerset-snippet doet dus hetzelfde als Haskell's subsequences-functie:

    >>>list(powerset([1,5,22,16,11]))
    [(), (1,), (5,), (22,), (16,), (11,), (1, 5), (1, 22), (1, 16), (1, 11), (5, 22), (5, 16), (5, 11), ...]

    NOTA: de powerset-functie vereist wel Python 2.6, want "chain.from_iterable" is pas dan toegevoegd:
    http://docs.python.org/whatsnew/2.6.html

    Op rosettacode (http://rosettacode.org/wiki/Power_set) staat nog een andere Python powerset-versie, welke compatibel is met oudere Python-versies:

    def powerset(s):     r = [[]]     for e in s:         r += [x+[e] for x in r]     return r

    NOTA: deze powerset-functie (powersetlist) geeft een iets andere volgorde van de list-elementen

    Nu we dit weten en nadat we powerset gedefinieerd hebben, kunnen we de Haskell-oneliner converteren naar zijn Python-versie:

    filter(lambda x: sum(x) == 27, powerset([1,5,22,16,11]))

    Dus in plaats van mijn eerste Python-versie, zie bovenaan, was de bovenstaande one-liner genoeg geweest! Er is wel een belangrijk verschil, mijn versie geeft maar één resultaat, het eerste dat hij tegenkomt. De powerset-variant levert alle mogelijke resultaten (en is sneller). Woops!


    NOTA: om de getallen wat leesbaarder te maken: map(lambda getal: format(getal, ".2f"), getallen)

    30-01-2012, 00:00 Geschreven door Fibergeek  


    Categorie:Code: Python
    Tags:Python
    12-12-2011
    Klik hier om een link te hebben waarmee u dit artikel later terug kunt lezen.Hoofdpagina SBS 2003 dynamisch maken met ASP

    Voor een van de klanten van het bedrijf waarvoor ik werk was het nodig om de startpagina van de Microsoft Small Business Server 2003 er een beetje anders te laten uitzien afhankelijk van het feit of de site al dan niet intern of extern werd geopend.

    In dit geval voldeed het om enkel een paar URL's naar een andere te site te laten verwijzen.

    De te nemen stappen, er vanuit gaande van een standaardinstallatie van SBS, zijn eenvoudig:

    1) Hernoem "default.htm" naar "default.asp", dit zorgt ervoor dat de pagina ASP-code kan bevatten.

    2) Voeg volgende code bovenaan de file toe.

    <%
    If Left(Request.ServerVariables("REMOTE_ADDR"), 10) = "192.168.1." Then
      URLx = "http://webstation/default.htm"
    Else
      URLx = "https://www.mijnwebsite.be/""
    End If
    %>
    3) Vervang de URL's door de volgende code:
    <% Response.Write URLx  %>
    4) Opslaan en testen. Klaar is kees!

    5) Als je server publiek op het internet staat, hou hem up-to-date!

     

    12-12-2011, 11:14 Geschreven door Fibergeek  


    Categorie:Programmeren
    11-12-2011
    Klik hier om een link te hebben waarmee u dit artikel later terug kunt lezen.Text op het Windows Clipboard zetten in Python
    # Deze code is getest met Python 2.6 en Python 3.1

    import ctypes
    from sys import version_info
    strcpy           = ctypes.cdll.msvcrt.strcpy
    wcscpy           = ctypes.cdll.msvcrt.wcscpy
    OpenClipboard    = ctypes.windll.user32.OpenClipboard
    EmptyClipboard   = ctypes.windll.user32.EmptyClipboard
    GetClipboardData = ctypes.windll.user32.GetClipboardData
    SetClipboardData = ctypes.windll.user32.SetClipboardData
    CloseClipboard   = ctypes.windll.user32.CloseClipboard
    GlobalAlloc      = ctypes.windll.kernel32.GlobalAlloc
    GlobalLock       = ctypes.windll.kernel32.GlobalLock
    GlobalUnlock     = ctypes.windll.kernel32.GlobalUnlock
    CF_TEXT          = 1
    CF_UNICODETEXT   = 13
    GMEM_DDESHARE    = 0x2000
    is_python3 = version_info[0] >= 3
    if is_python3:
     default_type = CF_UNICODETEXT
    else:
     default_type = CF_TEXT
    def get_globaldata_as_ansistring(hCd):
     GlobalLock.restype = ctypes.c_char_p
     
     ptr = GlobalLock(hCd)
     GlobalUnlock(hCd)
     
     return ptr
    def get_globaldata_as_unicodestring(hCd):
     GlobalLock.restype = ctypes.c_wchar_p
     
     ptr = GlobalLock(hCd)
     GlobalUnlock(hCd)
     
     return ptr
    def get_ansistring_as_globaldata(string):
     GlobalLock.restype = ctypes.c_long
     
     hCd = GlobalAlloc(GMEM_DDESHARE, len(string) + 1)
     ptr = GlobalLock(hCd)
     strcpy(ctypes.c_char_p(ptr), string)
     GlobalUnlock(hCd)
     
     return hCd
    def get_unicodestring_as_globaldata(string):
     GlobalLock.restype = ctypes.c_long
     
     hCd = GlobalAlloc(GMEM_DDESHARE, len(string) * 2 + 2)
     ptr = GlobalLock(hCd)
     wcscpy(ctypes.c_wchar_p(ptr), string)
     GlobalUnlock(hCd)
     
     return hCd
    def SetText(data):
     if type(data) != str:
      raise TypeError("Data must be a string!")
     OpenClipboard(None)
     try:
      EmptyClipboard()
      if is_python3:
       data = get_unicodestring_as_globaldata(data)
      else:
       data = get_ansistring_as_globaldata(data)
      SetClipboardData(default_type, data)
     except:
      pass
     finally:
      CloseClipboard()
    def GetText():
     data = None
     OpenClipboard(None)
     try:
      data = GetClipboardData(default_type)
      if data:
       if is_python3:
        data = get_globaldata_as_unicodestring(data)
       else:
        data = get_globaldata_as_ansistring(data)
     except:
      pass
     finally:
      CloseClipboard()
     return data

    11-12-2011, 13:45 Geschreven door Fibergeek  


    Categorie:Code: Python
    Tags:Python,Clipboard
    23-09-2011
    Klik hier om een link te hebben waarmee u dit artikel later terug kunt lezen.Ik hou van programmeren...
    ... omdat ...

    Echt, ik weet niet waarom. Als kind speelde ik veel met LEGO en ik hou nog altijd van die blokjes. Dat kan je ook zien op mijn website: www.codegurus.be. LEGO was altijd creëren, nieuwe dingen, nieuwe vormen. Programmeren is ook zo. Zelf iets maken uit niets. Een leeg codeblad, een lege IDE, niets. Je begint te typen, je voert uit. Het werkt. De computer doet wat jij wilt wat hij doet.

    Ik vermoed dat ik daarom zo van programmeren hou: je creëert iets uit niets. Cool toch?

    23-09-2011, 12:32 Geschreven door Fibergeek  


    Categorie:Programmeren
    Tags:Programmeren
    18-08-2011
    Klik hier om een link te hebben waarmee u dit artikel later terug kunt lezen.iSCSI via de Powershell

    De onderstaande PowerShell-scripts heb ik geschreven als aanvulling op de beperkte Windows Server 2008 backup. In een latere blog zal ik het exacte hoe-en-waarom uitleggen.

    Veel uitleg over de scripts is niet nodig denk ik, maar weet dat het verre van een complete PowerShell-interface naar de Microsoft iSCSI-Initiator toe is. De volgende functies kan je gebruiken, de namen spreken voor zichzelf, op voorwarde dat je Engels kan:
    Get-All-iSCSI-Targets
    Get-All-iSCSI-Sessions
    Get-All-iSCSI-Sessions-And-Names
    Print-All-iSCSI-Targets
    Print-All-iSCSI-Sessions
    Print-All-iSCSI-Sessions-And-Names
    Mount-iSCSI-Target
    Close-iSCSI-Session
    Close-iSCSI-Sessions-With-Matching-Target
    Close-All-iSCSI-Sessions


    Ik ben geen PowerShell-fan en ik 'vrees' dat ik het nooit zal worden. Sommige functies zijn zo cryptisch dat het lijkt alsof de code altijd obfuscated is. Ik heb de Clean Code-principes zo goed als mogelijk toegepast. Als je deze scripts handig vind, laat dan aub een berichtje achter. Bedankt.


    #
    # Support functions related with iSCSI
    #

    Function GetRawISCSISessionAndNameStrings()
    {
      iscsicli ReportTargetMappings | findstr "Session Name"
    }

    Function GetRawISCSISessionStrings()
    {
      iscsicli ReportTargetMappings | findstr /C:"Session Id"
    }

    Function GetRawISCSITargetStrings()
    {
      iscsicli ListTargets | findstr /C:"    " | ForEach-Object { $_.Trim() }
    }

     

    #
    # All the cool functions related with iSCSI
    #

    Function Get-All-iSCSI-Sessions-And-Names()
    {
      $ret = @()
      $SessionsAndNames = GetRawISCSISessionAndNameStrings
      if($SessionsAndNames)
      {
        for($i = 0; $i -lt $SessionsAndNames.Length; $i += 2)
        {
          $session = $SessionsAndNames[$i+0].split(":")
          $name    = $SessionsAndNames[$i+1].split(":")
          if($session[0].trim() -eq "Session Id")
          {
            if($name[0].trim() -eq "Target Name")
            {
              $session = $session[1].trim()
              $name    = $name[1].trim()
              $ret += ,($session,$name)
            }
            else
            {
              throw (new-object Exception("Target Name-field was not found!"))
            }
          }
          else
          {
            throw (new-object Exception("Session Id-field was not found!"))
          }
        }
      }
      ,$ret
    }

    Function Get-All-iSCSI-Targets()
    {
      GetRawISCSITargetStrings | ForEach-Object { $_.Trim() }
    }

    Function Get-All-iSCSI-Sessions()
    {
      $sessions = GetRawISCSISessionStrings
      if($sessions)
      {
        $sessions = $sessions | ForEach-Object{ $_.Split(":")[1].Trim() }
      }
      $sessions
    }

    Function Print-All-iSCSI-Targets()
    {
      $targets = Get-All-iSCSI-Targets
      if($targets)
      {
        foreach($target in $targets)
        {
          Write-Host $target
        }
      }
    }

    Function Print-All-iSCSI-Sessions()
    {
      $sessions = Get-All-iSCSI-Sessions
      if($sessions)
      {
        foreach($session in $sessions)
        {
          Write-Host $session
        }
      }
    }

    Function Print-All-iSCSI-Sessions-And-Names()
    {
      $sessions = Get-All-iSCSI-Sessions-And-Names
      if($sessions)
      {
        foreach($pair in $sessions)
        {
          $session = $pair[0]
          $name    = $pair[1]
          Write-Host $session "->" $name
        }
      }
    }

    Function Mount-iSCSI-Target($TargetToMount)
    {
      iscsicli LoginTarget $TargetToMount T * * * * * * * * * * * * * * * 0
    }

    Function Close-iSCSI-Session($session)
    {
      iscsicli LogoutTarget $Session
    }

    Function Close-iSCSI-Sessions-With-Matching-Target($mustContain)
    {
      $sessions = Get-All-iSCSI-Sessions-And-Names
      if($sessions)
      {
        foreach($pair in $sessions)
        {
          $session = $pair[0]
          $name    = $pair[1]
          if($name.contains($iscsiTargetPrefix))
          {
            Close-iSCSI-Session $session
          }
        }
      }
    }

    Function Close-All-iSCSI-Sessions()
    {
      $sessions = Get-All-iSCSI-Sessions
      if($sessions)
      {
        foreach($session in $Sessions)
        {
          "Closing session " + $session + "..."
          Close-iSCSI-Session $session
        }
      }
    }

    18-08-2011, 00:00 Geschreven door Fibergeek  


    Categorie:Code: Powershell
    Tags:PowerShell,Windows Server Backup,iSCSI,iscsicli
    05-08-2011
    Klik hier om een link te hebben waarmee u dit artikel later terug kunt lezen.BaseHTTPServer-voorbeeldcode in Python

    De code in deze blogpost is een refactoring van de BaseHTTPServer-code die je kan terug vinden op de Python-website: http://wiki.python.org/moin/BaseHttpServer. Waarom heb ik die code gerefactored? Omdat er van uit het Clean Code-oogpunt vanalles mis was met de originele versie, met name: duplicate code, functions with too many responsibilities, functions with mixed languages.

    Het programma zal een HTTP-server starten op poort 85 en zal ieder URL-pad als antwoord terug sturen. Vrij simpel, maar het vormt een goede basis voor uitgebreidere functionaliteit.

    import BaseHTTPServer

    HEADER__ContentTypeIsTextHtml = ("Content-type", "text/html")
    TextHtmlHeaders = [HEADER__ContentTypeIsTextHtml]

    def get_address_pair_using_local_host(port_number):
        return ("", port_number)

    class HTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
        def do_HEAD(self):
            self.send_response_and_headers(200, TextHtmlHeaders)
       
        def do_GET(self):
            self.send_response_and_headers(200, TextHtmlHeaders)
            self.send_path_as_answer()
       
        def send_headers(self, headers):
            for header in headers:
              self.send_header(header[0], header[1])
            self.end_headers()
       
        def send_response_and_headers(self, response_code, headers):
            self.send_response(response_code)
            self.send_headers(headers)
       
        def send_path_as_answer(self):
            self.wfile.write(self.build_path_answer_string())
       
        def build_path_answer_string(self):
            return "<html><p>Pad=%s</p></html>" % self.path

    class HTTPServer():
        def __init__(self, port_number):
            address = get_address_pair_using_local_host(port_number)
            self._TheServer = BaseHTTPServer.HTTPServer(address, HTTPRequestHandler)
       
        def serve_forever(self):
            self._TheServer.serve_forever()
       
        def serve_until_keyboard_interrupt(self):
            try:
                self.serve_forever()
            except KeyboardInterrupt:
                pass

    PORT_NUMBER_TO_USE = 85
    httpd = HTTPServer(PORT_NUMBER_TO_USE)
    httpd.serve_until_keyboard_interrupt()

    Een programma moet je kunnen lezen zoals een boek. De code moet duidelijk zijn, daarom hebben de functies in de bovenstaande programmacode slechts of 1 of 2 verantwoordelijkheden en iedere verantwoordelijkheid wordt vooraf duidelijk gemaakt door de functienaam.

    05-08-2011, 22:28 Geschreven door Fibergeek  


    Categorie:Code: Python
    Tags:Python,Clean Code,Refactoring,BaseHTTPServer
    05-07-2011
    Klik hier om een link te hebben waarmee u dit artikel later terug kunt lezen.Hoger Lager herdaan

    Hoger Lager; toen mijn kameraad op school mij leerde programmeren, heeft hij mij IF's uitgelegd via het spelletje hoger-lager. Hoger Lager is dan ook het eerste spelleke da'k heb geschreven. Nu 20 jaar later (ik ben er 34) wil ik dit spel eens opnieuw schrijven maar dan ondersteund met moderne technieken zoals Test-Units.

    20 jaar geleden begon ik met Basic, dit is mijn moderne Python-variant:

    import random

    getal = random.randint(0, 100)
    while True:
      invoer = int(input("Geef een getal van 0 tot 100: "))
      if invoer == getal:
        print "Goed zo!"
        break
      elif invoer < getal:
        print "Neen! Hoger!"
      else:
        print "Neen! Lager:"

    Na de eerste & tweede refactoring-sessies kwam ik tot deze versie (in het donkerrood gekleurd omdat de code maar tijdelijk is):

    import random

    def GenereerWillekeurigGetal(min, max):
      return random.randint(min, max)

    def PrintVraagEnLeesGetalUit(min, max):
      return int(input("Geef een getal van %d tot %d: " % (min, max)))

    def VerwerkEnVergelijkInvoer(IngevoerdGetal, TeRadenGetal):
      if IngevoerdGetal == TeRadenGetal:
        print "Goed zo!"
        return True
      elif IngevoerdGetal < TeRadenGetal:
        print "Neen! Hoger!"
      else:
        print "Neen! Lager:"
      return False

    def SpeelHogerLager():
      MinGetal = 0
      MaxGetal = 100
      TeRadenGetal = GenereerWillekeurigGetal(MinGetal, MaxGetal)
      while True:
        IngevoerdGetal = PrintVraagEnLeesGetalUit(MinGetal, MaxGetal)
        if VerwerkEnVergelijkInvoer(IngevoerdGetal, TeRadenGetal):
          break

    def main():
      SpeelHogerLager()

    if __name__ == "__main__":
      main()


    Volgens TDD (Test Driven Development) moeten we eerst een test schrijven en dan pas de code, maar goed, het spel Hoger Lager was er al. Dit is dus de testcode die ik geschreven heb om de bovenstaande spelcode te testen:

    from HogerLager import *
    import unittest

    class test_HogerLager(unittest.TestCase):
      def TestGenereerWillekeurigGetalMetMinMax(self, min, max):
        for j in range((max - min) * 10):
          getal = GenereerWillekeurigGetal(min, max)
          self.failIf(getal < min)
          self.failIf(getal > max)
     
      def test_GenereerWillekeurigGetal(self):
        for i in range(60):
          min = 50 - i
          max = 50 + i
          self.TestGenereerWillekeurigGetalMetMinMax(min, max)
     
      def test_VergelijkGetallen(self):
        self.assertFalse(VerwerkEnVergelijkInvoer(50, 49))
        self.assertTrue (VerwerkEnVergelijkInvoer(50, 50))
        self.assertFalse(VerwerkEnVergelijkInvoer(50, 51))

    Het staat me echter niet aan dat de bovenstaande testcode ook het antwoord (Hoger of Lager) afdrukt. Dit gebeurt indirect door VerwerkEnVergelijkInvoer. Daarom heb ik VerwerkEnVergelijkInvoer gesplitst:

    import random

    def GenereerWillekeurigGetal(min, max):
      return random.randint(min, max)

    def PrintVraagEnLeesGetalUit(min, max):
      return int(input("Geef een getal van %d tot %d: " % (min, max)))

    def BerekenVerschil(IngevoerdGetal, TeRadenGetal):
      return IngevoerdGetal - TeRadenGetal

    def VerwerkInvoer(IngevoerdGetal, TeRadenGetal, UitvoerActie):
      UitvoerActie(IngevoerdGetal, TeRadenGetal)
      return IngevoerdGetal == TeRadenGetal

    def PrintResultaat(IngevoerdGetal, TeRadenGetal):
      Verschil = BerekenVerschil(IngevoerdGetal, TeRadenGetal)
      if Verschil == 0:
        print "Goed zo!"
      elif Verschil < 0:
        print "Neen! Hoger!"
      else:
        print "Neen! Lager:"

    def VerwerkInvoerEnPrintResultaat(IngevoerdGetal, TeRadenGetal):
      return VerwerkInvoer(IngevoerdGetal, TeRadenGetal, PrintResultaat)

    def SpeelHogerLager():
      MinGetal = 0
      MaxGetal = 100
      TeRadenGetal = GenereerWillekeurigGetal(MinGetal, MaxGetal)
      while True:
        IngevoerdGetal = PrintVraagEnLeesGetalUit(MinGetal, MaxGetal)
        if VerwerkInvoerEnPrintResultaat(IngevoerdGetal, TeRadenGetal):
          break

    def main():
      SpeelHogerLager()

    if __name__ == "__main__":
      main()


    Tenslotte volgt de bijhorende testcode:

    from HogerLager import *
    import unittest

    def GeenUitvoerActie(a, b):
      pass

    class test_HogerLager(unittest.TestCase):
      def TestGenereerWillekeurigGetalMetMinMax(self, min, max):
        for j in range((max - min) * 10):
          getal = GenereerWillekeurigGetal(min, max)
          self.failIf(getal < min)
          self.failIf(getal > max)
     
      def test_GenereerWillekeurigGetal(self):
        for i in range(60):
          min = 50 - i
          max = 50 + i
          self.TestGenereerWillekeurigGetalMetMinMax(min, max)
     
      def test_BerekenVerschil(self):
        self.failUnless(BerekenVerschil(50, 49) >  0)
        self.failUnless(BerekenVerschil(50, 50) == 0)
        self.failUnless(BerekenVerschil(50, 51) <  0)
     
      def test_VerwerkInvoer(self):
        self.assertFalse(VerwerkInvoer(50, 49, GeenUitvoerActie))
        self.assertTrue (VerwerkInvoer(50, 50, GeenUitvoerActie))
        self.assertFalse(VerwerkInvoer(50, 51, GeenUitvoerActie))

    if __name__ == '__main__':
      unittest.main()


    Eindnota: Ik ben nog niet overtuigd dat deze laatste versie van Hoger Lager de beste is, want de hoofdlus SpeelHogerLager is verantwoordelijk voor zowel het spel, als de invoer en de uitvoer via de console. Daardoor is het onmogelijk de hoofdlus te testen via een unittest-test_XXX-functie. Het is beter om de invoer en uitvoer via polyformisme te implementeren, de hoofdlus kan dan vervolgens via "mocking" getest worden. Voor het polyformisme zou ik in .NET een Interface definiëren en gebruiken, in Python zal ik dit anders moeten oplossen (via een abstracte class). Ook de functie PrintVraagEnLeesGetalUit moet herbekeken worden want ze is zowat de enigste functie in het programma die een crash kan veroorzaken. Met name als een niet-cijfer ingevoerd wordt, zal de int-functieaanroep een exception triggeren. Deze "bug" bestaat ook in de eerste versie van het spel. Dit alles is voor een volgende blog.

    05-07-2011, 00:00 Geschreven door Fibergeek  


    Categorie:Code: Python
    10-06-2011
    Klik hier om een link te hebben waarmee u dit artikel later terug kunt lezen.Console-applicaties starten in C en de invoer/uitvoer (Clean Code-variant)
    Deze C++ code doet exact hetzelfde als de vorige versie van het programma (zie mijn vorige blog). Echter, ik heb het programma proberen te herschrijven volgens de richtlijnen uit het boek Clean Code: A Handbook of Agile Software Craftsmanship van Robert C. Martin. Wat zeker nog ontbreekt is het vrijgeven van het geheugen en het vrijgeven van de handles (zowel de Pipes als de Process-handle) (dit ontbreekt ook in the originele versie). Zelfs buiten dat, ben ik nog niet volledig overtuigd dat het programma klaar is. Misschien volgt een nieuwe versie als ik het boek uitgelezen heb?...

    #include "stdafx.h"

    #include <windows.h>

     

    // SecurityAttribute-stuff (needed for the Pipes)

    class SecurityAttributes

    {

    private:

      SECURITY_ATTRIBUTES security_attributes;

     

    public:

      SecurityAttributes();

      LPSECURITY_ATTRIBUTES GetPointer();

    };

     

    // Pipe-stuff

    typedef struct

    {

      HANDLE Read;

      HANDLE Write;

    } PipeHandlePair;

     

    typedef struct

    {

      PipeHandlePair input;

      PipeHandlePair output;

      PipeHandlePair error;

    } PipeHandles;

     

    void CreatePipes(PipeHandles &ThePipeHandles);

    void CreatePipe(PipeHandlePair &PipePair, SecurityAttributes &TheSecurityAttributes);

    void SendTextMessageToPipe(HANDLE PipeHandle, LPCSTR Message);

    DWORD GetAmountOfDataAvailableOnPipe(HANDLE PipeHandle);

    DWORD ReadDataFromPipe(HANDLE PipeHandle, LPBYTE Buffer, DWORD BytesToRead);

    LPSTR ReadStringDataFromPipe(HANDLE PipeHandle, DWORD StringLength);

    LPSTR ReadAvailableDataFromPipeAsString(HANDLE PipeHandle);

    void ReadAndDisplayOutputFromPipes(PipeHandles &ThePipeHandles);

     

    // ProcessStartupInfo-stuff (needed for the Processes)

    class ProcessStartupInfo

    {

    private:

      STARTUPINFO startup_info;

     

    public:

      ProcessStartupInfo(const PipeHandles &ThePipeHandles);

      LPSTARTUPINFO GetPointer();

    };

     

    // Process-stuff

    void WaitForProcessToEnd(HANDLE ProcessHandle);

    void WaitForProcessToEnd(PROCESS_INFORMATION &process_info);

    bool CreateProcess(PROCESS_INFORMATION &process_info, LPCSTR CommandLine, PipeHandles &ThePipeHandles);

    bool CreateProcessAndWaitForItToEnd(LPCSTR CommandLine, PipeHandles &ThePipeHandles, LPCSTR OptionalInputToSend = NULL);

     

    // Other stuff

    void SimpleMessageBox(LPCSTR Title, LPCSTR Content);

    void DisplayNormalOrErrorOutput(LPSTR OutputString, LPSTR ErrorString);

     

    int main()

    {

      PipeHandles OurPipeHandles;

      CHAR InputToSend[] = "";

     

      CreatePipes(OurPipeHandles);

      if(CreateProcessAndWaitForItToEnd("cmd /C "echo Test"", OurPipeHandles, InputToSend))

        ReadAndDisplayOutputFromPipes(OurPipeHandles);

     

      return 0;

    }

     

    void CreatePipes(PipeHandles &ThePipeHandles)

    {

      SecurityAttributes TheSecurityAttributes;

      CreatePipe(ThePipeHandles.output, TheSecurityAttributes);

      CreatePipe(ThePipeHandles.input,  TheSecurityAttributes);

      CreatePipe(ThePipeHandles.error,  TheSecurityAttributes);

    }

     

    void CreatePipe(PipeHandlePair &PipePair, SecurityAttributes &TheSecurityAttributes)

    {

      CreatePipe(&PipePair.Read, &PipePair.Write, TheSecurityAttributes.GetPointer(), 0);

    }

     

    bool CreateProcess(PROCESS_INFORMATION &process_info, LPCSTR CommandLine, PipeHandles &ThePipeHandles)

    {

      ProcessStartupInfo StartupInfo(ThePipeHandles);

      return CreateProcess(NULL, (LPSTR) CommandLine, NULL, NULL, TRUE, 0, NULL, NULL, StartupInfo.GetPointer(), &process_info) != FALSE;

    }

     

    bool CreateProcessAndWaitForItToEnd(LPCSTR CommandLine, PipeHandles &ThePipeHandles, LPCSTR OptionalInputToSend)

    {

      PROCESS_INFORMATION process_info;

     

      if(!CreateProcess(process_info, CommandLine, ThePipeHandles))

        return false;

      SendTextMessageToPipe(ThePipeHandles.input.Write, OptionalInputToSend);

      WaitForProcessToEnd(process_info.hProcess);

      return true;

    }

     

    void SendTextMessageToPipe(HANDLE PipeHandle, LPCSTR Message)

    {

      if(Message && *Message) // Is valid?

      {

        DWORD bytes_written = 0;

        WriteFile(PipeHandle, Message, strlen(Message), &bytes_written, NULL);

      }

    }

     

    void WaitForProcessToEnd(HANDLE ProcessHandle)

    {

      WaitForSingleObject(ProcessHandle, INFINITE);

    }

     

    void WaitForProcessToEnd(PROCESS_INFORMATION &process_info)

    {

      WaitForSingleObject(process_info.hProcess, INFINITE);

    }

     

    DWORD ReadDataFromPipe(HANDLE PipeHandle, LPBYTE Buffer, DWORD BytesToRead)

    {

      DWORD BytesRead = 0;

      if(BytesToRead)

        ReadFile(PipeHandle, Buffer, BytesToRead, &BytesRead, NULL);    

      return BytesRead;

    }

     

    void ReadAndDisplayOutputFromPipes(PipeHandles &ThePipeHandles)

    {

      CHAR *OutputString = ReadAvailableDataFromPipeAsString(ThePipeHandles.output.Read);

      CHAR *ErrorString  = ReadAvailableDataFromPipeAsString(ThePipeHandles.error.Read);

      DisplayNormalOrErrorOutput(OutputString, ErrorString);

    }

     

    LPSTR ReadAvailableDataFromPipeAsString(HANDLE PipeHandle)

    {

      DWORD BytesAvailable = GetAmountOfDataAvailableOnPipe(PipeHandle);

      LPSTR StringData = ReadStringDataFromPipe(PipeHandle, BytesAvailable);

      return StringData;

    }

     

    DWORD GetAmountOfDataAvailableOnPipe(HANDLE PipeHandle)

    {

      DWORD BytesToRead = 0;

      if(!PeekNamedPipe(PipeHandle, NULL, 0, NULL, &BytesToRead, NULL))

        return 0;

      return BytesToRead;

    }

     

    LPSTR ReadStringDataFromPipe(HANDLE PipeHandle, DWORD StringLength)

    {

      CHAR *ReturnBuffer = new CHAR[StringLength + 1];

      if(ReturnBuffer)

      {

        DWORD BytesRead = ReadDataFromPipe(PipeHandle, (LPBYTE) ReturnBuffer, StringLength);

        ReturnBuffer[BytesRead] = 0;

      }

      return ReturnBuffer;

    }

     

    void DisplayNormalOrErrorOutput(LPSTR OutputString, LPSTR ErrorString)

    {

      if(OutputString && ErrorString)

      {

        if(*ErrorString)

          SimpleMessageBox("There were errors!", ErrorString);
        
    else

          SimpleMessageBox("Output code", OutputString);

      }

    }

     

    void SimpleMessageBox(LPCSTR Title, LPCSTR Content)

    {

      MessageBox(HWND_DESKTOP, Content, Title, MB_OK);

    }

     

    SecurityAttributes::SecurityAttributes()

    {

      security_attributes.nLength              = sizeof(SECURITY_ATTRIBUTES);

      security_attributes.bInheritHandle       = TRUE;

      security_attributes.lpSecurityDescriptor = NULL;

    }

     

    LPSECURITY_ATTRIBUTES SecurityAttributes::GetPointer()

    {

      return &security_attributes;

    }

     

    ProcessStartupInfo::ProcessStartupInfo(const PipeHandles &ThePipeHandles)

    {

      ZeroMemory(&startup_info, sizeof(STARTUPINFO));

      startup_info.cb         = sizeof(STARTUPINFO);

      startup_info.hStdInput  = ThePipeHandles.input.Read;

      startup_info.hStdOutput = ThePipeHandles.output.Write;

      startup_info.hStdError  = ThePipeHandles.error.Write;

      startup_info.dwFlags    = STARTF_USESTDHANDLES;

    }

     

    LPSTARTUPINFO ProcessStartupInfo::GetPointer()

    {

      return &startup_info;

    }

    10-06-2011, 00:00 Geschreven door Fibergeek  


    Categorie:Code: C/C++
    09-06-2011
    Klik hier om een link te hebben waarmee u dit artikel later terug kunt lezen.Console-applicaties starten in C en de invoer/uitvoer

    Het onderstaande programma is een voorbeeld van hoe een console-applicatie op te starten, en van hoe de uitvoer van dit programma uit te lezen en weer te geven. Alles gebeurt via de 3 standaard-handles: stdout, stdin en stderr (via Pipes).

    #include
    "stdafx.h"

    #include <windows.h>

     

    int main()

    {

      HANDLE input_PipeRead;

      HANDLE input_PipeWrite;

      HANDLE output_PipeRead;

      HANDLE output_PipeWrite;

      HANDLE error_PipeRead;

      HANDLE error_PipeWrite;

     

      CHAR message[] = "";

     

      PROCESS_INFORMATION process_info;

      STARTUPINFO startup_info;

      SECURITY_ATTRIBUTES security_attributes;

     

      // Set the security attributes for the pipe handles created

      security_attributes.nLength              = sizeof(SECURITY_ATTRIBUTES);

      security_attributes.bInheritHandle       = TRUE;

      security_attributes.lpSecurityDescriptor = NULL;

     

      // Create the pipes used for the communication

      CreatePipe(&output_PipeRead, &output_PipeWrite, &security_attributes, 0);

      CreatePipe(&input_PipeRead,  &input_PipeWrite,  &security_attributes, 0);

      CreatePipe(&error_PipeRead,  &error_PipeWrite,  &security_attributes, 0);

     

      // Prepare the startup structure for the child process

      ZeroMemory(&startup_info, sizeof(STARTUPINFO));

      startup_info.cb         = sizeof(STARTUPINFO);

      startup_info.hStdInput  = input_PipeRead;

      startup_info.hStdOutput = output_PipeWrite;

      startup_info.hStdError  = error_PipeWrite;

      startup_info.dwFlags    = STARTF_USESTDHANDLES;

     

      // Create the child process

      if(!CreateProcess(NULL, "cmd /C "echo Test"", NULL, NULL, TRUE, 0, NULL, NULL, &startup_info, &process_info))

        return 0;

     

      // Write a message to the child process

      if(*message)

      {

        DWORD bytes_written = 0;

        WriteFile(input_PipeWrite, message, strlen(message), &bytes_written, NULL);

      }

     

      // Wait for the process to end

      WaitForSingleObject(process_info.hProcess, INFINITE);

     

      // Read the message from the child process

      DWORD output_BytesToRead = 0, error_BytesToRead = 0;

      if(  !PeekNamedPipe(output_PipeRead, NULL, 0, NULL, &output_BytesToRead, NULL)
         || 
           !PeekNamedPipe(error_PipeRead, NULL, 0, NULL, &error_BytesToRead, NULL))

        return 0;

     

      CHAR *output_Buffer = new CHAR[output_BytesToRead + 1];

      CHAR *error_Buffer = new CHAR[error_BytesToRead + 1];

      if(output_Buffer && error_Buffer)

      {

        DWORD tmp_BytesRead;

     

        // Read the output code

        tmp_BytesRead = 0;

        if(output_BytesToRead)

          ReadFile(output_PipeRead, output_Buffer, output_BytesToRead, &tmp_BytesRead, NULL);

        output_Buffer[tmp_BytesRead] = 0;

     

        // Read any errors

        tmp_BytesRead = 0;

        if(error_BytesToRead)

          ReadFile(error_PipeRead, error_Buffer, error_BytesToRead, &tmp_BytesRead, NULL);

        error_Buffer[tmp_BytesRead] = 0;

     

        // Output

        if(*error_Buffer)

          MessageBox(NULL, error_Buffer,  "There were errors!", MB_OK);

        else

          MessageBox(NULL, output_Buffer, "Output code",        MB_OK);

      }

     

      return 0;

    }

     

    09-06-2011, 00:00 Geschreven door Fibergeek  


    Categorie:Code: C/C++
    08-06-2011
    Klik hier om een link te hebben waarmee u dit artikel later terug kunt lezen.Mijn begin met WPF (INotifyPropertyChanged)
    Een van de leuke dingen van WPF is dat de UI automatisch kan aangepast worden als de data veranderd (en omgekeerd ook). In het onderstaande voorbeeld wordt de test-tekst omgedraaid als er op geklikt wordt:

    <Application x:Class="Application"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                
    StartupUri="MainWindow.xaml">
    </
    Application>
    <
    Window x:Class="MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            DataContext="{Binding RelativeSource={RelativeSource Mode=Self}}"
           
    Title="Een simpele test" Height="350" Width="525">
      <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="25"
                 MouseDown="OnEvent__TextBlock_MouseDown"
                
    Text="{Binding Path=TestTekst}"
                />
    </
    Window>

    De bijhorende code:

    Imports

    System.ComponentModel

    Class

    MainWindow
      Implements INotifyPropertyChanged

      Public Property TestTekst As String = "Dit is een simpele test..."

      Private
    Sub OnEvent__TextBlock_MouseDown(ByVal sender As System.Object,
                                              
    ByVal e As System.Windows.Input.MouseButtonEventArgs)
        TestTekst = StrReverse(TestTekst)
        NotifyPropertyChanged(
    "TestTekst")
      End Sub

    #Region

    "INotifyPropertyChanged implementation"
      Private Event PropertyChanged(ByVal sender As Object,
                                   
    ByVal e As System.ComponentModel.PropertyChangedEventArgs)
                    
    Implements INotifyPropertyChanged.PropertyChanged

      Protected Sub NotifyPropertyChanged(ByVal sender As Object, ByVal propertyName As String)
        RaiseEvent PropertyChanged(sender, New PropertyChangedEventArgs(propertyName))
      End Sub
      Private Sub NotifyPropertyChanged(ByVal propertyName As String)
        NotifyPropertyChanged(
    Me, propertyName)
      End Sub
    #End
    Region
    End
    Class


    In het bovenstaande voorbeeld is de OnEvent_TextBlock_MouseDown-functie verantwoordelijk voor het aanroepen van de NotifyPropertyChanged-functie. We kunnen deze verantwoordelijkheid ook geven aan de TestTekst-Property als volgt:

    Imports System.ComponentModel

    Class MainWindow
      Implements INotifyPropertyChanged

      Protected _TestTekst As String = "Dit is een simpele test..."

      Public
    Property TestTekst As String
        Get
          Return _TestTekst
        End Get
        Set(ByVal value As String)
          _TestTekst = value
          NotifyPropertyChanged(
    "TestTekst")
        End Set
      End Property


      Private
    Sub OnEvent__TextBlock_MouseDown(ByVal sender As System.Object,
                                              
    ByVal e As System.Windows.Input.MouseButtonEventArgs)
        TestTekst = StrReverse(TestTekst)
        NotifyPropertyChanged(
    "TestTekst")
      End Sub

    #Region

    "INotifyPropertyChanged implementation"
      Private Event PropertyChanged(ByVal sender As Object,
                                   
    ByVal e As System.ComponentModel.
    PropertyChangedEventArgs)
                    
    Implements
    INotifyPropertyChanged.PropertyChanged

      Protected Sub NotifyPropertyChanged(ByVal sender As Object, ByVal propertyName As String)
        RaiseEvent PropertyChanged(sender, New PropertyChangedEventArgs(propertyName))
      End Sub
      Private Sub NotifyPropertyChanged(ByVal propertyName As String)
        NotifyPropertyChanged(
    Me, propertyName)
      End Sub
    #End
    Region
    End
    Class

    08-06-2011, 00:00 Geschreven door Fibergeek  


    Categorie:Code: WPF
    07-06-2011
    Klik hier om een link te hebben waarmee u dit artikel later terug kunt lezen.Mijn begin met WPF (Data Binding)
    Het is nu al bijna een jaar geleden dat ik met WPF (Windows Presentation Foundation) begonnen ben. Echter, ik ben het meeste al weer vergeten. Het is aanpassen . Eens de "basics" van Data-Binding herbekijken...

    Een simpel programma, een tekst in het midden van het scherm:

    <

    Application x:Class="Application"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                
    StartupUri="MainWindow.xaml">
    </
    Application>
    <Window x:Class="MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns
    :x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="Een simpele test" Height="350" Width="525">
           
    <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="25">
             
    Dit is een simpele test...
           
    </TextBlock>
    </
    Window>


    Dit is dezelfde versie van het programma, maar de tekst is nu verhuist naar een Resource-blok op het Window-niveau:

    <Application x:Class="Application"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                
    StartupUri="MainWindow.xaml">
    </
    Application>
    <

    Window x:Class="MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:s="clr-namespace:System;assembly=mscorlib"
            Title="Een simpele test" Height="350" Width="525">
      <Window.Resources>
        <s:String x:Key="TestTekst">Dit is een simpele test...</s:String>
      </Window.Resources>
      <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="25"
                 Text="{Binding Source={StaticResource TestTekst}}"
                />
    </
    Window>


    We kunnen de TestTekst-variable ook verhuizen naar het Application-blok (met als voordeel dat deze variable nu gebruikt kan worden in andere schermen):

    <Application x:Class="Application"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:s="clr-namespace:System;assembly=mscorlib"
                
    StartupUri="MainWindow.xaml">
      <Application.Resources>
        <s:String x:Key="TestTekst">Dit is een simpele test...</s:String>
      </Application.Resources>
    </
    Application>
    <Window x:Class="MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="Een simpele test" Height="350" Width="525">
      <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="25"
                 Text="{Binding Source={StaticResource TestTekst}}"
                />
    </
    Window>


    Het is ook mogelijk om deze tekst te laten komen uit een variable uit de bijhorende Window-class. De DataContext van het window moet dan naar zichzelf wijzen:

    <Application x:Class="Application"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                
    StartupUri="MainWindow.xaml">
    </
    Application>
    <

    Window x:Class="MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            DataContext="{Binding RelativeSource={RelativeSource Mode=Self}}"
           
    Title="Een simpele test" Height="350" Width="525">
      <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="25"
                
    Text="{Binding Path=TestTekst}"
                />
    </
    Window>

    De bijhorende classe-code:

    Class

    MainWindow
     
    Public Property TestTekst As String = "Dit is een simpele test..."
    End Class


    Maar omdat de Designer nu de inhoud van de TestTekst zelf niet kan bepalen wordt deze niet weergegeven. Vreed lastig dus tijdens de ontwikkeling van de schermen. Dit kan men oplossen door een FallbackValue in te stellen. Hetzelfde programma als hierboven maar het TextBlock in het Window-blok is een klein beetje aangepast:

    <Window x:Class="MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            DataContext="{Binding RelativeSource={RelativeSource Mode=Self}}"
           
    Title="Een simpele test" Height="350" Width="525">
      <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="25"
                 T
    ext="{Binding Path=TestTekst,FallbackValue='(hier komt de TestTekst)'}"
                />
    </
    Window>

    07-06-2011, 00:00 Geschreven door Fibergeek  


    Categorie:Code: WPF
    Archief per week
  • 25/11-01/12 2013
  • 05/11-11/11 2012
  • 07/05-13/05 2012
  • 05/03-11/03 2012
  • 20/02-26/02 2012
  • 13/02-19/02 2012
  • 30/01-05/02 2012
  • 12/12-18/12 2011
  • 05/12-11/12 2011
  • 19/09-25/09 2011
  • 15/08-21/08 2011
  • 01/08-07/08 2011
  • 04/07-10/07 2011
  • 06/06-12/06 2011

    E-mail mij

    Druk op onderstaande knop om mij te e-mailen.


    Gastenboek

    Druk op onderstaande knop om een berichtje achter te laten in mijn gastenboek


    Blog als favoriet !


    Blog tegen de wet? Klik hier.
    Gratis blog op https://www.bloggen.be - Meer blogs