作者:浪子花梦,一个有趣的程序员 ~
此文将完成interactivity事件的使用,为什么需要用到这个功能呢?因为程序中需要使用加载事件,如果我们需要将事件处理器定义在隐藏代码中,这就破坏了MVVM设计模式了,使用interactivity可以很好的与业务逻辑中的命令进行绑定,实现解耦,另外,此文章将实现查找窗口的功能,搜索指定的文本内容,在上面的演示上展示过了,包括一些细节很重要的知识点,另外还有使用配置文件将程序中一些数据保存下来,已供下次程序运行时加载初始化 . . .
前文如下所示:
【WPF高仿 Windows记事本】开发日记 (一) UI布局(帮助菜单功能实现
)
【WPF高仿 Windows记事本】开发日记 (二) 初步使用MVVM模式设计 帮助菜单 的三个命令
【WPF高仿 Windows记事本】开发日记 (三) 完善所有菜单的命令绑定并创建Models层的设计
【WPF高仿 Windows记事本】开发日记 (四) 菜单命令快捷键实现,实现查看菜单功能(三层架构初步使用
)
【WPF高仿 Windows记事本】开发日记 (五) 使用成熟的自动更新代码(ObservableObject)、实现是否显示状态栏功能、鼠标的滚轮命令绑定(查看菜单功能实现
)
【WPF高仿 Windows记事本】开发日记 (六) 自动换行、滚动条命令绑定,实现字体模式的选择功能(格式菜单功能实现
)
【WPF高仿 Windows记事本】开发日记 (七) 编辑菜单中的内置命令使用,查看与替换的窗口设计(包含窗口变为对话框)
【WPF高仿 Windows记事本】开发日记 (八) Bing搜索菜单功能实现,利用消息
和依赖注入
解除程序间的耦合
.
此文章实现的效果如下所示:
查找窗口功能演示
1)如果想用交互事件,并且不安装第三方插件的情况下,需要定义如下的类:
.
.
2)DocumentModel 数据层的类中定义TextBox类型,所有的操作都基于此实现:
.
.
3)MainWindow.xaml 中使用interactivity绑定程序加载事件相关的命令:
此命令有个参数是 textBox,代表主文本框,方法实现如下所示,将这个文本框赋值给数据层:
.
.
4)主文本框的两个属性注意:选中时的颜色,失去焦点时是否允许高度:
.
.
5)查找窗口初始加载时,绑定命令,选中所有的文本框内容:
命令响应方法如下:
.
.
6)发送消息打开查找窗口时,判断是否选中了主文本中的内容,并给查找窗口中的文本内容赋值:
.
.
7)查找下一个 和 取消按钮的命令与实现逻辑如下所示:
xaml代码部分:
命令初始化:
逻辑实现:
private void OnCancelCommand(Window window)
{
window.Close(); // 关闭窗口
}
private void OnFindTextCommandArgs(TextBox textBox)
{
OnFindTextCommand(); // 查找文本的逻辑实现
textBox.Focus(); // 解决按钮热点无法一直按的bug
}
private void OnFindTextCommand()
{
int indexStart = 0; // 选择文本的起点
int indexFind = 0; // 查找到下/上一个文本的点
// 向上查找
if (this.DocumentModel.IsFindUp)
{
indexStart = this.DocumentModel.TextBox.SelectionStart;
// 需要查找的文本 查找的起始点 是否区分大小写
indexFind = this.DocumentModel.TextBox.Text.LastIndexOf(
this.DocumentModel.FindText,
indexStart,
this.DocumentModel.IsFindCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
// 判断是否选择了循环按钮
if (indexFind == -1)
{
if (this.DocumentModel.IsFindCirculated)
{
this.DocumentModel.TextBox.ScrollToEnd(); // 显示文本框的最下面
indexFind = this.DocumentModel.TextBox.Text.LastIndexOf(
this.DocumentModel.FindText,
this.DocumentModel.TextBox.Text.Length, // 文本框的最后面开始检索
this.DocumentModel.IsFindCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase
);
}
}
}
// 向下查找
else
{
// 加上本身的长度,不然向下查找到的就是本身
indexStart = this.DocumentModel.TextBox.SelectionStart + this.DocumentModel.FindText.Length;
indexFind = this.DocumentModel.TextBox.Text.IndexOf(
this.DocumentModel.FindText,
indexStart,
this.DocumentModel.IsFindCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
// 判断是否选择了循环按钮
if (indexFind == -1)
{
if (this.DocumentModel.IsFindCirculated)
{
this.DocumentModel.TextBox.ScrollToHome(); // 显示文本框的最下面
indexFind = this.DocumentModel.TextBox.Text.IndexOf(
this.DocumentModel.FindText,
0, // 文本框的最开始开始检索
this.DocumentModel.IsFindCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase
);
}
}
}
if (indexFind == -1) // 没有找到显示对话框
{
MessageBox.Show("找不到“" + this.DocumentModel.FindText + "”", "记事本", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
this.DocumentModel.TextBox.Select(indexFind, this.DocumentModel.FindText.Length);
this.DocumentModel.TextBox.Focus(); // 聚焦到TextBox,作出相应的显示效果
}
.
.
8)在数据层中新建一个类,用于序列化与反序列化(其中用于保存一些需要的字段):
using System;
using System.Runtime.Serialization;
namespace Notepad_WPF.Models
{
[Serializable]
public class SavedSettings : ISerializable
{
public String FindText;
public Boolean IsFindCaseSensitive;
public Boolean IsFindCirculated;
public Boolean IsFindUp;
public String ReplacedText;
public SavedSettings()
{
}
// 当类被序列化时,做出的一些操作
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("FindText", FindText, typeof(String));
info.AddValue("IsFindCaseSensitive", IsFindCaseSensitive, typeof(Boolean));
info.AddValue("IsFindCirculated", IsFindCirculated, typeof(Boolean));
info.AddValue("IsFindUp", IsFindUp, typeof(Boolean));
info.AddValue("ReplacedText", ReplacedText, typeof(String));
}
// 反序列时,从配置文件中加载数据,返回 SavedSettings对象
public SavedSettings(SerializationInfo info, StreamingContext context)
{
FindText = info.GetString("FindText");
IsFindCaseSensitive = info.GetBoolean("IsFindCaseSensitive");
IsFindCirculated = info.GetBoolean("IsFindCirculated");
IsFindUp = info.GetBoolean("IsFindUp");
ReplacedText = info.GetString("ReplacedText");
}
}
}
.
.
9)在数据层DocumentModel类中定义两个方法,用于保存和加载配置文件中的数据:
保存数据到配置文件中(序列化):
从配置文件中加载数据到DocumentModel类的实例中(反序列化):
当DocumentModel类被实例时,加载数据:
当查找窗口被打开时,保存数据(临时数据):
当按下取消按钮时,数据还原到窗口没打开时的数据状态:
当按下查找按钮时,保存最新的数据:
.
.