目录
介绍
背景
使用代码
MVVM结构/功能
这是内容的快速概述
安装MVVM工具包
MainWindow概念和代码
应用程序
Mod_Public
MVVM模式——详细信息
把事情放在一起——WPF概念和代码
QAT(快速访问工具栏)
依赖注入或服务注入
使用ISaveAsFileDlgVM保存文件对话框示例
Messenger 测试
PropertyChanged测试
事件触发器(EventTrigger)
可观察集合(ObservableCollection)
使用ObservableCollection进行测试
富文本(RichText)
结论
Credits / Reference
- 下载源代码 - 7.1 MB
本文和演示是关于开始使用MVVM工具包和一些为MessageBox和一些对话框自创建的接口/服务。
背景有许多关于其他MVVM框架的CodeProject文章,但几乎没有关于WPF和MVVM Toolkit的文章。所以我开始创建这个文档。
模型、视图和视图模型(MVVM模式)是组织或构建代码的 好方法,可帮助您简化、开发和测试(例如单元测试)应用程序。
模型保存数据, 与应用程序逻辑无关。
ViewModel 充当模型和视图之间的连接。
视图是用户界面。
我不会描述和解释完整演示项目的每一个细节。重点是如何测试一些特性。
使用代码 MVVM结构/功能MVVM工具包来自Microsoft,并且其他一些使用的功能不是我自己的:来源在Credits/Reference部分中列出。
这是内容的快速概述- MVVM工具包和.NET 4.7.2
- RelayCommand
- OnPropertyChanged
- ObservableRecipient(Messenger和ViewModelBase)
- DependencyInjection(作为服务运行MsgBox和Dialog )
- ObservableCollection(对于Credits Listbox)
- Ribbon 菜单
- 服务/对话框
- 使用ICommand将按钮绑定到ViewModel
随着MVVM Toolkit的NuGet包的安装,它会安装6或7个其他包。
- MVVM工具包简介——Windows社区工具MVVM工具包简介——Windows社区工具
对于DependencyInjection,我们需要安装另一个NuGet包:
我为MsgBox和一些对话框做了接口/服务。
DependencyInjection设法独立于viewmodel启动以下对话框:
- FontDlgVM
- MsgBoxService
- DialogVM
- OpenFileDlgVM
- RibbonStatusService
- SaveAsFileDlgVM
- SearchDlgVM
这允许使用自定义Messagebox/对话框以及单元测试。
MainWindow概念和代码MainWindow显示Ribbon。
在其下方有一个Tabcontrol, 带有用于MVVM工具包测试的选项卡和RichText。
类MainWindow背后的代码是:
{
///
/// Interaktionslogik für MainWindow.xaml
///
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new TestingViewModel();
}
}
}
注册服务/视图模型在后面的App代码中。
public partial class App : Application
{
private bool blnReady;
public App()
{
InitializeComponent();
Exit += (_, __) => OnClosing();
Startup += Application_Startup;
try
{
Mod_Public.sAppPath = Directory.GetCurrentDirectory();
Ioc.Default.ConfigureServices(
new ServiceCollection()
.AddSingleton()
.AddSingleton((IDialog)new DialogVM())
.AddSingleton((IOpenFileDlgVM)new OpenFileDlgVM())
.AddSingleton((ISaveAsFileDlgVM)new SaveAsFileDlgVM())
.AddSingleton((IRichTextDlgVM)new RichTextDlgVM())
.BuildServiceProvider());
}
catch (Exception ex)
{
File.AppendAllText(Mod_Public.sAppPath + @"\Log.txt",
string.Format("{0}{1}", Environment.NewLine,
DateAndTime.Now.ToString() + "; " + ex.ToString()));
var msgBoxService = Ioc.Default.GetService();
msgBoxService.Show("Unexpected error:" + Constants.vbNewLine +
Constants.vbNewLine + ex.ToString(), img: MessageBoxImage.Error);
}
}
private void OnClosing()
{
}
private void Application_Startup(object sender, EventArgs e)
{
blnReady = true;
}
}
Mod_Public包括:
public static void ErrHandler(string sErr)
和:
public static string ReadTextLines(string FileName)
从WPF Ribbon的角度来看,数据结构/模型很简单:
Ribbon有菜单项/ribbon按钮与ActiveRichTextBox或ActiveTextBox一起工作。
这就是我们在模型类中看到的TextData。
模型类称为TextData:
{
public class TextData : ObservableRecipient, INotifyPropertyChanged
{
private string _text;
private string _richText;
private RibbonTextBox _NotifyTest;
private string _readText;
private TextBox _ActiveTextBox;
private RichTextBox __ActiveRichTextBox;
private RichTextBox _ActiveRichTextBox
{
[MethodImpl(MethodImplOptions.Synchronized)]
get
{
return __ActiveRichTextBox;
}
[MethodImpl(MethodImplOptions.Synchronized)]
set
{
__ActiveRichTextBox = value;
}
}
private Ribbon _MyRibbonWPF;
private MainWindow _MyMainWindow;
public TextData()
{
//
}
public MainWindow MyMainWindow
{
get
{
return _MyMainWindow;
}
set
{
_MyMainWindow = (MainWindow)value;
}
}
public Ribbon MyRibbonWPF
{
get
{
return _MyRibbonWPF;
}
set
{
_MyRibbonWPF = value;
}
}
public RibbonTextBox NotifyTestbox
{
get
{
return _NotifyTest;
}
set
{
_NotifyTest = value;
}
}
public string RichText
{
get
{
return _richText;
}
set
{
_richText = value;
OnPropertyChanged("RichText");
}
}
public string GetText
{
get
{
return _text;
}
set
{
_text = value;
OnPropertyChanged("GetText");
}
}
public string ReadText
{
get
{
return _readText;
}
set
{
_readText = value;
GetText = _readText;
OnPropertyChanged("ReadText");
}
}
public TextBox ActiveTextBox
{
get
{
return _ActiveTextBox;
}
set
{
_ActiveTextBox = value;
OnPropertyChanged("ActiveTextBox");
}
}
public RichTextBox ActiveRichTextBox
{
get
{
return _ActiveRichTextBox;
}
set
{
_ActiveRichTextBox = value;
}
}
}
}
ViewModel class叫TestingViewModel
名为TestingViewModel的类包含用于测试某些MVVM功能的属性ICommands和方法。它还包含ObservableCollection(Of Credits)的代码,该代码用于Listview本文的References / Credits部分。
把事情放在一起——WPF概念和代码 QAT(快速访问工具栏)您可以从QAT中删除按钮(右键单击,会出现一个上下文菜单)。您可以在Ribbon下面显示QAT。您也可以从设置选项卡中恢复QAT。并且您可以更改Ribbon的backcolor。
依赖注入或服务注入正如已经提到的,在应用程序的代码后面有一些代码。
使用ISaveAsFileDlgVM保存文件对话框示例它使用interface ISaveAsFileDlgVM和service/ viewmodel SaveAsFileDlgVM。
public class TestingViewModel : ObservableRecipient, INotifyPropertyChanged
...
public ICommand SaveAsFileDlgCommand { get; set; }
...
RelayCommand cmdSAFD = new RelayCommand(SaveAsFileDialog);
SaveAsFileDlgCommand = cmdSAFD;
...
private void SaveAsFileDialog()
{
var dialog = Ioc.Default.GetService();
if (ActiveRichTextBox is object)
{
dialog.SaveAsFileDlg(_textData.RichText, ActiveRichTextBox);
}
if (ActiveTextBox is object)
{
dialog.SaveAsFileDlg(_textData.GetText, ActiveTextBox);
}
}
...
而且,非常重要的是,Command="{Binding SaveAsFileDlgCommand}"/>在XAML文件中。
从Ribbon中,您可以启动和测试其他对话框或messagebox,以便:
- 打开对话框
- 搜索(来源在Credits/References中列出)
- OpenFileDialog
- 选项卡帮助 > 信息
- FontDialog
添加Inherits ObservableRecipient很重要,这个和其他细节在ObservableObject - Windows Community Toolkit | 微软文档中有描述。
“视图特定消息应该在视图的Loaded事件中注册,并在Unloaded事件中注销,以防止内存泄漏和多次回调注册的问题。”
我们可以从Class TestingViewModel发送Msg:
Imports Microsoft.Toolkit.Mvvm.Messaging
public class TestingViewModel : ObservableRecipient, INotifyPropertyChanged
private string msg;
…
_cmdMsg = new Command(SendMsgRibbonButton_Click);
…
public ICommand SendMsg
{
get
{
return _cmdMsg;
}
}
…
private void SendMsgRibbonButton_Click()
{
try
{
// DataExchange / Messenger
string msg = "Test Msg...";
SetStatus("TestingViewModel", msg);
}
catch (Exception ex)
{
SetStatus("TestingViewModel", ex.ToString());
Mod_Public.ErrHandler(ex.ToString());
}
}
...
public void SetStatus(string r, string m)
{
try
{
Messenger.Send(new DialogMessage(m));
}
catch (Exception ex)
{
SetStatus("TestingViewModel", ex.ToString());
Mod_Public.ErrHandler(ex.ToString());
}
}
...
public class StatusMessage
{
public StatusMessage(string status)
{
NewStatus = status;
}
public string NewStatus { get; set; }
}
仅当消息已注册时Msg才可以发送:
using Microsoft.Toolkit.Mvvm.Messaging;
...
Messenger.Register(this, (r, m) => DialogMessage = m.NewStatus);
Messenger.Register(this, (r, m) => StatusBarMessage = m.NewStatus);
...
Messenger.Unregister(this);
Messenger.Unregister(this);
在关闭viewmodel时,我们必须注销该消息。
消息出现在StatusBar和Ribbon上。
PropertyChanged测试
两个textbox通常仅在activeTextbox与"RichText"或"PlainText"相关时显示。但是如果你手动编辑上面的,你可以看到下面的内容立即改变了。
这是由XAML文件中的UpdateSourceTrigger=PropertyChanged引起的。
事件触发器(EventTrigger)要求:Microsoft.Xaml.Behaviors.Wpf(NuGet包)
xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
...
...
当Ribbon通过ContextMenu和其他东西最小化时使用它。
可观察集合(ObservableCollection)它是viewmodel TestingViewModel的一部分,并用于Credits / References的listbox。
public class TestingViewModel : ObservableRecipient, INotifyPropertyChanged
#Region " fields"
...
private ObservableCollection _credit = new ObservableCollection();
...
#End Region
...
_credit = new ObservableCollection()
{
new Credits()
{
Item = "MVVM Toolkit",
Note = "Microsoft",
Link = "https://docs.microsoft.com/en-us/windows/
communitytoolkit/mvvm/introduction"
},
new Credits()
{
Item = "MVVMLight",
Note = "GalaSoft",
Link = "https://www.codeproject.com/Articles/768427/
The-big-MVVM-Template"
},
new Credits()
{
Item = "ICommand with MVVM pattern",
Note = "CPOL",
Link = "https://www.codeproject.com/Articles/863671/
Using-ICommand-with-MVVM-pattern"
},
new Credits()
{
Item = "C# WPF WYSIWYG HTML Editor - CodeProject",
Note = "CPOL",
Link = "https://www.codeproject.com/Tips/870549/
Csharp-WPF-WYSIWYG-HTML-Editor"
},
new Credits()
{
Item = "SearchDialog",
Note = "Forum Msg",
Link = "https://social.msdn.microsoft.com/forums/vstudio/en-US/
fc46affc-9dc9-4a8f-b845-89a024b263bc/
how-to-find-and-replace-words-in-wpf-richtextbox"
}
};
...
public class Credits
{
public string Item { get; set; }
public string Note { get; set; }
public string Link { get; set; }
}
单击清除列表框以删除学分。
读取XML到Listbox以恢复引用。
ObservableCollection的优点是我们不需要Listbox的UpdateTrigger。
富文本(RichText)从选项卡“RichText”中,您可以选择RichTextBox中的一些文本并使用RibbonButton对其进行格式化。其中许多是EditingCommand并且仅出现在UserCtlRibbonWPF.xaml文件中。
结论这只是一个演示——它还没有准备好生产。
但我认为MVVM Toolkit将允许您进行各种扩展。
Credits / Reference- [1] Using ICommand with MVVM pattern - CodeProject
- [2] Introduction to the MVVM Toolkit - Windows Community Toolkit | Microsoft Docs
- [3] C# WPF WYSIWYG HTML Editor - CodeProject
- [4] The big MVVM Template - CodeProject
- [5] How to find and replace words in WPF richTextBox
https://www.codeproject.com/Articles/5332442/Csharp-MVVM-Toolkit-Demo