using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Windows.Forms; using Developpez.Dotnet.Reflection; using Developpez.Dotnet.System; namespace Developpez.Dotnet.Windows.Forms { /// /// Méthodes d'extensions pour les contrôles Windows Forms /// public static class WinFormsExtensions { #region DragMove extension private static readonly Dictionary _draggedControls = new Dictionary(); /// /// Active temporairement le comportement "DragMove" pour le contrôle spécifié, jusqu'à ce que le bouton /// de la souris soit relaché /// /// Le contrôle à déplacer public static void DragMove(this Control ctl) { Point absolutePosition = Control.MousePosition; Point relativePosition = ctl.PointToClient(absolutePosition); new DraggedControl(ctl, relativePosition.X, relativePosition.Y); } /// /// Active ou désactive le comportement "DragMove" pour le contrôle spécifié /// /// Le contrôle pour lequel le DragMove doit être activé ou désactivé /// Une valeur indiquant s'il faut activer (true) ou désactiver (false) le DragMove pour le contrôle public static void EnableDragMove(this Control ctl, bool value) { if (value) { if (!_draggedControls.ContainsKey(ctl.Handle)) _draggedControls.Add(ctl.Handle, new DraggedControl(ctl)); } else { if (_draggedControls.ContainsKey(ctl.Handle)) { _draggedControls[ctl.Handle].Dispose(); _draggedControls.Remove(ctl.Handle); } } } /// /// Teste si le DragMove est actif pour le contrôle spécifié /// /// Le contrôle à tester /// true si le DragMove est actif pour ce contrôle; sinon, false public static bool IsDragMoveEnabled(this Control ctl) { return _draggedControls.ContainsKey(ctl.Handle); } private class DraggedControl : IDisposable { private readonly Control _target; private int _xStart; private int _yStart; private bool _isMoving; private readonly bool _isTemporary; public DraggedControl(Control target) { _target = target; _isMoving = false; _isTemporary = false; _target.MouseDown += Target_MouseDown; _target.MouseMove += Target_MouseMove; _target.MouseUp += Target_MouseUp; _target.Disposed += Target_Disposed; } public DraggedControl(Control target, int xStart, int yStart) : this(target) { _xStart = xStart; _yStart = yStart; _isMoving = (Control.MouseButtons & MouseButtons.Left) == MouseButtons.Left; _isTemporary = true; } void Target_MouseDown(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { _isMoving = true; _xStart = e.X; _yStart = e.Y; } } void Target_MouseUp(object sender, MouseEventArgs e) { _isMoving = false; if (_isTemporary) { Dispose(); } } private void Target_MouseMove(object sender, MouseEventArgs e) { if (_isMoving) { int x = _target.Location.X + e.X - _xStart; int y = _target.Location.Y + e.Y - _yStart; _target.Location = new Point(x, y); } } void Target_Disposed(object sender, EventArgs e) { _target.EnableDragMove(false); } public void Dispose() { _target.MouseDown -= Target_MouseDown; _target.MouseMove -= Target_MouseMove; _target.MouseUp -= Target_MouseUp; _target.Disposed -= Target_Disposed; } } #endregion #region Symbole "Bouclier" UAC pour les actions administratives #region UAC Shield Interop [DllImport("user32")] static extern UInt32 SendMessage (IntPtr hWnd, UInt32 msg, UInt32 wParam, UInt32 lParam); const int BcmFirst = 0x1600; //Normal button const int BcmSetshield = (BcmFirst + 0x000C); //Elevated button #endregion /// /// Rajoute le bouclier UAC si le processus actuel n'est pas /// élevé et qu'on est bien sur Windows Vista /// /// Le bouton pour lequel ajouter le bouclier UAC public static void AddUacShieldIfNecessary(this Button b) { if (SystemInfos.SupportsElevation) { if (!SystemInfos.IsElevated) b.AddUacShield(); } } /// /// Affiche le bouclier UAC sur un bouton pour indiquer une action /// qui requiert les droits d'administrateur /// /// Le bouton pour lequel ajouter le bouclier UAC public static void AddUacShield(this Button b) { if (SystemInfos.SupportsElevation) { SendMessage(b.Handle, BcmSetshield, 0, 0xFFFFFFFF); } } /// /// Enlève le bouclier UAC d'un bouton /// /// Le bouton pour lequel enlever le bouclier UAC public static void RemoveUacShield(this Button b) { if (SystemInfos.SupportsElevation) { SendMessage(b.Handle, BcmSetshield, 0, 0x00000000); } } #endregion #region Capture de l'image d'un contrôle #region Interop [DllImport("user32.dll")] static extern IntPtr SendMessage(IntPtr hWnd, uint msg, IntPtr hdc, PrfFlags drawingOptions); const uint WmPrint = 0x317; [Flags] enum PrfFlags : uint { CheckVisible = 0x01, Children = 0x02, Client = 0x04, EraseBkgnd = 0x08, NonClient = 0x10, Owned = 0x20 } #endregion /// /// Capture le rendu d'un contrôle dans une image /// /// Le contrôle à capturer /// L'image du contrôle public static Image CaptureImage(this Control control) { Image img = new Bitmap(control.Width, control.Height); using (Graphics g = Graphics.FromImage(img)) { SendMessage( control.Handle, WmPrint, g.GetHdc(), PrfFlags.Client | PrfFlags.NonClient | PrfFlags.EraseBkgnd); } return img; } #endregion #region Localisation à la volée /// /// Applique aux composants d'une fenêtre ou d'un UserControl les ressources /// localisées pour la culture d'interface graphique courante (CurrentUICulture) /// ///La fenêtre ou le UserControl auquel appliquer les ressources localisées ///Type de la fenêtre ou du UserControl /// Cette méthode permet permet le changement de culture "à la volée", sans recréer la form ou le UserControl. public static void ApplyResources(this T control) where T : Control { var resources = new ComponentResourceManager(typeof(T)); var rs = resources.GetResourceSet(CultureInfo.CurrentUICulture, true, true); if (rs == null) return; var controlNames = rs.Cast() .Select(e => ExtractControlName((string)e.Key)) .Distinct() .ToList(); foreach (var name in controlNames) { object ctl = name == "$this" ? control : GetComponent(control, name); if (ctl != null) resources.ApplyResources(ctl, name); } } private static object GetComponent(T control, string name) where T : Control { const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public; var field = typeof(T).GetField(name, flags); if (field != null && field.FieldType.Is()) return field.GetValue(control); // In VB.NET, WithEvents fields are wrapped in properties var property = typeof(T).GetProperty(name, flags); if (property != null && property.PropertyType.Is()) return property.GetValue(control, null); return null; } private static string ExtractControlName(string key) { return key.TrimStart('>').Split('.')[0]; } #endregion #region InvokeIfRequired /// /// Exécute une action sur le thread de l'interface graphique, en utilisant Invoke si nécessaire. /// /// Contrôle sur le thread duquel l'action doit être exécutée /// Action à exécuter public static void InvokeIfRequired(this ISynchronizeInvoke target, Action action) { if (target.InvokeRequired) target.Invoke(action, null); else action(); } /// /// Exécute une action sur le thread de l'interface graphique, en utilisant Invoke si nécessaire. /// /// Type du contrôle sur lequel l'action est exécutée /// Contrôle sur le thread duquel l'action doit être exécutée /// Action à exécuter public static void InvokeIfRequired(this T target, Action action) where T : ISynchronizeInvoke { if (target.InvokeRequired) target.Invoke(action, new object[] { target }); else action(target); } #endregion #region FlashWindow /// /// Fait clignoter une fenêtre pour attirer l'attention de l'utilisateur. Le clignotement s'arrête quand la fenêtre passe au premier plan. /// ///Fenêtre à faire clignoter public static void FlashWindow(this IWin32Window window) { FlashWindow(window, FlashWindowFlags.All | FlashWindowFlags.UntilForeground); } /// /// Fait clignoter une fenêtre pour attirer l'attention de l'utilisateur. /// ///Fenêtre à faire clignoter ///Options de clignotement public static void FlashWindow(this IWin32Window window, FlashWindowFlags flags) { int count = 3; if (flags.HasFlag(FlashWindowFlags.Continuous) || flags.HasFlag(FlashWindowFlags.UntilForeground)) count = 0; FlashWindow(window, flags, count); } /// /// Fait clignoter une fenêtre pour attirer l'attention de l'utilisateur. Le clignotement s'arrête quand la fenêtre passe au premier plan. /// ///Fenêtre à faire clignoter ///Nombre de clignotements public static void FlashWindow(this IWin32Window window, int count) { FlashWindow(window, FlashWindowFlags.All | FlashWindowFlags.UntilForeground, count); } /// /// Fait clignoter une fenêtre pour attirer l'attention de l'utilisateur /// ///Fenêtre à faire clignoter ///Options de clignotement ///Nombre de clignotements public static void FlashWindow(this IWin32Window window, FlashWindowFlags flags, int count) { FlashWindow(window, flags, count, 0); } /// /// Fait clignoter une fenêtre pour attirer l'attention de l'utilisateur /// ///Fenêtre à faire clignoter ///Options de clignotement ///Nombre de clignotements ///Intervalle de temps, en millisecondes, entre 2 clignotements (0 : intervalle par défaut) public static void FlashWindow(this IWin32Window window, FlashWindowFlags flags, int count, int intervalMilliseconds) { var fwi = new FLASHWINFO(); fwi.cbSize = (uint)Marshal.SizeOf(typeof(FLASHWINFO)); fwi.hwnd = window.Handle; fwi.dwFlags = flags; fwi.uCount = (uint)count; fwi.dwTimeout = (uint)intervalMilliseconds; FlashWindowEx(ref fwi); } [DllImport("user32.dll")] private static extern bool FlashWindowEx(ref FLASHWINFO fwi); [StructLayout(LayoutKind.Sequential)] private struct FLASHWINFO { public uint cbSize; public IntPtr hwnd; public FlashWindowFlags dwFlags; public uint uCount; public uint dwTimeout; } #endregion } }