WPF框架教程 | 从0到1:使用Caliburn.Micro(WPF和MVVM)开发简单的计算器

640?wx_fmt=gif

之前时间一直在使用Caliburn.Micro这种应用了MVVM模式的WPF框架做开发,是时候总结一下了。

Caliburn.Micro(https://blog.csdn.net/lzuacm/article/details/78886436)是一个轻量级的WPF框架,简化了WPF中的不少用法,推荐做WPF开发时优先使用。

真正快速而熟练地掌握一门技术就可以尝试着用最快的速度去构建一个玩具项目(Toy project),然后不断地优化、重构之。比如本文将介绍如何使用Caliburn.Micro v3.2开发出一个简单的计算器,里面用到了C#中的async异步技术,Caliburn.Micro中的Conductor等等~


>>>1.在VS中创建WPF项目<<<

640?

640?wx_fmt=png


>>>2.使用NuGet包管理工具为当前项目安装Caliburn.Micro <<<

640?

对于Caliburn.Micro 1.x和2.x版,只能使用.dll,需手动给项目加Reference。而3.0以后的版本可使用NuGet包管理工具来管理,安装和卸载既方便又彻底,推荐使用。(ps: NuGet之于Visual Studio(C++, C#等), 犹pip之于Python, npm之于node, maven之于Java, gem之于Ruby等等)

640?wx_fmt=png


>>>3.框架搭建  <  <  <

640?
  1. 删除项目根目录下的MainWindow.xaml

  2. 按下图调整 App.xaml
    删除语句

    StartupUri="MainWindow.xmal"。

    640?wx_fmt=png

  3. 填充Application.Resources

    <Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary>
<local:Bootstrapper x:Key="bootstrapper"/>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>

   4 . 创建Bootstrapper类
然后让其继承自BootstrapperBase类,并加上构造函数,另外再重写函数OnStartup即可。


using System.Windows;
using Caliburn.Micro;
using CaliburnMicro_Calculator.ViewModels;

namespace CaliburnMicro_Calculator
{
public class Bootstrapper : BootstrapperBase
{
public Bootstrapper()
{
Initialize();
}

protected override void OnStartup(object obj, StartupEventArgs e)
{
DisplayRootViewFor<ShellViewModel>();
}
}
}

   5 . 在项目目录下新建Models, ViewModels, Views这3个文件夹
在ViewModel文件夹中添加ShellViewModel.cs,并创建Left, Right和Result这3个属性。

需要注意的是 ShellViewModel.cs需要继承类 

Screen 和 INotifyPropertyChanged(用于感知并同步所绑定属性的变化),ShellViewModel具体代码为:


using System.ComponentModel;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using Caliburn.Micro;

namespace CaliburnMicro_Calculator.ViewModels
{
public class ShellViewModel : Screen, INotifyPropertyChanged
{
private double _left;
private double _right;
private double _result;

public double Left
{
get { return _left; }
set
{
_left = value;
NotifyOfPropertyChange();
}
}

public double Right
{
get { return _right; }
set
{
_right = value;
NotifyOfPropertyChange();
}
}

public double Result
{
get { return _result; }
set
{
_result = value;
NotifyOfPropertyChange();
}
}
}

说明: 最开始布局xaml时,设计位置时采用的是左(operand 1), 中(operand 2), 右(result),于是属性值使用了Left, Right和Result。


>>>4.设计XAML并绑定属性 <   <  <

640?

在Views文件夹中创建Window,命名为ShellView.xaml,在Views文件夹下创建子文件夹Images,用于存放+,-,*,/这4种操作对应的小图标,其具体代码如下:

<Window x:Class="CaliburnMicro_Calculator.Views.ShellView"
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:CaliburnMicro_Calculator.Views"
xmlns:cal="http://www.caliburnproject.org"
mc:Ignorable="d"
Title="Calculator" SizeToContent="Height" Width="240">


<StackPanel Background="Beige">
<StackPanel Orientation="Horizontal">
<Label Margin="10"
Target="{Binding ElementName=left}">

Operand _1:
</Label>
<TextBox Margin="10"
Width="72"
x:Name="left"/>

</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Margin="10"
Target="{Binding ElementName=right}">

Operand _2:
</Label>
<TextBox Margin="10"
Width="72"
x:Name="right"/>

</StackPanel>
<StackPanel Orientation="Horizontal">
<Button Margin="10"
x:Name="btnPlus"
cal:Message.Attach="[Event Click]=[Action Plus(left.Text, right.Text):result.Text]">

<Image Source="Images/op1.ICO"/>
</Button>

<Button Margin="10"
x:Name="btnMinus"
cal:Message.Attach="[Event Click]=[Action Minus(left.Text, right.Text):result.Text]">

<Image Source="Images/op2.ICO"/>
</Button>

<Button Margin="10"
x:Name="btnMultiply"
cal:Message.Attach="[Event Click]=[Action Multipy(left.Text, right.Text):result.Text]">

<Image Source="Images/op3.ICO"/>
</Button>

<Button Margin="10"
x:Name="btnDivide" IsEnabled="{Binding Path=CanDivide}"
cal:Message.Attach="[Event Click]=[Action Divide(left.Text, right.Text):result.Text]">

<Image Source="Images/op4.ICO"/>
</Button>

</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Margin="10">
Answer:
</Label>
<TextBox Margin="10"
Width="72"
Text ="{Binding Path=Result, StringFormat={}{0:F4}}" IsReadOnly="True" />

</StackPanel>
</StackPanel>
</Window>

说明:对操作数Operand _1和Operand _2,按Alt键+数字可以选中该处,这是WPF的一个特殊用法。由于计算结果不希望被修改,于是加上了属性IsReadOnly="True"


>>>5.设计并绑定事件  <  <  <

640?

由于暂时只打算实现+, -, *, /四种操作,于是我们只需创建相应的4个函数即可,由于除数是0这个操作不允许,于是需再加个判断函数CanDivide。

Caliburn.Micro中绑定事件的写法是:
cal:Message.Attach="[Event E]=[Action A]"

(E是操作,比如Click, MouseDown, KeyDown等等,A是ViewModel中具体的函数。)

向ShellViewModel中加入事件中要做的事,此时ShellViewModel为:

using System.ComponentModel;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using Caliburn.Micro;

namespace CaliburnMicro_Calculator.ViewModels
{
public class ShellViewModel : Screen, INotifyPropertyChanged
{
private double _left;
private double _right;
private double _result;

public double Left
{
get { return _left; }
set
{
_left = value;
NotifyOfPropertyChange();
}
}

public double Right
{
get { return _right; }
set
{
_right = value;
NotifyOfPropertyChange();
}
}

public double Result
{
get { return _result; }
set
{
_result = value;
NotifyOfPropertyChange();
}
}
public bool CanDivide(double left, double right)
{
return right != 0;
}

public async void Divide(double left, double right)
{
Thread.Sleep(600);
if (CanDivide(left, right) == true)
Result = left / right;
else MessageBox.Show("Divider cannot be zero.", "Warning", MessageBoxButton.OK, MessageBoxImage.Warning);
}

public async void Plus(double left, double right)
{
Result = left + right;
}

public async void Minus(double left, double right)
{
Result = left - right;
}

public async void Multipy(double left, double right)
{
Result = left * right;
}
}
}

此时计算器的功能已基本完成,但我们可以对ViewModel进行适当的调整:
1.创建新的ViewModel - CalculatorViewModel,将原来的ShellViewModel中具体的计算逻辑移入到CalculatorViewModel中;
2.此时让ShellViewModel继承Conductor<Object>,于是ShellViewModel拥有了管理Screen实例的功能(ViewModel中使用ActivateItem函数,而View中使用X:Name="ActivateItem"标签),其具体代码为:

using System.ComponentModel;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using Caliburn.Micro;

namespace CaliburnMicro_Calculator.ViewModels
{
public class ShellViewModel : Conductor<object>
{
public ShellViewModel()
{
}
public void ShowCalculator()
{
ActivateItem(new CalculatorViewModel());
}
}
}

此时,CalculatorViewModel的具体代码为:

using System.ComponentModel;
using System.Threading;
using System.Windows;
using Caliburn.Micro;

namespace CaliburnMicro_Calculator.ViewModels
{
public class CalculatorViewModel: Screen, INotifyPropertyChanged
{
private double _left;
private double _right;
private double _result;

public double Left
{
get { return _left; }
set
{
_left = value;
NotifyOfPropertyChange();
}
}

public double Right
{
get { return _right; }
set
{
_right = value;
NotifyOfPropertyChange();
}
}

public double Result
{
get { return _result; }
set
{
_result = value;
NotifyOfPropertyChange();
}
}

public CalculatorViewModel()
{
}

public bool CanDivide(double left, double right)
{
return right != 0;
}

public async void Divide(double left, double right)
{
Thread.Sleep(600);
if (CanDivide(left, right) == true)
Result = left / right;
else MessageBox.Show("Divider cannot be zero.", "Warning", MessageBoxButton.OK, MessageBoxImage.Warning);
}

public async void Plus(double left, double right)
{
Result = left + right;
}

public async void Minus(double left, double right)
{
Result = left - right;
}

public async void Multipy(double left, double right)
{
Result = left * right;
}
}
}

  3 . 对于View,只需把CalculatorViewModel对应的CalculatorView作为ContentControl控件嵌入ShellView即可。此时ShellView的代码调整为:

<Window x:Class="CaliburnMicro_Calculator.Views.ShellView"
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:CaliburnMicro_Calculator.Views"
xmlns:cal="http://www.caliburnproject.org"
mc:Ignorable="d"
Title="Calculator" SizeToContent="Height" Width="240">


<Grid MinHeight="200">
<Button Content="Show Calculator" x:Name="ShowCalculator" Grid.Row="0"></Button>
<ContentControl x:Name="ActiveItem"></ContentControl>
</Grid>
</Window>

另外提一点,向ViewModel A中嵌入ViewModel B,一般来说需要做的操作是:
在A的view中使用ContentControl,绑定B的ViewModel只需使用语句cal:View.Model="{Binding BViewModel}"即可,而B的view是UserControl就可以啦。

此时CalculatorView是一个UserControl,其代码为:

<UserControl x:Class="CaliburnMicro_Calculator.Views.CalculatorView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:CaliburnMicro_Calculator.Views"
xmlns:cal="http://www.caliburnproject.org"
mc:Ignorable="d"
Width="240">


<StackPanel Background="Beige">
<StackPanel Orientation="Horizontal">
<Label Margin="10"
Target="{Binding ElementName=left}">

Operand _1:
</Label>
<TextBox Margin="10"
Width="72"
x:Name="left"/>

</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Margin="10"
Target="{Binding ElementName=right}">

Operand _2:
</Label>
<TextBox Margin="10"
Width="72"
x:Name="right"/>

</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Button Margin="10"
x:Name="btnPlus"
cal:Message.Attach="[Event Click]=[Action Plus(left.Text, right.Text):result.Text]">

<Image Source="Images/op1.ICO"/>
</Button>

<Button Margin="10"
x:Name="btnMinus"
cal:Message.Attach="[Event Click]=[Action Minus(left.Text, right.Text):result.Text]">

<Image Source="Images/op2.ICO"/>
</Button>

<Button Margin="10"
x:Name="btnMultiply"
cal:Message.Attach="[Event Click]=[Action Multipy(left.Text, right.Text):result.Text]">

<Image Source="Images/op3.ICO"/>
</Button>

<Button Margin="10"
x:Name="btnDivide" IsEnabled="{Binding Path=CanDivide}"
cal:Message.Attach="[Event Click]=[Action Divide(left.Text, right.Text):result.Text]">

<Image Source="Images/op4.ICO"/>
</Button>

</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Margin="10">
Answer:
</Label>
<TextBox Margin="10"
Width="72"
Text ="{Binding Path=Result, StringFormat={}{0:F4}, UpdateSourceTrigger=PropertyChanged}" IsReadOnly="True" />

</StackPanel>
</StackPanel>
</UserControl>

好啦,就酱,由于本例中逻辑并不复杂,Model暂时用不上,对于复杂一点的项目,Model主要负责数据的读取,如文件操作、数据库操作、service调用等,以后有机会举例具体来说。

如果需要持久化(persistent),则还需给给每对M-VM(Model和ViewModel)加入State,这个实际工程中也用得特别多。


>>>6.功能举例  <  <  <

640?

Calculator主页:
640?wx_fmt=png

点击按钮“ShowCalculator”即可看到具体的计算器~

乘法举例:
640?wx_fmt=png

除法举例:
640?wx_fmt=png


最后附上代码:
CaliburnMicro-Calculator: A simple Calculator using Caliburn.Micro
https://github.com/yanglr/CaliburnMicro-Calculator
欢迎fork和star,如有改进意见欢迎提交pull request~



640?wx_fmt=gif


原文地址:

https://blog.csdn.net/lzuacm/article/details/80559517

更多精彩文章,欢迎访问本人博客https://enjoy233.cnblogs.com 或 知乎搜索Bravo Yeung.

欢迎转发到朋友圈,公众号转载请后台联系本人申请授权~



推荐阅读

中英文电子书下载网站大搜罗

英语语法工具 | 那些可以纠正英语文章中语法的神器们

开发者见闻 | ASP.NET Core开发者路线图



smiley_66.png点击在看的人,

2019都会变得特别好看

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/316025.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微软全都要!Win10引入真Linux内核

继将 Bash shell、原生 OpenSSH、WSL 引入 Windows&#xff0c;以及在微软商店提供 Ubuntu、SUSE Linux 和 Fedora 等发行版&#xff0c;正在举办的 Microsoft Build 2019 大会上&#xff0c;微软又宣布了一个重大的决定 —— 将完整的 Linux 内核引入 Windows 10。按照微软的说…

Kafka基本知识整理

首先Kafka是一个分布式消息队列中间件&#xff0c;Apache顶级项目&#xff0c;https://kafka.apache.org/ 高性能、持久化、多副本备份、横向扩展。生产者Producer往队列里发送消息&#xff0c;消费者Consumer从队列里消费消息&#xff0c;然后进行业务逻辑。应用场景主要有&…

牛客练习赛89——牛牛小数点(未解决)

牛牛小数点 题意&#xff1a; 题解&#xff1a; 本题先说结论&#xff1a; 对于一个数x2a∗5b∗px2^a*5^b*px2a∗5b∗p 如果p1,也就是质因子只有2和5&#xff0c;则x是不循环小数&#xff0c;即f(x)0如果p!1,则x是循环的&#xff0c;且循环开始于小数点后第1max{p2,p5p_{2},…

针对.NET Core, Xamarin以及.NET的自动类型安全Rest库: Refit

本文大部分内容是针对Refit官网的翻译。官网地址&#xff1a; https://github.com/reactiveui/refitRefit是一个类似于Retrofit的Restful Api库&#xff0c;使用它&#xff0c;你可以将你的Restful Api定义在接口中。例如&#xff1a;public interface IGitHubApi { [Get(&quo…

用ProGet搭建本地私有NuGet仓库

搭建ProGet下载官网下载Windows版本的Inedo Hub &#xff08;https://inedo.com/proget/download&#xff09;下载下来的软件名&#xff1a; ProGetInstaller.exe安装点击ProGetInstaller.exe&#xff0c;出现如下安装界面Registration 选项选择 Free ;SQL Sever 选项选择 Spec…

CQRS架构下Equinox开源项目分析

一.DDD分层架构介绍本篇分析CQRS架构下的Equinox开源项目。该项目在github上star占有2.4k。便决定分析Equinox项目来学习下CQRS架构。再讲CQRS架构时&#xff0c;先简述下DDD风格&#xff0c;在DDD分层架构中&#xff0c;一般包含表现层、应用程序层(应用服务层)、领域层(领域服…

仿B站(一) 目的分析以及创建 WebAPI + Angular7 项目

前言&#xff1a;本系列文章主要为对所学 Angular 框架的一次微小的实践&#xff0c;对 b站页面作简单的模仿。本系列文章主要参考资料&#xff1a;微软文档&#xff1a;    https://docs.microsoft.com/zh-cn/aspnet/core/getting-started/?viewaspnetcore-2.1&tabsw…

Mac中搭建Kubernetes

Kubernetes是Google和RadHat公司共同主导的开源容器编排项目&#xff0c;功能非常强大&#xff0c;也非常的火热和流行&#xff0c;但同时里面也有很多的概念和名词需要我们去学习和理解。学习任何一个技术先需要把基础环境搭建起来&#xff0c;本篇就介绍怎样在Mac中启动单节点…

树莓派也跑Docker和.NET Core

树莓派就是一个卡片大小的迷你电脑。有了电脑&#xff0c;我们当然得先安装系统。系统下载https://www.raspberrypi.org/downloads/raspbian/ &#xff0c;我选择的Raspbian Stretch Lite&#xff0c;不带界面的最小安装。下载win32diskimager&#xff08;烧录系统&#xff09;…

开源]OSharpNS 步步为营系列 - 1. 业务模块设计

OSharpNS全称OSharp Framework with .NetStandard2.0&#xff0c;是一个基于.NetStandard2.0开发的一个.NetCore快速开发框架。这个框架使用最新稳定版的.NetCore SDK&#xff08;当前是.NET Core 2.2&#xff09;&#xff0c;对 AspNetCore 的配置、依赖注入、日志、缓存、实体…

CF1479A Searching Local Minimum

CF1479A Searching Local Minimum 题意&#xff1a; 题解&#xff1a; 先说结论&#xff1a; 若l&#xff0c;r满足&#xff1a; al−1>al,ar<ar1a_{l-1}>a_{l},a_{r}<a_{r1}al−1​>al​,ar​<ar1​al,al1,....,ara_{l},a_{l1},....,a_{r}al​,al1​,....…

C#8.0的两个有趣的新特性以及gRPC

最近每天忙着跑很多地方&#xff0c;回家就不想动了&#xff0c;没什么心情写东西。今天有空&#xff0c;稍微写一点。下文中&#xff1a;关于C#语法特性的部分需要Visual Studio 2019支持。关于.NET Core的部分需要安装.NET 3.0 Preview4&#xff0c;低版本或许也可以但我没实…

CF1479C Continuous City

CF1479C Continuous City 题意&#xff1a; 给定 L, R. 构造一个有向带权图, 其中点数不大于 32, 且所有边都是从较小的点指向较大的点. 假设这个有向图有 n 个点, 你需要保证从 1到n 的所有路径的权值都在 [L, R]内且不存在 x∈[L,R], 使得不存在或存在多于一条从 1 到 n 的…

Office转PDF,Aspose太贵,怎么办?

在程序开发中经常需要将Office文件转换成PDF&#xff0c;著名的Aspose的三大组件可以很容易完成这个功能&#xff0c;但是Aspose的每个组件都单独收费&#xff0c;而且每个都卖的不便宜。在老大的提示下&#xff0c;换了一种思路来解决这个问题。环境dotNetCore:2.1CentOS:7.5D…

收起.NET程序的dll来

作为上床后需要下床检查好几次门关了没有的资深强迫症患者&#xff0c;有一个及其搞我的问题&#xff0c;就是dll问题。曾几何时&#xff0c;在没有nuget的年代&#xff0c;当有依赖项需要引用的时候&#xff0c;只能通过文件引用来管理引用问题&#xff0c;版本问题&#xff0…

从壹开始 [ Ids4实战 ] 之三║ 详解授权持久化 用户数据迁移

哈喽大家周三好&#xff0c;今天终于又重新开启 IdentityServer4 的落地教程了&#xff0c;不多说&#xff0c;既然开始了&#xff0c;就要努力做好?。书接上文&#xff0c;在很久之前的上篇文章《二║ 基础知识集合 & 项目搭建一》中&#xff0c;我们简单的说了说 Identi…

微软XAML Studio - WPF, UWP, Xamarin等技术开发者的福音

最近在继续倒腾WPF的项目&#xff0c;继续使用Caliburn.Micro和Xceed来堆代码。每次调试xaml上的binding&#xff0c;都有种要疯的赶脚。今天路过 https://channel9.msdn.com/ 浏览 WPF相关的学习视频时&#xff0c;遇到微软推荐的相关视频 - XAML sutdio简介https://channel9.…

AddMvc 和 AddMvcCore 的区别

目录本文出自《从零开始学 ASP.NET CORE MVC》目录 视频课程效果更佳&#xff1a;从零开始学 Asp.Net Core MVC ASP.NET Core 为什么有 AddMvc 和 AddMvcCore 他们是什么关系&#xff1f;在本视频中&#xff0c;我们将讨论 AddMvc()和 AddMvcCore()方法之间的区别。要在 ASP.NE…

浅谈容量规划

俗话说&#xff0c;”人无远虑&#xff0c;必有近忧”&#xff0c;容量规划就是”远虑”。所谓容量规划&#xff0c;是一个产品满足用户目标需求而决定生产能力的过程。当产品发展到一个较为稳定成熟的阶段&#xff0c;产品的整体处理能力的把控自然是不可或缺&#xff0c;尽管…

CF1063C Dwarves, Hats and Extrasensory Abilities

CF1063C Dwarves, Hats and Extrasensory Abilities 题意&#xff1a; 首先题目会给出 n &#xff0c;表示要输入多少点。 然后你输出n 个点的坐标&#xff0c;每输出一个点会告诉你这个点的颜色是黑色或者白色。 最后你需要输出两个点的坐标代表一条直线&#xff0c;这条直线…