Prosty kafelkowy pulpit
Wstęp
Artykuł pokazuje, jak stworzyć prosty kafelkowy pulpit na Windows 7 i Windows XP.
W chwili publikacji na rynku funkcjonują już systemy Windows 8, Windows 8.1, a wkrótce ma wejść do sprzedaży wersja Windows 10.
Wszystkie one posiadają kafelkowe pulpity, przy czym pulpity te są znacznie bardziej rozbudowane, niż pokazany w artykule.
Sylistyka kafelków jednym przypada do gustu, innym nie. Autor jest w tej pierwszej grupie, niemniej ze względów zawodowych porusza się wciąż po systemach starszych.
Ale i w tych systemach pokazany pulpit może znaleźć zastosowanie, jako element urozmaicający interfejs użytkownika.
Można tak pokazywać niekoniecznie pulpit, ale wybrane foldery związane np. z tworzonymi aplikacjami.
Kontrolka kafelek (Tile) nie została wykorzystana wprost, ale za pośrednictwem panelu TileDesktopGroup, który umieszcza się na formie podczas projektowania.
Na końcu artykułu autor zamieścił projekt do pobrania.
Kod kontrolki kafelek (Tile)
using System;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using System.IO;
using System.Diagnostics;
namespace MetroEx
{
// kontrolka kafelek
public partial class Tile : UserControl
{
// kolory kafelków
private static Color[] BackColorPalette =
{
Color.Goldenrod,
Color.DarkSlateBlue,
Color.LimeGreen,
Color.OrangeRed,
Color.Blue,
Color.Teal
};
private string fileName; // nazwa pliku lub skrótu
// obliczenie ilości kolorów w palecie
private readonly int paletteBackColorCount = BackColorPalette.Count<Color>();
private readonly int gap = 8; // odstęp między kafelkami
private Font font = new Font("Tahoma", 8, FontStyle.Bold); // czcionka label1
// dla XP ikony będą 32x32 ze względu na dużą ilość starszych programów które nie miały ikon 48x48
private bool IsWinXP()
{
OperatingSystem OS = Environment.OSVersion;
return (OS.Platform == PlatformID.Win32NT) && ((OS.Version.Major == 5) && (OS.Version.Minor > 0));
}
// konstruktor kafelka - rozmieszczanie i nadawanie koloru
public Tile(int left, int top, int colorIndex)
{
InitializeComponent();
//
// Tile
//
this.Size = new Size(128, 128);
this.Location = new Point(left * (this.Width + gap) + gap, top * (this.Height + gap) + gap);
this.BackColor = BackColorPalette[colorIndex % paletteBackColorCount];
this.DoubleBuffered = true;
// podwójne kliknięcie w obszarze kafelka poza pictureBox1 i label1
// funkcja obsługi identyczna dla wszystkich składników komponentu
this.DoubleClick += new EventHandler(Tile_DoubleClick);
//
// pictureBox1
//
if (IsWinXP())
{
pictureBox1.Size = new Size(32, 32);
}
else
{
pictureBox1.Size = new Size(48, 48);
}
pictureBox1.Location = new Point(4, 4);
pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
// podwójne kliknięcie w obszarze pictureBox1
pictureBox1.DoubleClick += new EventHandler(Tile_DoubleClick);
//
// label1
//
label1.AutoSize = false;
label1.Location = new Point(4, pictureBox1.Bottom);
label1.Size = new Size(Width - 8, Height - pictureBox1.Height - 8);
label1.Font = font;
label1.TextAlign = ContentAlignment.BottomLeft;
label1.ForeColor = Color.White;
// podwójne kliknięcie w obszarze label1
label1.DoubleClick += new EventHandler(Tile_DoubleClick);
}
// obsługa podwójnego kliknięcia na kafelku; o rodzaju obsługi decyduje system w oparciu o rozszerzenie nazwy pliku
private void Tile_DoubleClick(object sender, EventArgs e)
{
try
{
Process Run = new Process();
Run.StartInfo.FileName = fileName;
Run.Start();
}
catch
{
// uruchomienie programu czasem poprzedza dialog z pytaniem czy zezwolić na uruchomienie
// odmowa użytkownia prowadzi do wyjątku który musi zostać tu obsłużony; wystarczy catch { }
}
}
// własność nazwa pliku (skrót do pliku docelowego lub plik docelowy)
public string FileName
{
set
{
fileName = value; // podstawienie wartości nazwa pliku
// wyłączenie wyświetlania wszystkich rozszerzeń nazw
label1.Text = Path.GetFileNameWithoutExtension(value); // nazwa wyświetlana na kafelku w label1
// rozwiązanie alternatywne - programista dokonuje wyboru ukrywanych rozszerzeń
/*
label1.Text = label1.Text.Replace(".dpr", "");
label1.Text = label1.Text.Replace(".csproj", "");
label1.Text = label1.Text.Replace(".exe", "");
label1.Text = label1.Text.Replace(".lnk", "");
*/
// pominięcie tekstu "Skrót do " w Windows XP
label1.Text = label1.Text.Replace("Skrót do ", "");
FileInfo fi = new FileInfo(value);
string ext = fi.Extension.ToLower();
if (ext == ".png" || ext.EndsWith(".jpg") || ext == ".bmp")
{
// szczególne zachowanie w celu wyświetlenia podglądu obrazka - zmienia się położenie i rozmiar pictureBox1 i label1
// autor pominął pliki których proporcje są typu portret (portrait)
int labelHeight = TextRenderer.MeasureText(label1.Text, font).Height;
pictureBox1.Location = new Point(0, 0);
pictureBox1.Size = new Size(this.Width, this.Height - 3 * labelHeight - 2);
Bitmap b = new Bitmap(value);
pictureBox1.Image = b;
label1.Location = new Point(0, pictureBox1.Bottom - 2);
label1.Size = new Size(this.Width, 3 * labelHeight);
}
else
{
pictureBox1.Image = ShellEx.GetBitmapFromPath(value);
}
}
get // pobranie wartości nazwa pliku
{
return fileName;
}
}
}
}
Kod kontrolki grupa kafelków (TileDesktopGroup)
using System;
using System.Drawing;
using System.Windows.Forms;
using System.IO;
namespace MetroEx
{
// kontrolka grupa kafelków pulpitu
class TileDesktopGroup : Panel
{
// stałe związane z rozmieszczniem kafelków (Tile)
// w szczególności vertDivider określa ilość kafelków w pionie
// istnieje zależność między paletą kolorów a doborem tych stałych
private const int vertDivider = 4;
private const int horzDivider = 4;
// tablice nazw plików
private string[] userFiles;
private string[] publicFiles;
// ścieżka do pulpitu użykownika
string userDesktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
// ścieżka do pulpitu publicznego (wspólnego wszystkich użykowników)
// część skrótów pomimo że jest wyświetlana na pulpicie użytkownika
// jest pobierana z katalogu pulpit publiczny
// i nie istnieje w katalogu pulpitu danego użytkownika
string publicDesktopPath = Environment.GetFolderPath(Environment.SpecialFolder.CommonDesktopDirectory);
// przygotowanie grupy kafelków
private void Prepare()
{
// użyte w celu zmniejszenia efektu migotania
this.DoubleBuffered = true;
// przejrzystość koloru między kafelkami
this.BackColor = Color.Transparent;
// prosty sposób na zamianę panelu c# na odpowiednik ScrollBox delphi
// dzięki temu zawartość można przesuwać suwakami (scrollBar)
this.AutoScroll = true;
}
// pobranie nazw plików do tablic
private void GetFiles()
{
userFiles = Directory.GetFileSystemEntries(userDesktopPath);
publicFiles = Directory.GetFileSystemEntries(publicDesktopPath);
}
// filtr wykluczający część plików
// w szczególności dotyczy Windows 7 i komputera autora (.picasaoriginals)
private bool Filter(string name, string ext)
{
return !(ext.EndsWith(".ini") || ext.EndsWith(".tmp") || ext.EndsWith(".picasaoriginals") || name.StartsWith("~"));
}
// utworzenie i wyświetlenie grupy kafelków
public void ShowDesktop()
{
int i = 0;
int color = 0;
Prepare();
GetFiles();
foreach (string s in userFiles)
{
FileInfo fi = new FileInfo(s);
if (Filter(fi.Name, fi.Extension.ToLower()))
{
// utworzenie nowego obiektu kafelek (Tile)
// nadanie mu położenia i koloru
Tile t = new Tile(i / horzDivider, i % vertDivider, color);
// nadanie tytułu kafelka i i dodanie do zbioru kontrolek TileDesktopGroup
t.FileName = s;
this.Controls.Add(t);
color++;
i++;
}
}
// analogicznie jak opis poprzedniej pętli foreach
foreach (string s in publicFiles)
{
FileInfo fi = new FileInfo(s);
if (Filter(fi.Name, fi.Extension.ToLower()))
{
Tile t = new Tile(i / horzDivider, i % vertDivider, color);
t.FileName = s;
this.Controls.Add(t);
color++;
i++;
}
}
}
}
}
Kod klasy ShellEx
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Drawing;
namespace MetroEx
{
/// <summary>
/// Źródło
/// http://www.vbaccelerator.com/home/NET/Code/Libraries/Shell_Projects/SysImageList/article.asp
/// http://lluisfranco.com/2014/04/16/extract-extra-large-icon-from-a-file-including-network-paths/
/// </summary>
class ShellEx
{
[DllImport("shell32.dll")]
private static extern int SHGetImageList(int iImageList, ref Guid riid, out IImageList ppv);
[DllImport("shell32.dll")]
public static extern int SHGetFileInfo(string pszPath, int dwFileAttributes, ref SHFILEINFO psfi, int cbFileInfo, uint uFlags);
[DllImport("user32")]
public static extern int DestroyIcon(IntPtr hIcon);
// struktura SHFILEINFO
[StructLayout(LayoutKind.Sequential)]
public struct SHFILEINFO
{
public IntPtr hIcon;
public int iIcon;
public uint dwAttributes;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string szDisplayName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
public string szTypeName;
};
// niezbędne flagi
public const int SHIL_EXTRALARGE = 0x2;
public const int SHIL_JUMBO = 0x4;
public const uint SHGFI_SYSICONINDEX = 0x4000;
public const uint SHGFI_ICON = 0x100;
public const uint SHGFI_USEFILEATTRIBUTES = 0x10;
public const int FILE_ATTRIBUTE_NORMAL = 0x80;
public const int FILE_ATTRIBUTE_DIRECTORY = 0x10;
// struktura do interfejsu COM IImageList
public struct IMAGELISTDRAWPARAMS
{
public int cbSize;
public IntPtr himl;
public int i;
public IntPtr hdcDst;
public int x;
public int y;
public int cx;
public int cy;
public int xBitmap;
public int yBitmap;
public int rgbBk;
public int rgbFg;
public int fStyle;
public int dwRop;
public int fState;
public int Frame;
public int crEffect;
}
// interfejs COM IImageList
[ComImportAttribute()]
[GuidAttribute("46EB5926-582E-4017-9FDF-E8998DAA0950")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
public interface IImageList
{
[PreserveSig]
int Add(IntPtr hbmImage, IntPtr hbmMask, ref int pi);
[PreserveSig]
int ReplaceIcon(int i, IntPtr hicon, ref int pi);
[PreserveSig]
int SetOverlayImage(int iImage, int iOverlay);
[PreserveSig]
int Replace(int i, IntPtr hbmImage, IntPtr hbmMask);
[PreserveSig]
int AddMasked(IntPtr hbmImage, int crMask, ref int pi);
[PreserveSig]
int Draw(ref IMAGELISTDRAWPARAMS pimldp);
[PreserveSig]
int Remove(int i);
[PreserveSig]
int GetIcon(int i, int flags, ref IntPtr picon);
};
// rozmiary ikon
public enum IconSizeEnum
{
LargeIcon48 = SHIL_EXTRALARGE, // 48x48
ExtraLargeIcon = SHIL_JUMBO // 256x256
}
// pobranie bitmapy z ikony folderu lub pliku
public static Bitmap GetBitmapFromPath(string filepath)
{
IntPtr hIcon = IntPtr.Zero;
var shinfo = new SHFILEINFO();
if (Directory.Exists(filepath))
{
hIcon = getIconHandle(filepath, IconSizeEnum.LargeIcon48, ref shinfo, FILE_ATTRIBUTE_DIRECTORY, SHGFI_ICON | SHGFI_USEFILEATTRIBUTES);
}
else
{
if (File.Exists(filepath))
{
hIcon = getIconHandle(filepath, IconSizeEnum.LargeIcon48, ref shinfo, FILE_ATTRIBUTE_NORMAL, SHGFI_SYSICONINDEX);
}
}
return getBitmapFromIconHandle(hIcon);
}
// pobranie bitmapy w oparciu o uchwyt ikony (bez tła ikony)
private static Bitmap getBitmapFromIconHandle(IntPtr hIcon)
{
if (hIcon == IntPtr.Zero) throw new FileNotFoundException();
var myIcon = Icon.FromHandle(hIcon);
var bitmap = myIcon.ToBitmap();
myIcon.Dispose();
DestroyIcon(hIcon);
return bitmap;
}
// pobranie uchwytu ikony w oparciu o ścieżkę pliku lub folderu
private static IntPtr getIconHandle(string filepath, IconSizeEnum iconsize, ref SHFILEINFO shinfo, int fileAttributeFlag, uint flags)
{
const int ILD_TRANSPARENT = 1;
var retval = SHGetFileInfo(filepath, fileAttributeFlag, ref shinfo, Marshal.SizeOf(shinfo), flags);
if (retval == 0) throw new FileNotFoundException();
var iconIndex = shinfo.iIcon;
var iImageListGuid = new Guid("46EB5926-582E-4017-9FDF-E8998DAA0950");
IImageList iml;
var hres = SHGetImageList((int)iconsize, ref iImageListGuid, out iml);
var hIcon = IntPtr.Zero;
hres = iml.GetIcon(iconIndex, ILD_TRANSPARENT, ref hIcon);
return hIcon;
}
}
}
Przykładowe użycie
using System;
using System.Windows.Forms;
namespace Metro
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
//
// Form1
//
this.Load += new EventHandler(Form1_Load);
}
void Form1_Load(object sender, EventArgs e)
{
tileDesktopGroup1.ShowDesktop();
//tileDesktopGroup2.ShowDesktop();
//...
}
}
}
Zrzuty ekranu z Windows 7 i Windows XP
Kafelki na Windows 7
Kafelki na Windows w dwóch grupach
Kafelki na Windows XP