【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
好多同学都认为上位机只是纯软件开发,不涉及到硬件设备,比如听听音乐、看看电影、写写小的应用等等。如果是消费电子,确实可能是这种情况。但是除了消费电子、互联网领域之外,上位机还可能涉及到工业生产、医疗和军工领域,每一个细分市场都有很大的规模。这个时候,上位机就可能需要通过USB、can、232、485、网络等很多cabel形式和外部设备进行沟通,那么上位机的功能边际就会一下子拓展很多。此外,就算是同一个领域,不同的行业也都会有不同的know-how,很多的know-how就是以软件的形式固化在软件逻辑里面的,这就是上位机真正的竞争力。
当然说了这么多,今天我们还是从简单的会员管理软件说起。通俗一点说,它就是简单的学生管理系统。所以的文件需要保存到一个文本里面,通常可以认为是json形式。软件启动后,界面可以完成增删改查的操作。当然,我们可以说增删改查不是那么高端,但它确实软件开发很基础的一个环节。
1、确定文件保存的形式,比如data.json
{
"count": 6,
"items": [
{
"ID": 1,
"NAME": "aaa"
},
{
"ID": 2,
"NAME": "bbb"
},
{
"ID": 3,
"NAME": "ccc"
},
{
"ID": 5,
"NAME": "ddd"
},
{
"ID": 6,
"NAME": "eee"
},
{
"ID": 4,
"NAME": "fff"
}
]
}
目前如果不用数据库的话,比较方便数据读取和保存的方式就是json格式。如上图所示,这里包含了数据的基本信息,包括了数据的数量,每一组数据的ID和NAME等等。
2、使用Newtonsoft.Json库读写json文件
前面我们决定用json格式读写文件,接下来需要面对的问题就是如何解析和保存这些数据。好在NuGet上面可以通过第三方库Newtonsoft.Json来直接解析json文件,这就变得非常方便了。
3、设计界面
界面部分的设计不难。简单一点难说,可以分成两个部分。左边就是数据的增删改查工作,右边就是当前所有数据的显示部分。之所以要添加右边这部分显示内容,主要还是为了直观地去观察,我们当前的操作有没有问题。
设计好界面之后,再转成xaml代码就不难了,
<Window x:Class="WpfApp.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"
xmlns:local="clr-namespace:WpfApp"
mc:Ignorable="d"
Title="MemberInfo" Height="300" Width="800">
<Grid>
<RadioButton x:Name="radio_add" Checked="Add_Checked" Content="add" HorizontalAlignment="Left" Margin="51,44,0,0" VerticalAlignment="Top"/>
<RadioButton x:Name="radio_del" Checked="Del_Checked" Content="del" HorizontalAlignment="Left" Margin="129,44,0,0" VerticalAlignment="Top"/>
<RadioButton x:Name="radio_update" Checked="Update_Checked" Content="update" HorizontalAlignment="Left" Margin="194,44,0,0" VerticalAlignment="Top"/>
<RadioButton x:Name="radio_search" Checked="Search_Checked" Content="search" HorizontalAlignment="Left" Margin="284,44,0,0" VerticalAlignment="Top"/>
<Label Content="ID:" HorizontalAlignment="Left" Margin="75,97,0,0" VerticalAlignment="Top"/>
<TextBox x:Name="id" HorizontalAlignment="Left" Height="23" Margin="193,97,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="120"/>
<Label Content="Name" HorizontalAlignment="Left" Margin="75,134,0,0" VerticalAlignment="Top"/>
<TextBox x:Name="name" HorizontalAlignment="Left" Height="23" Margin="193,138,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="120" RenderTransformOrigin="0.498,2.609"/>
<Button Content="OK" Click="OK_Click" HorizontalAlignment="Left" Margin="51,202,0,0" VerticalAlignment="Top" Width="75"/>
<Button Content="Cancel" Click="Cancel_Click" HorizontalAlignment="Left" Margin="157,202,0,0" VerticalAlignment="Top" Width="75"/>
<Button Content="Save" Click="Save_Click" HorizontalAlignment="Left" Margin="263,202,0,0" VerticalAlignment="Top" Width="75"/>
<Label Content="Member Details" HorizontalAlignment="Left" Margin="435,38,0,0" VerticalAlignment="Top"/>
<TextBox x:Name="all_data" HorizontalAlignment="Left" Height="146" Margin="435,75,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="315"/>
<Border BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Left" Height="235" Margin="395,10,0,0" VerticalAlignment="Top" Width="376"/>
<Border BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Left" Height="235" Margin="15,10,0,0" VerticalAlignment="Top" Width="360"/>
</Grid>
</Window>
软件部分的控件都是常见的控件,比如RadioButton、Label、TextBox、Button,同时为了进行左右区别,我们还额外添加了一个Boarder,这样稍微美观一点。
4、代码编写
代码整体上难度不大,最重要的就是OK按钮的处理,因为它需要判断下当前是哪一种操作模式,是添加,还是其他的操作模式。每一种操作模式的处理逻辑也是不一样的。这部分内容大家可以直接查看OK_Click的函数内容。
除了OK按钮的处理之外,另外比较重要的代码,就是json文件的加载和保存。加载是在软件启动的时候自动加载的,而保存则需要用户单击Save按钮才会自动保存。
最后大家可以关注下update_data这个函数,只要是正常的操作,数据内容发生变化,都会调用这个函数更新Textbox里面的内容。
using System;
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;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace WpfApp
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
private int select_option = 0;
private int total = 0;
private int[] id_array = new int[1000];
private string[] name_array = new string[1000];
public MainWindow() // construct function
{
InitializeComponent();
// initialize data
for(int i = 0; i < 1000;i++)
{
id_array[i] = 0;
name_array[i] = "";
}
// load file defined here
load_file();
radio_add.IsChecked = true; // set as default value
//display data here
all_data.Text = "";
all_data.IsEnabled = false;
update_data();
}
private void load_file()
{
string jsonfile = "data.json";
JObject jObject;
JToken items;
// parse script file
using (System.IO.StreamReader file = System.IO.File.OpenText(jsonfile))
{
using (JsonTextReader reader = new JsonTextReader(file))
{
jObject = (JObject)JToken.ReadFrom(reader);
}
}
// fetch data from json script
total = Convert.ToInt32(jObject["count"].ToString());
if(total <= 0)
{
return;
}
items = jObject["items"];
if(items == null)
{
return;
}
// fetch each data
for (int i = 0; i < total; i ++)
{
int id = Convert.ToInt32(items[i]["ID"].ToString());
string name = items[i]["NAME"].ToString();
id_array[i] = id;
name_array[i] = name;
}
}
private void OK_Click(object sender, RoutedEventArgs e)
{
int id_val;
string name_val = name.Text;
int i = 0;
int j = 0;
if (id.Text == "")
{
MessageBox.Show("ID input should not be empty!");
return;
}
id_val = Convert.ToInt32(id.Text);
if(id_val >= 1000)
{
MessageBox.Show("ID should be smaller than 1000!");
goto Final;
}
switch(select_option)
{
case 1: //add
if(name_val == "")
{
MessageBox.Show("Name can not be empty!");
goto Final;
}
if (total == 1000)
{
MessageBox.Show("Array is full!");
goto Final;
}
for (i = 0; i < total; i++)
{
if(id_array[i] == id_val)
{
MessageBox.Show("Find same id!");
goto Final;
}
}
id_array[total] = id_val;
name_array[total] = name_val;
total += 1;
MessageBox.Show("Add successfully!");
break;
case 2://del
if (total == 0)
{
MessageBox.Show("Array is empty!");
goto Final;
}
for (i = 0; i < total; i++)
{
if (id_array[i] == id_val)
{
break;
}
}
if(i == total)
{
MessageBox.Show("Failed to find relevant id!");
goto Final;
}
for(j = i+1; j < total; j++)
{
id_array[j - 1] = id_array[j];
name_array[j - 1] = name_array[j];
}
total -= 1;
MessageBox.Show("Del successfully!");
break;
case 3://update
if (name_val == "")
{
MessageBox.Show("Name can not be empty!");
goto Final;
}
if (total == 0)
{
MessageBox.Show("Array is empty!");
goto Final;
}
for (i = 0; i < total; i++)
{
if (id_array[i] == id_val)
{
break;
}
}
if (i == total)
{
MessageBox.Show("Failed to find relevant id!");
goto Final;
}
name_array[i] = name_val;
MessageBox.Show("Update successfully!");
break;
case 4://search
if (total == 0)
{
MessageBox.Show("Array is empty!");
goto Final;
}
for (i = 0; i < total; i++)
{
if (id_array[i] == id_val)
{
break;
}
}
if (i == total)
{
MessageBox.Show("Failed to find relevant id!");
goto Final;
}
MessageBox.Show("Name is " + name_array[i]);
break;
default:
break;
}
// display data
update_data();
Final:
id.Text = "";
name.Text = "";
}
private void update_data()
{
string data_text = "";
for(int i = 0; i < total;i++)
{
data_text += Convert.ToString(id_array[i]);
data_text += " ";
data_text += name_array[i];
data_text += "\n";
}
all_data.Text = "";
all_data.Text = data_text;
}
private void Cancel_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
private void Save_Click(object sender, RoutedEventArgs e) // important callback function
{
JArray items = new JArray();
JObject header = new JObject();
// save to array
for(int i = 0; i < total; i++)
{
JObject jobj = new JObject();
jobj["ID"] = id_array[i];
jobj["NAME"] = name_array[i];
items.Add(jobj);
}
// save to header
header["count"] = total;
header["items"] = items;
// save data
using (System.IO.StreamWriter file = new System.IO.StreamWriter("data.json"))
{
file.Write(header.ToString());
}
MessageBox.Show("Save successfully!");
}
private void Add_Checked(object sender, RoutedEventArgs e)
{
select_option = 1; //add
name.IsEnabled = true;
}
private void Del_Checked(object sender, RoutedEventArgs e)
{
select_option = 2; //del
name.Text = "";
name.IsEnabled = false;
}
private void Update_Checked(object sender, RoutedEventArgs e)
{
select_option = 3; //update
name.IsEnabled = true;
}
private void Search_Checked(object sender, RoutedEventArgs e)
{
select_option = 4; //search
name.Text = "";
name.IsEnabled = false;
}
}
}
至于说代码中的入参处理、异常处理、使用习惯处理,这些部分只能靠大家自己去慢慢体会和了解了。有了这份代码做基础,以后的crud代码,也就是增删改查操作都逃不了这个范畴。而且我们的数据是真正保存在json文件中的,不存在丢失的风险,这也让开发的软件进一步拓展了实用性和可靠性。
5、运行测试
运行测试就比较简单了,直接编译执行就好了,不出意外就可以看到这样的运行画面了,和之前设计稍微有点区别,