Pergunta

Vamos dizer que eu criar um Sub (não uma função), cuja missão na vida é levar a célula ativa (ou seja Selecção) e definir uma célula adjacente a algum valor. Esta multa funciona.

Quando você tenta converter esse Sub para uma função e tentar avaliá-lo a partir da folha de cálculo (por exemplo, definindo-o da fórmula para "= MyFunction ()") Excel irá latir para o fato de que você está tentando afetar o valor do de células não-activa, e simplesmente forçar a função a #VALUE retorno sem tocar na célula adjacente.

É possível desativar esse comportamento protetor? Se não, o que é uma boa maneira de contornar isso? Eu estou procurando algo um desenvolvedor competente poderia realizar ao longo de um período de 1-2 semana, se possível.

Saudações, Alan.

Nota: eu estou usando o 2002, então eu seria a favor de uma solução que iria trabalhar para essa versão. Tendo isto dito, se as versões futuras fazer este significativamente mais fácil, eu gostaria de saber sobre isso também.

Foi útil?

Solução

Ele não pode ser feito, o que faz sentido porque:

  • Quando uma função de planilha é chamado, a célula que contém a função não é necessariamente a célula ativa. Então você não consegue encontrar a célula adjacente de forma confiável.

  • Quando o Excel é recalcular uma planilha, ele precisa manter as dependências entre as células. Portanto, não pode permitir que funções de planilha para arbitrariamente modificar outras células.

O melhor que você pode fazer é um dos seguintes:

  • Manipular o evento SheetChange. Se uma célula que contém sua função está a mudar, modificar a célula adjacente.

  • Coloque uma função de planilha na célula adjacente para retornar o valor que quiser.

Atualizar

Quanto ao comentário: "Eu gostaria que essa função para trabalhar em uma planilha 'em branco', então eu realmente não pode contar com o evento SelectionChange de planilhas que ainda não podem existir, mas terá de chamar essa função" :

  • Você pode colocar a sua função em um XLA add-in? Em seguida, seu XLA add-in pode lidar com a SheetChange Aplicação (*) evento para todos os livros que são abertos nessa instância do Excel?

Quanto ao comentário: "Ainda assim, se você manter Excel em CalculationMode = xlManual e preencha valores apenas, você deve ser muito bem"

  • Mesmo quando CalculationMode é xlManual, Excel precisa manter uma árvore de dependência de referências entre as células para que ele possa calcular na ordem certa. E se uma das funções pode atualizar uma célula arbitrária, isso vai atrapalhar a ordem. Que é provavelmente por isso que Excel impõe essa restrição.

(*) I originalmente escreveu AlterarSelecção acima, corrigido agora -., Claro, o evento correto é SheetChange para a pasta de trabalho ou aplicativo objetos, ou Mudar para o objeto planilha

Update 2 Algumas observações sobre de AlanR pós que descreve como 'tipo' de fazê-lo funcionar usando um timer:

  • Não está claro como a função de temporizador ( "Woohoo") vai saber quais as células a atualização. Você não tem nenhuma informação que indique qual célula contém a fórmula que desencadeou o temporizador.

  • Se a fórmula existe em mais de uma célula (nas mesmas ou diferentes pastas de trabalho), em seguida, a UDF será chamado várias vezes durante um novo cálculo, substituindo o timerId. Como resultado, você vai deixar de destruir o timer de forma confiável, e vai vazar recursos do Windows.

Outras dicas

De acordo com a Como Criar usuário personalizada funções do Excel definidos :

Limitações da UDF

  • Não é possível colocar um valor em uma célula diferente da célula (ou intervalo) contendo a fórmula. Em outras palavras, da UDF são destina-se a ser usado como "fórmulas", não necessariamente "macros".

Assim, parece que ele não pode ser feito.

Estou usando o Excel 2007, e ele não funciona. Excel menciona que cria uma referência circular. Eu não acho que você pode alterar outras células de uma função, basta retornar um valor.

É uma espécie de programação funcional, sem efeitos colaterais. Se você pudesse outras células apenas alter dentro de uma função (usado a partir de uma planilha), então não há nenhuma maneira para que o Excel para saber a ordem eo que recalcular se um alterações celulares.

Este artigo também contém uma grande quantidade de informações sobre como o Excel faz recálculo. Mas nunca afirma que as outras células são congelados.

Eu não sei o que você está tentando fazer, mas, por que não basta colocar uma outra função na célula adjacente, que leva a primeira célula como um parâmetro?

Exemplo:

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

Obrigado a todos por responder. É possível fazer isso! Meio. Eu digo 'meio', porque tecnicamente falando da 'função' não está afetando as células em torno dele. Na prática, no entanto, nenhum usuário poderia dizer a diferença.

O truque é usar uma API Win32 para iniciar um timer, e assim que ele sai você faz o que você quer para qualquer celular e desligar o temporizador.

Agora, eu não sou um especialista sobre como COM segmentação funciona (embora eu saiba VBA é Única Apartment Threaded), mas tenha cuidado sobre o seu temporizador fugindo com seu processo de Excel e bater-lo. Isto não é realmente algo que eu gostaria de sugerir como uma solução para todos os outros planilha.

Basta fazer um módulo com este conteúdo:

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

Enquanto você não pode fazer isso no Excel, é possível em Resolver Um (embora ainda é uma bonita coisa estranha de se fazer).

É uma planilha que permite definir funções personalizadas em Python, que você pode chamar de uma fórmula de célula na grade.

Como um exemplo do que você está pedindo, você pode querer definir uma função safeDivide que (em vez de levantar uma ZeroDivisionError) lhe disse sobre o problema colorindo a célula denominador, e colocar uma mensagem de erro ao lado dele. Você pode defini-lo como este:

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

Há uma ruga extra: funções que são células passaram apenas se passou o valor da célula, então para contornar isso insistimos em que está sendo passado um CellRange uma célula para o denominador.

Se você está tentando fazer coisas incomuns com planilhas que não muito se encaixam em Excel, ou você está interessado em usar o poder do Python para trabalhar com dados de sua planilha, vale a pena ter um olhar para Resolver One.

Aqui está um VBA fácil solução que funciona. Para este exemplo, abra um novo livro do Excel e copiar o seguinte código para a área de código para Sheet1 (não ThisWorkbook ou um Module VBA). Então vá em Sheet1 e colocar algo em uma das celas superior esquerdo da planilha. Se você digitar um número e pressione Enter, em seguida, a célula à direita será atualizado com 4 vezes o número, e o fundo da célula se tornará azul-claro. Qualquer outro valor faz com que a próxima célula a ser apuradas. Aqui está o código:

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

A sub-rotina captura todos os eventos de alteração de células na folha. Se a linha e a coluna são ambos <= 10, então a célula para a direita está definido para 4 vezes a célula alterada, se o valor for numérico; caso contrário, a célula à direita é apagada.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top