sty/100
[C#] Easy solution for AutoScroll in GridView
Poking around standard WinForms GridView control I found it have not any “Autoscroll” properties. A proper approach would be create new control inherited from base GridView with altered component drawing and focusing etc, but I didn’t have time and will to do this, especially for my extremally simple FileSorter tool. So I came up with easy solution.
The Problem:
Application have a grid, which is in real time adding new rows. When it’s view area height is reached, more rows are showed hidden “below” the view area of grid, and user is forced to use vertical scroll bar to scroll to them.
Solution:
We can use “FirstDisplayCell” property of DataGridView property
Code:
if(_grid.Rows.Count>0 && _grid.Rows[_grid.Rows.Count-1].Cells.Count>0) _grid.FirstDisplayedCell = _grid.Rows[_grid.Rows.Count-1].Cells[0];
Solution in 2 lines of code
Additional Notes:
Method is thread safe and updates GUI properly using Invoke while invoking on a _grid object
gru/090
[C#] How to play simple system sound ?
Its pretty simple to play system sounds, for example at the end of some operation:
using System.Media; //.... SystemSounds.Asterisk.Play(); //Makes standard "warning" ding
lis/090
[.NET] Showing menu on button click
This extremally easy thing, sometimes makes so much problem for someone who don’t know how to use .NET build in functions. As I saw, similar problem implementation in six lines of code (with pretty much calculations) it’s can be easily achived in one line.
Problem: we want to show menu, on center of a button, which was clicked.
Graphic representation:

Solution:
_context_menu.Show(_button_myButton, new Point(_button_myButton.Width/2, _button_myButton.Height/2));
lis/090
[.NET] Relative path
Below is a quick and dirty implementation of solution for how to get relative path to file from a folder
Problem1:
Path1 (base): C:\data\my_path
Path2 (target): C:\some_other\folder\data\file.txt
Result: ..\..\some_other\folder\data\file.txt
Problem2:
Path1 (base): C:\data\path
Path2 (target): C:\data\path\somedata\file.txt
Result: .\somedata\file.txt
Solution:
public static string getRelativePath(String baseDirectory, string destinationFile)
{
List<String> _from = new List<String>(baseDirectory.Split('\\'));
List<String> _dest = new List<String>(destinationFile.Split('\\'));
if (_from[0].ToLower() != _dest[0].ToLower() || _dest.Count==0 || _from.Count==0)
{
return destinationFile; //Not same disk
}
int lvl = 0;
for(int i=0; i<_from.Count; i++)
{
if (_from[i].ToLower() != _dest[i].ToLower())
{
break;
}
lvl++;
}
for (int j = 0; j < lvl; j++)
{
_from.RemoveAt(0);
_dest.RemoveAt(0);
}
String _resultPath = "";
if (_from.Count == 0) //the file is in higher folder
{
_resultPath += ".\\";
for (int i = 0; i < _dest.Count; i++)
{
_resultPath += _dest[i];
if (i + 1 < _dest.Count)
_resultPath += "\\";
}
}
else
{
for (int i = 0; i < _from.Count; i++)
{
_resultPath += "..\\";
}
for (int i = 0; i < _dest.Count; i++)
{
_resultPath += _dest[i];
if (i + 1 < _dest.Count)
_resultPath += "\\";
}
}
return _resultPath;
}
paź/090
[C#] How to associate user component with other component which is null at initialization
Problem:
The idea is to put my own component on form and associate it with already existing TabControl object. Also, I wanted to capture user pressed keys and react on them before they will reach other controls (which require to set KeyPreview to true and add KeyDown/KeyUp event). Willing to encapsulate this process behind developer eyes, I write appropriate code in property of my component. But when I added control in Component Designer I was receiving error while starting application, initialize code of my component was written in InitializeComponent() method before the base Form object was initialized. Like this:
void InitializeComponent()
{
//....
//
// MyComponent
//
this.myComponent.ParentTab = this.tabControl1;
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(522, 273);
this.Controls.Add(this.tabControl1);
this.KeyPreview = true;
this.Name = "Form1";
this.Text = "Form1";
this.myComponent1.ResumeLayout(false);
this.ResumeLayout(false);
}
And in my code I was calling the uninitialized code:
//... myComponent1.FindForm().KeyDown += //...
FindForm() was returning null (as the form wasn’t yet initialized)
Solution:
- Create KeyDown (or any) event for the TabControl itself
- At runtime, check in KeyDown state of the form you want to hook to. If it’s null, you can use the FindForm() because at runtime all controls are already initialized !
Solution was implemented in TabSwitcher component I wrote, for the TabControl control.
It’s that simple!
paź/090
[C#] Własny ciąg wynikowy z daty i czasu
Tabela ze skrótami umożliwiającymi formatowania daty i czasu z wykorzystaniem…
DateTime.Now.ToString("ddMMyyyyHHmmss") ;
// Wynik: 01102009094315
… znajduje się tutaj: http://msdn.microsoft.com/en-us/library/8kb3ddd4.aspx
sie/090
[C#] Niskopoziomowe przechwytywanie klawiszy klawiatury
Aby przechwycić naciśnięcie klawiszy na niskim poziomie (np. przed wszystkimi innymi aplikacjami) należy skorzystać z InteropSys aby “wpiąć” się do API systemu operacyjnego. Poniższy przykład z bloga Stephena Touba pokazuje jak to szybko i prosto zrobić:
using System;
using System.Diagnostics;
using System.Windows.Forms;
using System.Runtime.InteropServices;
class InterceptKeys
{
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
private const int WM_KEYUP = 0x0101;
private static LowLevelKeyboardProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;
public static void Main()
{
_hookID = SetHook(_proc);
Application.Run();
UnhookWindowsHookEx(_hookID);
}
private static IntPtr SetHook(LowLevelKeyboardProc proc)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
return SetWindowsHookEx(WH_KEYBOARD_LL, proc,
GetModuleHandle(curModule.ModuleName), 0);
}
}
private delegate IntPtr LowLevelKeyboardProc(
int nCode, IntPtr wParam, IntPtr lParam);
private static IntPtr HookCallback(
int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
int vkCode = Marshal.ReadInt32(lParam);
Console.WriteLine((Keys)vkCode);
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook,
LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
}
“Problem” zaczyna się w przypadku gdy chcemy obsłużyć naciśnięcie kombinacji klawiszy, a w szczególnym przypadku ALT i jakiegoś innego klawisza. ALT nie jest “przechwytywany” przez hook, przez co funkcję private static IntPtr HookCallback należy nieco przerobić, korzystając ze wcześniej zdefiniowanej struktury KeyboardHookStruct w której to możemy sprawdzić również flagi naciśniętych przycisków (w tym przypadku ALT+ESC):
public struct KeyboardHookStruct
{
public int vkCode;
public int scanCode;
public int flags;
public int time;
public int dwExtraInfo;
};
// ...
KeyboardHookStruct hookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
if (hookStruct.vkCode == ESCAPE && (hookStruct.flags & LLKHF_ALTDOWN) == hookStruct.flags)
MessageBox.Show("I pressed ALT + ESC!");
Kilka uwag:
- Należy zauważć że w tym przypadku najpierw sprawdzamy drugi klawisz z kombinacji, a potem fakt czy jest wciśnięty ALT.
- Druga istotna rzecz to fakt zastosowania porównania wartości klawiszy do znanych z WinAPI nazw klawiszy wirtualnych.
- Listę kodów klawiszy wirtualnych można znaleźć na tej stronie Microsoft MSDN
- Łatwo napisać aplikację która przechwytuje klawisze, ale nie przekazuje ich dalej do kolejnych aplikacji zarejestrowanych w systemie w ramach łańcucha uchwytów. Wystarczy usunąć wywołania do funkcji: CallNextHookEx
- Jeżeli wciśniemy klawisz i przytrzymamy, informacje o jego naciśnięciu (KEYDOWN) będą w kółko (w odpowiednim odstępie czasowym) przesyłane. Należy to odpowiednio obsłużyć.
- Przykład z klawiszem ALT będzie wykonywał się dwa razy, ze względu na fakt że w nacisnięcie klawisza to w rzeczywistości dwie akcje. Naciśnięcie (KEY_DOWN) oraz podniesienie (KEY_UP) klawisza. Można to naprawić poprzez rozpoznanie jaka aktualnie akcja (DOWN/UP) jest wykonywana (poprzez porównanie z wParam) lub poprzez dodanie prostego semaforu
lip/090
[.NET] Klikanie myszą w danej lokalizacji
Klikanie myszą w danej lokalizacji (W punkcie p) zrealizowane w C#. Niby proste, jednak nie oczywiste. Poniższy kod umożliwia wykonanie kliknięcia lewym przyciskiem myszy. Należy zauważyć że składa się on z dwóch eventów które można przetłumaczyć na “LeftMouseButtonDown” oraz “LeftMouseButtonUp”:
using System.Runtime.InteropServices;
//...
[DllImport("user32.dll")]
public static extern void mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo);
public void DoMouseClick(Point p)
{
mouse_event(2, p.X, p.Y, 0, 0);
mouse_event(4, p.X, p.Y, 0, 0);
}
Więcej tutaj: http://msdn.microsoft.com/en-us/library/ms646260(VS.85).aspx
lip/090
[.NET] Zwalnianie nadmiarowej pamięci zaalokowanej przez CLR
Poniżej przedstawiony kawałek kodu osobiście uważam za “Świętego Grala” .NET, chociaż pochodzi prosto z WinApi. Umożliwia wymuszenie na aplikacji napisanej w .NET na zwolnienie za alokowanej pamięci w której aktualnie nie przechowuje obiektów.
.NET wykonując swoje programy zawsze alokuje więcej pamięci niż potrzebuje, gdyż polityka kompilatora zakłada że proces alokowania pamięci jest czasochłonny. Faktycznie tak jest, jednak w zależności od sytuacji ta czasochłonność może nie być zauważona. Wielu użytkowników panicznie interpretuje zapisy w Menedżerze zadań (taskmgr.exe) w którym to zwykła aplikacja posiadająca pojedynczą formatkę i przycisk zajmuje 11MB pamięci. Wskazanie to jest nieprawidłowe, jednak w tej chwili nie będę wdawał się w szczegóły, poza krótkim stwierdzeniem, że obiekty aplikacji zajmują mniej pamięci, a znaczna większość z tych 11MB to za alokowana przestrzeń pamięci dla aplikacji, która pozostaje pusta (a można ją zwrócić do systemu).
Do zalet stosowania poniższej funkcji dotychczas mogę zaliczyć dwie rzeczy:
- Możliwość pisania aplikacji rezydentnych w .NET (aplikacja co jakiś wywołuje zwolnienie pamięci, przez co jest w stanie zminimalizowanym może zajmować nawet 500KB)
- Możliwość uniknięcia wyjątku “OutOfMemoryException”, który jest generowany na podstawie wiadomości wysyłanej przez system operacyjny do wszystkich aplikacji, gdy ilość pamięci spadnie do 32MB (Sprawdzone w kodzie produkcyjnym).
using System.Runtime.InteropServices;
//(....)
[DllImport("kernel32.dll")]
public static extern bool SetProcessWorkingSetSize(IntPtr proc, int min, int max);
public void CutRamUsage()
{
GC.Collect();
GC.WaitForPendingFinalizers();
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
{
SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1);
}
}
Więcej informacji pod adresem:
http://msdn.microsoft.com/en-us/library/ms686234(VS.85).aspx
cze/090
[.NET] Ukrywanie okna konsoli
Aby schować okno konsoli, w ramach Console Window Application należy skorzystać z interops i biblioteki systemu windows:
using System.Runtime.InteropServices
//...[DllIMport("user32.dll")]
private static extern IntPtr FindWindow(IntPtr hWnd, int nCmdShow);
[DllIMport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
//...
if(hWnd != IntPtr.Zero)
{
ShowWindow(hWnd, 0) // Ukryj okno
//ShowWindow(hWnd, 1) // Pokaż okno
}