Skip to content

Commit a0eff73

Browse files
committed
refactor: Clean up unused namespaces and improve code organization
1 parent e3305f2 commit a0eff73

4 files changed

Lines changed: 383 additions & 51 deletions

File tree

src/LiveWordXml.Wpf/MainWindow.xaml

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,7 @@
582582
Style="{StaticResource ModernCardStyle}">
583583
<Grid>
584584
<Grid.RowDefinitions>
585+
<RowDefinition Height="Auto"/>
585586
<RowDefinition Height="Auto"/>
586587
<RowDefinition Height="*"/>
587588
<RowDefinition Height="Auto"/>
@@ -626,14 +627,95 @@
626627
</Grid>
627628
</Grid>
628629

630+
<!-- Node Details Panel -->
631+
<Border Grid.Row="1"
632+
Background="{StaticResource SurfaceVariantBrush}"
633+
BorderBrush="{StaticResource BorderBrush}"
634+
BorderThickness="0,1,0,1"
635+
Margin="0,0,0,8"
636+
Visibility="{Binding SelectedStructureNode, Converter={StaticResource NullToVisibilityConverter}}">
637+
<Expander Header="📋 Node Details"
638+
IsExpanded="True"
639+
Margin="8">
640+
<Grid Margin="8,8,8,0">
641+
<Grid.RowDefinitions>
642+
<RowDefinition Height="Auto"/>
643+
<RowDefinition Height="Auto"/>
644+
<RowDefinition Height="Auto"/>
645+
<RowDefinition Height="Auto"/>
646+
</Grid.RowDefinitions>
647+
<Grid.ColumnDefinitions>
648+
<ColumnDefinition Width="Auto"/>
649+
<ColumnDefinition Width="*"/>
650+
</Grid.ColumnDefinitions>
651+
652+
<!-- Node Type -->
653+
<TextBlock Grid.Row="0"
654+
Grid.Column="0"
655+
Text="Type:"
656+
FontWeight="SemiBold"
657+
Margin="0,0,8,4"
658+
VerticalAlignment="Top"/>
659+
<TextBlock Grid.Row="0"
660+
Grid.Column="1"
661+
Text="{Binding SelectedStructureNode.NodeType}"
662+
Margin="0,0,0,4"
663+
TextWrapping="Wrap"/>
664+
665+
<!-- XPath -->
666+
<TextBlock Grid.Row="1"
667+
Grid.Column="0"
668+
Text="XPath:"
669+
FontWeight="SemiBold"
670+
Margin="0,0,8,4"
671+
VerticalAlignment="Top"/>
672+
<TextBlock Grid.Row="1"
673+
Grid.Column="1"
674+
Text="{Binding SelectedStructureNode.XPath}"
675+
Margin="0,0,0,4"
676+
TextWrapping="Wrap"
677+
FontFamily="Consolas"/>
678+
679+
<!-- Text Preview -->
680+
<TextBlock Grid.Row="2"
681+
Grid.Column="0"
682+
Text="Preview:"
683+
FontWeight="SemiBold"
684+
Margin="0,0,8,4"
685+
VerticalAlignment="Top"/>
686+
<TextBlock Grid.Row="2"
687+
Grid.Column="1"
688+
Text="{Binding SelectedStructureNode.TextPreview}"
689+
Margin="0,0,0,4"
690+
TextWrapping="Wrap"
691+
FontStyle="Italic"/>
692+
693+
<!-- Attributes -->
694+
<TextBlock Grid.Row="3"
695+
Grid.Column="0"
696+
Text="Attributes:"
697+
FontWeight="SemiBold"
698+
Margin="0,0,8,4"
699+
VerticalAlignment="Top"/>
700+
<TextBlock Grid.Row="3"
701+
Grid.Column="1"
702+
Text="{Binding SelectedStructureNode.AttributesInfo}"
703+
Margin="0,0,0,4"
704+
TextWrapping="Wrap"
705+
FontFamily="Consolas"
706+
FontSize="11"/>
707+
</Grid>
708+
</Expander>
709+
</Border>
710+
629711
<!-- XML Editor -->
630-
<views:XmlEditor Grid.Row="1"
712+
<views:XmlEditor Grid.Row="2"
631713
x:Name="XmlPreviewEditor"
632714
XmlText="{Binding SelectedXml, Mode=OneWay}"
633715
SearchText="{Binding SearchHighlightText, Mode=OneWay}"/>
634716

635717
<!-- Action Buttons -->
636-
<StackPanel Grid.Row="2"
718+
<StackPanel Grid.Row="3"
637719
Orientation="Horizontal"
638720
HorizontalAlignment="Right"
639721
Margin="0,16,0,0">

src/LiveWordXml.Wpf/Models/DocumentStructureNode.cs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ public class DocumentStructureNode : ObservableObject
4848
/// </summary>
4949
public int Level { get; set; }
5050

51+
/// <summary>
52+
/// 元素的属性集合(键值对形式存储)
53+
/// </summary>
54+
public Dictionary<string, string> Attributes { get; set; } = [];
55+
5156
/// <summary>
5257
/// 父节点引用
5358
/// </summary>
@@ -229,6 +234,41 @@ public string Icon
229234
}
230235
}
231236

237+
/// <summary>
238+
/// 获取格式化的属性信息字符串
239+
/// </summary>
240+
public string AttributesInfo
241+
{
242+
get
243+
{
244+
if (Attributes == null || Attributes.Count == 0)
245+
return "No attributes";
246+
247+
var attributeStrings = Attributes.Select(kvp => $"{kvp.Key}=\"{kvp.Value}\"");
248+
return string.Join(", ", attributeStrings);
249+
}
250+
}
251+
252+
/// <summary>
253+
/// 检查是否有指定的属性
254+
/// </summary>
255+
/// <param name="attributeName">属性名称</param>
256+
/// <returns>是否存在该属性</returns>
257+
public bool HasAttribute(string attributeName)
258+
{
259+
return Attributes?.ContainsKey(attributeName) == true;
260+
}
261+
262+
/// <summary>
263+
/// 获取指定属性的值
264+
/// </summary>
265+
/// <param name="attributeName">属性名称</param>
266+
/// <returns>属性值,如果不存在则返回null</returns>
267+
public string? GetAttributeValue(string attributeName)
268+
{
269+
return Attributes?.TryGetValue(attributeName, out var value) == true ? value : null;
270+
}
271+
232272
/// <summary>
233273
/// 添加子节点
234274
/// </summary>

src/LiveWordXml.Wpf/Services/DocumentService.cs

Lines changed: 39 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
using DocumentFormat.OpenXml.Packaging;
2-
using DocumentFormat.OpenXml.Wordprocessing;
3-
using DocumentFormat.OpenXml;
41
using System;
52
using System.IO;
63
using System.Text;
74
using System.Threading;
85
using System.Xml;
6+
using DocumentFormat.OpenXml;
7+
using DocumentFormat.OpenXml.Packaging;
8+
using DocumentFormat.OpenXml.Wordprocessing;
99

1010
namespace LiveWordXml.Wpf.Services
1111
{
@@ -36,7 +36,9 @@ public void RefreshDocument()
3636
{
3737
if (string.IsNullOrEmpty(_filePath))
3838
{
39-
throw new InvalidOperationException("No document path is set. Please load a document first.");
39+
throw new InvalidOperationException(
40+
"No document path is set. Please load a document first."
41+
);
4042
}
4143

4244
if (!File.Exists(_filePath))
@@ -56,7 +58,9 @@ private WordprocessingDocument GetReadOnlyDocument()
5658
{
5759
if (string.IsNullOrEmpty(_filePath))
5860
{
59-
throw new InvalidOperationException("No document is loaded. Please load a document first.");
61+
throw new InvalidOperationException(
62+
"No document is loaded. Please load a document first."
63+
);
6064
}
6165

6266
if (!File.Exists(_filePath))
@@ -68,15 +72,7 @@ private WordprocessingDocument GetReadOnlyDocument()
6872

6973
try
7074
{
71-
// First try to open the original file directly
72-
var openSettings = new OpenSettings
73-
{
74-
MarkupCompatibilityProcessSettings = new MarkupCompatibilityProcessSettings(
75-
MarkupCompatibilityProcessMode.ProcessAllParts,
76-
FileFormatVersions.Office2019)
77-
};
78-
79-
var document = WordprocessingDocument.Open(fileToOpen, false, openSettings);
75+
var document = WordprocessingDocument.Open(fileToOpen, false);
8076

8177
// Validate that we can actually read the document content
8278
if (document?.MainDocumentPart?.Document != null)
@@ -93,27 +89,24 @@ private WordprocessingDocument GetReadOnlyDocument()
9389
try
9490
{
9591
fileToOpen = CreateTemporaryCopy(_filePath);
96-
97-
var openSettings = new OpenSettings
98-
{
99-
MarkupCompatibilityProcessSettings = new MarkupCompatibilityProcessSettings(
100-
MarkupCompatibilityProcessMode.ProcessAllParts,
101-
FileFormatVersions.Office2019)
102-
};
103-
104-
var document = WordprocessingDocument.Open(fileToOpen, false, openSettings);
92+
var document = WordprocessingDocument.Open(fileToOpen, false);
10593

10694
if (document?.MainDocumentPart?.Document == null)
10795
{
10896
document?.Dispose();
109-
throw new InvalidOperationException("Unable to access document content from temporary copy.");
97+
throw new InvalidOperationException(
98+
"Unable to access document content from temporary copy."
99+
);
110100
}
111101

112102
return document;
113103
}
114104
catch (Exception ex)
115105
{
116-
throw new InvalidOperationException($"Cannot open document. File may be locked and unable to create temporary copy: {ex.Message}", ex);
106+
throw new InvalidOperationException(
107+
$"Cannot open document. File may be locked and unable to create temporary copy: {ex.Message}",
108+
ex
109+
);
117110
}
118111
}
119112
catch (Exception ex)
@@ -136,7 +129,10 @@ private string CreateTemporaryCopy(string originalFilePath)
136129
string tempDirectory = Path.GetTempPath();
137130
string fileName = Path.GetFileNameWithoutExtension(originalFilePath);
138131
string extension = Path.GetExtension(originalFilePath);
139-
_tempFilePath = Path.Combine(tempDirectory, $"{fileName}_temp_{DateTime.Now:yyyyMMdd_HHmmss}{extension}");
132+
_tempFilePath = Path.Combine(
133+
tempDirectory,
134+
$"{fileName}_temp_{DateTime.Now:yyyyMMdd_HHmmss}{extension}"
135+
);
140136

141137
// 尝试多种方式复制文件
142138
if (TryCopyWithFileStream(originalFilePath, _tempFilePath))
@@ -154,7 +150,10 @@ private string CreateTemporaryCopy(string originalFilePath)
154150
}
155151
catch (Exception ex)
156152
{
157-
throw new InvalidOperationException($"Failed to create temporary copy: {ex.Message}", ex);
153+
throw new InvalidOperationException(
154+
$"Failed to create temporary copy: {ex.Message}",
155+
ex
156+
);
158157
}
159158
}
160159

@@ -165,8 +164,18 @@ private bool TryCopyWithFileStream(string source, string destination)
165164
{
166165
try
167166
{
168-
using var sourceStream = new FileStream(source, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
169-
using var destStream = new FileStream(destination, FileMode.Create, FileAccess.Write, FileShare.None);
167+
using var sourceStream = new FileStream(
168+
source,
169+
FileMode.Open,
170+
FileAccess.Read,
171+
FileShare.ReadWrite
172+
);
173+
using var destStream = new FileStream(
174+
destination,
175+
FileMode.Create,
176+
FileAccess.Write,
177+
FileShare.None
178+
);
170179
sourceStream.CopyTo(destStream);
171180
return true;
172181
}
@@ -277,7 +286,7 @@ public string FormatXml(string xml)
277286
IndentChars = " ",
278287
NewLineChars = "\r\n",
279288
NewLineHandling = NewLineHandling.Replace,
280-
OmitXmlDeclaration = true
289+
OmitXmlDeclaration = true,
281290
};
282291

283292
using var xmlWriter = XmlWriter.Create(stringBuilder, xmlWriterSettings);

0 commit comments

Comments
 (0)