Question

Supposons que je crée un Sub (pas une fonction) dont la mission dans la vie est de prendre la cellule active (c'est-à-dire la sélection) et de définir une cellule adjacente à une certaine valeur. Cela fonctionne bien.

Lorsque vous essayez de convertir ce sous-ensemble en une fonction et que vous essayez de l'évaluer à partir d'une feuille de calcul (c.-à-d. en définissant sa formule sur " = MyFunction () "), Excel aboyera du fait que vous tentent d’affecter la valeur de la cellule non active et forcent simplement la fonction à renvoyer #VALUE sans toucher à la cellule adjacente.

Est-il possible de désactiver ce comportement de protection? Si non, quel est le bon moyen de le contourner? Je recherche quelque chose qu'un développeur compétent pourrait accomplir sur une période de 1 à 2 semaines, si possible.

Cordialement, Alan.

Remarque: j'utilise 2002, je suis donc favorable à une solution qui fonctionnerait pour cette version. Cela dit, si les futures versions rendent cela beaucoup plus facile, j'aimerais aussi le savoir.

Était-ce utile?

La solution

Cela ne peut pas être fait, ce qui est logique car:

  • Lorsqu'une fonction de feuille de calcul est appelée, la cellule contenant la fonction n'est pas nécessairement la cellule active. Vous ne pouvez donc pas trouver la cellule adjacente de manière fiable.

  • Lorsque Excel recalcule une feuille de calcul, il doit conserver des dépendances entre les cellules. Il ne peut donc pas permettre aux fonctions de la feuille de calcul de modifier arbitrairement d’autres cellules.

Le mieux que vous puissiez faire est l’un des suivants:

  • Gère l'événement SheetChange. Si une cellule contenant votre fonction change, modifiez la cellule adjacente.

  • Placez une fonction de feuille de calcul dans la cellule adjacente pour renvoyer la valeur souhaitée.

Mettre à jour

En ce qui concerne le commentaire: & "; J'aimerais que cette fonction fonctionne sur une feuille de calcul" vierge ". Par conséquent, je ne peux pas vraiment compter sur l'événement SelectionChange de feuilles de calcul qui n'existent peut-être pas encore, mais qui devront appeler cette fonction ":

  • Pouvez-vous mettre votre fonction dans un complément XLA? Ensuite, votre complément XLA peut gérer l'événement Application SheetChange (*) de tous les classeurs ouverts dans cette instance d'Excel?

En ce qui concerne le commentaire: & "; Quoiqu’il en soit, si vous gardez Excel dans CalculationMode = xlManual et n’indiquez que des valeurs, vous devriez vous en contenter &";

.
  • Même lorsque CalculationMode est défini sur xlManual, Excel doit conserver un arbre de dépendance composé de références entre les cellules afin de pouvoir effectuer le calcul dans le bon ordre. Et si l’une des fonctions peut mettre à jour une cellule arbitraire, l’ordre sera perturbé. C’est probablement la raison pour laquelle Excel impose cette restriction.

(*) A l'origine, j'ai écrit SelectionChange ci-dessus, corrigé maintenant. Évidemment, l'événement approprié est SheetChange pour les objets Workbook ou Application ou Change pour l'objet Worksheet.

Mise à jour 2 Quelques remarques sur le article d'AlanR décrivant comment "faire un peu" pour que ça marche en utilisant une minuterie:

  • On ne voit pas comment la fonction de minuterie (& "; Woohoo &") saura quelles cellules mettre à jour. Vous n'avez aucune information indiquant quelle cellule contient la formule qui a déclenché le chronomètre.

  • Si la formule existe dans plusieurs cellules (dans le même classeur ou dans des classeurs différents), la fonction UDF sera appelée plusieurs fois au cours d'un recalcul en écrasant le paramètre timerId. Par conséquent, vous ne réussirez pas à détruire le chronomètre de manière fiable et vous perdrez des ressources Windows.

Autres conseils

Selon Comment créer des fonctions Excel personnalisées définies par l'utilisateur :

  

Limitations des fonctions définies par l'utilisateur

     
      
  • Impossible de placer une valeur dans une cellule autre que la cellule (ou la plage) contenant   la formule. En d'autres termes, les FDU sont   destiné à être utilisé comme " formules " ;, non   nécessairement & "; macros &";.
  •   

On dirait que cela ne peut pas être fait.

J'utilise Excel 2007 et cela ne fonctionne pas. Excel mentionne qu'il crée une référence circulaire. Je ne pense pas que vous puissiez modifier d'autres cellules d'une fonction, mais simplement renvoyer une valeur.

C'est une sorte de programmation fonctionnelle, sans effets secondaires. Si vous pouviez simplement modifier d’autres cellules dans une fonction (utilisée dans une feuille de calcul), Excel n’a aucun moyen de connaître l’ordre et de savoir quoi recalculer si une cellule est modifiée.

Cet article contient également beaucoup d'informations sur la manière dont Excel effectue le recalcul. Mais il ne dit jamais que les autres cellules sont gelées.

Je ne sais pas ce que vous essayez de faire, mais pourquoi ne pas simplement placer une autre fonction dans la cellule adjacente, qui prend la première cellule en tant que paramètre?

Exemple:

Public Function Bar(r As Range) As Integer
  If r.Value = 2 Then
    Bar = 0
  Else
    Bar = 128
  End If
End Function

Merci à tous d'avoir répondu. C'est possible de faire ça! Kinda. Je dis "un peu" parce que techniquement, la "fonction" n'affecte pas les cellules qui l'entourent. Toutefois, dans la pratique, aucun utilisateur ne peut faire la différence.

L'astuce consiste à utiliser une API Win32 pour démarrer une minuterie. Dès qu'elle s'éteint, vous faites ce que vous voulez dans n'importe quelle cellule et éteignez la minuterie.

À présent, je ne suis pas un expert du fonctionnement du threading COM (bien que je sache que VBA est un thread unique pour appartement), mais faites attention à ce que votre minuterie fuit avec votre processus Excel et le bloque. Ce n’est vraiment pas quelque chose que je suggérerais comme solution à toutes les autres feuilles de calcul.

Créez simplement un module avec le contenu suivant:

Option Explicit

Declare Function SetTimer Lib "user32" (ByVal HWnd As Long, _
  ByVal IDEvent As Long, ByVal mSec As Long, _
  ByVal CallFunc As Long) As Long

Declare Function KillTimer Lib "user32" (ByVal HWnd As Long, _
  ByVal timerId As Long) As Long

Private timerId As Long

Private wb As Workbook
Private rangeName As String
Private blnFinished As Boolean

Public Sub RunTimer()

    timerId = SetTimer(0, 0, 10, AddressOf Woohoo)


End Sub


Public Sub Woohoo()

    Dim i As Integer

'    For i = 0 To ThisWorkbook.Names.Count - 1
'        ThisWorkbook.Names(i).Delete
'    Next

     ThisWorkbook.Worksheets("Sheet1").Range("D8").Value = "Woohoo"

     KillTimer 0, timerId

End Sub

Bien que vous ne puissiez pas le faire dans Excel, il est possible d’utiliser le premier résolveur (bien que ce chose étrange à faire).

C'est une feuille de calcul qui vous permet de définir des fonctions personnalisées en Python que vous pouvez ensuite appeler à partir d'une formule de cellule dans la grille.

Comme exemple de ce que vous demandez, vous pouvez définir une fonction safeDivide qui (au lieu de générer un ZeroDivisionError) vous a signalé le problème en coloriant la cellule du dénominateur et en plaçant un message d'erreur à côté de il. Vous pouvez le définir comme ceci:

def safeDivide(numerator, cellRange):
    if not isinstance(cellRange, CellRange):
        raise ValueError('denominator must be a cell range')
    denominator = cellRange.Value
    if denominator == 0:
        cell = cellRange.TopLeft
        cell.BackColor = Color.Red
        cell.Offset(1, 0).Value = 'Tried to divide by zero'
        return 0
    return numerator / denominator

Il existe un problème supplémentaire: les fonctions qui obtiennent des cellules passées se voient simplement transmettre la valeur de la cellule. Par conséquent, pour contourner ce problème, nous insistons sur le fait de nous transmettre une plage de cellules à une cellule pour le dénominateur.

Si vous essayez de faire des choses inhabituelles avec des feuilles de calcul qui ne s'intègrent pas parfaitement dans Excel, ou si vous souhaitez utiliser la puissance de Python pour exploiter vos données de feuille de calcul, il vaut la peine de jeter un coup d'œil à Resolver One.

Voici une solution de contournement VBA simple et efficace. Pour cet exemple, ouvrez un nouveau classeur Excel et copiez le code suivant dans la zone de code pour Sheet1 (pas ThisWorkbook ou un VBA Module). Puis entrez dans <=> et mettez quelque chose dans l’une des cellules situées en haut à gauche de la feuille de calcul. Si vous tapez un nombre et appuyez sur Entrée, la cellule de droite sera mise à jour avec 4 fois le nombre et l'arrière-plan de la cellule deviendra bleu clair. Toute autre valeur entraîne la suppression de la cellule suivante. Voici le code:

Dim busy As Boolean
Private Sub Worksheet_Change(ByVal Target As Range)
  If busy Then Exit Sub
  busy = True
  If Target.Row <= 10 And Target.Column <= 10 Then
    With Target.Offset(0, 1)
      If IsNumeric(Target) Then
        .Value = Target * 4
        .Interior.Color = RGB(212, 212, 255)
      Else
        .Value = Empty
        .Interior.ColorIndex = xlColorIndexNone
      End If
    End With
  End If
  busy = False
End Sub

Le sous-programme capture tous les événements de changement de cellule de la feuille. Si la ligne et la colonne sont toutes deux & Lt; = 10, la cellule de droite est définie sur 4 fois la cellule modifiée si la valeur est numérique; sinon, la cellule à droite est effacée.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top