Questo sito utilizza i cookie per migliorare servizi ed esperienza del lettore. Se decidi di continuare la navigazione consideriamo che accetti il loro uso . Per e informazioni sulla Privacy leggere la Google Policies Privacy

Se continui nella navigazione accetti il loro uso. OK
Visualizzazione post con etichetta WPF. Mostra tutti i post
Visualizzazione post con etichetta WPF. Mostra tutti i post

martedì 24 agosto 2010

WPFNotes (Parte 2): Un po' di API Win32

Vediamo ora come aggiungere due funzionalità essenziali per l'applicazione:
  • Filtrare le note in base alle finestre che sono aperte in windows
  • Filtrare le note in base alla finestra attiva per dare maggiore priorità alle note contestuali
Per svolgere queste due operazioni dobbiamo utilizzare alcune funzioni di windows perchè il .NET non ci mette a disposizione questo genere di funzionalità. Partiamo con la più facile: trovare la finestra attiva e leggere la sua Caption. Le funzioni api che ci servono sono due:
GetForegroundWindow e GetWindowText. La prima ci permette di reperire la finestra attiva su cui sta lavorando l'utente (in pratica la funzione cerca la finestra attiva che ha la priorità leggermente più alta, cosa che viene fatat da windows per le finestre in Foreground).
La seconda invece invia un messaggio WM_GETTEXT a un determinato handle di finestra (Nota: non funziona con i controlli ospitati in altre finestre, ma con i caption delle window sì, cosa che per noi va benissimo!).
Vediamo di scrivere le due firme delle funzioni (copiate da msdn):

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);

[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern IntPtr GetForegroundWindow();
Quello che ora dobbiamo scrivere è una funzione che, eseguita in polling con un timer ci permetta di sapere sempre quel'è la finestra attiva:
/// 
/// Restituisce il caption della finestra specificata.
/// 
const int MAXTITLE = 255;
public static string GetWindowText(IntPtr hWnd) {
    StringBuilder titolo = new StringBuilder(MAXTITLE);
    int titleLength = GetWindowText(hWnd, titolo, titolo.Capacity + 1);
    titolo.Length = titleLength;

    return titolo.ToString();
}

//Reperisce la finestra attiva e ne legge la caption
IntPtr ptr = EnumerateWindow.GetForegroundWindow();
if (ptr!=IntPtr.Zero)
    CurrentWindowText = EnumerateWindow.GetWindowText(ptr);
Fin qui tutto semplice. Vediamo adesso di risolvere un problema più complesso: la lettura di tutti i caption delle finestre che sono aperte.
Per svolgere questa operazione di avvaliamo della funzione EnumDesktopWindows. La funzione accetta 3 parametri. Il primo è il desktop su cui eseguire la ricerca (NULL per eseguire la ricerca nel desktop corrente), una funzione call back (EnumWindowsProc) che verrà richiamata ad ogni nuova finestra trovata e un lParam che verrà passato alla funzione di callback. Vediamo le dichiarazioni:
private delegate bool EnumDelegate(IntPtr hWnd, int lParam);

[DllImport("user32.dll", ExactSpelling = false, CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool EnumDesktopWindows(IntPtr hDesktop, EnumDelegate lpEnumCallbackFunction, IntPtr lParam);

private static ArrayList listaTitoli;
/// 
/// Restituisce i caption di tutte le finestre nel desktop di windows
/// 
public static string[] GetDesktopWindowsCaptions() {
    listaTitoli = new ArrayList();
    EnumDelegate delegateFunction = new EnumDelegate(EnumWindowsProc);
    bool success = EnumDesktopWindows(IntPtr.Zero, delegateFunction, IntPtr.Zero);

    if (success) {
        // Copia il risultato in un'array di stringhe
        string[] titoli = new string[listaTitoli.Count];
        listaTitoli.CopyTo(titoli);
        return titoli;
    }
    else {
        // ERRORE
        int errore = Marshal.GetLastWin32Error();
        string errorMessage = String.Format("EnumDesktopWindows ha fallito con errore: {0}.", errore);
        throw new Exception(errorMessage);
    }
}
private static bool EnumWindowsProc(IntPtr hWnd, int lParam) {
    string titolo = GetWindowText(hWnd);
    if (titolo != "")
        listaTitoli.Add(titolo);
    return true;
}
Chiaramente la funzione di ricerca può essere resa più efficiente andando a cercare solo caption con determinate caratteristiche. Ma questo lo lascio alla vostra fantasia.

WPFNotes (Parte 1): Progettazione applicazione e Database

WPFNotes (Parte 1): Progettazione applicazione e Database

Scommetto che capita un po' a tutti di dover gestire una gran quantità di post-it e di liste di TODO che crescono inesorabilmente. Per non parlare poi di tutti gli username-password da ricordare oppure della vostra lista di HowTo che è diventata talmente grande da essere ormai inutile perchè non riuscite più a trovare quello che vi serve.
Ebbene, ho sempre avuto questa idea per la mente, delle note che apparissero in base alla finestra che state visualizzando.
Così se per caso dovete ricordarvi una determinata cosa all'apertura della finestra di Excel, ecco che quella nota appare. Oppure non ricordate mai la password quando dovete loggarvi in un sito che usate una volta ogni diversi eoni e dovete sempre cercarla dentro a un'enormità di mail, ecco che in base al titolo della finestra del browser la nota viene visualizzata.
Ho deciso quindi di creare una bella applicazione che facesse al caso mio.
Vediamo allora di crearci un'elenco di funzionalità che vogliamo che la nostra applicazione abbia:
  • Ricerca fra tutte le finestre aperte in windows per controllare se ci sono note contestuali che combaciano
  • Ricerca nella finestra attiva per rendere più visibile la notifica di note
  • Possibilità di aggiungere delle note in cui specificare la finestra a cui 'agganciarle' usando regular expression in modo da rendere più flessibile la ricerca.
  • Visualizzazione, modifica ed eliminazione di note già scritte.
  • Associare Tag a ogni singola nota in modo da poter visualizzare anche note correlate.
Inanzi tutto vediamo di creare un database per salvarci le nostre note. Useremo SQLServer Express 2008.
Andiamo a creare la tabella per il salvataggio delle note:

USE [bsd_Notes]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[T_NOTE](
[NOT_KEY] [int] IDENTITY(1,1) NOT NULL,
[NOT_NOTA] [text] NULL,
[NOT_WINDOW_NAME] [nvarchar](4000) NULL,
CONSTRAINT [PK_T_NOTE] PRIMARY KEY CLUSTERED 
(
[NOT_KEY] ASC
)WITH (PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
a questo punto creiamo una anagrafe per la memorizzazione dei tag da associare (così potranno essere condivisi e più facilmente utilizzati per correlare fra loro le note)
USE [bsd_Notes]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[T_TAG](
 [TAG_KEY] [int] IDENTITY(1,1) NOT NULL,
 [TAG_NOME] [nvarchar](100) NULL,
 CONSTRAINT [PK_T_TAG] PRIMARY KEY CLUSTERED 
(
 [TAG_KEY] ASC
)WITH (PAD_INDEX  = OFF,
STATISTICS_NORECOMPUTE  = OFF,
IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS  = ON,
ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
Quindi creiamo la tabella per la correlare le note con i tag:
USE [bsd_Notes]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[T_NOTE_TAG](
 [TNO_NOT_KEY_NOTE] [int] NOT NULL,
 [TNO_TAG_KEY_TAG] [int] NOT NULL,
 CONSTRAINT [PK_T_NOTE_TAG] PRIMARY KEY CLUSTERED 
(
 [TNO_NOT_KEY_NOTE] ASC,
 [TNO_TAG_KEY_TAG] ASC
)WITH (PAD_INDEX  = OFF,
STATISTICS_NORECOMPUTE  = OFF,
IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS  = ON,
ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[T_NOTE_TAG]  WITH CHECK 
ADD  CONSTRAINT [FK_T_NOTE_TAG_T_NOTE] 
FOREIGN KEY([TNO_NOT_KEY_NOTE])
REFERENCES [dbo].[T_NOTE] ([NOT_KEY])
GO
ALTER TABLE [dbo].[T_NOTE_TAG] CHECK CONSTRAINT [FK_T_NOTE_TAG_T_NOTE]
GO
ALTER TABLE [dbo].[T_NOTE_TAG]  WITH CHECK 
ADD  CONSTRAINT [FK_T_NOTE_TAG_T_TAG] 
FOREIGN KEY([TNO_TAG_KEY_TAG])
REFERENCES [dbo].[T_TAG] ([TAG_KEY])
GO
ALTER TABLE [dbo].[T_NOTE_TAG] CHECK CONSTRAINT [FK_T_NOTE_TAG_T_TAG]
GO
Il diagramma che ci troviamo è il seguente:
Diagramma del database

WPFNotes (Parte 2): Un po' di API Win32