Skip to content

Commit 013ff8a

Browse files
Update v3.4.4
1 parent 8eae5cc commit 013ff8a

File tree

5 files changed

+91
-53
lines changed

5 files changed

+91
-53
lines changed

Changelog.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
# Change Log
22

3+
## v3.4.4
4+
5+
---
6+
Release Date: **30.09.2025**
7+
8+
- Fixed handling of worksheet protection (selecting locked or unlocked cells)
9+
10+
Note: The default value of `Style.CurrentCellXf.Locked` is now true, to be consistent with Excel behavior. This change only affects worksheets with protection enabled and may require
11+
explicit unlocking of cells that should remain editable
12+
13+
314
## v3.4.3
415

516
---

Demo/Demo.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
1616
<TargetFrameworks>net45;net5.0</TargetFrameworks>
1717
<StartupObject>Demo.Program</StartupObject>
18-
<Version>3.4.3</Version>
19-
<AssemblyVersion>3.4.3.0</AssemblyVersion>
20-
<FileVersion>3.4.3.0</FileVersion>
18+
<Version>3.4.4</Version>
19+
<AssemblyVersion>3.4.4.0</AssemblyVersion>
20+
<FileVersion>3.4.4.0</FileVersion>
2121
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
2222
<PackageLicenseExpression>MIT</PackageLicenseExpression>
2323
<PackageTags>XLSX Excel ExcelWriter Office</PackageTags>

PicoXLSX/LowLevel.cs

Lines changed: 73 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1132,20 +1132,15 @@ private string CreateRowString(DynamicRow dynamicRow, Worksheet worksheet)
11321132
/// <summary>
11331133
/// Method to create the protection string of the passed worksheet
11341134
/// </summary>
1135-
/// <param name="sheet">Worksheet to process.</param>
1136-
/// <returns>Formatted string with protection statement of the worksheet.</returns>
1135+
/// <param name="sheet">Worksheet to process</param>
1136+
/// <returns>Formatted string with protection statement of the worksheet</returns>
11371137
private string CreateSheetProtectionString(Worksheet sheet)
11381138
{
11391139
if (!sheet.UseSheetProtection)
11401140
{
11411141
return string.Empty;
11421142
}
11431143
Dictionary<Worksheet.SheetProtectionValue, int> actualLockingValues = new Dictionary<Worksheet.SheetProtectionValue, int>();
1144-
if (sheet.SheetProtectionValues.Count == 0)
1145-
{
1146-
actualLockingValues.Add(Worksheet.SheetProtectionValue.selectLockedCells, 1);
1147-
actualLockingValues.Add(Worksheet.SheetProtectionValue.selectUnlockedCells, 1);
1148-
}
11491144
if (!sheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.objects))
11501145
{
11511146
actualLockingValues.Add(Worksheet.SheetProtectionValue.objects, 1);
@@ -1154,46 +1149,80 @@ private string CreateSheetProtectionString(Worksheet sheet)
11541149
{
11551150
actualLockingValues.Add(Worksheet.SheetProtectionValue.scenarios, 1);
11561151
}
1157-
if (!sheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.selectLockedCells) && !actualLockingValues.ContainsKey(Worksheet.SheetProtectionValue.selectLockedCells))
1152+
bool allowSelectLocked = sheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.selectLockedCells);
1153+
bool allowSelectUnlocked = sheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.selectUnlockedCells);
1154+
if (allowSelectLocked && !allowSelectUnlocked)
1155+
{
1156+
// This shouldn't happen in Excel's UI, but handle it by allowing both
1157+
allowSelectUnlocked = true;
1158+
}
1159+
if (!allowSelectLocked)
11581160
{
11591161
actualLockingValues.Add(Worksheet.SheetProtectionValue.selectLockedCells, 1);
11601162
}
1161-
if ((!sheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.selectUnlockedCells) || !sheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.selectLockedCells)) && !actualLockingValues.ContainsKey(Worksheet.SheetProtectionValue.selectUnlockedCells))
1163+
if (!allowSelectUnlocked)
11621164
{
11631165
actualLockingValues.Add(Worksheet.SheetProtectionValue.selectUnlockedCells, 1);
11641166
}
1165-
if (sheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.formatCells)) { actualLockingValues.Add(Worksheet.SheetProtectionValue.formatCells, 0); }
1166-
if (sheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.formatColumns)) { actualLockingValues.Add(Worksheet.SheetProtectionValue.formatColumns, 0); }
1167-
if (sheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.formatRows)) { actualLockingValues.Add(Worksheet.SheetProtectionValue.formatRows, 0); }
1168-
if (sheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.insertColumns)) { actualLockingValues.Add(Worksheet.SheetProtectionValue.insertColumns, 0); }
1169-
if (sheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.insertRows)) { actualLockingValues.Add(Worksheet.SheetProtectionValue.insertRows, 0); }
1170-
if (sheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.insertHyperlinks)) { actualLockingValues.Add(Worksheet.SheetProtectionValue.insertHyperlinks, 0); }
1171-
if (sheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.deleteColumns)) { actualLockingValues.Add(Worksheet.SheetProtectionValue.deleteColumns, 0); }
1172-
if (sheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.deleteRows)) { actualLockingValues.Add(Worksheet.SheetProtectionValue.deleteRows, 0); }
1173-
if (sheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.sort)) { actualLockingValues.Add(Worksheet.SheetProtectionValue.sort, 0); }
1174-
if (sheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.autoFilter)) { actualLockingValues.Add(Worksheet.SheetProtectionValue.autoFilter, 0); }
1175-
if (sheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.pivotTables)) { actualLockingValues.Add(Worksheet.SheetProtectionValue.pivotTables, 0); }
1167+
// Explicit permissions (set to 0 when allowed)
1168+
if (sheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.formatCells))
1169+
{
1170+
actualLockingValues.Add(Worksheet.SheetProtectionValue.formatCells, 0);
1171+
}
1172+
if (sheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.formatColumns))
1173+
{
1174+
actualLockingValues.Add(Worksheet.SheetProtectionValue.formatColumns, 0);
1175+
}
1176+
if (sheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.formatRows))
1177+
{
1178+
actualLockingValues.Add(Worksheet.SheetProtectionValue.formatRows, 0);
1179+
}
1180+
if (sheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.insertColumns))
1181+
{
1182+
actualLockingValues.Add(Worksheet.SheetProtectionValue.insertColumns, 0);
1183+
}
1184+
if (sheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.insertRows))
1185+
{
1186+
actualLockingValues.Add(Worksheet.SheetProtectionValue.insertRows, 0);
1187+
}
1188+
if (sheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.insertHyperlinks))
1189+
{
1190+
actualLockingValues.Add(Worksheet.SheetProtectionValue.insertHyperlinks, 0);
1191+
}
1192+
if (sheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.deleteColumns))
1193+
{
1194+
actualLockingValues.Add(Worksheet.SheetProtectionValue.deleteColumns, 0);
1195+
}
1196+
if (sheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.deleteRows))
1197+
{
1198+
actualLockingValues.Add(Worksheet.SheetProtectionValue.deleteRows, 0);
1199+
}
1200+
if (sheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.sort))
1201+
{
1202+
actualLockingValues.Add(Worksheet.SheetProtectionValue.sort, 0);
1203+
}
1204+
if (sheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.autoFilter))
1205+
{
1206+
actualLockingValues.Add(Worksheet.SheetProtectionValue.autoFilter, 0);
1207+
}
1208+
if (sheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.pivotTables))
1209+
{
1210+
actualLockingValues.Add(Worksheet.SheetProtectionValue.pivotTables, 0);
1211+
}
11761212
StringBuilder sb = new StringBuilder();
1177-
sb.Append("<sheetProtection");
1213+
sb.Append("<sheetProtection sheet=\"1\"");
1214+
11781215
string temp;
11791216
foreach (KeyValuePair<Worksheet.SheetProtectionValue, int> item in actualLockingValues)
11801217
{
1181-
try
1182-
{
1183-
temp = Enum.GetName(typeof(Worksheet.SheetProtectionValue), item.Key); // Note! If the enum names differs from the OOXML definitions, this method will cause invalid OOXML entries
1184-
sb.Append(" ").Append(temp).Append("=\"").Append(item.Value.ToString("G", culture)).Append("\"");
1185-
}
1186-
catch
1187-
{
1188-
// no-op
1189-
}
1218+
temp = Enum.GetName(typeof(Worksheet.SheetProtectionValue), item.Key); // Note! If the enum names differs from the OOXML definitions, this method will cause invalid OOXML entries
1219+
sb.Append(" ").Append(temp).Append("=\"").Append(item.Value.ToString("G", culture)).Append("\"");
11901220
}
1191-
if (!string.IsNullOrEmpty(sheet.SheetProtectionPassword))
1221+
if (!string.IsNullOrEmpty(sheet.SheetProtectionPasswordHash))
11921222
{
1193-
string hash = GeneratePasswordHash(sheet.SheetProtectionPassword);
1194-
sb.Append(" password=\"").Append(hash).Append("\"");
1223+
sb.Append(" password=\"").Append(sheet.SheetProtectionPasswordHash).Append("\"");
11951224
}
1196-
sb.Append(" sheet=\"1\"/>");
1225+
sb.Append("/>");
11971226
return sb.ToString();
11981227
}
11991228

@@ -1454,20 +1483,17 @@ private string CreateStyleXfsString()
14541483
alignmentString = sb2.ToString();
14551484
}
14561485

1457-
if (style.CurrentCellXf.Hidden || style.CurrentCellXf.Locked)
1486+
if (style.CurrentCellXf.Hidden && style.CurrentCellXf.Locked)
14581487
{
1459-
if (style.CurrentCellXf.Hidden && style.CurrentCellXf.Locked)
1460-
{
1461-
protectionString = "<protection locked=\"1\" hidden=\"1\"/>";
1462-
}
1463-
else if (style.CurrentCellXf.Hidden && !style.CurrentCellXf.Locked)
1464-
{
1465-
protectionString = "<protection hidden=\"1\" locked=\"0\"/>";
1466-
}
1467-
else
1468-
{
1469-
protectionString = "<protection hidden=\"0\" locked=\"1\"/>";
1470-
}
1488+
protectionString = "<protection locked=\"1\" hidden=\"1\"/>";
1489+
}
1490+
else if (style.CurrentCellXf.Hidden && !style.CurrentCellXf.Locked)
1491+
{
1492+
protectionString = "<protection hidden=\"1\" locked=\"0\"/>";
1493+
}
1494+
else if (!style.CurrentCellXf.Hidden && !style.CurrentCellXf.Locked)
1495+
{
1496+
protectionString = "<protection locked=\"0\"/>";
14711497
}
14721498

14731499
sb.Append("<xf numFmtId=\"");

PicoXLSX/PicoXLSX.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@
2323
<PackageLicenseFile></PackageLicenseFile>
2424
<PackageReleaseNotes>Please see https://github.com/rabanti-github/PicoXLSX/blob/master/Changelog.md for the release notes</PackageReleaseNotes>
2525
<PackageLicenseExpression>MIT</PackageLicenseExpression>
26-
<Version>3.4.3</Version>
27-
<AssemblyVersion>3.4.3.0</AssemblyVersion>
28-
<FileVersion>3.4.3.0</FileVersion>
26+
<Version>3.4.4</Version>
27+
<AssemblyVersion>3.4.4.0</AssemblyVersion>
28+
<FileVersion>3.4.4.0</FileVersion>
2929
<RepositoryType>git</RepositoryType>
3030
<GenerateDocumentationFile>True</GenerateDocumentationFile>
3131
</PropertyGroup>

PicoXLSX/Style.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -786,6 +786,7 @@ public CellXf()
786786
Alignment = DEFAULT_ALIGNMENT;
787787
textDirection = DEFAULT_TEXT_DIRECTION;
788788
VerticalAlign = DEFAULT_VERTICAL_ALIGNMENT;
789+
Locked = true; // Default in Excel
789790
textRotation = 0;
790791
Indent = 0;
791792
}

0 commit comments

Comments
 (0)