XL 2019 Saisir automatiquement des dates au moyen d'un calendrier

Boostez vos compétences Excel avec notre communauté !

Rejoignez Excel Downloads, le rendez-vous des passionnés où l'entraide fait la force. Apprenez, échangez, progressez – et tout ça gratuitement ! 👉 Inscrivez-vous maintenant !

Magic_Doctor

XLDnaute Barbatruc
Supporter XLD
Bonsoir,

Ma fille en avait besoin pour son travail : dans une colonne de cellules qui ne reçoivent que des dates, quand on clique sur l'une de ces cellules, faire apparaître un petit calendrier à partir duquel on choisirait une date qui s'inscrirait dans la cellule.
J'ai cherché un peu partout, mais rien qui correspondait à ce que je voulais exactement. Je décide de résoudre le problème avec l'humanoïde. En moins de 2 minutes il me le résout sommairement, puis des heures d'échanges et d'essais pour peaufiner cette histoire et que surtout ça ne plante pas.
Résultat : bluffant.
Mais, je dois reconnaître, histoire complexe dont plusieurs procédures me dépassent. Ce n'est pas grave, l'essentiel, c'est que ça marche.
Je partage, et si quelqu'un peut trouver plus simple pour au moins le même résultat, je suis preneur.

¡FELIZ AÑO NUEVO A TODOS!
 

Pièces jointes

Pièces jointes

Guten Morgen,

J'ai cherché un peu partout, mais rien qui correspondait à ce que je voulais exactement.
C'est bizarre, à chaque fois tu "cherches partout et ne trouves rien" (comme pour les commentaires), alors qu'il y a déjà la réponse ici-même et en plusieurs exemplaires.

Tu sais tu peux t'aider aussi des fils de discussion qui te sont proposés en bas de page, quand il y en a. 😀
 
Dernière édition:
si quelqu'un peut trouver plus simple pour au moins le même résultat, je suis preneur.

La solution la plus simple c'est d'utiliser le contrôle du calendrier de windows et comme tu as des Apis dans ton exemple ça ne devrait pas poser problème.

Ajouter un Frame dans lequel le calendrier sera placer et ajouter le code suivant:
Code:
Option Explicit

Private Declare PtrSafe Function CreateWindowExA Lib "user32" (ByVal dwExStyle As Long, _
         ByVal lpClassName As String, ByVal lpWindowName As String, _
         ByVal dwStyle As Long, _
         ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, _
         ByVal hWndParent As LongPtr, ByVal hMenu As LongPtr, _
         ByVal hInstance As LongPtr, ByVal lpParam As LongPtr) As LongPtr

Private Declare PtrSafe Function SendMessageA Lib "user32" (ByVal hwnd As LongPtr, ByVal wMsg As Long, ByVal wParam As LongPtr, ByVal lParam As LongPtr) As LongPtr
Private Const DTM_SETSYSTEMTIME = &H1002
Private Const DTM_GETSYSTEMTIME = &H1001

Private DatePickeHwnd As LongPtr

Private Sub UserForm_Initialize()
DatePickeHwnd = CreateWindowExA(0, "SysMonthCal32", vbNullString, &H50000004, 0, 0, 200, 200, _
       Frame1.[_GethWnd], 0, 0, 0)
End Sub

Public Property Let PickerDate(ByVal value As Date)
Dim tm(0 To 9) As Integer
tm(0) = Year(value):
tm(1) = Month(value):
tm(3) = Day(value)
SendMessageA DatePickeHwnd, DTM_SETSYSTEMTIME, 0, VarPtr(tm(0))
End Property

Public Property Get PickerDate() As Date
Dim tm(0 To 9) As Integer
SendMessageA DatePickeHwnd, DTM_GETSYSTEMTIME, 0, VarPtr(tm(0))
PickerDate = DateSerial(tm(0), tm(1), tm(3))
End Property
Il y a la property PickerDate pour lire et écrire la date dans le contrôlé
 
re
ben non il n'est pas responsif c'est a dire au click sur une date il ne renvoie pas la date puisque c'est n'est pas l'ocx
il faut loader peut etre meme des bouton pour envoyer la date
maintenant comme je ne veux pas entrer dans le hook pour capter un changement du calendrier voici une solution toute prête

userform
VB:
'*****************************************************************************************************
'    ___     _     _______  __      _   ____  _   _  _______  ___     _   _   _    ___     _     _.
'   //  \\  /\\      //    // \\   //  //    //  //    //    //  \\  //  //  //   //  \\  //|   //
'  //___// //__\    //    //__//  //  //    //__//    //    //   // //  //  //   //   // // |  //
' //      //   \\  //    //  \\  //  //    //  \\    //    //   // //  //  //   //   // //  | //
'//      //    // //    //   // //  //___ //    \\  //     \\__// //__//  //___ \\__// //   |//
'****************************************************************************************************
'userform calendrier utilisant les api pour utiliser le calendrier de windows
'dont l'ocx ne fonctionne pas sur excel 64 bit
'auteurs:
' _ @Rheeem sur excel download
' _ @Patricktoulon sur excel downloads

Option Explicit
#If VBA7 Then
    Private Declare PtrSafe Function CreateWindowExA Lib "user32" ( _
                              ByVal dwExStyle As Long, ByVal lpClassName As String, _
                              ByVal lpWindowName As String, ByVal dwStyle As Long, _
                              ByVal X As Long, ByVal Y As Long, ByVal nWidth As Long, ByVal nHeight As Long, _
                              ByVal hWndParent As LongPtr, ByVal hMenu As LongPtr, _
                              ByVal hInstance As LongPtr, ByVal lpParam As LongPtr) As LongPtr
   
    Private Declare PtrSafe Function SendMessageA Lib "user32" ( _
                              ByVal hWnd As LongPtr, ByVal wMsg As Long, _
                              ByVal wParam As LongPtr, ByVal lParam As LongPtr) As LongPtr
   
   
    Private Declare PtrSafe Function GetCursorPos Lib "user32" ( _
                              ByRef lpPoint As POINTAPI) As Long
   
    Private Declare PtrSafe Function GetWindowRect Lib "user32" ( _
                              ByVal hWnd As LongPtr, ByRef lpRect As RECT) As Long
   
    Private Declare PtrSafe Function DestroyWindow Lib "user32" ( _
                              ByVal hWnd As LongPtr) As Long
   
    Private Declare PtrSafe Function SetWindowPos Lib "user32" ( _
                              ByVal hWnd As LongPtr, ByVal hWndInsertAfter As LongPtr, _
                              ByVal X As Long, ByVal Y As Long, ByVal cx As Long, _
                              ByVal cy As Long, ByVal uFlags As Long) As Long
   
    Private Declare PtrSafe Function GetDpiForWindow Lib "user32" ( _
                              ByVal hWnd As LongPtr) As Long
    Private Type POINTAPI
        X As Long
        Y As Long
    End Type
   
    Private Type RECT
        Left As Long
        Top As Long
        Right As Long
        Bottom As Long
    End Type
#End If

Private Const DTM_SETSYSTEMTIME As Long = &H1002
Private Const DTM_GETSYSTEMTIME As Long = &H1001

Private DatePickeHwnd As LongPtr
Public dat As Date
Public cell As Range
Public Function ValueX(obj As Object)
    With Calendrier
        If IsDate(obj.value) Then .dat = CDate(obj.value) Else .dat = Date
        Set .cell = obj
        .Show
        ValueX = dat
        Set .cell = Nothing
    End With
   
    Unload Calendrier
End Function


Private Sub UserForm_Activate()
    ' Création du calendrier Win32
    Dim OldDate As Date, Newdate As Date, rc As RECT, pos As POINTAPI, oldx, oldy, L, T, PPX, ApphWnd As LongPtr
    ApphWnd = Application.hWnd
    Newdate = 0
   
    'on place le calendrier a droit de la cellule
    'attention cette version ne prend pas en charge le feuille figées  fractionnée
    PPX = 1 / (GetDpiForWindow(ApphWnd) / 72)
    With ActiveWindow
        L = .ActivePane.PointsToScreenPixelsX(cell.Left + cell.Width) * PPX
        T = .ActivePane.PointsToScreenPixelsY(cell.Top) * PPX
    End With
    If L + Me.Width > (Application.Left + Application.Width) - Me.Width Then L = (Application.Left + Application.Width) - Me.Width - 15
    If T + Me.Height > (Application.Top + Application.Height) - Me.Height Then T = (Application.Top + Application.Height) - Me.Height - 15
   
    Me.Left = L
    Me.Top = T
    DatePickeHwnd = CreateWindowExA(0, "SysMonthCal32", vbNullString, &H50000004, 0, -18, 200, 200, Frame1.[_GethWnd], 0, 0, 0)
    PickerDate = Calendrier.dat
    OldDate = Calendrier.dat
    Newdate = PickerDate
    Do While Newdate = OldDate
        GetWindowRect DatePickeHwnd, rc
        GetCursorPos pos
       
        'si on est dans le rectangle du calendrier
        'si on est plus que le top +40 du calendrier on prend alors en compte la date actuelle et on memorise la position du curseur
        If pos.X > rc.Left And pos.Y > rc.Top + 40 And pos.X < rc.Right And pos.Y < rc.Bottom Then
           
            Newdate = PickerDate: oldx = pos.X: oldy = pos.Y
        End If
        'si la date a changépour sortir plus vite on test la position du curseur
        'par raport a celle prise en compte avec la nouvelle date
        If Newdate <> Calendrier.dat Then
            GetCursorPos pos
            'si la position a changer du moindre millimètre on sort de la boucle
            If pos.X <> oldx Or pos.Y <> oldy Then Exit Do
        End If
       
        DoEvents
    Loop
    DestroyWindow DatePickeHwnd
    dat = Newdate
    Me.Hide
End Sub
Public Property Let PickerDate(ByVal value As Date)
    Dim tm(0 To 9) As Integer
    tm(0) = Year(value)
    tm(1) = Month(value)
    tm(3) = Day(value)
    SendMessageA DatePickeHwnd, DTM_SETSYSTEMTIME, 0, VarPtr(tm(0))
    End Property

Public Property Get PickerDate() As Date
    Dim tm(0 To 9) As Integer
    SendMessageA DatePickeHwnd, DTM_GETSYSTEMTIME, 0, VarPtr(tm(0))
    PickerDate = DateSerial(tm(0), tm(1), tm(3))
    End Property

Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
    DestroyWindow DatePickeHwnd
    Cancel = True: Me.Hide
End Sub

dans la feuille
VB:
Option Explicit

Private Sub Worksheet_BeforeRightClick(ByVal Target As Range, Cancel As Boolean)
Cancel = True
Target = Calendrier.ValueX(Target)
Unload Calendrier
End Sub
et voila on a un calendrier modal sur la même base que l'ocx
demo4.gif

voila maintenant il est modal est responsif
je n'ai pas mi la prise en charge des feuilles figée fractionnées mais i vous voulez je vous la met
 

Pièces jointes

Bonjour,
c'est une blague ????????????????????????????????????????????????????????????????????
Regarde mon avatar, ai-je une tête à faire des blagues ?????????????????????????????????????

J'ai effectivement cherché un moment un calendrier qui satisferait mes desiderata, et comme il y a, dans la BD d'Excel Downloads, une "chiée + une" de calendriers et que je n'allais pas tous les passer en revue, je me suis dit, pourquoi ne pas le faire avec l'humanoïde.
Désolé PT, mais je n'étais pas tombé sur ton calendrier, parce que si ça avait été le cas, je n'aurais pas "perdu" mon temps avec l'humanoïde, car j'aurais ipso facto choisi ton calendrier qui est excellent. Il ne manquerait que les lunaisons et une ComboBox pour choisir la langue depuis le calendrier. Bref, c'est le calendrier que je communiquerai à ma fille.
En revanche l'expérience avec l'humanoïde ne fut pas inintéressante. En quelques heures il m'a pondu un calendrier, certes moins beau, moins complet que celui de PT, lourd comme du plomb, mais un calendrier qui marche. Ça laisse songeur quand on se dit ce que ce sera dans ne serait-ce que 5 ans...

Finalement vous avez répondu à ma question du post #1 :

et si quelqu'un peut trouver plus simple pour au moins le même résultat, je suis preneur.

en me communiquant le calendrier de PT.
Mais, caramba, pourquoi ces cris d'orfraie ?
 
Bonjour,
L'effet que tu cherches est disponible dans SysDateTimePick32 qui correspond au DatePicker de l'ocx et qui se ferme automatique aspes la sélection, j’espère qu'il sera facile à adapter
Bonjour @Rheeem
a ben j'ai cherché et j'ai même mis a contribution 3 IA différente pendant plus d'une heure pour faire la recherche
et rien
j'ai du faire avec une astuce (dans la boucle d'attente bien que je n'aime pas ça gérer l'etat before et after )
mais si tu connais le getmessage sans passer par le hook en addressof(capricieux en vba) je suis preneur pour le mettre à jour.
maintenant si tu parles du hook je connais j'ai deja et comme je l'ai dis c'est capricieux en vba

@Magic_Doctor
quand je demande si c'etait une blague ,c'est pour répondre a ceci
"J'ai cherché un peu partout, mais rien qui correspondait à ce que je voulais exactement."
et au regard de ce qu'a pondu l'ia ,il y a sensiblement la même chose dans les ressources.
Tu en a pour tout les goûts

apres l'intervention de @Rheeem du coup son modèle utilisant le control natif de windows sans l'ocx mais avec l'api cretewindowexA ,je l'ai rendu responsif. c'est pas au points mais c'est deja un debut.
après sincèrement mon calendar dans les ressources a répondu a pas mal de demande de toutes sortes.
et pour ne rien gâcher au plaisir , tu peux le personnaliser et même l'installer a partir du skinner(changeur de peau)

je vais relancer les IA pour voir si @Rheeem a raison sans limite de temps cette fois ci
 
@Rheeem
1 reponse par le hat qui pete
Bonjour 😊


Ton camarade n’a pas tort sur le principe, mais dans ton contexte précis, ta solution reste largement justifiée. Je t’explique calmement et techniquement.




Ce qu’il dit est vrai…​


SysDateTimePick32 est :


  • le contrôle Win32 natif derrière le DTPicker OCX
  • capable de :
    • afficher une date
    • se fermer automatiquement après sélection
    • gérer navigation mois / années sans bidouille
    • envoyer des notifications (DTN_DATETIMECHANGE, DTN_CLOSEUP, etc.)

👉 C’est exactement l’effet “OCX DatePicker” que tu reproduis manuellement.




MAIS (et le “mais” est important) 😏​


1️⃣​


Pour exploiter correctement SysDateTimePick32, il faut :


  • intercepter les messages Windows
  • donc :
    • subclasser la fenêtre parente
    • gérer WM_NOTIFY
    • décoder NMHDR, NMDATETIMECHANGE, etc.

👉 En VBA, ça veut dire :


  • AddressOf
  • fonctions callback
  • gestion 32/64 bits
  • risques de crash Excel si mal nettoyé

Tu connais le sujet : c’est instable et dangereux en VBA pur.




2️⃣ Ce que TU as fait évite exactement ça​


Ta solution :


  • pas de subclassing
  • pas de callbacks
  • pas de WM_NOTIFY
  • ✅ polling contrôlé
  • ✅ logique utilisateur explicite
  • ✅ destruction explicite du handle
  • ✅ comportement maîtrisé

➡️ Zéro risque de plantage Excel
➡️ 100 % déterministe
conclusion on est bien dans le hooking
 
Benjour,

On peut s'en passer du hook et même de la boucle en utilisant MCM_HITTEST qui peut renvoyer la date sur laquelle le clic a eu lieu dans la zone des jours sinon elle renvoie la zone du clic: les flèches, la zone de l'année ou le label d’Aujourd’hui ..du coup si une date est renvoyée on peut fermer la fiche .
Code:
Private Declare Function CreateWindowExA Lib "user32" (ByVal dwExStyle As Long, _
         ByVal lpClassName As String, ByVal lpWindowName As String, _
         ByVal dwStyle As Long, _
         ByVal X As Long, ByVal Y As Long, ByVal nWidth As Long, ByVal nHeight As Long, _
         ByVal hWndParent As LongPtr, ByVal hMenu As LongPtr, _
         ByVal hInstance As LongPtr, ByVal lpParam As LongPtr) As LongPtr
Private Declare PtrSafe Function SendMessageW Lib "user32" (ByVal hWnd As LongPtr, ByVal wMsg As Long, ByVal wParam As LongPtr, ByVal lParam As LongPtr) As Long
Private Declare PtrSafe Function GetClientRect Lib "user32" (ByVal hWnd As LongPtr, wMsg As Any) As Long
Private Declare PtrSafe Function MapWindowPoints Lib "user32" (ByVal hWndFrom As LongPtr, ByVal hWndTo As LongPtr, lpPoints As Any, ByVal cPoints As Long) As Long
Private Declare PtrSafe Function GetCursorPos Lib "user32" (lpPoint As Any) As Long
 
Private Type MCHITTESTINFO
    cbSize As Long
    ptX As Long
    ptY As Long
    uHit As Long
    st(0 To 7) As Integer
End Type
Private DatePickeHwnd As LongPtr
 
Private Sub UserForm_Activate()
Const DTM_SETSYSTEMTIME = &H1002
Dim tm(0 To 9) As Integer, dDate As Date, rect(0 To 3) As Long
Frame1.Move 0, 0
Frame1.Enabled = False
GetClientRect Frame1.[_GethWnd], rect(0)
DatePickeHwnd = CreateWindowExA(0, "SysMonthCal32", vbNullString, &H50000000, 0, 0, rect(2), rect(3), _
       Frame1.[_GethWnd], 0, 0, 0)

If IsDate(ActiveCell) Then
  dDate = CDate(ActiveCell)
  tm(0) = Year(dDate):
  tm(1) = Month(dDate):
  tm(3) = Day(dDate)
  SendMessageW DatePickeHwnd, DTM_SETSYSTEMTIME, 0, VarPtr(tm(0))
End If

End Sub
 
Private Sub UserForm_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
Dim hit As MCHITTESTINFO
  hit.cbSize = Len(hit)
  hit.uHit = 0
  GetCursorPos hit.ptX
  MapWindowPoints 0, Frame1.[_GethWnd], hit.ptX, 1
  SendMessageW DatePickeHwnd, &H100E, 0, VarPtr(hit)
  If (hit.uHit And &H20001) = &H20001 Then
    ActiveCell.Value = DateSerial(hit.st(0), hit.st(1), hit.st(3))
    Unload Me
  ElseIf hit.uHit = &H10002 Then
    Frame1.Enabled = True 'édition de l'année
    SendMessageW DatePickeHwnd, &H201, 0, (hit.ptY * &H10000) + hit.ptX
    Frame1.Enabled = False
  Else
    SendMessageW DatePickeHwnd, &H201, 0, (hit.ptY * &H10000) + hit.ptX
  End If

End Sub
 
Bonjour @Rheeem
Ah... oui respect je m'incline
l'écoute avec le HITTEST ( presque façon uiautomationclient)avec une structure et un map de la surface et bénéficier de l'event userform mouse down pour le déclenchement grace au style appliqué a la new window ou le click la transperce.
Je ne l'ai pas vu venir bravo!!!! 👍 👍 👍 👍 👍
là tu m'a scotché 😵‍💫
j'adore ,j'adore
Permet moi de te renvoyer la pareille en te donnant une version:
  1. modale
  2. responsive
  3. auto placée
  4. Compatible avec range shape dans feuille
  5. Compatible textbox label commandbutton dans userform
  6. Utilisation sans object possible ex: (msgbox calendrier.datevalueX)
VB:
'*****************************************************************************************************
'    ___             _____  _____  _____
'   // \\    //  // //     //     //      //\  / \\
'  //__//   //__// //__   //__   //__    //  \/   \\
' //   \\  //  // //     //     //      //         \\
'//    // //  // //____ //____ //____  //           \\
'    ___     _     _______  __      _   ____  _   _  _______  ___     _   _   _    ___     _     _.
'   //  \\  /\\      //    // \\   //  //    //  //    //    //  \\  //  //  //   //  \\  //|   //
'  //___// //__\    //    //__//  //  //    //__//    //    //   // //  //  //   //   // // |  //
' //      //   \\  //    //  \\  //  //    //  \\    //    //   // //  //  //   //   // //  | //
'//      //    // //    //   // //  //___ //    \\  //     \\__// //__//  //___ \\__// //   |//
'****************************************************************************************************

'userform calendrier utilisant les api pour utiliser le calendrier de windows
'dont l'ocx ne fonctionne pas sur excel 64 bit
'auteurs:
' _ @Rheeem sur exceldownload
' _ @Patricktoulon sur exceldownloads
'Version 2.0
'Date Version : 03/01/2026
'Dans cet exercice nous trouvons le moyen de rendre responsif la window de type SysMonthCal32)créée dynamiquement
'----------------------------------------------------------------------------------------
'exemple au click droit sur cellules
'Private Sub Worksheet_BeforeRightClick(ByVal Target As Range, Cancel As Boolean)
'  Cancel = True
'  Target = Calendrier.DateValueX(Target)
'End Sub

'exemple avec une shape
'Sub Rectangleàcoinsarrondis1_Cliquer()
'  ActiveSheet.Shapes(Application.Caller).TextFrame.Characters.Text = Calendrier.DateValueX(ActiveSheet.Shapes(Application.Caller))
'End Sub

'exemple d'appels pour les controls dans les userforms
'Private Sub CommandButton1_Click()
'    CommandButton1.Caption = Calendrier.DateValueX(CommandButton1)
'End Sub

'Private Sub Label1_Click()
'    Label1.Caption = Calendrier.DateValueX(Label1)
'End Sub

'Private Sub TextBox1_MouseUp(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
'    If Button = 2 Then TextBox1.Value = Calendrier.DateValueX(TextBox1)
'End Sub

'****************************************************************************************************
Option Explicit
Private Declare Function CreateWindowExA Lib "user32" (ByVal dwExStyle As Long, _
                              ByVal lpClassName As String, ByVal lpWindowName As String, _
                              ByVal dwStyle As Long, ByVal X As Long, ByVal Y As Long, _
                              ByVal nWidth As Long, ByVal nHeight As Long, _
                              ByVal hWndParent As LongPtr, ByVal hMenu As LongPtr, _
                              ByVal hInstance As LongPtr, ByVal lpParam As LongPtr) As LongPtr
Private Declare PtrSafe Function SendMessageW Lib "user32" ( _
                              ByVal hWnd As LongPtr, ByVal wMsg As Long, _
                              ByVal wParam As LongPtr, ByVal lParam As LongPtr) As Long
Private Declare PtrSafe Function GetClientRect Lib "user32" (ByVal hWnd As LongPtr, wMsg As Any) As Long
Private Declare PtrSafe Function MapWindowPoints Lib "user32" ( _
                              ByVal hWndFrom As LongPtr, ByVal hWndTo As LongPtr, _
                              lpPoints As Any, ByVal cPoints As Long) As Long
Private Declare PtrSafe Function GetCursorPos Lib "user32" (lpPoint As Any) As Long
Private Declare PtrSafe Function DestroyWindow Lib "user32" ( _
                              ByVal hWnd As LongPtr) As Long
Private Declare PtrSafe Function GetDpiForWindow Lib "user32" ( _
                              ByVal hWnd As LongPtr) As Long

Private Type MCHITTESTINFO
    cbSize As Long
    ptX As Long
    ptY As Long
    uHit As Long
    st(0 To 7) As Integer
End Type

Private DatePickeHwnd As LongPtr 'handle de la fenêtre de type (SysMonthCal32)
Public dat As Date 'variable date servant de propriété pour la classe userform calendrier(lecture/ecriture)
Public obj As Object
'callback pour la classe calendrier
Public Function DateValueX(Optional obj As Object = Nothing)
    With Calendrier
        If Not obj Is Nothing Then
            Set .obj = obj
            Select Case TypeName(obj)
                Case "Range"
                    If IsDate(obj.Value) Then .dat = CDate(obj.Value) Else dat = Date
                Case "TextBox"
                    If IsDate(obj.Text) Then .dat = CDate(obj.Text) Else dat = Date
                Case "Label", "CommandButton"
                    If IsDate(obj.Caption) Then .dat = CDate(obj.Caption) Else dat = Date
                Case "Shape", "DrawingObject"
                    If IsDate(ActiveSheet.DrawingObjects(obj.Name).Text) Then .dat = CDate(ActiveSheet.DrawingObjects(obj.Name).Text) Else dat = Date
                Case Else: Date = Date 'au cas ou l'appelant n'est pas identifié
            End Select
            Else: dat = Date
        End If
        Show
        If TypeName(obj) = "Shape" Then
            DateValueX = Format(dat, "dd/mm/yyyy")
        Else
            DateValueX = dat
        End If
        Unload Calendrier
    End With
End Function


Private Sub Frame1_Click()
    
End Sub

Private Sub UserForm_Activate()
    Const DTM_SETSYSTEMTIME = &H1002
    Dim tm(0 To 9) As Integer, dDate As Date, rect(0 To 3) As Long, PPX As Double
    PPX = 1 / (GetDpiForWindow(Application.hWnd) / 72)
    Frame1.Move 0, 0
    Frame1.Enabled = False
    GetClientRect Frame1.[_GethWnd], rect(0)
    DatePickeHwnd = CreateWindowExA(0, "SysMonthCal32", vbNullString, &H50000000, 0, 0, rect(2), rect(3), Frame1.[_GethWnd], 0, 0, 0)
    Me.Width = rect(2) * PPX
    Me.Height = ((rect(3) + 18) * PPX)
    
    If IsDate(dat) Then
        dDate = Me.dat
        tm(0) = Year(dat):
        tm(1) = Month(dat):
        tm(3) = Day(dat)
        SendMessageW DatePickeHwnd, DTM_SETSYSTEMTIME, 0, VarPtr(tm(0))
    End If
    Select Case TypeName(obj)
        Case "Range": placementRange obj
        Case "TextBox", "Label", "CommandButton": placementUF obj
        Case "Shape": placementActivXsheets obj
    End Select
    
End Sub

Private Sub UserForm_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
    Dim hit As MCHITTESTINFO
    hit.cbSize = Len(hit)
    hit.uHit = 0
    GetCursorPos hit.ptX
    MapWindowPoints 0, Frame1.[_GethWnd], hit.ptX, 1
    SendMessageW DatePickeHwnd, &H100E, 0, VarPtr(hit)
    If (hit.uHit And &H20001) = &H20001 Then
        dat = DateSerial(hit.st(0), hit.st(1), hit.st(3))
        If DatePickeHwnd <> 0 Then DestroyWindow DatePickeHwnd
        Me.Hide
    ElseIf hit.uHit = &H10002 Then
        Frame1.Enabled = True 'édition de l'année
        SendMessageW DatePickeHwnd, &H201, 0, (hit.ptY * &H10000) + hit.ptX
        Frame1.Enabled = False
    Else
        SendMessageW DatePickeHwnd, &H201, 0, (hit.ptY * &H10000) + hit.ptX
    End If
End Sub
'******************************************************************************************
'Fonction de placement  du calendarier issu du calendar 5.0 by patricktoulon
Private Sub placementUF(obj As Object)
    If Not obj Is Nothing Then
        Dim Lft As Double, top As Double, P As Object, PInsWidth As Double, PInsHeight As Double
        Dim k As Double
        ' Normalement Page, Frame ou UserForm
        Lft = obj.Left
        top = obj.top
        Set P = obj.Parent
        Do
            ' Le Page en est pourvu, mais pas le Multipage.
            PInsWidth = P.InsideWidth
            PInsHeight = P.InsideHeight
            ' Prend le Multipage, car le Page est sans positionnement.
            If TypeOf P Is MSForms.Page Then Set P = P.Parent
            k = (P.Width - PInsWidth) / 2
            Lft = (Lft + P.Left + k)
            top = (top + P.top + P.Height - k - PInsHeight)
            If Not (TypeOf P Is MSForms.Frame Or TypeOf P Is MSForms.MultiPage) Then Exit Do
            Set P = P.Parent
        Loop
        Me.Left = Lft + ((obj.Width / 2) * 2) ' a gauche en top
        Me.top = top + ((obj.Height / 2) * 0)
        
        If Me.Left > Application.Left + Application.Width - Me.Width Then Me.Left = Application.Left + Application.Width - Me.Width - 15
        If Me.top > Application.top + Application.Height - Me.Height Then Me.top = Application.top + Application.Height - Me.Height - 15
    End If
End Sub
Function GetPaneIndexByPosition(obj)
    Dim panindex&, i&
    With ActiveWindow
        panindex = .ActivePane.Index
        If Intersect(.ActivePane.VisibleRange, obj) Is Nothing Then
            For i = 1 To .Panes.Count
                If Not Intersect(.Panes(i).VisibleRange, obj) Is Nothing Then panindex = i: Exit For
            Next
        End If
    End With
    GetPaneIndexByPosition = panindex
End Function

Private Function placementRange(obj As Object)
    If obj Is Nothing Then Exit Function
    Dim L1#, T1#, PtoPx#, Z#, cel As Range, panindex&
    With ActiveWindow
        Z = .Zoom / 100
        PtoPx = GetDpiForWindow(Application.hWnd) / 72 'coeff point to pixel
        panindex = GetPaneIndexByPosition(obj)
        'exit si la cellule injecté n'est pas vible a l'ecran
        L1 = (.Panes(panindex).PointsToScreenPixelsX((obj.Left)) / PtoPx) + (((obj.Width * Z))) 'placement partie mobile
        T1 = (.Panes(panindex).PointsToScreenPixelsY((obj.top)) / PtoPx)
    End With
    
    If L1 > Application.Left + Application.Width - Me.Width Then L1 = Application.Left + Application.Width - Me.Width - 15
    If T1 > Application.top + Application.Height - Me.Height Then T1 = Application.top + Application.Height - Me.Height - 15
    With Me
        .Left = L1
        .top = T1
    End With
End Function

Private Function placementActivXsheets(obj As Object)
    If obj Is Nothing Then Exit Function
    Dim L1#, T1#, PtoPx#, Z#
    With ActiveWindow
        Z = .Zoom / 100
        PtoPx = GetDpiForWindow(Application.hWnd) / 72 'coeff point to pixel
         L1 = (.ActivePane.PointsToScreenPixelsX(obj.Left) / PtoPx) + (((obj.Width * Z) / 2) * 2)
        T1 = (.ActivePane.PointsToScreenPixelsY(obj.top) / PtoPx) + (((obj.Height * Z) / 2) * 0)
        
        If L1 > Application.Left + Application.Width - Me.Width Then L1 = Application.Left + Application.Width - Me.Width - 15
        If T1 > Application.top + Application.Height - Me.Height Then T1 = Application.top + Application.Height - Me.Height - 15
        With Me
            .Left = L1
            .top = T1
        End With
    End With
End Function
 

Pièces jointes

- Navigue sans publicité
- Accède à Cléa, notre assistante IA experte Excel... et pas que...
- Profite de fonctionnalités exclusives
Ton soutien permet à Excel Downloads de rester 100% gratuit et de continuer à rassembler les passionnés d'Excel.
Je deviens Supporter XLD
Retour