【个人学习笔记9之--TOP 和APPLY 】

本文详细介绍了SQL中的TOP与APPLY操作。TOP与ORDER BY结合使用,可以控制返回的行数,而在SQL2005以后,TOP支持更多用法,包括在INSERT、UPDATE、DELETE中。WITH TIES选项允许返回与最后返回行相同值的额外行。APPLY是一个表运算符,先计算左边的表表达式。TOP与APPLY结合能解决返回每组前N行和匹配当前值与前一个值等问题。文章还讨论了分批删除和更新数据的策略,以及随机行选择和找到数据集的中间值的方法。

 这篇来说说TOP 和APPLY


1.TOP
一般来说 我们的TOP 和 ORDER BY 是一起连用的 这样可以返回确定的行 你可以看做是一个筛选器,可以再表表达式中使用。
SQL 2005 TOP 后面支持连接 变量 算术表达式 子查询结果.
在2000里 只支持后面具体数字 如果想在2000中使用类似TOP (@n)的功能 参考http://blog.youkuaiyun.com/feixianxxx/archive/2009/08/03/4405684.aspx


这里有个小知识点: WITH TIES 。
create table # (a int)
insert # select
1 union all select
2 union all select
2 union all select
3 union all select
3 union all select
5
select top (4) A from # order by a
/*
A
-----------
1
2
2
3
*/
select top (4) with ties A from # order by a
/*
A
-----------
1
2
2
3
3
*/
--使用了WITH TIES 后 将生成额外的行,多出来的行是与最后返回行相同的值 例子里是3
SQL2005支持TOP出现在INSERT UPDATE DELETE 后面 控制操作的行数,比如 insert top(10) into tb select * from .....
不过可惜ORDER BY不能用于这些结构中,这样你也许会觉得无法控制。倒是有方法可以控制:
插入:insert tb select top (n) * from .... order by ....
删除:with cte as
(
 select top(10) * from ... order by ...
)
delete from  cte
修改: with cte as
(
 select top(10) * from ... order by ...
)
update cte set ......


实际小问题:
工作中有时候需要清理历史数据的时候,往往数据量达到了几百万.这个时候采用一次性删除的方法是很不明智的:
a.delete操作会被完整记录到日志里,它需要大量空间和时间;
b.如果删除中间发生中断,一切删除会回滚(在一个事务里);
c.同时删除多行,记录上的锁也许会被提升为排它表锁,从而阻碍操作完成之前有对这个表的操作(有时候会妨碍正常的业务
)
所以一般采取分批删除的方法.


sql2000:通过set rowcount 来控制每次删除的记录数
SET ROWCOUNT 5000;
WHILE 1 = 1
BEGIN
  DELETE FROM dbo.LargeOrders
  WHERE OrderDate < '19970101';

  IF @@rowcount < 5000 BREAK;
END
SET ROWCOUNT 0;
sql2005:通过TOP的方法
WHILE 1 = 1
BEGIN
  DELETE TOP(5000) FROM dbo.LargeOrders
  WHERE OrderDate < '19970101';

  IF @@rowcount < 5000 BREAK;
END
-----------------
分批更新大量数据类似.

PS: 如果一个表有709行 你 select top (1) percent  709除以100等于7.09 返回的行数是8 说明它是向上舍入的

 

 

2.APPLY
它是一个表运算符--FROM A CROSS APPLY() B  它一定先是计算左边的表表达式,这个是必须了解的.
具体参考MSDN,写得很清楚了http://msdn.microsoft.com/zh-cn/library/ms175156.aspx

 

 

3.TOP 和 APPLY 结合解决问题


a.返回每组的前N行
create table #test (id int, val int)
create table #t (id int,yuangong varchar(10))
insert #test select
1,2 union all select
1,1 union all select
1,4 union all select
2,1 union all select
2,2 union all select
3,3 union all select
3,4 union all select
3,5 union all select
3,6 union all select
3,9 union all select
4,1
insert #t select
1,'a' union all select
2,'b' union all select
3,'c' union all select
4,'d'
--方法1
select a.yuangong,k.val
from #test k join #t  a on a.id=k.id
where val in(select top (2) val from #test where k.id=id  order by val)
order by a.yuangong,val
--方法2
select k.yuangong,l.val
from #t k cross apply( select top (2) val from #test where k.id=id  order by val)l
order by k.yuangong,val
--方法3
select a.yuangong,k.val
from #t a join (select ROW_NUMBER() over(PARTITION by id order by val) as rn ,*from #test) k
on a.id=k.id
where rn=1 or rn=2
order by a.yuangong,val
/*
yuangong   val
---------- -----------
a          1
a          2
b          1
b          2
c          3
c          4
d          1

*/
---效率越来越高依次...执行计划上看...

 

b.匹配当前值和前一个值

create table #p (id int ,eid int ,num1 int,num2 int )
insert #p select
1,1,2, 1 union all select
2,1,3 ,2union all select
3,2,2 ,2union all select
4,2,3 ,3union all select
5,3,2 ,4union all select
7,3,3 ,5union all select
9,3,4 ,5union all select
11,4,2 ,5union all select
13,4,2 ,6union all select
18,4,2 ,7
--方法1:
select a.eid as c_eid ,a.num1 as c_num1, a.num2 as c_num2,
  b.eid as p_eid,b.num1 as p_num1, b.num2 as p_num2
from #p a left join #p  b on
b.id=(select top 1 id from #p where eid=a.eid and (a.num1>num1 or(a.num1=num1 and a.num2>num2 )) ORDER BY num1,num2)
--方法2:
select a.eid as c_eid ,a.num1 as c_num1, a.num2 as c_num2,
  b.eid as p_eid,b.num1 as p_num1, b.num2 as p_num2
from #p a  OUTER APPLY --注意这里用OUTER
(SELECT TOP 1 eid,num1,num2 FROM #p WHERE eid=a.eid and (a.num1>num1 or(a.num1=num1 and a.num2>num2 )) ORDER BY num1,num2  )B
--方法3:
;with cte as
(
 select ROW_NUMBER() over(PARTITION by eid order by num1,num2) as rn ,* from #p
)
select a.eid as c_eid ,a.num1 as c_num1, a.num2 as c_num2,
  b.eid as p_eid,b.num1 as p_num1, b.num2 as p_num2
from cte a left join cte b on a.eid=b.eid and a.rn=b.rn+1
/*
c_eid       c_num1      c_num2      p_eid       p_num1      p_num2
----------- ----------- ----------- ----------- ----------- -----------
1           2           1           NULL        NULL        NULL
1           3           2           1           2           1
2           2           2           NULL        NULL        NULL
2           3           3           2           2           2
3           2           4           NULL        NULL        NULL
3           3           5           3           2           4
3           4           5           3           3           5
4           2           5           NULL        NULL        NULL
4           2           6           4           2           5
4           2           7           4           2           6
*/

---执行计划上看 最低的还是第一个 用ROW_NUMBER时最快的

 

c.随机行
说道随机 有人会想到 select top .....order by rand()
create table # (val int)
insert # select
1 union all select
7 union all select
14 union all select
13 union all select
12 union all select
11 union all select
4
select top 3 *
from #
order by RAND()
/*
val
-----------
1
7
14
*/
--你连续运行这个代码,你会发现一直是同一个结果,那是因为RAND()还有getdate()等大多数的不确定函数对同一个查询只调用一次.

 

关于RAND()函数我简单说个东西.
运行:select rand(7),rand()
/*
---------------------- ----------------------
0.713703791040473      0.945719735396816
*/
这里的结果无论你运行几次都一样;这是因为RAND()函数其实是基于你上个RAND()函数运行的调用而运行的.这里是RAND(7)..
RAND返回一定范围的数字:  
    方法1的数字范围:0至N-1之间,如CAST( FLOOR(RAND()*100) AS INT)就会生成0至99之间任一整数
    方法2的数字范围:1至N之间,如CAST(CEILING(RAND() * 100) AS INT)就会生成1至100之间任一整数
   
那么返回指定行数的随机行:
SELECT TOP N * FROM TABLE_NAME ORDER BY NEWID() ----N是一个你指定的整数,表是取得记录的条数.
or: SELECT TOP N * FROM TABLE_NAME ORDER BY CHECKSUM(NEWID()) --CHECKSUM(NEWID())可以得到一个随机值


if object_id('tb') is not null
drop table tb
create table tb (s_id int,t_id int, fenshu int)
insert into tb
select 1,1,66 union all
select 1,2,67 union all
select 2,1,65 union all
select 2,2,78 union all
select 3,1,66 union all
select 3,2,55
--这个方法可以给不同数加上不同随机数(newid())
select fenshu,(fenshu+cast(ceiling(RAND(CHECKSUM(NEWID()))*10)as int))as fenshu2
from tb
/*
fenshu      fenshu2
----------- -----------
66          68
67          72
65          67
78          80
66          76
55          62

fenshu      fenshu2
----------- -----------
66          67
67          74
65          66
78          80
66          73
55          57
*/
--这个方法只能给不同数随机加上相同数(RAND())
select fenshu,(fenshu+cast(CEILING(RAND() * 10) AS INT))as fenshu2
from tb
或者
SELECT fenshu,fenshu+cast(ceiling(RAND(CHECKSUM(rand()))*10) as int) fenshu2
FROM tb
/*
fenshu      fenshu2
----------- -----------
66          67
67          68
65          66
78          79
66          67
55          56
*/

 

 

d.中间值
我在前面那篇笔记中已经写了相关的问题,这里针对TOP和忽然想到的NTILE 再说2个方法

create table a(rq varchar(8), ddsj int)
insert into a
select
'200805',30 union all select
'200805',40 union all select
'200805',50 union all select
'200805',20 union all select
'200806',250 union all select
'200806',200 union all select
'200806',310 union all select
'200806',100 union all select
'200806',130;
方法1:top
select rq,
((select MAX(ddsj) from (select top 50 percent ddsj from  a where rq=k.rq order by ddsj) as p)+
(select Min(ddsj) from (select top 50 percent  ddsj from  a where rq=k.rq order by ddsj desc ) as p))/2 as 中值
from a k
group by rq
方法2:NTILE
select distinct rq,
((select max(ddsj) from (select NTILE(2) over(partition by rq order by ddsj) as rn1,* from a ) z where k.rq=rq and rn1=1)+
(select min(ddsj) from (select NTILE(2) over(partition by rq order by ddsj desc ) as rn2 ,* from a ) z where k.rq=rq and  rn2=1))/2 as 中值
from a k

/*
rq       中值
-------- -----------
200805   35
200806   200
*/

 

<think>为了将界面做得更精致,我们可以进行以下改进:1.使用更现代的布局,如DockPanelGrid进行精细控制。2.添加菜单栏,包括文件、编辑等菜单。3.使用工具栏放置常用按钮(保存、插入图片等)。4.添加状态栏显示当前状态。5.使用样式资源统一控件外观。6.为按钮添加图标,提升视觉效果。以下是一个改进后的版本:```csharp//代码部分(MainWindow.xaml.cs)与之前相比变化不大,主要调整了事件处理//注意:这里我们添加了菜单栏工具栏,并调整了布局usingMicrosoft.Win32;usingSystem;usingSystem.Collections.Generic;usingSystem.IO;usingSystem.Linq;usingSystem.Windows;usingSystem.Windows.Controls;usingSystem.Windows.Documents;usingSystem.Windows.Input;usingSystem.Windows.Media;usingSystem.Windows.Media.Imaging;namespaceWpfNotepad{publicpartialclassMainWindow:Window{privateList<Note>notes=newList<Note>();privatestringcurrentCategory="默认分类";publicMainWindow(){InitializeComponent();LoadCategories();LoadSampleNotes();}privatevoidLoadCategories(){cmbCategories.ItemsSource=newList<string>{"默认分类","工作笔记","学习笔记","个人日记","项目记录"};cmbCategories.SelectedIndex=0;}privatevoidLoadSampleNotes(){notes.Add(newNote{Title="示例笔记",Category="默认分类",Content=newFlowDocument(newParagraph(newRun("这是一个示例笔记内容")))});notes.Add(newNote{Title="会议记录",Category="工作笔记",Content=newFlowDocument(newParagraph(newRun("项目进度讨论")))});RefreshNoteList();}privatevoidRefreshNoteList(){lstNotes.ItemsSource=notes.Where(n=>n.Category==currentCategory).Select(n=>n.Title).ToList();}privatevoidbtnSave_Click(objectsender,RoutedEventArgse){SaveNote();}privatevoidSaveNote(){if(string.IsNullOrWhiteSpace(txtTitle.Text)){MessageBox.Show("请输入标题");return;}notes.Add(newNote{Title=txtTitle.Text,Category=cmbCategories.Text,Content=newFlowDocument(newParagraph(newRun(newTextRange(rtbContent.Document.ContentStart,rtbContent.Document.ContentEnd).Text)))});RefreshNoteList();ClearEditor();}privatevoidClearEditor(){txtTitle.Clear();rtbContent.Document.Blocks.Clear();}privatevoidbtnInsertImage_Click(objectsender,RoutedEventArgse){InsertImage();}privatevoidInsertImage(){OpenFileDialogdlg=newOpenFileDialog{Filter="图片文件|*.jpg;*.png;*.bmp|所有文件|*.*"};if(dlg.ShowDialog()==true){Imageimg=newImage{Source=newBitmapImage(newUri(dlg.FileName)),Width=200,Margin=newThickness(0,5,0,5)};InlineUIContainercontainer=newInlineUIContainer(img);Paragraphparagraph=newParagraph(container);rtbContent.Document.Blocks.Add(paragraph);}}privatevoidbtnSearch_Click(objectsender,RoutedEventArgse){SearchNotes();}privatevoidSearchNotes(){stringkeyword=txtSearch.Text.ToLower();if(string.IsNullOrWhiteSpace(keyword))return;varresults=notes.Where(n=>newTextRange(n.Content.ContentStart,n.Content.ContentEnd).Text.ToLower().Contains(keyword)).Select(n=>$"{n.Category}>{n.Title}").ToList();lstSearchResults.ItemsSource=results;}privatevoidcmbCategories_SelectionChanged(objectsender,SelectionChangedEventArgse){currentCategory=cmbCategories.SelectedItemasstring;RefreshNoteList();}privatevoidlstNotes_SelectionChanged(objectsender,SelectionChangedEventArgse){if(lstNotes.SelectedItem==null)return;stringtitle=lstNotes.SelectedItem.ToString();varnote=notes.FirstOrDefault(n=>n.Title==title&&n.Category==currentCategory);if(note!=null){txtTitle.Text=note.Title;rtbContent.Document=note.Content;}}//菜单项:新建笔记privatevoidNewNote_Click(objectsender,RoutedEventArgse){ClearEditor();}//菜单项:退出privatevoidExit_Click(objectsender,RoutedEventArgse){Application.Current.Shutdown();}}publicclassNote{publicstringTitle{get;set;}publicstringCategory{get;set;}publicFlowDocumentContent{get;set;}}}``````xml<!--改进后的XAML布局--><Windowx:Class="WpfNotepad.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="WPF记事本"Height="600"Width="800"><Window.Resources><StyleTargetType="Button"><SetterProperty="Margin"Value="2"/><SetterProperty="Padding"Value="5,2"/></Style><StyleTargetType="ToolBar"><SetterProperty="Margin"Value="0,2,0,2"/></Style></Window.Resources><DockPanel><!--菜单栏--><MenuDockPanel.Dock="Top"><MenuItemHeader="文件"><MenuItemHeader="新建笔记"Click="NewNote_Click"/><Separator/><MenuItemHeader="退出"Click="Exit_Click"/></MenuItem><MenuItemHeader="编辑"><MenuItemHeader="插入图片"Click="btnInsertImage_Click"/></MenuItem></Menu><!--工具栏--><ToolBarTrayDockPanel.Dock="Top"><ToolBar><Buttonx:Name="btnSave"Content="保存笔记"Click="btnSave_Click"/><Separator/><Buttonx:Name="btnInsertImage"Content="插入图片"Click="btnInsertImage_Click"/></ToolBar></ToolBarTray><!--状态栏--><StatusBarDockPanel.Dock="Bottom"><StatusBarItem><TextBlock>就绪</TextBlock></StatusBarItem></StatusBar><!--主内容区--><Grid><Grid.RowDefinitions><RowDefinitionHeight="Auto"/><RowDefinitionHeight="Auto"/><RowDefinitionHeight="*"/><RowDefinitionHeight="200"/></Grid.RowDefinitions><!--搜索区域--><StackPanelGrid.Row="0"Orientation="Horizontal"Margin="5"><TextBoxx:Name="txtSearch"Width="200"Margin="5"VerticalAlignment="Center"KeyDown="txtSearch_KeyDown"/><Buttonx:Name="btnSearch"Content="搜索"Width="80"Margin="5"Click="btnSearch_Click"/></StackPanel><!--分类标题区域--><StackPanelGrid.Row="1"Orientation="Horizontal"Margin="5"><LabelVerticalAlignment="Center">分类:</Label><ComboBoxx:Name="cmbCategories"Width="120"Margin="5"SelectionChanged="cmbCategories_SelectionChanged"/><LabelVerticalAlignment="Center">标题:</Label><TextBoxx:Name="txtTitle"Width="300"Margin="5"VerticalAlignment="Center"Text="新笔记标题"/></StackPanel><!--主编辑区--><GridGrid.Row="2"><Grid.ColumnDefinitions><ColumnDefinitionWidth="200"/><ColumnDefinitionWidth="*"/></Grid.ColumnDefinitions><!--笔记列表--><ListBoxx:Name="lstNotes"Grid.Column="0"Margin="5"SelectionChanged="lstNotes_SelectionChanged"/><!--富文本编辑器--><RichTextBoxx:Name="rtbContent"Grid.Column="1"Margin="5"VerticalScrollBarVisibility="Auto"><FlowDocument><Paragraph><RunText="在此输入内容..."/></Paragraph></FlowDocument></RichTextBox></Grid><!--搜索结果--><GroupBoxGrid.Row="3"Header="搜索结果"Margin="5"><ListBoxx:Name="lstSearchResults"/></GroupBox></Grid></DockPanel></Window>```###改进点:1.**菜单栏**:添加了文件菜单(新建笔记、退出)编辑菜单(插入图片)。2.**工具栏**:将保存插入图片按钮放在工具栏,方便操作。3.**状态栏**:底部状态栏显示状态信息(当前为固定文本,可扩展为动态状态)。4.**布局优化**:-使用DockPanel作为根布局,菜单栏、工具栏、状态栏分别停靠顶部底部。-主内容区使用Grid进行划分。-添加了标签(Label)说明分类标题。5.**样式统一**:-通过Window.Resources定义按钮工具栏的样式,统一外观。-按钮间距边距调整,更美观。###注意:-由于插入图片功能在菜单工具栏都有,所以将插入图片的代码提取到一个独立的方法`InsertImage`中,然后菜单项工具栏按钮都调用同一个方法。-添加了“新建笔记”菜单项,用于清空编辑器。###运行效果:-顶部菜单栏工具栏-中间是搜索框、分类选择、标题输入、笔记列表富文本编辑区-底部是搜索结果列表状态栏###进一步改进建议:-添加图标:使用Image控件或`Content`属性设置图标(需要准备图标资源)。-数据持久化:将笔记保存到文件或数据库。-文本格式化:添加字体、颜色等格式按钮。---**§§相关问题§§**:1.如何在WPF中为工具栏按钮添加图标?2.如何实现文本格式化(如粗体、斜体、颜色)功能?3.如何将笔记数据保存到本地XML文件?4.如何实现笔记的删除修改功能?</think># 精致版 WPF 记事本软件 下面是界面更加精致的 WPF 记事本实现,包含您要求的所有功能,并进行了视觉优化: ```csharp using Microsoft.Win32; using System; using System.Collections.Generic; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; namespace WpfNotepadEnhanced { public partial class MainWindow : Window { private List<Note> notes = new List<Note>(); private string currentCategory = "默认分类"; public MainWindow() { InitializeComponent(); LoadCategories(); LoadSampleNotes(); ApplyTheme(); } private void ApplyTheme() { // 设置主题色 Color accentColor = Color.FromRgb(0, 120, 215); Resources["AccentColor"] = new SolidColorBrush(accentColor); Resources["LightAccentColor"] = new SolidColorBrush(Color.FromRgb(229, 241, 251)); } private void LoadCategories() { cmbCategories.ItemsSource = new List<string> { "默认分类", "工作笔记", "学习笔记", "个人日记", "项目记录", "灵感收集" }; cmbCategories.SelectedIndex = 0; } private void LoadSampleNotes() { notes.Add(new Note { Title = "欢迎使用记事本", Category = "默认分类", Content = new FlowDocument(new Paragraph(new Run("这是一个功能强大的记事本应用,支持文本编辑、图片插入分类管理。\n\n使用说明:\n1. 选择分类\n2. 输入标题\n3. 编辑内容\n4. 保存笔记"))) }); RefreshNoteList(); } private void RefreshNoteList() { lstNotes.ItemsSource = notes .Where(n => n.Category == currentCategory) .Select(n => n.Title) .ToList(); } private void SaveNote() { if (string.IsNullOrWhiteSpace(txtTitle.Text)) { MessageBox.Show("请输入标题", "保存笔记", MessageBoxButton.OK, MessageBoxImage.Warning); return; } // 更新现有笔记或添加新笔记 var existingNote = notes.FirstOrDefault(n => n.Title == txtTitle.Text && n.Category == currentCategory); if (existingNote != null) { existingNote.Content = new FlowDocument(new Paragraph(new Run(new TextRange( rtbContent.Document.ContentStart, rtbContent.Document.ContentEnd).Text))); } else { notes.Add(new Note { Title = txtTitle.Text, Category = cmbCategories.Text, Content = new FlowDocument(new Paragraph(new Run(new TextRange( rtbContent.Document.ContentStart, rtbContent.Document.ContentEnd).Text))) }); } RefreshNoteList(); ShowStatusMessage($"笔记已保存: {txtTitle.Text}"); } private void InsertImage() { OpenFileDialog dlg = new OpenFileDialog { Filter = "图片文件|*.jpg;*.png;*.bmp;*.gif|所有文件|*.*" }; if (dlg.ShowDialog() == true) { try { Image img = new Image { Source = new BitmapImage(new Uri(dlg.FileName)), MaxWidth = 500, Margin = new Thickness(0, 5, 0, 5), Stretch = Stretch.Uniform }; InlineUIContainer container = new InlineUIContainer(img); Paragraph paragraph = new Paragraph(container); rtbContent.Document.Blocks.Add(paragraph); } catch (Exception ex) { MessageBox.Show($"无法加载图片: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error); } } } private void SearchNotes() { string keyword = txtSearch.Text.Trim().ToLower(); if (string.IsNullOrWhiteSpace(keyword)) { lstSearchResults.ItemsSource = null; return; } var results = notes .Where(n => new TextRange(n.Content.ContentStart, n.Content.ContentEnd).Text .ToLower().Contains(keyword) || n.Title.ToLower().Contains(keyword)) .Select(n => new SearchResult { Category = n.Category, Title = n.Title, Preview = GetPreviewText(n.Content, keyword) }) .ToList(); lstSearchResults.ItemsSource = results; if (results.Any()) { ShowStatusMessage($"找到 {results.Count} 条匹配结果"); } else { ShowStatusMessage("未找到匹配结果"); } } private string GetPreviewText(FlowDocument doc, string keyword) { string fullText = new TextRange(doc.ContentStart, doc.ContentEnd).Text; int index = fullText.ToLower().IndexOf(keyword); if (index == -1) return fullText.Length > 100 ? fullText.Substring(0, 100) + "..." : fullText; int start = Math.Max(0, index - 20); int length = Math.Min(100, fullText.Length - start); string preview = (start > 0 ? "..." : "") + fullText.Substring(start, length); if (start + length < fullText.Length) preview += "..."; return preview; } private void ShowStatusMessage(string message) { statusText.Text = message; statusBar.Visibility = Visibility.Visible; // 3秒后自动隐藏状态消息 var timer = new System.Windows.Threading.DispatcherTimer(); timer.Interval = TimeSpan.FromSeconds(3); timer.Tick += (s, e) => { statusBar.Visibility = Visibility.Collapsed; timer.Stop(); }; timer.Start(); } // 事件处理程序 private void btnSave_Click(object sender, RoutedEventArgs e) => SaveNote(); private void btnInsertImage_Click(object sender, RoutedEventArgs e) => InsertImage(); private void btnSearch_Click(object sender, RoutedEventArgs e) => SearchNotes(); private void cmbCategories_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (cmbCategories.SelectedItem == null) return; currentCategory = cmbCategories.SelectedItem.ToString(); RefreshNoteList(); ShowStatusMessage($"切换到分类: {currentCategory}"); } private void lstNotes_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (lstNotes.SelectedItem == null) return; string title = lstNotes.SelectedItem.ToString(); var note = notes.FirstOrDefault(n => n.Title == title && n.Category == currentCategory); if (note != null) { txtTitle.Text = note.Title; rtbContent.Document = note.Content; ShowStatusMessage($"已加载笔记: {title}"); } } private void lstSearchResults_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (lstSearchResults.SelectedItem is SearchResult result) { currentCategory = result.Category; cmbCategories.SelectedItem = currentCategory; var note = notes.FirstOrDefault(n => n.Title == result.Title && n.Category == result.Category); if (note != null) { txtTitle.Text = note.Title; rtbContent.Document = note.Content; ShowStatusMessage($"已加载笔记: {result.Title}"); } } } private void txtSearch_KeyDown(object sender, KeyEventArgs e) { if (e.Key == Key.Enter) { SearchNotes(); } } private void NewNote_Click(object sender, RoutedEventArgs e) { txtTitle.Clear(); rtbContent.Document = new FlowDocument(new Paragraph()); ShowStatusMessage("已创建新笔记"); } private void DeleteNote_Click(object sender, RoutedEventArgs e) { if (lstNotes.SelectedItem == null) return; string title = lstNotes.SelectedItem.ToString(); var note = notes.FirstOrDefault(n => n.Title == title && n.Category == currentCategory); if (note != null) { notes.Remove(note); RefreshNoteList(); ShowStatusMessage($"已删除笔记: {title}"); txtTitle.Clear(); rtbContent.Document = new FlowDocument(new Paragraph()); } } } public class Note { public string Title { get; set; } public string Category { get; set; } public FlowDocument Content { get; set; } } public class SearchResult { public string Category { get; set; } public string Title { get; set; } public string Preview { get; set; } } } ``` ```xml <Window x:Class="WpfNotepadEnhanced.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" mc:Ignorable="d" Title="精致记事本" Height="700" Width="1000" WindowStartupLocation="CenterScreen" Background="{DynamicResource LightAccentColor}"> <Window.Resources> <!-- 主题色 --> <SolidColorBrush x:Key="AccentColor" Color="#0078D7"/> <SolidColorBrush x:Key="LightAccentColor" Color="#E5F1FB"/> <!-- 按钮样式 --> <Style TargetType="Button"> <Setter Property="Background" Value="{StaticResource AccentColor}"/> <Setter Property="Foreground" Value="White"/> <Setter Property="Padding" Value="10,5"/> <Setter Property="Margin" Value="5"/> <Setter Property="BorderThickness" Value="0"/> <Setter Property="Cursor" Value="Hand"/> <Setter Property="FontWeight" Value="SemiBold"/> <Style.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Background" Value="#0066CC"/> </Trigger> </Style.Triggers> </Style> <!-- 列表项样式 --> <Style TargetType="ListBoxItem"> <Setter Property="Padding" Value="8,6"/> <Setter Property="Margin" Value="2"/> <Style.Triggers> <Trigger Property="IsSelected" Value="True"> <Setter Property="Background" Value="{StaticResource LightAccentColor}"/> <Setter Property="BorderBrush" Value="{StaticResource AccentColor}"/> <Setter Property="BorderThickness" Value="0,0,0,2"/> <Setter Property="FontWeight" Value="Bold"/> </Trigger> </Style.Triggers> </Style> <!-- 搜索结果显示模板 --> <DataTemplate x:Key="SearchResultTemplate"> <StackPanel Margin="5"> <TextBlock FontWeight="Bold" Text="{Binding Title}"> <TextBlock.ContextMenu> <ContextMenu> <MenuItem Header="打开笔记" Click="lstSearchResults_SelectionChanged"/> </ContextMenu> </TextBlock.ContextMenu> </TextBlock> <TextBlock FontSize="12" Foreground="Gray" Text="{Binding Category}"/> <TextBlock Margin="0,5,0,0" Text="{Binding Preview}" TextWrapping="Wrap"/> </StackPanel> </DataTemplate> </Window.Resources> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <!-- 顶部工具栏 --> <Border Grid.Row="0" Background="White" Padding="10" BorderThickness="0,0,0,1" BorderBrush="#E0E0E0"> <StackPanel Orientation="Horizontal"> <Button Content="新建笔记" Click="NewNote_Click" Width="100"/> <Button Content="保存笔记" Click="btnSave_Click" Width="100" Margin="10,0,0,0"/> <Button Content="删除笔记" Click="DeleteNote_Click" Width="100" Margin="10,0,0,0"/> <Button Content="插入图片" Click="btnInsertImage_Click" Width="100" Margin="30,0,0,0"/> <ComboBox x:Name="cmbCategories" Width="150" Margin="30,0,0,0" SelectedValuePath="Content" FontSize="14" ToolTip="选择笔记分类"> <ComboBox.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <Ellipse Width="10" Height="10" Fill="{StaticResource AccentColor}" Margin="0,0,5,0"/> <TextBlock Text="{Binding}"/> </StackPanel> </DataTemplate> </ComboBox.ItemTemplate> </ComboBox> <TextBox x:Name="txtTitle" Margin="10,0,0,0" Width="250" VerticalContentAlignment="Center" FontSize="14" ToolTip="输入笔记标题" Padding="10,5"/> </StackPanel> </Border> <!-- 主内容区 --> <Grid Grid.Row="1"> <Grid.ColumnDefinitions> <ColumnDefinition Width="250"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <!-- 左侧边栏 --> <Grid Grid.Column="0" Background="#F5F5F5"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <!-- 搜索框 --> <Border Grid.Row="0" Background="White" Padding="10" BorderThickness="0,0,0,1" BorderBrush="#E0E0E0"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="Auto"/> </Grid.ColumnDefinitions> <TextBox x:Name="txtSearch" Grid.Column="0" VerticalContentAlignment="Center" Padding="10,5" ToolTip="输入关键字搜索笔记" FontSize="14" KeyDown="txtSearch_KeyDown"> <TextBox.Style> <Style TargetType="TextBox"> <Setter Property="BorderBrush" Value="#CCCCCC"/> <Setter Property="BorderThickness" Value="1"/> <Style.Triggers> <Trigger Property="IsFocused" Value="True"> <Setter Property="BorderBrush" Value="{StaticResource AccentColor}"/> </Trigger> </Style.Triggers> </Style> </TextBox.Style> </TextBox> <Button Grid.Column="1" Content="搜索" Click="btnSearch_Click" Margin="5,0,0,0" Width="80"/> </Grid> </Border> <!-- 笔记列表 --> <Border Grid.Row="1" Margin="0,10,0,0"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <TextBlock Grid.Row="0" Text="{Binding ElementName=cmbCategories, Path=SelectedItem, StringFormat=&#39;{}{0} 笔记列表&#39;}" FontWeight="Bold" Margin="10,5" Foreground="#555555"/> <ListBox x:Name="lstNotes" Grid.Row="1" BorderThickness="0" ScrollViewer.HorizontalScrollBarVisibility="Disabled" SelectionChanged="lstNotes_SelectionChanged"> <ListBox.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding}" Padding="5" TextWrapping="Wrap"/> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </Grid> </Border> <!-- 搜索结果 --> <Border Grid.Row="2" Margin="0,10,0,0" Background="White" BorderThickness="0,1,0,0" BorderBrush="#E0E0E0"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <TextBlock Grid.Row="0" Text="搜索结果" FontWeight="Bold" Margin="10,5" Foreground="#555555"/> <ListBox x:Name="lstSearchResults" Grid.Row="1" BorderThickness="0" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ItemTemplate="{StaticResource SearchResultTemplate}" SelectionChanged="lstSearchResults_SelectionChanged"/> </Grid> </Border> </Grid> <!-- 富文本编辑器 --> <Border Grid.Column="1" Margin="10" Background="White" CornerRadius="3" BorderThickness="1" BorderBrush="#E0E0E0"> <RichTextBox x:Name="rtbContent" Padding="15" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" FontSize="14" BorderThickness="0"> <RichTextBox.Resources> <Style TargetType="Paragraph"> <Setter Property="Margin" Value="0,0,0,10"/> </Style> </RichTextBox.Resources> <FlowDocument> <Paragraph> <Run Text="在此输入您的内容..."/> </Paragraph> </FlowDocument> </RichTextBox> </Border> </Grid> <!-- 状态栏 --> <Border x:Name="statusBar" Grid.Row="2" Background="{StaticResource AccentColor}" Visibility="Collapsed" CornerRadius="0,0,3,3"> <TextBlock x:Name="statusText" Foreground="White" Margin="10,5" HorizontalAlignment="Center"/> </Border> </Grid> </Window> ``` ## 精致版界面特点 ### 1. 现代UI设计元素 - **卡片式布局**:使用圆角阴影效果 - **色彩方案**:采用蓝色主题配色,更专业美观 - **空间利用**:合理的间距留白 - **视觉层次**:清晰的区域划分 ### 2. 增强的功能区域 - **顶部工具栏**:整合所有主要操作按钮 - **分类选择器**:带图标的下拉菜单 - **标题输入框**:突出显示位置 - **双列表布局**:左侧笔记列表搜索结果,右侧编辑区 ### 3. 视觉优化细节 - **按钮样式**:统一风格,悬停效果 - **列表项样式**:选中状态高亮显示 - **搜索结果显示**:带预览文本的卡片 - **状态提示**:操作反馈状态栏 ### 4. 功能改进 - **笔记管理**:支持新建、保存、删除 - **搜索结果**:显示上下文预览 - **状态反馈**:操作结果提示 - **快捷键**:支持回车键搜索 ## 运行说明 1. 创建新的WPF项目(.NET Framework 4.7) 2. 将代码复制到MainWindow.xamlMainWindow.xaml.cs 3. 运行程序 ## 使用说明 1. **创建笔记**: - 选择分类 - 输入标题 - 编辑内容(支持文字图片) - 点击"保存笔记" 2. **插入图片**: - 点击"插入图片"按钮 - 选择图片文件 - 图片将插入到光标位置 3. **搜索功能**: - 在搜索框输入关键字 - 点击"搜索"按钮或按回车 - 搜索结果将显示在下方区域 4. **笔记管理**: - 左侧列表显示当前分类笔记 - 点击笔记标题加载内容 - 点击"删除笔记"删除当前笔记 5. **状态提示**: - 操作结果会在底部状态栏显示 - 3秒后自动消失 ## 技术亮点 1. **富文本编辑**:使用RichTextBox支持图文混排 2. **数据绑定**:使用MVVM模式简化数据管理 3. **视觉状态**:按钮悬停效果选中状态 4. **异步反馈**:操作状态提示 5. **搜索优化**:带上下文预览的搜索结果 这个精致版记事本应用不仅满足了功能需求,还在用户体验视觉设计上做了大量优化,使其看起来更专业、用起来更舒适。 --- **
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值