<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
	<title type="html"><![CDATA[Серый форум &mdash; VBA: Excel обход ограничений UDF]]></title>
	<link rel="self" href="http://forum.script-coding.com/extern.php?action=feed&amp;tid=9522&amp;type=atom" />
	<updated>2014-04-23T01:31:29Z</updated>
	<generator>PunBB</generator>
	<id>http://forum.script-coding.com/viewtopic.php?id=9522</id>
		<entry>
			<title type="html"><![CDATA[VBA: Excel обход ограничений UDF]]></title>
			<link rel="alternate" href="http://forum.script-coding.com/viewtopic.php?pid=82159#p82159" />
			<content type="html"><![CDATA[<p>Всем доброго времени суток! Как известно, пользовательсие функции, вызываемые из формул в ячейках листа Excel (UDF), не могут никоим образом изменять среду приложения. А при попытке модификации - функция просто прерывается и возвращает #ЗНАЧ!. Хочу предложить вашему вниманию метод обхода данного ограничения. Метод основан на отложенном выполнении целевых функций по событию Workbook_SheetCalculate, в то время как вызов UDF лишь заносит необходимые данные в список отложенных. Данный метод позволит, например, изменять формат ячейки c UDF, или содержимое соседних ячеек, листов, или даже <em>любых доступных данных приложения, не переступая ограничения UDF</em>. Я рассмотрю его реализацию на примере задачи, в которой UDF принимает в качестве аргументов название листа и путь к закрытой книге, и возвращает первую использующуюся строку на этом листе.</p><p><em>Данный код разместить в одном из модулей VBAProject:</em></p><div class="codebox"><pre><code>
Public Tasks, Permit, Transfer

Function GetFirstRowSched(FileName, SheetName) &#039; UDF откладывает занесение значения в ячейку до выполнения всех UDF
    If IsEmpty(Tasks) Then TasksInit
    If Permit Then Tasks.Add Application.Caller, Array(FileName, SheetName) &#039; упаковывает аргументы в массив, ключом словаря является сам объект ячейки UDF
    GetFirstRowSched = Transfer
End Function

Sub TasksInit() &#039; задаются начальные параметры
    Set Tasks = CreateObject(&quot;Scripting.Dictionary&quot;)
    Transfer = &quot;&quot;
    Permit = True
End Sub

Function GetFirstRowConv(FileName, SheetName) &#039; функция работает без ограничений UDF, как обычные процедуры, фактически, расчеты выполняются в данной функции
    With Application.Workbooks.Open(FileName)
        GetFirstRowConv = .Sheets(SheetName).UsedRange.Row
        .Close
    End With
End Function
</code></pre></div><p><em>Данный код разместить в разделе VBAProject - Microsoft Excel Objects - ThisWorkbook:</em></p><div class="codebox"><pre><code>
Private Sub Workbook_SheetCalculate(ByVal Sh As Object) &#039; событие пересчета листа, выполняющее все отложенные вызовы, и помещающее значения в ячейки с UDF
    Dim Task, TempFormula
    If IsEmpty(Tasks) Then TasksInit
    Application.EnableEvents = False
    Permit = False
    For Each Task In Tasks &#039; цикл по объектам всех вызванных ячеек с UDF
        TempFormula = Task.FormulaR1C1
        Transfer = GetFirstRowConv(Tasks(Task)(0), Tasks(Task)(1)) &#039; распаковывает аргументы из массива для выполнения вычислений
        Task.FormulaR1C1 = TempFormula &#039; после данной строки повторно вызывается UDF ячейки Task, и в качестве результата в ячейку возвращается Transfer
        Tasks.Remove Task
    Next
    Application.EnableEvents = True
    Transfer = &quot;&quot;
    Permit = True
End Sub
</code></pre></div><p>Впрочем, для решения конкретно данной задачи, было достаточно составить следующую UDF, использующую позднее связывание, без применения столь витиеватого вышеописанного метода:</p><div class="codebox"><pre><code>
Function GetFirstRowLbind(FileName, SheetName)
    On Error Resume Next
    With CreateObject(&quot;Excel.Application&quot;)
        .Workbooks.Open (FileName)
        GetFirstRowLbind = .Sheets(SheetName).UsedRange.Row
        .Quit
    End With
End Function
</code></pre></div>]]></content>
			<author>
				<name><![CDATA[omegastripes]]></name>
				<uri>http://forum.script-coding.com/profile.php?id=29228</uri>
			</author>
			<updated>2014-04-23T01:31:29Z</updated>
			<id>http://forum.script-coding.com/viewtopic.php?pid=82159#p82159</id>
		</entry>
</feed>
