Copie de Variable range
Bonjour à tous,
Je vous la fais courte, je fais la copie ci-dessous qui fonctionne ws (étant un autre classeur) :
ThisWorkbook.Sheets("Données").Range("C4:G900") = ws.Range("B2:F898").ValueMais si je fais :
Public plage1 As Range
Set plage1 = Range("C4:G900")
ThisWorkbook.Sheets("Données").ThisWorkbook.plage1 = ws.Range("B2:F898").ValueJ'ai le droit à cette erreur :
Propriété ou méthode non géré par cet objet.
Je suppose que c'est dû à une mauvaise utilisation de ma part d'une variable de type range, mais je ne sais pas m'en sortir...
J'ai utilisé ma variable plage1 ailleurs dans mon code et ça fonctionne donc ma variable et son attribution de valeur fonctionne mais c'est ma façon de l'utiliser dans ce cas précis qui pose problème.
Merci d'avance de votre aide.
Salut,
Essayes ça:
ThisWorkbook.Sheets("Données").plage1 = ws.Range("B2:F898").ValueJ'ai essayé ça ne fonctionne pas, j'ai l'erreur :
Variable objet ou variable de bloc With non définie
Bonjour,
plage1 est une variable Range !
plage1.Value = ws.Range("B2:F898").ValueBonjour,
plage1 est une variable Range !
Ce qui est facilement vérifiable :
Sub Test()
Dim plage1 As Range
Set plage1 = ThisWorkbook.Sheets("Feuil1").Range("C4:G900")
MsgBox TypeName(plage1)
MsgBox TypeName(plage1.Value)
End SubAlors j'ai essayé ça ne fonctionne pas...
plage1.Value = ws.Range("B2:F898").ValuePropriété ou méthode non géré par cet objet.
Et quand je cherche à savoir le type avec :
MsgBox TypeName(plage1)
MsgBox TypeName(plage1.Value)En premier : Range
En second : Variant()
J'ai essayé plusieurs méthodes mais j'ai l'erreur cité plus haut, je ne comprend pas...
Il ne reste que ws comme variable objet... à vérifier !
ws c'est worksheet, une feuille donc.
Oui !
Il faut aussi voir comment tes variables sont initialisées (et peut-être dans quel type de module cela se passe).
Tu donnes des bouts de code décousus, on ne peut faire une analyse complète.
La confidentialité de mon fichier fait que je ne peu exposer tout le code, mais je comprend la difficulté je vais donc faire un effort :
Public ws As Worksheet
Public plage1, plage2, plage3 As Range
Public numerobdd, nombdd, rdgmoyen, croissancemois As Range
Public Sub Workbook_Open()
Set nombdd = Range("FK7")
Set numerobdd = Range("FK8")
Set wkbdd = Workbooks("exemple.xlsx")
Set plage1 = Range("C4:G900")
Set plage2 = Range("FE4:FG900")
Set plage3 = Range("FK9:FK900")Le code ci-dessous fonctionne :
Sub Zonecombinée6_QuandChangement()
Set ThisWorkbook.ws = ThisWorkbook.wkbdd.Sheets("" & ThisWorkbook.nombdd & "")
ThisWorkbook.Sheets("Données").Range("C4:G900") = ThisWorkbook.ws.Range("B2:F898").Value
ThisWorkbook.Sheets("Données").Range("FE4:FG900") = ThisWorkbook.ws.Range("G2:I898").Value
ThisWorkbook.Sheets("Données").Range("FK9:FK900") = ThisWorkbook.ws.Range("J7:J898").ValueEt c'est celui-ci que j'aurais aimé faire fonctionner :
Sub Zonecombinée6_QuandChangement()
Set ThisWorkbook.ws = ThisWorkbook.wkbdd.Sheets("" & ThisWorkbook.nombdd & "")
ThisWorkbook.Sheets("Données").ThisWorkbook.plage1 = ThisWorkbook.ws.Range("B2:F898").Value
ThisWorkbook.Sheets("Données").ThisWorkbook.plage2 = ThisWorkbook.ws.Range("G2:I898").Value
ThisWorkbook.Sheets("Données").ThisWorkbook.plage3 = ThisWorkbook.ws.Range("J7:J898").ValueJ'espère avoir clarifié mon problème, merci encore !
Oh ! Que non !
Déjà tes déclarations sont défectueuses ! Les variables se typent individuellement, tu as donc 5 variables non typées.
Ensuite, si tu n'hésites pas à sauter des lignesà l'intérieur des procédures, là où il serait bon d'en sauter, entre les procédures, tu ne le fais, et tu vas coller ta première procédure aux déclarations. Ton code n'étant pas indenté, tu fais tout pour compliquer la lecture !
Tu ne précises pas où l'on est ! S'il y a Workbook_Open, on doit être dans le module ThisWorkbook, sinon elle ne fonctionnera pas.
Dans ce cas, tes variables publiques sont mal placées, elles devraient être déclarées dans un module Standard.
Et Public Sub Workbook_Open est une ânerie sans nom.
Une procédure évènementielle est toujours privée, cela ne peut se changer, et elle ne peut fonctionner que dans le module qui lui est dévolu.
De plus on ne prend pas la peine d'utiliser le mot Public dans une déclaration de procédure, parce que toutes les procédures sont publiques par défaut. On met Private quand elle ne doivent pas l'être, et pour les évènements c'est une information et non une décision...
Ensuite tu affectes des plages à l'ouverture, en ne qualifiant pas les propriétés Range, sans savoir sur quelle feuille le classeur va s'ouvrir ! Déjà quand on sait quelle est la feuille active, cette méthode est mauvaise et il est toujours préférable de préciser. Là c'est vouloir se réserver des surprises pour l'avenir et jouer à la roulette russe...
On doit supposer que FK7 contient un nom de feuille. Mais que viennent faire ces ajouts de chaînes vides pour l'encadrer !
Si le nom n'est pas du texte, on le convertit avec CStr, sinon il se suffit à lui-même.
Dans la première version, tu affectes les valeurs de plages de ta base à des plages de la feuille Données. Sois content si ça fonctionne..
Dans la seconde, tes variables plage, dès lors qu'elles sont initialisées, te permettent de t'y référer par leur utilisation.
Le ThisWorkbook.Sheets("Données"). est de trop !
Je ne saurais trop conseiller de réécrire le code pour le fiabiliser et le simplifier.
Bonjour,
Là, il y a un sacré mélange !
Ceci fonctionne, les valeurs sont mise les unes à la suite des autres :
Sub Zonecombinéet()
Dim Ws As Worksheet
Dim WsDonnee As Worksheet
Dim Plage1 As Range
Dim Plage2 As Range
Dim Plage3 As Range
Set Ws = ThisWorkbook.Sheets("Le nom que tu veux")
Set WsDonnee = ThisWorkbook.Sheets("Données")
Set Plage1 = WsDonnee.Range("B2:F898")
Set Plage2 = WsDonnee.Range("G2:I898")
Set Plage3 = WsDonnee.Range("J7:J898")
Ws.Range(Ws.Cells(1, 1), Ws.Cells(Plage1.Rows.Count, Plage1.Columns.Count)).Value = Plage1.Value
Ws.Range(Ws.Cells(Plage1.Rows.Count + 1, 1), Ws.Cells(Plage2.Rows.Count, Plage2.Columns.Count)).Value = Plage2.Value
Ws.Range(Ws.Cells(Plage1.Rows.Count + Plage2.Rows.Count + 1, 1), Ws.Cells(Plage3.Rows.Count, Plage3.Columns.Count)).Value = Plage3.Value
End Subceci par contre, ne pourra jamais fonctionner :
Sub Zonecombinée6_QuandChangement()
Set ThisWorkbook.Ws = ThisWorkbook.wkbdd.Sheets("" & ThisWorkbook.nombdd & "")
ThisWorkbook.Sheets("Données").ThisWorkbook.Plage1 = ThisWorkbook.Ws.Range("B2:F898").Value
ThisWorkbook.Sheets("Données").ThisWorkbook.Plage2 = ThisWorkbook.Ws.Range("G2:I898").Value
ThisWorkbook.Sheets("Données").ThisWorkbook.Plage3 = ThisWorkbook.Ws.Range("J7:J898").Value
End SubPar exemple, quand tu écris :
ThisWorkbook.nombdd ça peut fonctionné si dans le module du classeur (ThisWorkbook) tu as une variable déclarée "Public" de type String (je ne vois pas l'intêret de "" &)
mais quand tu écris :
ThisWorkbook.wkbdd.Sheetslà, ça ne peux pas fonctionner et normalement, tu aurais dû t'en rendre compte quand l'intellisense ne te propose pas l'entrée "wkbdd"
@MFerrand
MFerrand a écrit :Oh ! Que non !
Déjà tes déclarations sont défectueuses ! Les variables se typent individuellement, tu as donc 5 variables non typées.
C'est à dire ?
MFerrand a écrit :Tu ne précises pas où l'on est ! S'il y a Workbook_Open, on doit être dans le module ThisWorkbook, sinon elle ne fonctionnera pas.
Si si le workbook_open est bien dans le module Thisworkbook
MFerrand a écrit :Dans ce cas, tes variables publiques sont mal placées, elles devraient être déclarées dans un module Standard.
Un module "Standard" c'est ceux qui s'appellent par défaut module1, module2 ?
L’intérêt pour moi de mettre mes variables publiques ici était de pouvoir leur affecter une valeur dès l'ouverture de façon à qu'après quelque soit la feuille où le module la valeur soit bien affectée et que je puisse utiliser ma variable directement.
MFerrand a écrit :Et Public Sub Workbook_Open est une ânerie sans nom.
Une procédure évènementielle est toujours privée, cela ne peut se changer, et elle ne peut fonctionner que dans le module qui lui est dévolu.
De plus on ne prend pas la peine d'utiliser le mot Public dans une déclaration de procédure, parce que toutes les procédures sont publiques par défaut. On met Private quand elle ne doivent pas l'être, et pour les évènements c'est une information et non une décision...
Bon bon ok je savais pas.
MFerrand a écrit :Ensuite tu affectes des plages à l'ouverture, en ne qualifiant pas les propriétés Range, sans savoir sur quelle feuille le classeur va s'ouvrir ! Déjà quand on sait quelle est la feuille active, cette méthode est mauvaise et il est toujours préférable de préciser. Là c'est vouloir se réserver des surprises pour l'avenir et jouer à la roulette russe...
Qu'es-ce que tu veux dire par qualifier les proriétés range ?
MFerrand a écrit :On doit supposer que FK7 contient un nom de feuille. Mais que viennent faire ces ajouts de chaînes vides pour l'encadrer !
Si le nom n'est pas du texte, on le convertit avec CStr, sinon il se suffit à lui-même.
Ca me met incompatibilité de type si je ne les mets pas alors j'ai trouvé cette solution... Mais si tu peux me donner un exemple de ce que serait une conversion.
MFerrand a écrit :Dans la première version, tu affectes les valeurs de plages de ta base à des plages de la feuille Données. Sois content si ça fonctionne..
Dans la seconde, tes variables plage, dès lors qu'elles sont initialisées, te permettent de t'y référer par leur utilisation.
Le ThisWorkbook.Sheets("Données"). est de trop !
Je ne saurais trop conseiller de réécrire le code pour le fiabiliser et le simplifier.
C'est justement pour cela que je sollicite votre aide sur ce forum c'est dans le but d'optimiser mon code, car je suis conscient que mon autodidactie m'a conduit à faire du VBA façon agricole...
@ Theze
Theze a écrit :Bonjour,
Ws.Range(Ws.Cells(1, 1), Ws.Cells(Plage1.Rows.Count, Plage1.Columns.Count)).Value = Plage1.Value Ws.Range(Ws.Cells(Plage1.Rows.Count + 1, 1), Ws.Cells(Plage2.Rows.Count, Plage2.Columns.Count)).Value = Plage2.Value Ws.Range(Ws.Cells(Plage1.Rows.Count + Plage2.Rows.Count + 1, 1), Ws.Cells(Plage3.Rows.Count, Plage3.Columns.Count)).Value = Plage3.Value End Sub
Pardon cette partie que tu as corrigé, je n'y comprend rien, peux-tu me l'expliquer ?
Theze a écrit :mais quand tu écris :
ThisWorkbook.wkbdd.Sheetslà, ça ne peux pas fonctionner et normalement, tu aurais dû t'en rendre compte quand l'intellisense ne te propose pas l'entrée "wkbdd"
Hmm oui mais tu sais tant que ça marche... Comme je l'ai dis plus haut, je n'ai pas un haut niveau et en suis conscient je fais du bricolage mais disons que jusqu'à présent je suis toujours arrivé à mes fins.
Mais il est bien sûr intéressant de venir ici de trouver des gens meilleur que soit et d'apprendre à coder de la façon la plus propre possible !
Re,
Ce que veux dire MFerrand par "déclarations défectueuses", c'est que quand tu fais ce genre de déclaration :
Public plage1, plage2, plage3 As Rangeseule la variable "plage3" est de type Range, les 2 autres sont de type Variant, il te faut préciser pour chaque variable le type que tu veux leur attribuer.
Concernant "Workbook_Open()", par défaut la procédure est évènementielle donc, liée à l'objet et le fait que par défaut elle soit "Private" empêche qu'on puisse l'appeler de l'extérieur de son module mais effectivement, ça fonctionne. Tu peux aussi utiliser une macro "Auto_Open()" mais cette dernière doit obligatoirement être dans un module standard. Effectivement, les modules standards sont ceux qui sont nommés par défaut Module1, etc...
Par "qualifier un Range ", MFerrand veux dire "parenté" un Range. Quand tu écris :
Set nombdd = Range("FK7")tu veux faire référence à la cellule FK7 mais par défaut ça sera la cellule FK7 de la feuille active et c'est là que tu peux avoir des surprises dans le résultat attendu. Il est mieux de "parenter" ton Range comme par exemple :
Dim nombdd As Range
'la cellule "FK7" de la feuille "Feuil1" du classeur qui possède ces lignes de code
Set nombdd = ThisWorkbook.Worksheets("Feuil1").Range("FK7")de cette façon, il n'y aura aucune ambiguïté.
ensuite, tu peux utiliser juste le nom de ta variable sans avoir besoin d'en préciser les parents car le compilateur c'est exactement à quel objet tu veux faire référence :
Set ws = ThisWorkbook.Sheets(nombdd.Value)Voir les commentaires dans le code pour les explications mais avant tout, je dois dire que j'ai fait une boulette dans mon adressage (seconde borne de la plage, j'ai oublié de rajouter les lignes des plages précédentes donc, c'est comme ceci et non comme le code précédemment posté) :
'Ws ayant été initialisée pour faire référence la la feuille portant le nom "Le nom que tu veux" (c'est pour l'exemple, tu l'as bien compris)
'Set Ws = ThisWorkbook.Sheets("Le nom que tu veux")
'il suffit juste d'utiliser le nom de la variable (Ws) pour savoir quelle feuille doit recevoir les valeurs des 3 plages
'pour la première plage, on commence en A1 "Ws.Cells(1, 1)" (pour une plage, c'est la cellule en haut à gauche)
'et là, tu vois que j'ai bien "parenté" la cellule "A1"
'ensuite, les plages devant être identiques, je défini la seconde borne de la plage (cellule en bas à droite) en utilisant le
'nombre de lignes et de colonnes que comprend la plage où sont récupé les valeurs et si tu fais :
MsgBox Ws.Range(Ws.Cells(1, 1), Ws.Cells(Plage1.Rows.Count, Plage1.Columns.Count)).Address(0, 0)
'tu aura l'adresse de la plage, par exemple si ta plage1 fait 20 lignes et 4 colonnes, l'adresse sera "A1:D20"
Ws.Range(Ws.Cells(1, 1), Ws.Cells(Plage1.Rows.Count, Plage1.Columns.Count)).Value = Plage1.Value
'ensuite, pour inscrire les valeurs de la seconde plage, il faut prendre en compte celles que tu as déjà inscrites et donc, tu ne peux
'plus prendre comme référence A1 mais le nombre de lignes qu'a déjà utilisée la plage Plage1
'mais là, je viens de m'apercevoir que j'ai fait une boulette en oubliant d'étendre la plage pour la seconde borne donc, c'est comme ça :
Ws.Range(Ws.Cells(Plage1.Rows.Count + 1, 1), Ws.Cells(Plage1.Rows.Count + Plage2.Rows.Count, Plage2.Columns.Count)).Value = Plage2.Value
'idem
Ws.Range(Ws.Cells(Plage1.Rows.Count + Plage2.Rows.Count + 1, 1), Ws.Cells(Plage1.Rows.Count + Plage2.Rows.Count + Plage3.Rows.Count, Plage3.Columns.Count)).Value = Plage3.ValueMais il est plus simple d'utiliser une variable pour définir la première ligne libre :
Sub Test()
Dim Ws As Worksheet
Dim WsDonnee As Worksheet
Dim Plage1 As Range
Dim Plage2 As Range
Dim Plage3 As Range
Dim Lg As Long
Set Ws = ThisWorkbook.Sheets("Le nom que tu veux")
Set WsDonnee = ThisWorkbook.Sheets("Données")
Set Plage1 = WsDonnee.Range("B2:F898")
Set Plage2 = WsDonnee.Range("G2:I898")
Set Plage3 = WsDonnee.Range("J7:J898")
Ws.Range(Ws.Cells(1, 1), Ws.Cells(Plage1.Rows.Count, Plage1.Columns.Count)).Value = Plage1.Value
Lg = Ws.Cells(Rows.Count, 1).End(xlUp).Row + 1 'sur colonne A
Ws.Range(Ws.Cells(Lg, 1), Ws.Cells(Lg + Plage2.Rows.Count, Plage2.Columns.Count)).Value = Plage2.Value
Lg = Ws.Cells(Rows.Count, 1).End(xlUp).Row + 1
Ws.Range(Ws.Cells(Lg, 1), Ws.Cells(Lg + Plage3.Rows.Count, Plage3.Columns.Count)).Value = Plage3.Value
End Subbien sûr ceci à condition que la colonne A est le même nombre de cellules remplies que les autres colonnes sinon, il faut faire la recherche sur toutes les colonnes.
Bonjour,
Déclarations de variables : elles doivent être typées individuellement, soit dans ton cas :
Public ws As Worksheet
Public plage1 As Range, plage2 As Range, plage3 As Range
Public numerobdd As Range, nombdd As Range, rdgmoyen As Range, croissancemois As RangeEvidemment, cette déclaration est à faire dans un module Standard. Les modules standard sont ceux que tu inséres, qui prennent par défaut le nom générique Module1, Module2...
Dans ThisWorkbook, tes variables déclarées Public seront certes publiques, mais le module ne l'est pas ! Donc, comme tu l'as expérimenté, pour les atteindre d'un autre module, tu dois appeler ta variable sous la forme : NomModule.NomVariable.
Ce qui allonge et peut devenir vite fastidieux, et au cas particulier te fait introduire une telle quantité de ThisWorkbook dans tes lignes de commandes que ça fait réfléchir à chaque ligne...
Déclarées dans un module Standard, le nom de la variable suffit pour l'appeler de n'importe où dans le projet.
Quand tu écris : Range, tu fais appel à la propriété Range d'un objet Worksheet, Workbook ou Application.
Range sans rien est équivalent de : Application.ActiveSheet.Range (Application n'est jamais indiqué dans la plupart des cas, mais est toujours implicite...). Cette propriété renvoie un objet Range. Si tu ne lui indiques pas dans quelle feuille de quel classeur, VBA va chercher la feuille active du classeur actif (il y en a le plus souvent une, mais cela pourrait ne pas être le cas et générer une erreur).
En outre tu fais chercher VBA, il va donc y mettre plus de temps ! Et même quand tu te contentes de ActiveSheet.Range, ce sera plus rapide que Range...
Mais au cas particulier de l'ouverture du classeur il va s'ouvrir sur la feuille qui était active lors de son enregistrement. Là, ne pas préciser c'est tirer à la loterie la feuille qui va gagner...
Il est donc judicieux et sage de lui dire que c'est la feuille Données qui est visée (ce qu'on déduit de ton code, j'espère que c'est le cas). C'est ce qu'on appelle doter l'expression d'un qualificateur d'objet (ce n'est pas moi qui ai inventé cette façon de désigner la chose...
Mais tu ne vas pas t'amuser à répéter Worksheets("Données") à chaque fois : tu vas initier un bloc d'instruction With... End With pour la feuille, et à l'intérieur de ce bloc, au lieu d'écrire Range, tu écris .Range(avec un point devant), le point fait référer automatiquement à l'objet désigné dans l'instruction With.
C'est pas très compliqué, pas long non plus, et ça assure une fiabilité dans le temps à ton code...
Plus : chaque fois que tu peux avoir l'occasion d'utiliser un bloc With... End With (et on peut en imbriquer : les plus extérieurs étant les objets parents de ceux plus intérieurs...), il faut le faire, car VBA met en mémoire pour un accès direct et sera d'autant plus rapide.
Cela sera aussi rapide que l'utilisation de variables et dans certains cas plus, et ça peut s'utiliser avec une variable : With NomVariableObjet...
Tu peux donc écrire ta proc. d'ouverture :
Sub Workbook_Open()
With Worksheets("Données")
Set nombdd = .Range("FK7")
Set numerobdd = .Range("FK8")
Set ws = Workbooks("exemple.xlsx").Worksheets(nombdd.Value)
Set plage1 = .Range("C4:G900")
Set plage2 = .Range("FE4:FG900")
Set plage3 = .Range("FK9:FK900")
End With
'...
End SubElle va atteindre les variables (publiques) qui sont dans un module public, de la même façon que si elles étaient dans le module...
J'y ai ajouté ws, que tu n'initialisais pas ici (on ne sait pourquoi) au profit d'une variable classeur apparemment inutilisée (non déclarée, donc confinée à la proc. et sans portée...). Tu n'en avais d'ailleurs aucun besoin car une fois initialisée ws tu n'as plus besoin du classeur pour accéder à la feuille.
Le problème de l'erreur 13 sur cette initialisation (qui se produisait ailleurs dans ton code mais qui se produira ici de la même façon...), je pense (pour avoir déjà eu le cas) que dans la désignation de la feuille VBA attend l'Item qui peut être soit un nombre (rang de la feuille dans le classeur), soit son nom. Or, nombdd est une variable objet désignant une plage, VBA décèle donc une incompatibilité et stoppe, sans chercher la propriété par défaut d'un objet Range, qui est Value (il le fait dans la majorité des cas, ce qui permet de se passer de l'indication Value, mais pas dans tous !).
Ce pourquoi, j'ajoute : nombdd.Value pour que ce soit bien la valeur de la cellule qui soit prise en compte, ce qui si c'est bien ce que je pense doit normalement suffire.
En ajoutant des "" (un seul devant aurait suffi), tu utilises une façon indirecte de forcer un type String, et pour y répondre VBA se trouve contraint d'aller chercher la valeur de la cellule qu'il renverra sous forme de chaîne. Mais autant utiliser une méthode plus orthodoxe et ne risquant pas de prêter à confusion...
Dans ce cas, il n'y a pas de problème de conversion. Ce dernier peut se poser quand tu nommes tes onglets de façon numérique, par exemple désigant l'année (un cas que l'on voit assez fréquemment). Pas de problème à l'affectation du nom : tu envoies un Integer et le résultat sera un String, conversion automatique. Mais quand tu voudras appeler la feuille, si tu l'appelles avec un Integer, VBA ne la retrouve plus et te renvoie une incompatibilité de type... Dans ce cas la conversion préalable s'impose : Worksheets (CStr(a)), a étant l'année...
A la suite ton code problématique peut s'écrire :
Sub Zonecombinée6_QuandChangement()
plage1.Value = ws.Range("B2:F898").Value
plage2.Value = ws.Range("G2:I898").Value
plage3.Value = ws.Range("J7:J898").Value
End Subet il n'y a pas de raison pour qu'il ne fonctionne pas... !
Note que dans ce débat, on ne s'intéresse qu'à la syntaxe et la façon d'écrire le code avec les ingrédients soumis. Cela n'a aucune inférence avec la rationalité de tes choix (nombre et articulation des variables entre elles, articulation et économie des procédures, etc.) qui est un autre débat !
Cordialement.
Salut Theze !
J'aurais allégé si j'avais vu ton intervention avant... Tant pis pour moi !