使用winform编写商场收银系统
C#键控集合(键值)和IDictionary与策略模式结合——商场收银系统_dlwlrma_516的博客-CSDN博客https://blog.csdn.net/dlwlrma_516/article/details/127143041?spm=1001.2014.3001.5501WPF的基础知识看这里
C#入门级-使用VS2022编写C#(包括WPF,XAML基础、创建新项目)_dlwlrma_516的博客-CSDN博客https://blog.csdn.net/dlwlrma_516/article/details/127159830?spm=1001.2014.3001.5501
主要是以学习为主,以前用Winform写过简单商场收银系统,现在转换到使用WPF来实现
目录
第一部分、WPF基础知识
对象属性赋值
①使用字符串进行简单赋值
②使用属性元素(Property Element)进行复杂赋值
标记扩展
x名称空间介绍
x:Name的含义
x:FieldModifier
x:Key
第二部分、商场收银系统
第一步、创建新项目
第二步、新建组件、创建类库——模块化
使用Canvas控件
第三步、主界面引用
XAML是一种声明性语言,XAML编译器会为每个标签创建一个与之对应的对象。创建对象后,需对其属性初始化。因为XAML语言不能编写程序的运行逻辑,所以一份XAML文档中除了使用标签声明对象就是初始化对象的属性。
XAML中为对象属性赋值共两种语法(例子为Rectangle类对象的Fill属性):
Attribute1=Value1
更复杂的字符串实例
由于使用Attribute1=Value1存在缺点:Value只能是一个字符串。如果一个类只能使用XAML语言进行声明,并允许其Property(面向对象理论范畴,对客观实物进行抽象出来的属性)与XAML标签的Attribute(编程语言文法层面,用于区分两个同类语法元素A和B的特征)互相映射,那就需要为这些Property进行转换。
定义TypeConverter类的派生类,重写TypeConverter的方法
使用TypeConverter类将XAML标签的Attribute与对象的Property进行映射
.cs文件中的代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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;
namespace ShopWpf
{
///
/// MainWindow.xaml 的交互逻辑
///
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
//先把Window资源检索出来,窗体自己的方法FindResource,把字典给方法,返回类型为object,要转换为Staff
Staff staff = this.FindResource("staff") as Staff;
if (staff != null)
{
MessageBox.Show("当前员工姓名:"+ staff.Name + "\n" +
"员工号码:" + staff.Telephone + "\n" +
"员工家庭成员:" + staff.Familymember.Name); //staff的Name属性本身就是字符串类型
}
}
}
//NameToStaffTypeConverter类以特性形式附加到Staff类上去
[TypeConverterAttribute(typeof(NameToStaffTypeConverter))]
public class Staff
{
public string Name { get; set; }
public string Telephone { get; set; }
public Staff Familymember { get; set; } //职员的家庭成员也有名字和电话号码
}
public class NameToStaffTypeConverter : TypeConverter //定义TypeConverter的派生类
{ //给出staff中的Familymember一个字符串就可以基于这个字符串就可以声明一个Staff的对象并且把这个字符串赋值给Name属性
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) //重写方法
{
string name = value.ToString();
Staff familymember = new Staff();
familymember.Name = name;
return familymember;
}
}
对应的.xaml文件
在XAML中,非空标签均具有内容(Content),标签内容指在开始标签和结束标签之间的一些子级标签,每个子级都是父级标签内容的一个元素(Element),简称为父级标签的一个元素,属性标签指的是某个标签的一个元素对应这个标签的一个属性,即以元素的形式来表达一个实例的属性。
以上一个例子改写
通过使用属性元素来进行赋值,可以设计一个渐变色的Canvas
也可以设计一个渐变的Rectangle
标记扩展,一种特殊的Attribute=Value语法,特殊在于Value字符串是由一堆花括号及其括起来的内容组成,XAML编译器会对这样的内容做出解析、生成相应的对象。
XAML中为对象属性赋值的语法中,大多数赋值时为属性生成一个新对象,但有时候需把同一个对象赋值给两个对象的属性,有时候需给对象的属性赋一个null值,WPF还允许一个对象的属性值依赖在其他对象的某个属性上,当需给对象的属性进行特殊类型赋值时就要使用标记扩展
Text=“{}”这句就是标记扩展了:
①当编译器看到这句代码时就会把花括号中的内容解析为相应的对象
②对象的数据类型名时紧邻做花括号的字符串
③对象的属性由一串以逗号连接的子字符串负责初始化(注意,属性值不再加引号)
两种方法去添加TextBlock的Text属性值
友谊商店欢迎您的到来
XAML是一种声明式语言,XAML的标签声明的式对象,一个XAML标签会对应着一个对象,一般是一个控件的实例。
XAML这种对象声明语言只负责声明对象而不负责为这些对象声明引用变量。如果需为对象准备一个引用变量以便在C#代码中直接访问就必须显示地告诉XAML编译器——为这个对象声明引用变量,使用x:Name
作用:1、告诉XAML编译器,当一个标签带有x:Name时除了为这个标签生成对应实例外,还要为实例声明一个引用变量,变量名就是x:Name的值
2、将XAML标签所对应对象的Name属性(如果有)也设为x:Name的值,并把这个值注册到UI树上,以方便查找
在XAML代码中应该使用Name还是x:Name?
Name属性定义在FrameworkElement类(WPF控件的基类),所以所有WPF控件都具有Name这个属性;当一个元素具有Name属性时,使用Name或x:Name效果一样:
XAML编译器的动作都是声明名为btn的Button类型变量并引用一个Button类型实例,而且此实例的Name属性亦为btn;此时Name和x:Name是可以互换的,但不能出现在一个元素中
对于没有Name属性的元素,为了在XAML声明时也创建引用变量以便在C#代码中访问,只能使用x:Name。
由于x:Name的功能涵盖了Name属性的功能,所以全部使用x:Name增强代码的同一性和可读性。
使用x:Name后,XAML标签对应的实例就具有了自己的引用变量,而且这些引用变量都是类的字段。默认情况下,这些字段的访问级别按照面向对象的封装原则被设置成了internal,在编程时,又是需要从一个程序集访问另一个程序集中窗体的元素,这时需要把访问控件的引用变量改为public级别,x:FieldModifier就是用来在XAML里改变引用变量访问级别的。
x:FieldModifier是用来改变引用变量访问级别的,所以使用x:FieldModifier的前提是这个标签同时也使用x:Name
使用实例:
在XAML文件中,通常把需多次使用的内容提取出来放资源字典(Resource Dictionary)中,需要使用这个资源时,就用它的Key把它检索出来:x:Key的作用就是为资源贴上用于检索的索引
在WPF中,几乎每个元素都有自己的Resources属性,这个属性是个“Key-Value”式的集合,只要把元素放进这个集合,这个元素就成为资源字典中的一个条目,为了能够检索到这个条件,需添加x:Key
示例:先在Window的资源字典中添加一个条目,这个条目是一个字符串,在后面将在XAML和C#中多次使用这个字符串
为了XAML中使用String类,使用xmlns:sys="clr-namespace:System;assembly=mscorlib"引用mscorlib.dll,并把其中的System命名空间映射为XAML中的sys命名空间
使用属性标签语法向Window.Resources里添加一个字符串,把它的x:Key设置为stringHello,窗体的Grid里包含了三个Textbox和一个Button,第一个Textbox设置了Text属性时,使用到了stringHello这个资源
友谊商店欢迎您的到来
资源不但可以在XAML中访问,在C#中也可以访问,下面是Button.Click的事件处理器:
private void Button_Click(object sender, RoutedEventArgs e)
{
string str=this.FindResource("stringHello") as string;
this.textBox1.Text=str;
}
调用一个拥有Resources属性的对象的FindResource方法就可以在它的资源字典中检索资源,检索到资源后再把它回复成正确的数据类型,点击按钮后,可见第二个文本框显示出同样的字符串:
先删除自带的UserControl1.xaml,再新建一个
Canvas控件可以自由对控件的位置进行安排,对Canvas的子元素应用HorizontalAlignment和VerticalAlignment属性不能改变这些元素的位置;可使用Margin属性来定位元素,但也可使用Canvas类公开的Canvas.Left、Canvas.Top、Canvas.Right和Canvas.Bottom附加属性(Top和Left属性的优先级高于Bottom和Right属性,例如,如果同时指定Top和Bottom属性,Bottom属性会被忽略掉)
UI完成后:使用的控件:Rectangle、Label、Button、TextBox、ListBox、ComboBox
MoneyCalculator.xaml代码如下:
MoneyCalculator.xaml.cs页面代码如下:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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;
namespace ControlLibrary
{
///
/// MoneyCalculator.xaml 的交互逻辑
///
public partial class MoneyCalculator : UserControl
{
public MoneyCalculator()
{
InitializeComponent();
//在ComboBox中加下拉选项
Choose.Items.Add("正常收费");
Choose.Items.Add("满300返100");
Choose.Items.Add("打8折");
Choose.SelectedIndex = 0;
}
///
/// Context上下文,用一个ConcreteStrategy来配置,维护一个对Strategy对象的引用
///
class CashContext
{
//声明一个CashSuper对象(父类)
private CashSuper cs;
//通过构造方法,传入具体的收费策略(子类)
public CashContext(CashSuper csuper)
{
//初始化时,传入具体的策略对象
this.cs = csuper;
}
//上下文接口
//根据收费策略的不同,获得计算结果
//输入参数为单种产品的总原价(单价乘以个数)
public double GetResult(double money)
{
//根据具体的策略对象,调用其算法的方法
return cs.acceptCash(money);
}
}
///
/// (父类)现金收取超类的抽象方法,收取现金,参数为原价,返回为当前价
/// 策略类Strategy,定义所有支持的算法的公共接口
///
public abstract class CashSuper
{
public abstract double acceptCash(double money);
}
///
/// 集合实现IDictionary接口(基类DictionaryBase)
/// 通过键访问索引符
///
public class CashSupers : DictionaryBase
{
public void Add(string newID, CashSuper newCashSuper)
{
Dictionary.Add(newID, newCashSuper);
}
public void Remove(string cashSuperID)
{
Dictionary.Remove(cashSuperID);
}
public CashSupers()
{
}
public CashSuper this[string cashSuperID]
{
get
{
return (CashSuper)Dictionary[cashSuperID];
}
set
{
Dictionary[cashSuperID] = value;
}
}
}
///
/// 正常收费子类
/// 具体策略类,封装了具体的算法或行为,继承于Strategy
///
class CashNormal : CashSuper
{
///
/// 正常收费,原价返回
///
///
///
public override double acceptCash(double money)
{
return money;
}
}
///
/// 打折收费子类
/// 打折收费,初始化时,必需要输入折扣率,如打八折,就是0.8
/// 具体策略类,封装了具体的算法或行为,继承于Strategy
///
class CashRebate : CashSuper
{
private double moneyRebate = 1d;
///
/// 构造函数
///
///
public CashRebate(string moneyRebate)
{
this.moneyRebate = double.Parse(moneyRebate);
}
public override double acceptCash(double money)
{
return money * moneyRebate;
}
}
///
/// 返利收费子类
/// 返利收费,初始化时必须要输入返利条件和返利值,比如满300反100,则moneyCondition为300,moneyReturn为100
/// 具体策略类,封装了具体的算法或行为,继承于Strategy
///
class CashReturn : CashSuper
{
private double moneyCondition = 0.0d;
private double moneyReturn = 0.0d;
///
/// 构造函数
///
///
///
public CashReturn(string moneyCondition, string moneyReturn)
{
this.moneyCondition = double.Parse(moneyCondition);
this.moneyReturn = double.Parse(moneyReturn);
}
public override double acceptCash(double money)
{
double result = money;
//若大于返利条件,则需要减去返利值
if (money >= moneyCondition)
{
result = money - Math.Floor(money / moneyCondition) * moneyReturn; //看符合多少次返利
}
return result;
}
}
///
/// 声明一个double变量total来计算总计
///
double total = 0.0d;
private void Set_Click(object sender, RoutedEventArgs e)
{
CashContext context = null;
CashSupers cashSuperList = new CashSupers();
cashSuperList.Add("正常收费", new CashNormal());
cashSuperList.Add("满300返100", new CashReturn("300", "100"));
cashSuperList.Add("打8折", new CashRebate("0.8"));
//利用策略模式根据下拉选择框,生成相应的对象,实例化策略类、某个子类
//实例化不同的策略,最终在调用context.GetResult();时,所获得的结果就不尽相同
context = new CashContext(cashSuperList[Choose.SelectedItem.ToString()]);
double totalPrices = 0d;
if (Price.Text == "" || Convert.ToDouble(Price.Text) == 0)
{
MessageBox.Show("请输入货物的单价");
}
else if (Number.Text == "" || Convert.ToDouble(Number.Text) == 0)
{
MessageBox.Show("请输入货物的数量");
}
else if (context != null)
{
totalPrices = context.GetResult(Convert.ToDouble(Price.Text) * Convert.ToDouble(Number.Text));
//将每个商品合计计入总计
total = total + totalPrices;
//在列表框中显示信息
//通过多台可以得到收取费用的结果
Detail.Items.Add("单价:" + Price.Text + "数量:" + Number.Text + "合计:" + totalPrices.ToString());
Total.Text = total.ToString(); //也可以写为 this.Total.Text = total.ToString();
}
}
private void Reset_Click(object sender, RoutedEventArgs e)
{
//清空单价和数量
Price.Text = "";
Number.Text = "";
//清空优惠选项
Choose.SelectedIndex = 0;
//清空明细列表
Detail.Items.Clear();
//清空总计数据
total = 0;
Total.Text = "";
}
}
}
上面代码创建的类库的命名空间和类名如下:
需要这界面所在工程引用类库
添加完成
在MainWindow.xaml中添加类库的命名空间
MainWindow.xaml代码如下:
友谊商店欢迎您的到来
MainWindow.xaml.cs页面代码如下:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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;
namespace ShopWpf
{
///
/// MainWindow.xaml 的交互逻辑
///
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
//先把Window资源检索出来,窗体自己的方法FindResource,把字典给方法,返回类型为object,要转换为Staff
Staff staff = this.FindResource("staff") as Staff;
if (staff != null)
{
MessageBox.Show("当前员工姓名:"+ staff.Name + "\n" +
"员工号码:" + staff.Telephone + "\n" +
"员工家庭成员:" + staff.Familymember.Name); //staff的Name属性本身就是字符串类型
}
}
}
//NameToStaffTypeConverter类以特性形式附加到Staff类上去
[TypeConverterAttribute(typeof(NameToStaffTypeConverter))]
public class Staff
{
public string Name { get; set; }
public string Telephone { get; set; }
public Staff Familymember { get; set; } //职员的家庭成员也有名字和电话号码
}
public class NameToStaffTypeConverter : TypeConverter //定义TypeConverter的派生类
{ //给出staff中的Familymember一个字符串就可以基于这个字符串就可以声明一个Staff的对象并且把这个字符串赋值给Name属性
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) //重写方法
{
string name = value.ToString();
Staff familymember = new Staff();
familymember.Name = name;
return familymember;
}
}
}
生成的UI: