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
}
}