UzKlikšķa

Bieži vien gribas darīt šādi:

<a href="javascript:ShowPic('image1.jpg')">skatīt bildi</a>

Tas ir, lai parādītu bildīti, izsaucam JavaScript funkciju, kurai parametros nododam attēla nosaukumu. Risinājums labi darbosies itin daudziem lietotājiem, tomēr viegli pārliecināties, ka rezultāts nebūs sasniegts, kad pārlūkā atslēgsim JavaScript atbalstu. Turklāt – javascript: lietošana hipersaites href atribūta vērtībā ir nekorekta – šajā atribūtā jānorāda URI shēmai atbilstoša vērtība. javascript: ir pseidoprotokols, līdzīgi kā about: vai chrome:.

Daži problēmu risinātu šādi:

<a href="#" onclick="ShowPic('image1.jpg')">skatīt bildi</a>

Diemžēl šāds risinājums vēl joprojām nepalīdzēs gadījumā, kad pārlūkā atslēgsim JavaScript atbalstu. Mēģināsim lietot <a> birku tam, kam tā domāta – saites norādīšanai. To varam izdarīt šādi

<a href="image1.jpg" onclick="ShowPic('image1.jpg');return false">skatīt bildi</a>

Nu jau izskatās itin labi. Tie pārlūki, kas neatbalsta JavaScript vai arī kam izslēgts javascript, atvērs pašu bildi, bet pārējie izpildīs funkciju ShowPic(), kas droši vien parāda attēlu citā un skaistākā veidā. Papildus pievienotais “return false” nodrošina to, ka pēc funkcijas izpildes notikums “ir ticis uzklikšķināts uz hipersaites” tiek atcelts, tādējādi pārlūks neatvērs adresi, kas aprakstīta href atribūtā.

Tomēr – ak vai, ak vai – šāds dokuments, kam DOCTYPE norādīts XHTML 1.0 Strict negrib "validēties" Tā kā XHTML standarts paredz striktu nodalījumu starp dokumenta saturu (pats XHTML), tā izkārtojumu (CSS stili) un prezentāciju (piekārtotie skripti), tad atribūts onclick vairs nav pieļaujams A elementam.

Tāpēc darām šādi: pašā XHTML dokumentā iekļaujam tikai šādu saiti:

<a href="image1.jpg" class="PictureLink">skatīt bildi</a>

Savukārt dokumentam piekārtotajā skriptu failā piesaistām šim elementam notikumu ķērāju:

//pēc dokumenta pilnas ielādes, izsauc funkciju, kas "salabo" saites
document.body.onload=InitPictureLinks;

//funkcija iziet cauri visām dokumenta hipersaitēm un vajadzīgās no tām "salabo" function InitPictureLinks(){ var oLinks; //šī būs visu hipersaišu kolekcija

//izmanto DOM funkciju, lai nolasītu visas hipersaites oLinks = document.getElementsByTagName("A");

for (var i=0;i<olinks .length;i++){ //apstrādā tikai tos A elementus, kuru klase ir PictureLink if (oLinks[i].className=="PictureLink") addLink(oLinks[i];) } }

//piesaista notikumu ķērāju "onclick". //pēc klikšķa tiek izsaukta funkcija ShowPic, parametros //nododot href atribūtā uzstādīto vērtību un atgriezts false, //lai atceltu klikšķa notikumu function addLink(opLink){ opLink.onclick = function(){ShowPic(opLink.href);return false}; }

VB.net klases atribūtu redzamība

VB.net klases atribūtu redzamība tiek definēta visam tipam, ne tipa instancei, kā mēs to varbūt sagaidām.
Tas ir, viena klases instance var mierīgi piekļūt citas klases instances privātajiem laukiem. Piemērs:

Class Test
 Private scValue As String
 Public ReadOnly Property Value() As String
    Get
    Return scValue
    End Get
 End Property

 Public Sub Export(ByRef opTest As Test)
    opTest.scValue = Me.scValue
 End Sub
End Class

Tas ir, mēs definējam klasi Test, kurai ir viens privāts lauks scValue. (Privāts = tikai pati klase drīkst mainīt atribūtu vērtības). Un vēl klasei ir ReadOnly atribūts Value, kas atgriež lauka scValue vērtību.

Bet patiesībā veiksmīgi izpildās funkcija export, kas pēc references saņem citu šīs pašas klases instanci un mierīgā garā uzstāda tās lauka vērtību. Jebkādas citas klases instance, protams, netiktu pie Test klases iekšējiem laukiem.

Tomēr – ar šo uzmanīgi! Var rasties problēmas.

Risinājumi un frameworki

Tikai viens citāts:

Nemēģini izstrādāt par kārtu sarežģītāku framework, kas risina tavu vienkāršo problēmu. Raksti vienkāršu algoritmu, tiek galā ar tavu sāpi. Jo tu nezini, kādas ir pārējās problēmas, kuras šim frameworkam vajadzētu aptvert.

Interesanta diskusija ar Anders Hejlsberg, valodas C# izstrādātāju. Diskusijā piedalās arī Bruce Eckel, kurš ir gŗamatu Thinking in C++ and Thinking in Java autors un arī Bill Venners no Artima.com

Unicode: UTF-8, UTF-16 un citi

Kāds no http://journal.bad.lv lietotājiem mani palūdza izskaidrot atšķirību starp UTF-8, UTF-16 un Unicodi. Šeit arī skaidrojums, kas varētu noderēt ne tikai viņam.

Reiz kaut kad sen Laacz bija iemetis linku uz jauku rakstu par Unicodi – to var lasīt Joel Spolsky rakstā. Bet ja nu gadījumā nav laika lasīt angliskus tekstus, šeit neliels satura atstāstījums.

Būtībā Unicode ir tikai standarts, kas definē abstraktu kodējumu valodas simboliem, katram simbolam piekārtojot savu kodu. Unicode pasaka, ka “a” == “U+0061”, “ā”=”U+0101” un tamlīdzīgi, bet nav runas par to, kā to nokodēt bitu virknēs.
UTF-16 ir tieša Unicode implementācija – ja ir pateikts, ka a=U0061, tad a kodējam to ar 16 bitiem, tas ir, diviem baitiem – 00 un 61, kas atbilst 003D heksadecimālajā sistēmā.
Unicode apzīmējumus var apskatīt programmā Character Map (Start->Programs->Accessories->System tools->Character map). Tur arī var pamanīt, ka visiem “normālajiem” Ascii burtiem pirmais baits ir vienmēr 00, bet, piemēram, latviešu simboliem pirmajam baitam ir nenulles vērtība (01).
Nez kāpēc amerikāņu un angļu programmētājiem sāka šķist, ka Unicode pamatīga liekvārdība — kāpēc gan viņiem starp katriem diviem baitiem būtu jāraksta 00???? – jo tiešām, viņiem Unicode ne ar ko citu neatšķiras no ASCII.
Tāpēc tika izdomāts UTF-8 kodējums, kuram, ja pirmais baits ir 00, tas tiek izlaists. Līdz ar to priekš “plain ASCII” valodām kā angļu (vai …. hmmm, vai ir vēl kāda?) UTF-8 ir praktiski vienbaitīgs. Toties tekstos, kuri satur arī reģionālos kodējumos nepieciešamos burtus, ik pa brīdim būs “vairāk nekā vienbaitīgi” simboli, tas ir, piemēram, latviešu burtu apzīmēšanai tiek lietoti vismaz divi baiti.
Patiesībā gan UTF-8 rada daudz problēmu. Piemēram, par journal.bad.lv aptaujām zināms, ka atbildes garums drīkst būt ne vairāk kā 255 simboli. Tomēr patiesībā ar to ir domāti 255 baiti, nevis simboli. Tātad, ja tu raksti ar latviešu burtiem, daži no tiem aizņems nevis vienu, bet vairākus baitus, līdz ar to kopējais teksta garums būs mazāks nekā 255 simboli.
Līdzīgi arī problēmas rodas ar tādu vienkāršu uzdevumu kā teksta garuma noskaidrošana – līdz šim varēja paskatīties, cik vietas atmiņā aizņem string tipa mainīgais, tagad ir katru reizi jāpārbauda, vai tajā gadījumā nav vairākbaitīgu simbolu. Arī string tipa vērtību sakārtošana augošā vai dilstošā secībā kļuvusi sarežģītāka – atmiņas apgabalā var būt biti, kas nav jāievēro, salīdzinot divus mainīgos.
Patiesībā priekš mums ērtāks būtu UTF-16 kodējums – salīdzināšana darbotos, garuma noteikšanas funkcijas darbotos, tikai rezultāts būtu jādala ar 2. Vienīgā problēma – tas aizņem tieši divreiz vairāk diska vietas, divreiz vairāk atmiņas un rada divreiz lielāku datu plūsmu, kad nepieciešams pārsūtīt informāciju.
Paldies sm par papildinājumu: UTF-8 var būt vienbaitīgs, bet var arī aizņemt līdz pat sešiem baitiem uz simbolu

Listbox lietojumi

Iepriekšējā rakstā minēju, ka “esam mantojuši ListView klasi, atliek tikai to izmantot”. Te nu jautāums, kā to izdarīt.
MS Visual Studio 2003 gada versija pagaidām nepiedāvā iespeju, vismaz – ne acīmredzami. Ir iespēja uz formas novietot gatavas kontroles, ir iespēja gatavās kontroles “ievilkt iekš toolbox-a”, bet nav iespējas iestāstīt VStudio, ka šī tava klase ir patiesībā mantota no windows formām un ka tā ir vizuāla un ka tai ir design-time interfeiss kā jau kārtīgai ActiveX kontrolei.
Tāpēc pagaidām rīkojos tā: uz formas novietoju to kontroli, no kuras esmu mantojis savu klasi. Piemēram, ja man vajag uz formas uzlikt MyListView:Widnows.Forms.ListView, tad uz formas uzlieku pašu ListView elementu. Pēc tam atveram formu dizainera ģenerēto kodu.
Kaut kur tajā kodā atrodam rindu:

Friend WithEvents ListView1 As System.Windows.Forms.ListView
Šajā vietā aizvietojam “System.Windows.Forms.ListView” ar “MyListView”.
Pēc tam pameklējam nedaudz tālāk un atrodam
Me.ListView1 = New System.Windows.Forms.ListView
, kur veicam analoģisku aizvietojumu.
Tālākais arī nav gluži acīmredzams. Nospiežam Save. Aizveram formu dizaineri. Aizveram pašu formu. Uztaisām projektam “rebuild”. Atveram formu dizaineri. Vizuāli nekas nav mainījies (jo design-time interfeisu taču neesam mainījuši), bet kontroles tips ir no Windows.Forms.ListView pārvērties par MyListView, kas arī bija vajadzīgs.
Jaunajā VStudijas versijā ir paredzēta vienkāršota kontroļu tipu maiņa, kur šādas izmaiņas būs veicamas ar vienu klikšķi. Viens “+” punkts jaunajai studijai.

Kārtošana un IComparer

Mazliet par elementu kārtošanu. Nē, šis nebūs par ātriem kārtošanas algoritmiem, bet par saraksta sakārtošanu pēc paša noteiktas funkcijas.
Reizēm gadās situācija, ka ir dota Windows forma, uz kuras ListView kontrole. Kontrolē iekšā kaut kāds saraksts. Jānis Bērziņš, Pēteris Vītoliņš un visi pārējie. Vai arī “01.05.2004”, “03.02.2003” un citi. Kaut kādu ierakstu saraksts. Parasti gribas šādu sarakstu redzēt sakārtotu. Piemēram, augošā secībā pēc datumiem.
Tā tas gadījās arī man, vajadzēja sakārtot šādus Latvijā tik pierastā veidā noformatētus datumus (dd.MM.yyyy) augošā secībā. Skaidrs ir tas, ka, ja vienkārši ieslēgsim “Sort ascending” ListView kontrolei, datumi tiks sakārtoti apmēram šādā secībā – 01.01.2003; 01.02.2003; 02.01.2003, kas, protams, neatbilst mūsu vēlmei.
Ko darīt? Iejaukties elementu kārtošanas funkcijā.
Ja šādai kontrolei (kā ListView) ir ieslēgts Sort (asc/desc), tad pēc katra elementa pievienošanas tā sevī iekšienē izsauc Sort() funkciju. Sort funkcija pēc kaut kāda (mums nezināma) algoritma sakārto elementus pieprasītajā secībā. Taču skaidrs ir viens, ka neatkarīgi no izvēlētā kārtošanas algoritma (shell sort, quicksort, heapsort), Sort funkcijai būs nepieciešams salīdzināt divus elementus – “kas lielāks, zirgs vai 4nieks?”. Tātad, lai veidotu savu sakārtojumu ListView kontrolē, atliekt tikai piespiest tās kārtotāju izmantot kādu programmētāja definētu salīdzināšanas operāciju.
Šī vajadzība .NET arhitektūrā ir risināta ar IComparer interfeisa palīdzību. Interfeiss pieprasa implementēt vienu funkciju – Compare(x,y), kas atgriež negatīvu vērtību, ja x<y, pozitīvu vērtīby, ja x>y vai 0, ja x=y.
Jāatceras, ka Compare() tiks izsaukts tiem elementiem, ko mēģinām salīdzināt, nevis to labeliem, piemēram. Tas ir, ListView gadījumā mums jāveido funkcija, kas māk salīdzināt objektus ar tipu ListViewItem.
Izveidoju šādu klasi:

Public Class SimpleComparer
  Implements IComparer
  Public Function Compare(ByVal x As Object, ByVal y As Object) _
        As Integer
    Implements System.Collections.IComparer.Compare
    Dim s1 As String = CType(x, ListViewItem).SubItems(0).Text
    Dim s2 As String = CType(y, ListViewItem).SubItems(0).Text
    Dim d1 As Date = DateSerial( _
        CInt(Mid(s1, 7)), CInt(Mid(s1, 4, 2)), CInt(Mid(s1, 1, 2)))
    Dim d2 As Date = DateSerial( _
        CInt(Mid(s2, 7)), CInt(Mid(s2, 4, 2)), CInt(Mid(s2, 1, 2)))
    Return Date.Compare(d1, d2)
  End Function
End Class
Šeit SubItems(0) tiek lietots, lai norādītu kolonnu, pēc kuras jāsalīdzina. Kā zināms, ListViewItem var saturēt vairākas datu kolonnas – piemēram, to bieži var redzēt Windows Explorer logā, kad ieslēgts “Detail view”.
Tātad tagad mums ir savs salīdzinātājs. Diemžēl neviens to neizmanto : ( Ko darīt – rakstīt savu ListView klasi (mantotu no Windows.Forms.ListView), kas izmanto tieši šo salīdzinātāju. To darīju šādi:
Public Class MyListView
  Inherits Windows.Forms.ListView
  Private ocComparer As SimpleComparer
   Public Sub New()
    ocComparer = New SimpleComparer
    Me.ListViewItemSorter = ocComparer
    Me.Sorting = SortOrder.Ascending
   End Sub
End Class

Tagad ir izveidota ListView klase, kas elementu salīdzināšanai izmanto savu salīdzināšanas funkciju. Atliek vien to ielikt kādā savā formā un lietot uz nebēdu.

CLR un COM3

Zināt, kāpēc produkts saucas CLR (The Common Language Runtime, kas izpilda jūsu .net aplikācijas), nevis COM3, kā tam bija patiesībā jāsaucas? Tikai tāpēc, ka ir vairākas Windows versijas, kurās nav iespējams uztaisīt direktoriju ar nosaukumu COM3 (traucē porta nosaukums, kurš ar to sakrīt).

BF valoda

Nav gan īsti par VB, bet nu tomēr ierakstīšu šeit.
Ir tāda valoda kā Brainfuck vai BF.
Tad nu cilvēki ir saņēmušies un uzrakstījuši šīs valodas kompilatoru. Tagad savus Brainfuck skriptus varat izpildīt arī ASP.net lapās un citur.
Kā izrādās, šādi cilvēki ir pat vairāki – šeit un arī šeit
Lai veicas skriptošanā!

Threadi

Mazliet par pavedieniem (threads) un VB.NET. Kopš .net CLR un CIL parādīšanās pavedieni vairs nav tikai "izvirtība priekš C++ programmētājiem" — tā ir arī ērti pieejama izvirtība VB programmētājiem.
Pirmkārt — kāpēc gan niekoties ar pavedieniem, ja viss tāpat jau darbojas? Viens no vienkāršākajiem pielietojumiem — ir vajadzīga aplikācija ar lietotāja interfeisu, kas paredzēta nepacietīgam cilvēkam, kuram neder, ka ineterfeiss "bremzē", bet ir nepieciešams veikt darbības, kuras aizņem daudz laika, kuras turklāt nav pārtraucamas. Piemēram, varam iedomāties aplikāciju, kas pār HTTP savienojumu ielādē ierakstus kādā listbox vai tamlīdzīgā kontrolē (kaut vai e-pasta vēstules, kas pa vienai parādās sarakstā). Kamēr dati lādējas, lietotājam varētu ļaut darīt arī kaut ko citu (piemēram, pakustināt formu).
Iedomājamies, ka ir nepieciešams ielādēt datus no HTTP, bet tajā laikā GUI jābūt brīvi pieejamam.
Datus varam dabūt ar šādu metodi

Public Function GetIt()
  Dim o As System.Net.HttpWebRequest
  o = System.Net.WebRequest.Create("http://www.asdf.lv/")
  Dim sText As String = New System.IO.StreamReader( _
    o.GetResponse.GetResponseStream()).ReadToEnd
  return sText
End Function
Skaidrs ir tas, ka šī metode izpildīsies "uzreiz", tātad, ja savienojums būs lēns, UI tajā laikā neatjaunosies.
Ko darīt. Izveidojam jaunu "darbinieka" klasi, kas veiks vajadzīgo darbu:
Public Class loader
  Public Event DataLoaded(ByVal result As String)
  Public Sub GetIt()
    Dim o As System.Net.HttpWebRequest
    o = System.Net.WebRequest.Create("http://www.asdf.lv/")
    Dim sText As String = New System.IO.StreamReader( _
    o.GetResponse.GetResponseStream()).ReadToEnd
    RaiseEvent DataLoaded(sText)
  End Sub
End Class
Šī klase definē arī savu notikumu (event) — DataLoaded. Notikums iestāsies tad, kad dati būs ielādēti. Notikums arī palīdzēs nogādāt ielādētos datus no izpildītāja pie "īpašnieka".
Ko darīt īpašniekam? Pieņemam, ka īpašnieks ir Windows forma, kurā nepieciešams ielādēt datus. Formai pirmkārt nepieciešama privāta izpildītāja klases instance.
Public WithEvents loaders As loader
Šo instanci nevaram deklarēt tās metodes ķermenī, kura izsauks strādātājpavedienu, jo mēs taču ceram, ka metode izsauks ielādes procedūru un turpinās darbu. Tātad, iespējams, beigsies pirms strādātājpavediena metodes.
Vēl nepieciešams deklarēt pašu pavedienu. To var izdarīt ar sekojošu rindu:
Public myThread As System.Threading.Thread
. Nu, kas tad vēl atliek… pavediena inicializācija un datu ķeršana. Šeit arī kopējais formas kods (izņemot kontroļu ģenerācijas kodu)
Public Class Forma
  Public WithEvents loaders As loader
  Public myThread As System.Threading.Thread
  Private Sub Button1Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
    Handles Button1.Click
    loaders = New loader()
    myThread = New System.Threading.Thread(AddressOf loaders.GetIt)
    myThread.Start() `sākas patiesā ielāde
  End Sub

  Private Sub loaders
a(ByVal result As String) _
     Handles loaders.DataLoaded
    Me.TextBox1.Text = result
  End Sub
End Class

Šeit arī redzams, ka, nospiežot pogu button1 tiek izveidota jauna pavediena instance, kurai tiek nodota izpildāmās metodes adrese. Šajā gadījumā tiek izmantota klases instances metode, bet tikpat labi metode varēja būt arī statiska.
Pēc tam pavediens tiek palaists darbā (un forma aizmirst par to, ka tas darbojas).
Vēlāk tiek apstrādāts notikums, ko radījusi ielādes klase – dati ir ielādēti.