给我修改后的完整代码:<Window x:Class="Zyan.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Zyan"
mc:Ignorable="d"
Title="Zyan" Height="550" Width="800">
<!-- ==================== Resources: Remove unused ObjectDataProvider ==================== -->
<Grid Background="#E5E5E5">
<!-- 主布局:两列 -->
<!-- 第一列:侧边栏(可折叠) -->
<!-- 第二列:主内容区(原全部内容放在这里) -->
<Grid.ColumnDefinitions>
<ColumnDefinition Name="colSidebar" Width="0"/>
<!-- 初始隐藏 -->
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- ==================================== 左侧面板(预留文件树) ==================================== -->
<Border Grid.Column="0"
Background="#F0F0F0"
BorderBrush="#D0D0D0"
BorderThickness="0,0,1,0"
Padding="5">
<!-- 暂时空着,未来放 TreeView -->
<TextBlock Text="File Tree Here"
VerticalAlignment="Top"
HorizontalAlignment="Center"
Foreground="Gray"
FontStyle="Italic"
Visibility="Collapsed"/>
<!-- 静默占位 -->
</Border>
<!-- ==================================== 主内容区 ==================================== -->
<Grid Grid.Column="1">
<Grid.RowDefinitions>
<!-- row.0 Tool Bar -->
<RowDefinition Height="Auto"/>
<!-- row.1 Data View -->
<RowDefinition Height="*"/>
<!-- row.2 Overview -->
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- ==================================== Tool Bar ==================================== -->
<TabControl Name="tab_wp" TabStripPlacement="Top" FontSize="12" Grid.Row="0">
<TabItem Name="Project" Header="Project">
<DockPanel LastChildFill="False">
<ToggleButton Name="btnToggleSidebar"
Content=">>"
Click="OnToggleSidebarClick"
Width="30"
Height="22"
Margin="0,0,5,0"
ToolTip="Toggle Sidebar (Show/Hide File Panel)"/>
<Button Name="bt_open" Content="Open" Click="chooseFile"/>
<Button Name="bt_save" Content="Save" Click="bt_save_Click"/>
<Button Name="bt_save_as" Content="Save As"/>
</DockPanel>
</TabItem>
<TabItem Name="Lib" Header="Library">
<DockPanel LastChildFill="False">
<Button Name="bt_add_lib" Content="Add"/>
</DockPanel>
</TabItem>
<TabItem Name="Online" Header="Online"/>
<TabItem Name="Review" Header="Review"/>
<TabItem Name="Settings" Header="Settings"/>
</TabControl>
<!-- ==================================== DataGrid ==================================== -->
<DataGrid Name="dg_wp" Grid.Row="1"
Background="White"
FrozenColumnCount="1"
AutoGenerateColumns="False"
CanUserReorderColumns="False"
CanUserSortColumns="False"
CanUserAddRows="False"
CanUserResizeColumns="False"
ColumnWidth="*">
<!-- ==================== DataGridRow Style with ContextMenu & Triggers ==================== -->
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<!-- 默认背景白色 -->
<Setter Property="Background" Value="White"/>
<!-- ==================== 右键菜单 ==================== -->
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<!-- Pending: 透明背景 -->
<MenuItem Header="Pending"
Command="{x:Static local:MainWindow.SetStateCommand}"
CommandParameter="{x:Static local:RowState.Pending}">
<MenuItem.Style>
<Style TargetType="MenuItem">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="Black"/>
<Setter Property="Padding" Value="10,5"/>
<Setter Property="FontSize" Value="12"/>
</Style>
</MenuItem.Style>
</MenuItem>
<!-- Marked: 浅黄 -->
<MenuItem Header="Marked"
Command="{x:Static local:MainWindow.SetStateCommand}"
CommandParameter="{x:Static local:RowState.Marked}">
<MenuItem.Style>
<Style TargetType="MenuItem">
<Setter Property="Background" Value="#FFF9E6"/>
<Setter Property="Foreground" Value="Black"/>
<Setter Property="Padding" Value="10,5"/>
<Setter Property="BorderBrush" Value="#FFE599"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="FontSize" Value="12"/>
</Style>
</MenuItem.Style>
</MenuItem>
<!-- Completed: 浅蓝 -->
<MenuItem Header="Completed"
Command="{x:Static local:MainWindow.SetStateCommand}"
CommandParameter="{x:Static local:RowState.Completed}">
<MenuItem.Style>
<Style TargetType="MenuItem">
<Setter Property="Background" Value="#E6F3FF"/>
<Setter Property="Foreground" Value="Black"/>
<Setter Property="Padding" Value="10,5"/>
<Setter Property="BorderBrush" Value="#99CFFF"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="FontSize" Value="12"/>
</Style>
</MenuItem.Style>
</MenuItem>
<!-- Rejected: 浅红 -->
<MenuItem Header="Rejected"
Command="{x:Static local:MainWindow.SetStateCommand}"
CommandParameter="{x:Static local:RowState.Rejected}">
<MenuItem.Style>
<Style TargetType="MenuItem">
<Setter Property="Background" Value="#FFE6E6"/>
<Setter Property="Foreground" Value="Black"/>
<Setter Property="Padding" Value="10,5"/>
<Setter Property="BorderBrush" Value="#FF9999"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="FontSize" Value="12"/>
</Style>
</MenuItem.Style>
</MenuItem>
<!-- Approved: 浅绿 -->
<MenuItem Header="Approved"
Command="{x:Static local:MainWindow.SetStateCommand}"
CommandParameter="{x:Static local:RowState.Approved}">
<MenuItem.Style>
<Style TargetType="MenuItem">
<Setter Property="Background" Value="#E6FFE6"/>
<Setter Property="Foreground" Value="Black"/>
<Setter Property="Padding" Value="10,5"/>
<Setter Property="BorderBrush" Value="#99CC99"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="FontSize" Value="12"/>
</Style>
</MenuItem.Style>
</MenuItem>
</ContextMenu>
</Setter.Value>
</Setter>
<!-- ==================== 根据 state 设置行背景色 ==================== -->
<Style.Triggers>
<DataTrigger Binding="{Binding state}" Value="{x:Static local:RowState.Pending}">
<Setter Property="Background" Value="Transparent"/>
</DataTrigger>
<DataTrigger Binding="{Binding state}" Value="{x:Static local:RowState.Marked}">
<Setter Property="Background" Value="#FFF9E6"/>
</DataTrigger>
<DataTrigger Binding="{Binding state}" Value="{x:Static local:RowState.Completed}">
<Setter Property="Background" Value="#E6F3FF"/>
</DataTrigger>
<DataTrigger Binding="{Binding state}" Value="{x:Static local:RowState.Rejected}">
<Setter Property="Background" Value="#FFE6E6"/>
</DataTrigger>
<DataTrigger Binding="{Binding state}" Value="{x:Static local:RowState.Approved}">
<Setter Property="Background" Value="#E6FFE6"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
<!-- ==================== Columns ==================== -->
<DataGrid.Columns>
<DataGridTextColumn Header="No." Width="Auto"
Binding="{Binding id, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsReadOnly="True"/>
<DataGridTextColumn Header="Original" Width="*"
Binding="{Binding original, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsReadOnly="True"/>
<DataGridTextColumn Header="Translation" Width="*"
Binding="{Binding translation, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataGrid.Columns>
</DataGrid>
<!-- ==================================== Overview Panel ==================================== -->
<Grid Name="Overview" Grid.Row="2">
<!-- 后续添加统计信息 -->
<StackPanel Name="sp_Overview" Orientation="Horizontal" >
<!-- Total: 显示全部 -->
<TextBlock Text="{Binding CurrentItem.TotalCount, StringFormat='Total {0}'}"
Tag="Total"
Cursor="Hand"
ToolTip="Click to show all"
MouseLeftButtonDown="OnStatusFilterClicked"
Margin="5" Foreground="Black"/>
<TextBlock Text="{Binding CurrentItem.PendingCount, StringFormat='Pending {0}'}"
Tag="Pending"
Cursor="Hand"
ToolTip="Click to filter: Pending"
MouseLeftButtonDown="OnStatusFilterClicked"
Margin="5" Foreground="Gray"/>
<TextBlock Text="{Binding CurrentItem.MarkedCount, StringFormat='Marked {0}'}"
Tag="Marked"
Cursor="Hand"
ToolTip="Click to filter: Marked"
MouseLeftButtonDown="OnStatusFilterClicked"
Margin="5" Foreground="#D4A017"/>
<TextBlock Text="{Binding CurrentItem.CompletedCount, StringFormat='Completed {0}'}"
Tag="Completed"
Cursor="Hand"
ToolTip="Click to filter: Completed"
MouseLeftButtonDown="OnStatusFilterClicked"
Margin="5" Foreground="Blue"/>
<TextBlock Text="{Binding CurrentItem.RejectedCount, StringFormat='Rejected {0}'}"
Tag="Rejected"
Cursor="Hand"
ToolTip="Click to filter: Rejected"
MouseLeftButtonDown="OnStatusFilterClicked"
Margin="5" Foreground="Red"/>
<TextBlock Text="{Binding CurrentItem.ApprovedCount, StringFormat='Approved {0}'}"
Tag="Approved"
Cursor="Hand"
ToolTip="Click to filter: Approved"
MouseLeftButtonDown="OnStatusFilterClicked"
Margin="5" Foreground="Green"/>
<TextBlock Text="{Binding CurrentItem.lastEditedTime, StringFormat='Last Edited: {0:g}'}"
Margin="10,5" Foreground="DarkSlateGray"/>
</StackPanel>
</Grid>
</Grid>
</Grid>
</Window>
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Win32;
using System.Collections.ObjectModel;
using System.IO;
using System.ComponentModel;
using Zyan.db;
using Microsoft.Win32; // 必须引入:用于 OpenFileDialog / SaveFileDialog
using System.Windows.Media.Animation;
using System.Windows.Controls.Primitives;
namespace Zyan
{
/// <summary>
/// 主窗口 - 使用 Item 类管理文档与状态统计
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
// 当前打开的项目
private Item? _currentItem;
private ICollectionView? _dataView;
private RowState? _currentFilterState = null; // 当前过滤状态,null 表示无过滤
private bool _isSidebarExpanded = false;
// 暴露给 XAML 绑定的属性
public Item? CurrentItem
{
get => _currentItem;
set
{
_currentItem = value;
OnPropertyChanged(nameof(CurrentItem));
}
}
// 定义命令
public static readonly RoutedCommand SetStateCommand = new RoutedCommand();
public MainWindow()
{
InitializeComponent();
// 设置 DataContext 为当前窗口本身(支持绑定 CurrentItem)
DataContext = this;
// 绑定命令处理
CommandBindings.Add(new CommandBinding(SetStateCommand, OnSetStateExecuted));
// 初始化 DataGrid 列(如果未在 XAML 中定义)
SetupDataGridColumns();
}
#region 命令处理
private void OnSetStateExecuted(object sender, ExecutedRoutedEventArgs e)
{
if (e.Parameter is RowState newState && dg_wp.SelectedItem is Row selectedRow && CurrentItem != null)
{
selectedRow.state = newState;
CurrentItem.UpdateStatistics(); // 手动触发统计更新
CurrentItem.UpdateLastEditedTime(); // 更新时间戳
}
}
#endregion
#region 文件操作
private void chooseFile(object sender, RoutedEventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog
{
Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*",
DefaultExt = ".txt"
};
if (openFileDialog.ShowDialog() == true)
{
LoadFile(openFileDialog.FileName);
}
}
private void LoadFile(string filePath)
{
try
{
string[] lines = File.ReadAllLines(filePath, Encoding.UTF8);
var item = new Item { id = 1 };
item.rows.Clear();
for (int i = 0; i < lines.Length; i++)
{
item.rows.Add(new Row
{
id = i,
original = lines[i],
translation = string.Empty,
state = RowState.Pending
});
}
item.UpdateStatistics();
item.UpdateLastEditedTime();
CurrentItem = item;
// ✅ 创建 CollectionView 并绑定到 DataGrid
_dataView = CollectionViewSource.GetDefaultView(CurrentItem.rows);
dg_wp.ItemsSource = _dataView;
MessageBox.Show($"Loaded {item.TotalCount} lines.", "Success", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
MessageBox.Show($"Failed to load file: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
#endregion
#region 辅助方法
private void SetupDataGridColumns()
{
// 如果你在 XAML 中已经定义了列,可以删除此部分
// 否则动态添加列(示例)
}
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler? PropertyChanged;
#endregion
// 其他事件留空...
private void click_page(object sender, RoutedEventArgs e) { }
private void OnStatusFilterClicked(object sender, MouseButtonEventArgs e)
{
if (sender is not TextBlock textBlock || _dataView == null)
return;
string? tag = textBlock.Tag as string;
if (string.IsNullOrEmpty(tag))
return;
// 清除当前筛选(无论点击什么,先清除)
_currentFilterState = null;
_dataView.Filter = null; // 先重置
// 如果点击的是 Total,则不设过滤 → 已经是全部
if (tag == "Total")
{
// 不需要 Filter,直接刷新即可
_dataView.Refresh();
return;
}
// 否则尝试解析为 RowState
if (!Enum.TryParse<RowState>(tag, out var targetState))
return;
// 如果已经在过滤这个状态,就取消过滤(回到全部)
if (_currentFilterState == targetState)
{
_currentFilterState = null;
_dataView.Filter = null;
}
else
{
// 设置新过滤条件
_currentFilterState = targetState;
_dataView.Filter = item =>
{
if (item is Row row)
{
return row.state == targetState;
}
return false;
};
}
// 刷新视图
_dataView.Refresh();
}
private void bt_save_Click(object sender, RoutedEventArgs e)
{
if (CurrentItem == null)
{
MessageBox.Show("No project to save.", "Warning", MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
try
{
// 创建保存文件对话框
var dialog = new SaveFileDialog
{
Title = "Save Translation Project",
Filter = "SQLite Database (*.db)|*.db|All Files (*.*)|*.*",
DefaultExt = ".db",
AddExtension = true,
FileName = CurrentItem.name + ".db" // 默认文件名为项目名.db
};
// 显示对话框并判断用户是否点击了“保存”
if (dialog.ShowDialog() != true) // 注意:WPF 的 ShowDialog 返回 bool?,true 表示 OK
return;
string selectedFilePath = dialog.FileName;
// 可选:清理表名防止 SQL 问题
string safeTableName = SanitizeTableName(CurrentItem.name);
// 创建 DatabaseHelper 使用用户指定的数据库路径
var dbHelper = new DatabaseHelper(selectedFilePath);
// 执行保存
dbHelper.SaveToTable(safeTableName, CurrentItem.rows);
MessageBox.Show(
$"Saved project '{CurrentItem.name}' to:\n{selectedFilePath}\nTable: {safeTableName}",
"Success",
MessageBoxButton.OK,
MessageBoxImage.Information);
}
catch (Exception ex)
{
MessageBox.Show($"Failed to save: {ex.Message}\n\nDetails: {ex.GetType().Name}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private string SanitizeTableName(string name)
{
if (string.IsNullOrWhiteSpace(name))
return "Project";
// 移除不允许的字符,只保留字母、数字、下划线
var clean = System.Text.RegularExpressions.Regex.Replace(name, @"[^a-zA-Z0-9_]", "_");
// 确保不为空
if (string.IsNullOrEmpty(clean))
clean = "Project";
// SQLite 表名不能以数字开头,加前缀
if (char.IsDigit(clean[0]))
clean = "_" + clean;
// 限制长度(例如最多64字符)
return clean.Substring(0, Math.Min(clean.Length, 64));
}
private void OnToggleSidebarClick(object sender, RoutedEventArgs e)
{
// 获取主窗口的根Grid(即最外层Grid)
if (this.Content is not Grid rootGrid)
return;
var colSidebar = rootGrid.ColumnDefinitions[0]; // 第一列是侧边栏
const double expandedWidth = 200;
const double collapsedWidth = 0;
double targetWidth = _isSidebarExpanded ? collapsedWidth : expandedWidth;
string buttonContent = _isSidebarExpanded ? ">>" : "<<";
var animation = new DoubleAnimation(
colSidebar.ActualWidth,
targetWidth,
new Duration(TimeSpan.FromMilliseconds(250))
);
colSidebar.BeginAnimation(ColumnDefinition.WidthProperty, animation);
// 更新按钮文本
if (sender is ToggleButton btn)
{
btn.Content = buttonContent;
}
_isSidebarExpanded = !_isSidebarExpanded;
}
}
}
最新发布