<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title><![CDATA[Серый форум &mdash; VBA Excel Сохранение пользовательских данных в CustomXMLParts]]></title>
		<link>https://forum.script-coding.com/viewtopic.php?id=14424</link>
		<atom:link href="https://forum.script-coding.com/extern.php?action=feed&amp;tid=14424&amp;type=rss" rel="self" type="application/rss+xml" />
		<description><![CDATA[Недавние сообщения в теме «VBA Excel Сохранение пользовательских данных в CustomXMLParts».]]></description>
		<lastBuildDate>Fri, 15 Oct 2021 14:37:02 +0000</lastBuildDate>
		<generator>PunBB</generator>
		<item>
			<title><![CDATA[Re: VBA Excel Сохранение пользовательских данных в CustomXMLParts]]></title>
			<link>https://forum.script-coding.com/viewtopic.php?pid=150081#p150081</link>
			<description><![CDATA[<p>class code - insert in Class Module</p><div class="codebox"><pre><code>Option Explicit
&#039;Wrapper class for ThisWorkbook.CustomXMLParts
Private customXMLstorage As Object

Private nameSpace1 As String

Private Const nSpPrefixXML = &quot;cxs&quot;

&#039;****
Private Sub Class_Initialize()
    Set customXMLstorage = ThisWorkbook.CustomXMLParts
        nameSpace1 = &quot;CXStorage&quot;
End Sub

Private Sub Class_Terminate()
    Set customXMLstorage = Nothing
        nameSpace1 = Empty
End Sub

&#039;****
Private Sub InitStorageNameSpace(ByVal value1 As String)
    &#039;if need more then one storage -&gt; Public this
    nameSpace1 = value1
End Sub

&#039;**
Public Function RemoveCustomXMLstorage() As Boolean
On Error GoTo Err
    If MsgBox(&quot;You will remove all stored data!&quot;, vbOKCancel) = vbCancel Then Exit Function
    &#039;**
    Dim oPart
    For Each oPart In ThisWorkbook.CustomXMLParts.SelectByNamespace(nameSpace1)
        oPart.Delete
    Next
    Set oPart = Nothing
    RemoveCustomXMLstorage = True
    Exit Function
Err:
    RemoveCustomXMLstorage = False
End Function

Public Sub AddItem(ByVal itemName1 As String, ByVal value1 As String)
    &#039;**
    With GetItemsNode()
        With .SelectNodes(&quot;//item[@name=&#039;&quot; &amp; itemName1 &amp; &quot;&#039;]&quot;)
            If .Count &gt; 0 Then .item(1).Delete
        End With
        &#039;**
        .AppendChildNode &quot;item&quot;, , msoCustomXMLNodeElement
        &#039;**
        With .LastChild
            .AppendChildNode &quot;name&quot;, , msoCustomXMLNodeAttribute, itemName1
            .AppendChildNode , , msoCustomXMLNodeText, value1
        End With
        &#039;**
    End With
End Sub

Public Function AddItems(ByRef cItems As Scripting.Dictionary) As Boolean
On Error GoTo Err
    Dim itemName1
    For Each itemName1 In cItems
        AddItem itemName1, cItems(itemName1)
    Next itemName1
    AddItems = True
    Exit Function
Err:
    AddItems = False
End Function

Public Function ItemExists(ByVal itemName1 As String) As Boolean
    ItemExists = Not (GetItemsNode().SelectSingleNode(&quot;//item[@name=&#039;&quot; &amp; itemName1 &amp; &quot;&#039;]&quot;) Is Nothing)
End Function

Public Function GetItem(ByVal itemName1 As String) As Variant
    With GetItemsNode().SelectNodes(&quot;//item[@name=&#039;&quot; &amp; itemName1 &amp; &quot;&#039;]&quot;)
        If .Count &gt; 0 Then
            If Not (.item(1).FirstChild Is Nothing) Then
                GetItem = .item(1).FirstChild.NodeValue
            End If
        End If
    End With
End Function

Public Function GetItems() As Scripting.Dictionary
    Dim oNode
    Set GetItems = CreateObject(&quot;Scripting.Dictionary&quot;)
    &#039;**
    For Each oNode In GetItemsNode().SelectNodes(&quot;//item&quot;)
        GetItems.item(oNode.Attributes.item(1).NodeValue) = oNode.FirstChild.NodeValue
    Next oNode
    &#039;**
    Set oNode = Nothing
End Function

Public Function RemoveItem(ByVal itemName1 As String) As Boolean
    If ItemExists(itemName1) Then
        With GetItemsNode().SelectNodes(&quot;//item[@name=&#039;&quot; &amp; itemName1 &amp; &quot;&#039;]&quot;)
            If .Count &gt; 0 Then .item(1).Delete
            RemoveItem = True
        End With
    End If
End Function

Private Function GetItemsNode() As Object
    With ThisWorkbook.CustomXMLParts.SelectByNamespace(nameSpace1)
        If .Count = 0 Then ThisWorkbook.CustomXMLParts.Add &quot;&lt;&quot; &amp; nSpPrefixXML &amp; &quot;:root xmlns:&quot; &amp; nSpPrefixXML &amp; &quot;=&#039;&quot; &amp; nameSpace1 &amp; &quot;&#039;&gt;&lt;items/&gt;&lt;/&quot; &amp; nSpPrefixXML &amp; &quot;:root&gt;&quot;
        Set GetItemsNode = .item(1).DocumentElement.FirstChild
    End With
End Function
</code></pre></div><p>test code - insert in Module<br /></p><div class="codebox"><pre><code>
Option Explicit

Private Sub Test_CustomXMLstorageCls()
    &#039;**
    Dim sv1 As CustomXMLstorageCls
    Set sv1 = New CustomXMLstorageCls
    &#039;**
        sv1.RemoveCustomXMLstorage
    &#039;**
        sv1.AddItem &quot;MyItem&quot;, &quot;MyValue&quot;
        sv1.AddItem &quot;MyXml&quot;, &quot;&lt;node&gt;text&lt;/node&gt;&quot;
        sv1.AddItem &quot;MyText&quot;, &quot;Line #1&quot; &amp; vbCrLf &amp; &quot;Line #2&quot; &amp; vbCrLf &amp; &quot;Line #3&quot;
        sv1.AddItem Empty, &quot;Empty&quot;
    &#039;**
    DebugPrintDictColl sv1.GetItems
    &#039;**
        sv1.RemoveCustomXMLstorage
    &#039;**
    Set sv1 = Nothing
    &#039;**
End Sub

Private Sub Test_CustomXMLstorageAdd_CapacityTest()
    &#039;**
    Dim sv1 As CustomXMLstorageCls
    Set sv1 = New CustomXMLstorageCls
    &#039;**
    With CreateObject(&quot;Scripting.Dictionary&quot;)
        Do
            .item(.Count) = GetGUID &#039;CreateGUID &#039;Mid(typeLib1.GUID, 1, 38)
        Loop Until .Count = 100000
        &#039;**
        sv1.AddItem &quot;CapacityTest&quot;, Join(.items(), &quot;; &quot;)
    End With
    &#039;**
    Logg &quot;Stored string length: &quot; &amp; Len(sv1.GetItem(&quot;CapacityTest&quot;)), True, False, True
    &#039;**
        sv1.RemoveCustomXMLstorage
    Set sv1 = Nothing
    &#039;**
End Sub

&#039;****
Private Function GetGUID() As String
  GetGUID = &quot;xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx&quot;
  GetGUID = Replace(GetGUID, &quot;y&quot;, Hex(Rnd() And &amp;H3 Or &amp;H8))
  Dim i As Long
  For i = 1 To 30
    GetGUID = Replace(GetGUID, &quot;x&quot;, Hex$(CLng(Rnd() * 15.9999)), 1, 1)
  Next i
End Function

Private Function DebugPrintDictColl(ByRef obj1 As Object) As Boolean
    If obj1 Is Nothing Then Debug.Print &quot;The object Is Nothing&quot;: Exit Function
    If obj1.Count = 0 Then Debug.Print &quot;The object does not have items&quot;: Exit Function
    &#039;**
    Dim i As Long, key1
    &#039;**
    If TypeOf obj1 Is Scripting.Dictionary Then
        For Each key1 In obj1.keys
            i = i + 1
            If TypeOf obj1.item(key1) Is Object  Then
                Debug.Print i &amp; &quot; key: &quot; &amp; key1 &amp; vbTab &amp; &quot; -&gt; item TypeName: &quot; &amp; TypeName(obj1.item(key1))
            Else
                Debug.Print i &amp; &quot; key: &quot; &amp; key1 &amp; vbTab &amp; &quot; -&gt; item: &quot; &amp; obj1.item(key1)
            End If
        Next key1
    Else
        &#039;collection
        Dim itm1
        For Each itm1 In obj1
            i = i + 1
            If Not TypeName(itm1) = &quot;String&quot; And Not TypeName(itm1) = &quot;Long&quot; Then
                Debug.Print i &amp; &quot; key: &quot; &amp; itm1.key &amp; &quot; -&gt; item TypeName: &quot; &amp; TypeName(itm1)
            Else
                Debug.Print i &amp; &quot; key number: &quot; &amp; i &amp; &quot; -&gt; item: &quot; &amp; itm1
            End If
        Next itm1
        Set itm1 = Nothing
    End If
    DebugPrintDictColl = True
End Function

Private Sub Logg(ByVal str1 As String, Optional ByVal debugPrint1 As Boolean = True, Optional ByVal statusBar1 As Boolean, Optional ByVal msgBox1 As Boolean)
    If Len(str1) = 0 Then Exit Sub
    If debugPrint1 Then Debug.Print str1
    If statusBar1 Then Application.StatusBar = str1
    If msgBox1 Then MsgBox str1, vbOKOnly
End Sub

</code></pre></div>]]></description>
			<author><![CDATA[null@example.com (mikegti@yandex.ru)]]></author>
			<pubDate>Fri, 15 Oct 2021 14:37:02 +0000</pubDate>
			<guid>https://forum.script-coding.com/viewtopic.php?pid=150081#p150081</guid>
		</item>
		<item>
			<title><![CDATA[Re: VBA Excel Сохранение пользовательских данных в CustomXMLParts]]></title>
			<link>https://forum.script-coding.com/viewtopic.php?pid=132110#p132110</link>
			<description><![CDATA[<p>Да и вообще можно просто Shift-Del на файле нажать <img src="//forum.script-coding.com/img/smilies/smile.png" width="15" height="15" />.</p>]]></description>
			<author><![CDATA[null@example.com (alexii)]]></author>
			<pubDate>Sun, 10 Feb 2019 15:30:53 +0000</pubDate>
			<guid>https://forum.script-coding.com/viewtopic.php?pid=132110#p132110</guid>
		</item>
		<item>
			<title><![CDATA[Re: VBA Excel Сохранение пользовательских данных в CustomXMLParts]]></title>
			<link>https://forum.script-coding.com/viewtopic.php?pid=132106#p132106</link>
			<description><![CDATA[<div class="quotebox"><cite>alexii пишет:</cite><blockquote><p>Есть вариант попроще, например:<br /></p><div class="codebox"><pre><code>ActiveWorkbook.Worksheets.Item(&quot;Лист2&quot;).Visible = xlVeryHidden</code></pre></div><p>Работает и в версиях до 2007.</p></blockquote></div><p>Метод с xlVeryHidden листом имеет пару недостатков по сравнению с CustomXMLParts.<br />1. Размер данных, которые можно поместить в одну ячейку, ограничен 32 КБ, для больших объемов придется изощраться с разбивкой и склейкой. В моем случае, ограничение около 100 МБ.<br />2. Нередко при генерации каких-либо отчетов в Excel, макрос начинается с создания листа и удаления всех остальных в цикле. Лист с данными может быть случайно удален вместе с остальными &quot;до кучи&quot;. Иными словами, риск потерять данные выше.</p>]]></description>
			<author><![CDATA[null@example.com (omegastripes)]]></author>
			<pubDate>Sun, 10 Feb 2019 13:48:54 +0000</pubDate>
			<guid>https://forum.script-coding.com/viewtopic.php?pid=132106#p132106</guid>
		</item>
		<item>
			<title><![CDATA[Re: VBA Excel Сохранение пользовательских данных в CustomXMLParts]]></title>
			<link>https://forum.script-coding.com/viewtopic.php?pid=130447#p130447</link>
			<description><![CDATA[<div class="quotebox"><cite>omegastripes пишет:</cite><blockquote><p>кодом, позволяющим хранить произвольные текстовые данные в коллекции CustomXMLParts книги Excel версии 2007 и выше. Данные сохраняются непосредственно в файле книги. С одной стороны, пользователь не имеет прямого доступа ко всем элементам этой коллекции, и как следствие, сохраняемые таким образом данные не подвержены случайной утере или порче, а также не болтаются на виду и не привлекают особого внимания. С другой стороны, их легко контролировать, достаточно просто открыть файл .xlsx как архив, и зайти в одноименную папку.</p></blockquote></div><p>Есть вариант попроще, например:<br /></p><div class="codebox"><pre><code>ActiveWorkbook.Worksheets.Item(&quot;Лист2&quot;).Visible = xlVeryHidden</code></pre></div><p>Работает и в версиях до 2007.</p>]]></description>
			<author><![CDATA[null@example.com (alexii)]]></author>
			<pubDate>Mon, 17 Dec 2018 21:09:50 +0000</pubDate>
			<guid>https://forum.script-coding.com/viewtopic.php?pid=130447#p130447</guid>
		</item>
		<item>
			<title><![CDATA[VBA Excel Сохранение пользовательских данных в CustomXMLParts]]></title>
			<link>https://forum.script-coding.com/viewtopic.php?pid=130446#p130446</link>
			<description><![CDATA[<p>Всем привет! Хочу поделиться кодом, позволяющим хранить произвольные текстовые данные в коллекции <strong>CustomXMLParts</strong> книги Excel версии 2007 и выше. Данные сохраняются непосредственно в файле книги. С одной стороны, пользователь не имеет прямого доступа ко всем элементам этой коллекции, и как следствие, сохраняемые таким образом данные не подвержены случайной утере или порче, а также не болтаются на виду и не привлекают особого внимания. С другой стороны, их легко контролировать, достаточно просто открыть файл .xlsx как архив, и зайти в одноименную папку. Речь, конечно, не идет о хранении конфиденциальной информации, но, например, для сохранения определенных настроек (путей к папкам или сетевым ресурсам, значений контролов пользовательской формы перед закрытием, в конце концов, JS кода для выполнения в htmlfile контейнере) вполне сгодится. Максимальный объем не тестировал, предположительно, речь порядка о сотне мегабайт. Бинарные данные следует предварительно конвертировать в base64 или т. п. Данные относительно неплохо сжимаются zip&#039;ом.</p><p>Взаимодействие с сохраняемыми данными реализовано подобно обычному словарю Scripting.Dictionary:<br /><strong>AddItem</strong> - добавление записи ключ - значение, если такой ключ уже есть - запись удаляется и создается заново;<br /><strong>AddItems</strong> - добавление множества записей ключ - значение, передаваемых в словаре;<br /><strong>GetItem</strong> - получения значения по заданному ключу;<br /><strong>GetItems</strong> - получение всех записей в виде словаря;<br /><strong>ItemExists</strong> - проверка наличия записи с заданным ключом;<br /><strong>RemoveItem</strong> - удаление записи с заданным ключом;<br /><strong>RemoveCXStorage</strong> - полное удаление из коллекции CustomXMLParts элемента CustomXMLPart, используемого для хранения данных. </p><p>Приведенный ниже код следует сохранить в отдельный модуль <strong>CXStorage</strong>:</p><div class="codebox"><pre><code>Option Explicit

Private Function GetItemsNode()
    
    With ThisWorkbook.CustomXMLParts.SelectByNamespace(&quot;CXStorage&quot;)
        If .Count = 0 Then ThisWorkbook.CustomXMLParts.Add &quot;&lt;cxs:root xmlns:cxs=&#039;CXStorage&#039;&gt;&lt;items/&gt;&lt;/cxs:root&gt;&quot;
        Set GetItemsNode = .Item(1).DocumentElement.FirstChild
    End With
    
End Function

Sub AddItem(sName, sValue)
    
    With GetItemsNode()
        With .SelectNodes(&quot;//item[@name=&#039;&quot; &amp; sName &amp; &quot;&#039;]&quot;)
            If .Count &gt; 0 Then .Item(1).Delete
        End With
        .AppendChildNode &quot;item&quot;, , msoCustomXMLNodeElement
        With .LastChild
            .AppendChildNode &quot;name&quot;, , msoCustomXMLNodeAttribute, sName
            .AppendChildNode , , msoCustomXMLNodeText, sValue
        End With
    End With
    
End Sub

Sub AddItems(cItems)
    
    Dim sName
    
    With GetItemsNode()
        For Each sName In cItems
            With .SelectNodes(&quot;//item[@name=&#039;&quot; &amp; sName &amp; &quot;&#039;]&quot;)
                If .Count &gt; 0 Then .Item(1).Delete
            End With
            .AppendChildNode &quot;item&quot;, , msoCustomXMLNodeElement
            With .LastChild
                .AppendChildNode &quot;name&quot;, , msoCustomXMLNodeAttribute, sName
                .AppendChildNode , , msoCustomXMLNodeText, cItems(sName)
            End With
        Next
    End With
    
End Sub

Function ItemExists(sName)
    
    ItemExists = Not (GetItemsNode().SelectSingleNode(&quot;//item[@name=&#039;&quot; &amp; sName &amp; &quot;&#039;]&quot;) Is Nothing)
    
End Function

Function GetItem(sName)
    
    With GetItemsNode().SelectNodes(&quot;//item[@name=&#039;&quot; &amp; sName &amp; &quot;&#039;]&quot;)
        If .Count &gt; 0 Then
            If Not (.Item(1).FirstChild Is Nothing) Then
                GetItem = .Item(1).FirstChild.NodeValue
            End If
        End If
    End With
    
End Function


Function GetItems()
    
    Dim oNode
    
    Set GetItems = CreateObject(&quot;Scripting.Dictionary&quot;)
    For Each oNode In GetItemsNode().SelectNodes(&quot;//item&quot;)
        GetItems.Item(oNode.Attributes.Item(1).NodeValue) = oNode.FirstChild.NodeValue
    Next
    
End Function

Sub RemoveItem(sName)
    
    With GetItemsNode().SelectNodes(&quot;//item[@name=&#039;&quot; &amp; sName &amp; &quot;&#039;]&quot;)
        If .Count &gt; 0 Then .Item(1).Delete
    End With
    
End Sub

Sub RemoveCXStorage()
    
    Dim oPart
    
    For Each oPart In ThisWorkbook.CustomXMLParts.SelectByNamespace(&quot;CXStorage&quot;)
        oPart.Delete
    Next
    
End Sub</code></pre></div><p>И простейший код для тестирования в другом модуле:</p><div class="codebox"><pre><code>Option Explicit

Sub Test1()
    
    Dim sKey
    
    RemoveCXStorage
    
    CXStorage.AddItem &quot;MyItem&quot;, &quot;MyValue&quot;
    CXStorage.AddItem &quot;MyXml&quot;, &quot;&lt;node&gt;text&lt;/node&gt;&quot;
    CXStorage.AddItem &quot;MyText&quot;, &quot;Line #1&quot; &amp; vbCrLf &amp; &quot;Line #2&quot; &amp; vbCrLf &amp; &quot;Line #3&quot;
    CXStorage.AddItem Empty, &quot;Empty&quot;
    
    With CXStorage.GetItems()
        For Each sKey In .Keys()
            MsgBox sKey &amp; vbCrLf &amp; .Item(sKey)
        Next
    End With
    
    
End Sub

Sub Test2()
    
    With CreateObject(&quot;Scripting.Dictionary&quot;)
        Do
            .Item(.Count) = Mid(CreateObject(&quot;Scriptlet.TypeLib&quot;).GUID, 1, 38)
        Loop Until .Count = 100000
        CXStorage.AddItem &quot;CapacityTest&quot;, Join(.Items())
    End With
    MsgBox Len(CXStorage.GetItem(&quot;CapacityTest&quot;))
    
End Sub</code></pre></div><p>Если кому лень копипастить код - прикрепляю готовую книгу.</p>]]></description>
			<author><![CDATA[null@example.com (omegastripes)]]></author>
			<pubDate>Mon, 17 Dec 2018 20:55:04 +0000</pubDate>
			<guid>https://forum.script-coding.com/viewtopic.php?pid=130446#p130446</guid>
		</item>
	</channel>
</rss>
