Zakończyła się kampania społeczna Stowarzyszenia Droga i Bezpieczeństwo. W ramach tej akcji Stowarzyszenie zbierało środki na pomoc ofiarom wypadków drogowych. Zakres działań obejmował spot w TV, informacje radiowe i prasowe, a także stronę internetową, którą zaprogramowałem wraz z mechanizmem obsługi SMSów. Każdy wysłany SMS zasilał konto fundacji, a na stronie, w czasie rzeczywistym, zapalał nową gwiazdę. Archiwum projektu można obejrzeć na serwerze Epeironu.
Monitoring stron internetowych
Uruchomiłem nową usługę w internecie, a w zasadzie przywróciłem do życia bardzo stary projekt. Jest to monitoring stron www, którego zadaniem jest kontrola pozycji stron w wyszukiwarkach internetowych oraz kontrola działania tych stron.
Projekt już kiedyś był zrealizowany – kilka lat temu, jednak chyba wtedy było zbyt wcześnie na tego typu usługę. W zasadzie szkoda – wtedy byłem pierwszy w polskim internecie, mogłem wytrwać i mieć dzisiaj ugruntowaną pozycję. Obecnie istnieje na rynku spora konkurencja. Piszę to jako zachętę dla was do działania – masz pomysł, to go realizuj i nie poddawaj się po pierwszych trudnościach.
Jak zrobić gradientowane tło kontrolki?
Należy nadpisać metodę OnPaintBackground() i samodzielnie zaprogramować rysowanie tła. Do rysowania gradientów można posłużyć się klasą LinearGradientBrush, np.:
using System.Drawing.Drawing2D;
override protected void OnPaintBackground(PaintEventArgs e)
{
LinearGradientBrush brush =
new System.Drawing.Drawing2D.LinearGradientBrush(ClientRectangle, Color.Red, Color.White, 0f);
e.Graphics.FillRectangle(brush, ClientRectangle);
}
Lista komunikatów Windows
Nawiązując do poprzedniego postu poniżej wklejam listę komunikatów windows.
Komunikaty windows:
WM_NULL = 0x00 WM_CREATE = 0x01 WM_DESTROY = 0x02 WM_MOVE = 0x03 WM_SIZE = 0x05 WM_ACTIVATE = 0x06 WM_SETFOCUS = 0x07 WM_KILLFOCUS = 0x08 WM_ENABLE = 0x0A WM_SETREDRAW = 0x0B WM_SETTEXT = 0x0C WM_GETTEXT = 0x0D WM_GETTEXTLENGTH = 0x0E WM_PAINT = 0x0F WM_CLOSE = 0x10 WM_QUERYENDSESSION = 0x11 WM_QUIT = 0x12 WM_QUERYOPEN = 0x13 WM_ERASEBKGND = 0x14 WM_SYSCOLORCHANGE = 0x15 WM_ENDSESSION = 0x16 WM_SYSTEMERROR = 0x17 WM_SHOWWINDOW = 0x18 WM_CTLCOLOR = 0x19 WM_WININICHANGE = 0x1A WM_SETTINGCHANGE = 0x1A WM_DEVMODECHANGE = 0x1B WM_ACTIVATEAPP = 0x1C WM_FONTCHANGE = 0x1D WM_TIMECHANGE = 0x1E WM_CANCELMODE = 0x1F WM_SETCURSOR = 0x20 WM_MOUSEACTIVATE = 0x21 WM_CHILDACTIVATE = 0x22 WM_QUEUESYNC = 0x23 WM_GETMINMAXINFO = 0x24 WM_PAINTICON = 0x26 WM_ICONERASEBKGND = 0x27 WM_NEXTDLGCTL = 0x28 WM_SPOOLERSTATUS = 0x2A WM_DRAWITEM = 0x2B WM_MEASUREITEM = 0x2C WM_DELETEITEM = 0x2D WM_VKEYTOITEM = 0x2E WM_CHARTOITEM = 0x2F WM_SETFONT = 0x30 WM_GETFONT = 0x31 WM_SETHOTKEY = 0x32 WM_GETHOTKEY = 0x33 WM_QUERYDRAGICON = 0x37 WM_COMPAREITEM = 0x39 WM_COMPACTING = 0x41 WM_WINDOWPOSCHANGING = 0x46 WM_WINDOWPOSCHANGED = 0x47 WM_POWER = 0x48 WM_COPYDATA = 0x4A WM_CANCELJOURNAL = 0x4B WM_NOTIFY = 0x4E WM_INPUTLANGCHANGEREQUEST = 0x50 WM_INPUTLANGCHANGE = 0x51 WM_TCARD = 0x52 WM_HELP = 0x53 WM_USERCHANGED = 0x54 WM_NOTIFYFORMAT = 0x55 WM_CONTEXTMENU = 0x7B WM_STYLECHANGING = 0x7C WM_STYLECHANGED = 0x7D WM_DISPLAYCHANGE = 0x7E WM_GETICON = 0x7F WM_SETICON = 0x80 WM_NCCREATE = 0x81 WM_NCDESTROY = 0x82 WM_NCCALCSIZE = 0x83 WM_NCHITTEST = 0x84 WM_NCPAINT = 0x85 WM_NCACTIVATE = 0x86 WM_GETDLGCODE = 0x87 WM_NCMOUSEMOVE = 0xA0 WM_NCLBUTTONDOWN = 0xA1 WM_NCLBUTTONUP = 0xA2 WM_NCLBUTTONDBLCLK = 0xA3 WM_NCRBUTTONDOWN = 0xA4 WM_NCRBUTTONUP = 0xA5 WM_NCRBUTTONDBLCLK = 0xA6 WM_NCMBUTTONDOWN = 0xA7 WM_NCMBUTTONUP = 0xA8 WM_NCMBUTTONDBLCLK = 0xA9 WM_KEYFIRST = 0x100 WM_KEYDOWN = 0x100 WM_KEYUP = 0x101 WM_CHAR = 0x102 WM_DEADCHAR = 0x103 WM_SYSKEYDOWN = 0x104 WM_SYSKEYUP = 0x105 WM_SYSCHAR = 0x106 WM_SYSDEADCHAR = 0x107 WM_KEYLAST = 0x108 WM_IME_STARTCOMPOSITION = 0x10D WM_IME_ENDCOMPOSITION = 0x10E WM_IME_COMPOSITION = 0x10F WM_IME_KEYLAST = 0x10F WM_INITDIALOG = 0x110 WM_COMMAND = 0x111 WM_SYSCOMMAND = 0x112 WM_TIMER = 0x113 WM_HSCROLL = 0x114 WM_VSCROLL = 0x115 WM_INITMENU = 0x116 WM_INITMENUPOPUP = 0x117 WM_MENUSELECT = 0x11F WM_MENUCHAR = 0x120 WM_ENTERIDLE = 0x121 WM_CTLCOLORMSGBOX = 0x132 WM_CTLCOLOREDIT = 0x133 WM_CTLCOLORLISTBOX = 0x134 WM_CTLCOLORBTN = 0x135 WM_CTLCOLORDLG = 0x136 WM_CTLCOLORSCROLLBAR = 0x137 WM_CTLCOLORSTATIC = 0x138 WM_MOUSEFIRST = 0x200 WM_MOUSEMOVE = 0x200 WM_LBUTTONDOWN = 0x201 WM_LBUTTONUP = 0x202 WM_LBUTTONDBLCLK = 0x203 WM_RBUTTONDOWN = 0x204 WM_RBUTTONUP = 0x205 WM_RBUTTONDBLCLK = 0x206 WM_MBUTTONDOWN = 0x207 WM_MBUTTONUP = 0x208 WM_MBUTTONDBLCLK = 0x209 WM_MOUSEWHEEL = 0x20A WM_MOUSEHWHEEL = 0x20E WM_PARENTNOTIFY = 0x210 WM_ENTERMENULOOP = 0x211 WM_EXITMENULOOP = 0x212 WM_NEXTMENU = 0x213 WM_SIZING = 0x214 WM_CAPTURECHANGED = 0x215 WM_MOVING = 0x216 WM_POWERBROADCAST = 0x218 WM_DEVICECHANGE = 0x219 WM_MDICREATE = 0x220 WM_MDIDESTROY = 0x221 WM_MDIACTIVATE = 0x222 WM_MDIRESTORE = 0x223 WM_MDINEXT = 0x224 WM_MDIMAXIMIZE = 0x225 WM_MDITILE = 0x226 WM_MDICASCADE = 0x227 WM_MDIICONARRANGE = 0x228 WM_MDIGETACTIVE = 0x229 WM_MDISETMENU = 0x230 WM_ENTERSIZEMOVE = 0x231 WM_EXITSIZEMOVE = 0x232 WM_DROPFILES = 0x233 WM_MDIREFRESHMENU = 0x234 WM_IME_SETCONTEXT = 0x281 WM_IME_NOTIFY = 0x282 WM_IME_CONTROL = 0x283 WM_IME_COMPOSITIONFULL = 0x284 WM_IME_SELECT = 0x285 WM_IME_CHAR = 0x286 WM_IME_KEYDOWN = 0x290 WM_IME_KEYUP = 0x291 WM_MOUSEHOVER = 0x2A1 WM_NCMOUSELEAVE = 0x2A2 WM_MOUSELEAVE = 0x2A3 WM_CUT = 0x300 WM_COPY = 0x301 WM_PASTE = 0x302 WM_CLEAR = 0x303 WM_UNDO = 0x304 WM_RENDERFORMAT = 0x305 WM_RENDERALLFORMATS = 0x306 WM_DESTROYCLIPBOARD = 0x307 WM_DRAWCLIPBOARD = 0x308 WM_PAINTCLIPBOARD = 0x309 WM_VSCROLLCLIPBOARD = 0x30A WM_SIZECLIPBOARD = 0x30B WM_ASKCBFORMATNAME = 0x30C WM_CHANGECBCHAIN = 0x30D WM_HSCROLLCLIPBOARD = 0x30E WM_QUERYNEWPALETTE = 0x30F WM_PALETTEISCHANGING = 0x310 WM_PALETTECHANGED = 0x311 WM_HOTKEY = 0x312 WM_PRINT = 0x317 WM_PRINTCLIENT = 0x318 WM_HANDHELDFIRST = 0x358 WM_HANDHELDLAST = 0x35F WM_PENWINFIRST = 0x380 WM_PENWINLAST = 0x38F WM_COALESCE_FIRST = 0x390 WM_COALESCE_LAST = 0x39F WM_DDE_FIRST = 0x3E0 WM_DDE_INITIATE = 0x3E0 WM_DDE_TERMINATE = 0x3E1 WM_DDE_ADVISE = 0x3E2 WM_DDE_UNADVISE = 0x3E3 WM_DDE_ACK = 0x3E4 WM_DDE_DATA = 0x3E5 WM_DDE_REQUEST = 0x3E6 WM_DDE_POKE = 0x3E7 WM_DDE_EXECUTE = 0x3E8 WM_DDE_LAST = 0x3E8 WM_USER = 0x400 WM_APP = 0x8000
DoubleClick na ListView nie działa na pustym obszarze
Kto by się spodziewał, programiści frameworka .NET doszli do wniosku, że zdarzenia Click oraz DoubleClick (i pochodne) mają działać tylko po kliknięciu myszką w istniejące elementy listy. Jakie to powoduje ograniczenia? Otóż wymyśliłem sobie, że podwójne kliknięcie w puste miejsce kontrolki ListView powinno powodować utworzenie nowego elementu. Z rozpaczą jednak stwierdziłem, że to nie działa….
…zacząłem szukać w google…
…i zdruzgotany nic nie znalazłem – poza – kilkoma poradami, aby wprowadzić własną obsługę za pomocą Timera i MouseDown/MouseUp. Do bani.
A rozwiązanie istnieje i jest ono całkiem eleganckie. Należy utworzyć własną kontrolkę rozszerzającą ListView, a następnie nadpisać metodę
protected override void WndProc(ref Message m)
takim kawałkem kodu:
if (m.Msg == 0×203) //WM_LBUTTONDBLCLK
{
this.OnDoubleClick(MouseEventArgs.Empty);
}
else
{
base.WndProc(ref m);
}
I już. Przechwytujemy wszystkie komunikaty i w przypadku gdy jest to podwójne kliknięcie, wywołujemy zawsze zdarzenie OnDoubleClick. Podobnie można zrobić dla pojedynczego kliknięcia, tylko sobie ustalcie ID komunikatu.
Maziagenerator
W toku jakże intensywnych prac nad efektami specjalnymi we flashu, przez przypadek stworzyłem maziageneratora – program, który także tobie pozwoli poczuć się prawdziwym impresjonistą.
Tak się kończą zabawy z Bitmap.draw, BlendMode.OVERLAY i krzywymi lissajou:
(kliknij i zacznij rysować)
Laden, bitte warten!
Sympatyczna strona na kampanię promocyjną firmy Komandor. I chociaż, jak to w życiu bywa, klient drastycznie zmienił naszą wstępną koncepcję, to mimo tego udało się na tyle zachować ducha strony, że przy jej pisaniu miałem odpowiednią ilość motywującego “funu” :)
Chcecie obejrzeć stronę na żywo? Klikajta w obrazek!
C# .NET – pobieranie pliku z serwera HTTP
W jaki sposób w C# zrealizować ściąganie pliku z serwera www? Rozwiązanie jest proste, nawet, zbyt proste :) Oto najkrótszy z możliwych sposobów:
using System.Net; namespace DownloadTest { class Program { static void Main(string[] args) { WebClient webClient = new WebClient(); try { webClient.DownloadFile("http://server/file", "myFileName"); } catch (WebException e) { //some error } } } }
Metoda DownloadFile z klasy webClient przyjmuje dwa parametry: String lub URI z adresem sieciowym pliku oraz String z nazwą pod jaką plik zostanie zapisany lokalnie. Jednak ta metoda jest częściowo nieelegancka – blokuje całkowicie wątek aż do zakończenia pobierania pliku. Nie możemy w tym czasie odebrać żadnych eventów (np. użytkownik nie będzie w stanie przerwać operacji ściągania) ani np. informować o postępie. Zamiast niej możemy jednak użyć podobnej metody – DownloadFileAsync(Uri, String):
using System; using System.Net; namespace DownloadTest { class Program { static void Main(string[] args) { WebClient webClient = new WebClient(); webClient.DownloadFileCompleted += new System.ComponentModel.AsyncCompletedEventHandler(webClient_DownloadFileCompleted); webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(webClient_DownloadProgressChanged); try { webClient.DownloadFileAsync(new Uri("http://server/file"), "myFileName"); } catch (WebException e) { //stało się coś bardzo niedobrego ;) } } static void webClient_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) { //zobaczmy ile procent pobrano: Console.WriteLine(e.ProgressPercentage.ToString()); } static void webClient_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e) { //zakończono pobieranie pliku } } }
Warto zapamiętać, że za pomocą DownloadFileAsync nie możemy w jednym wątku ściągać jednocześnie więcej niż jednego pliku – w przypadku próby dokonania takiej operacji otrzymamy wyjątek. Dlatego z pobieraniem kolejnego pliku musimy poczekać do zakończenia pobierania poprzedniego.
Prosto z Commodore 64: tunel
Kolejny sympatyczny efekt, wykorzystywany bardzo często w produkcjach demosceny to tunel. W milionach różnych wersji, wyewoluował od całkiem prostej realizacji rysowanych okręgów do hiper-super-triple teksturowanego tunelu z dynamicznym oświetleniem, cieniami i bajerami.
Realizacja tego efektu we flashu jest jak najbardziej możliwa. Sprawa wygląda prosto:
package { import flash.display.Sprite; import flash.events.Event; import flash.display.BitmapData; import flash.display.Bitmap; import flash.display.StageAlign; import flash.display.StageScaleMode; [SWF(width="500", height="500", backgroundColor="0x000000")] public class TunnelEfect extends Sprite { private var w:int = 250; private var h:int = 250; private var texWidth:int = 256; private var texHeight:int = 256; private var texture:Array = new Array(texWidth); private var distanceTable:Array = new Array(w * 2); private var angleTable:Array = new Array(w * 2); private var animationShift:Number = 0; private var bitmap:BitmapData = new BitmapData(w * 2, h * 2, false, 0x000000); public function TunnelEfect() { var angle:int, distance:int, ratio:Number = 32; this.stage.scaleMode = StageScaleMode.NO_SCALE; this.stage.align = StageAlign.TOP_LEFT; this.stage.frameRate = 20; this.stage.addEventListener(Event.ENTER_FRAME, onEnterFrame); this.addChild(new Bitmap(bitmap)); for(var x:int = 0; x < texWidth; x++) { texture[x] = new Array(texHeight); for(var y:int = 0; y < texHeight; y++) { texture[x][y] = ((x) ^ (y)) * 256; } } for(x = 0; x < w * 2; x++) { distanceTable[x] = new Array(h * 2); angleTable[x] = new Array(h * 2); for(y = 0; y < h * 2; y++) { distance = (ratio * texHeight / Math.sqrt(((x - w) * (x - w) + (y - h) * (y - h)))) % texHeight; angle = Math.abs(0.5 * texWidth * Math.atan2((y - h), (x - w)) / 3.1416); distanceTable[x][y] = distance; angleTable[x][y] = angle; } } } private function draw():void { var shiftX:int, shiftY:int, shiftLookX:int, shiftLookY:int, color:Number; shiftX = (texWidth * 1.0 * animationShift); shiftY = (texHeight * 0.25 * animationShift); shiftLookX = w / 2 + (w / 2 * Math.cos(animationShift)); shiftLookY = h / 2 + (h / 2 * Math.sin(animationShift * 2.0)); for(var x:int = 0; x < w; x++) { for(var y:int = 0; y < h; y++) { color = texture[Math.abs(distanceTable[x + shiftLookX][y + shiftLookY] + shiftX) % texWidth][Math.abs(angleTable[x + shiftLookX][y + shiftLookY] + shiftY) % texHeight]; bitmap.setPixel(x * 2, y * 2, color); } } } private function onEnterFrame(e:Event):void { draw(); animationShift += 0.05; } } }
A oto i gotowy tunel:



