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

Bonjour,
Et meilleurs vœux à tous, et notamment aux participants de ce fil
@patricktoulon , 2 petites erreurs de code :
- un End If de trop dans le code de l'usf
VB:
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 If <-- Ici
End Sub

Et il manque le PtrSafe de la 1ère déclaration :

VB:
....
Option Explicit
Private Declare Function CreateWindowExA ....

sinon, fonctionne très bien, Bravo à vous
 
Bonsoir,
Maintenant je pense qu'il faut améliorer la taille du calendrier et la rendre automatique puisque elle peut varier selon la région et la langue , essaie avec un autre pays dans les options régionales tu verras que la largeur du calendrier dépend de la taille des noms des jours et pour certains il est affiché partiellement , l'appel de MCM_GETMINREQRECT permet de connaître la taille adéquate , le calendrier accepte une taille nulle au moment de sa création pour être modifié après.
 
re
@Rheeem
d'accords
le voila avec le rectangle déterminé par MCM_GETMINREQRECT
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 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 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 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 Type MCHITTESTINFO
    cbSize As Long
    ptX As Long
    ptY As Long
    uHit As Long
    st(0 To 7) As Integer
End Type

Private Const MCM_GETMINREQRECT As Long = &H1009 'pour  requete du rectangle dans le sendmessage pour rect()
Private Const SWP_NOMOVE As Long = &H2 ' on bloque la fentre ou on la positionne
Private Const SWP_NOZORDER As Long = &H4 'pour eviter la prise de focus anticipée et le flicker (clignotement ou repaint intempestifs)

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 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)
    'creation du calendrier  rectangle 0X0
    DatePickeHwnd = CreateWindowExA(0, "SysMonthCal32", vbNullString, &H50000000, 0, 0, 0, 0, Frame1.[_GethWnd], 0, 0, 0)
    'on capte le rectangle necessaire
    SendMessageW DatePickeHwnd, MCM_GETMINREQRECT, 0, VarPtr(rect(0))
    'on repositionne le calendrier
    SetWindowPos DatePickeHwnd, 0, 0, 0, (rect(2) - rect(0)) + 6, (rect(3) - rect(1)), SWP_NOMOVE Or SWP_NOZORDER
    'on adapte le userform a la taille du calendrier
    Me.Width = 10 + (rect(2) * PPX) '10 pour les marge et ombre(DWM) non comptéespar les api windows
    Me.Height = ((rect(3) + 36) * PPX) '36 pixels=22 pour la marge de barre de titre qui saute +les marges cadre et ombre(DWM)
    'si on utilise getwindowRect avec une structure Rect il y a moins a rattraper mais j'ai voulu garder ton model de base
    'on pourrait plutot qu'en dur utiliser les valeurs de getsystemmetrics et même la dwmapi.dll pour parfaire le calcul du rectangle
    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
fichier V 3.0
Patrick
 

Pièces jointes

Quelques corrections dans le code:

La taille du Frame qui contient le calendrier aussi doit etre modifiée,la forme est redimensionnée mais certains calendriers sont partialement masqués par le frame alors qu'il y a suffisamment d'espace.

Petits remarques:
DestroyWindow a été retirer pas nécessaire et crée un effet visuel violent lors de la fermeture de la fiche,

On peut crée le controle sans le label Aujourd’hui car pas tous les calendriers ont besoin de se référer à le date cette date , pour le marque ajouter MCS_NOTODAY au paramètre style de CreateWindowEx. il est déclaré dans le code mais pas utilisé
Code:
'*****************************************************************************************************
'    ___             _____  _____  _____
'   // \\    //  // //     //     //      //\  / \\
'  //__//   //__// //__   //__   //__    //  \/   \\
' //   \\  //  // //     //     //      //         \\
'//    // //  // //____ //____ //____  //           \\
'    ___     _     _______  __      _   ____  _   _  _______  ___     _   _   _    ___     _     _.
'   //  \\  /\\      //    // \\   //  //    //  //    //    //  \\  //  //  //   //  \\  //|   //
'  //___// //__\    //    //__//  //  //    //__//    //    //   // //  //  //   //   // // |  //
' //      //   \\  //    //  \\  //  //    //  \\    //    //   // //  //  //   //   // //  | //
'//      //    // //    //   // //  //___ //    \\  //     \\__// //__//  //___ \\__// //   |//
'****************************************************************************************************

'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 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 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 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 GetDpiForWindow 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 Type POINT_API
  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

Private Type SystemTime
    wYear As Integer
    wMonth As Integer
    wDayOfWeek As Integer
    wDay As Integer
    wHour As Integer
    wMinute As Integer
    wSecond As Integer
    wMilliseconds As Integer
End Type

Private Type MCHITTESTINFO
    cbSize As Long
    pt As POINT_API
    uHit As Long
    st As SystemTime
End Type

Private Const MCM_FIRST = &H1000
Private Const MCM_SETCURSEL = MCM_FIRST + 2
Private Const MCM_GETMINREQRECT = MCM_FIRST + 9
Private Const MCM_HITTEST = MCM_FIRST + 14
Private Const MCM_GETMAXTODAYWIDTH = MCM_FIRST + 21

Private Const MCHT_CALENDARDATE = &H20001
Private Const MCHT_TITLEMONTH = &H10002
 
Private Const MCS_WEEKNUMBERS = 4
Private Const MCS_NOTODAYCIRCLE = 8
Private Const MCS_NOTODAY = &H10

Private Const WS_CHILD = &H40000000
Private Const WS_VISIBLE = &H10000000
Private Const WM_LBUTTONDOWN = &H201
 
Private Const SWP_NOMOVE = &H2 ' on bloque la fentre ou on la positionne
  
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
        If obj Is Nothing Then Me.StartUpPosition = 1
        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 UserForm_Initialize()
Dim Rc As RECT, H As Long, W As Long, TodayWidth  As Long, PPX As Double
PPX = 72 / AppDpi

Frame1.Enabled = False
DatePickeHwnd = CreateWindowExA(0, "SysMonthCal32", vbNullString, _
       WS_CHILD Or WS_VISIBLE, 0, 0, 0, 0, _
       Frame1.[_GethWnd], 0, 0, 0)
 
SendMessageW DatePickeHwnd, MCM_GETMINREQRECT, 0, VarPtr(Rc)
TodayWidth = SendMessageW(DatePickeHwnd, MCM_GETMAXTODAYWIDTH, 0, 0)
If TodayWidth > Rc.Right Then: Rc.Right = TodayWidth
SetWindowPos DatePickeHwnd, 0, 0, 0, Rc.Right, Rc.Bottom, SWP_NOMOVE
Frame1.Move 0, 0, Rc.Right * PPX, Rc.Bottom * PPX
Move 0, 5000, Frame1.Width + (Width - InsideWidth), Frame1.Height + (Height - InsideHeight)
 
End Sub

Private Sub UserForm_Activate()
  Dim tm As SystemTime
  If IsDate(dat) Then
      tm.wYear = Year(dat):
      tm.wMonth = Month(dat):
      tm.wDay = Day(dat)
      SendMessageW DatePickeHwnd, MCM_SETCURSEL, 0, VarPtr(tm)
  End If
  
  Select Case TypeName(obj)
      Case "Range": placementRange obj
      Case "TextBox", "Label", "CommandButton": placementUF obj
      Case "Shape": placementActivXsheets obj
  End Select
  If Left = 0 Then
     Move (Application.Width - Width) / 2, (Application.Height - Height) / 2
  End If
End Sub
Private Function AppDpi() As Long
AppDpi = 96
  On Error Resume Next
  AppDpi = GetDpiForWindow(Application.hWnd)
End Function
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.pt
    MapWindowPoints 0, Frame1.[_GethWnd], hit.pt, 1
    SendMessageW DatePickeHwnd, MCM_HITTEST, 0, VarPtr(hit)
    If (hit.uHit And MCHT_CALENDARDATE) = MCHT_CALENDARDATE Then
        dat = DateSerial(hit.st.wYear, hit.st.wMonth, hit.st.wDay)
        Me.Hide
    ElseIf hit.uHit = MCHT_TITLEMONTH Then
        Frame1.Enabled = True 'édition de l'année
        SendMessageW DatePickeHwnd, WM_LBUTTONDOWN, 0, (hit.pt.Y * &H10000) + hit.pt.X
        Frame1.Enabled = False
    Else
        SendMessageW DatePickeHwnd, WM_LBUTTONDOWN, 0, (hit.pt.Y * &H10000) + hit.pt.X
    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 = AppDpi / 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 = AppDpi / 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

Dernière édition:
Bonjour @cathodique
le code fonctionne en 32 bit vba 7
si tu es sur 2007 non car la on est en vb6
En fait, c'est juste par curiosité car moi j'utilise ton calendrier depuis un bon bout de temps.
Ma bécane qui date, fonctionne sous win7 64 bits et Office 2010 32 bits.
Alors est-il normal d'avoir l'erreur ci-dessous
CALENDRIER.gif


Bonne journée.
 
@Rheeem
important
Avec tout ca on a oublié l'annulation par la croix de l'userform qui quand on ferme met le 01/01/2026(la date du jour ).
Seule ma version 1 le faisait puisque j'ignorais le click au-dessus des jours


et puisque l'on en est à dimensionner le calendrier selon le texte (langue)
Il nous faut aussi changer le format dans les textbox label shape etc... car eux n'ont pas de numberformat
du coup j'en rajoute une couche facon calendar 5.0
On a maintenant la possibilité de
  1. Soit laisser excel avec son format natif selon la langue system
  2. soit le forcer en FR,US,CA
Et pour le coup je me suis débrouillé pour que la fermeture par la croix de l'userform serve d'annulation
Ce qui a pour conséquence soit elle ne renvoie rien si au départ l'appelant n’avait rien soit la date présente au départ.
du coup
demo4.gif
 

Pièces jointes

La valeur du MCHT_CALENDARDATE est incorrecte elle doit être = &H20001 (corrigé) , la déclaration initial était composée deux constants et pour éviter la redondance j'ai supprimé le premier sans corriger la valeur MCHT_CALENDARDATE :
Private Const MCHT_CALENDAR = &H20000
Private Const MCHT_CALENDARDATE = MCHT_CALENDAR + 1
Ce bug provoque la fermeture du calendrier si on clique sur Aujourd’hui en renvoyant une date invalide .
Avec tout ca on a oublié l'annulation par la croix de l'userform qui quand on ferme met le 01/01/2026(la date du jour ).
Seule ma version 1 le faisait puisque j'ignorais le click au-dessus des jours
Je pense que DateValueX doit renvoyer false si l'utilisateur annule la saisie (comme Application.InputBox)cela permet de sortir de l'opération au lieu d'insister avec fausse valeur.
 
- 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