前言
在实际的开发工作中,有时我们需要把大量的数据从数据库或是文件或是远程服务中获取过来,然后在程序中进行逻辑处理,然后呈现在界面上。传统的做法是先将这些数据获取,然后传给前台界面,比如给Textbox的Text赋值或是给Label的Content赋值等等。现在我们利用绑定技术,将数据与界面控件的相关属性直接绑定起来,也就是说,不需要我们在人为去控制界面与数据的关联。当界面上的数据发生变化时,后台数据会自动变化,反之当后台数据改变时,界面上显示的数据也会改变。
模拟
我们模拟一个玩家数据界面的例子,通过读取文件获取所有玩家数据,呈现在界面上,界面上可以对玩家数据进行修改,然后可以将修改后的内容保存回文件。整个过程,我们都不去手动处理界面与后台数据的关联。
创建类结构
我们首先将玩家数据抽象成类。
1.建立PlayerData类结构:
using System;
using System.ComponentModel;
namespace PlayerDataBinding
{
public class PlayerData : INotifyPropertyChanged
{
[field: NonSerializedAttribute()]
public virtual event PropertyChangedEventHandler PropertyChanged;
protected virtual void SetProperty(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
private int _Id; //Id
private string _Name; //名字
private double _HealthValue; //生命值
private double _MagicValue; //魔法值
private int _Age; //年龄
private int _Gender = -1; //性别
private double _AttackValue; //攻击力
private double _DefenseValue; //防御力
private int _Camp = -1; //阵营
public int Id
{
set
{
_Id = value;
SetProperty("Id");
}
get { return _Id; }
}
public string Name
{
set
{
_Name = value;
SetProperty("Name");
}
get { return _Name; }
}
public double HealthValue
{
set
{
_HealthValue = value;
SetProperty("HealthValue");
}
get { return _HealthValue; }
}
public double MagicValue
{
set
{
_MagicValue = value;
SetProperty("MagicValue");
}
get { return _MagicValue; }
}
public int Age
{
set
{
_Age = value;
SetProperty("Age");
}
get { return _Age; }
}
public int Gender
{
set
{
_Gender = value;
SetProperty("Gender");
}
get { return _Gender; }
}
public double AttackValue
{
set
{
_AttackValue = value;
SetProperty("AttackValue");
}
get { return _AttackValue; }
}
public double DefenseValue
{
set
{
_DefenseValue = value;
SetProperty("DefenseValue");
}
get { return _DefenseValue; }
}
public int Camp
{
set
{
_Camp = value;
SetProperty("Camp");
}
get { return _Camp; }
}
}
}
2.创建GameData类结构,即创建一个包含许多玩家信息的结构:
using System.Collections.Generic;
namespace PlayerDataBinding
{
public class GameData
{
private List<PlayerData> _PlayerDataList = null;
public List<PlayerData> PlayerDataList
{
set { _PlayerDataList = value; }
get { return _PlayerDataList; }
}
}
}
这部分代码不做过多解释。
绘制界面
我们简单做下界面,左侧放一个ListBox控件,用于显示所有玩家数据,点击不同的项右侧相关的数据显示会刷新。
这里放下布局代码:
<Window x:Class="PlayerDataBinding.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:PlayerDataBinding"
xmlns:c="clr-namespace:System.Collections;assembly=mscorlib"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
Title="PlayerData" Icon="Images/sonic.ico"
Height="450" Width="800"
WindowStartupLocation="CenterScreen">
<Window.Resources>
<c:ArrayList x:Key="al_GenderList">
<sys:String>男</sys:String>
<sys:String>女</sys:String>
</c:ArrayList>
<c:ArrayList x:Key="al_CampList">
<sys:String>哥布林</sys:String>
<sys:String>古侗</sys:String>
<sys:String>匹萨夫</sys:String>
</c:ArrayList>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Button x:Name="btn_Save" Width="60" Height="25" Content="Save" Click="btn_Save_Click"></Button>
<StackPanel Orientation="Horizontal" Grid.Row="1" Grid.Column="0">
<ListBox x:Name="lb_PlayerList" Margin="10,10,0,10" SelectionMode="Single"
Width="300" SelectionChanged="lb_PlayerList_SelectionChanged"></ListBox>
<StackPanel Orientation="Vertical">
<Button x:Name="btn_AddPlayer" Margin="5,20,0,0" BorderThickness="0"
Width="20" Height="20" Click="btn_AddPlayer_Click">
<Button.Background>
<ImageBrush ImageSource="Images/Add.png"/>
</Button.Background>
</Button>
<Button x:Name="btn_SubPlayer" Margin="5,10,0,0" BorderThickness="0"
Width="20" Height="20" Click="btn_SubPlayer_Click">
<Button.Background>
<ImageBrush ImageSource="Images/Substract.png"/>
</Button.Background>
</Button>
</StackPanel>
</StackPanel>
<StackPanel x:Name="sp_playerData" Grid.Row="1" Grid.Column="1" Orientation="Vertical">
<StackPanel Orientation="Horizontal" Margin="10,10,0,0">
<Label Width="60" Height="25" HorizontalContentAlignment="Right" VerticalContentAlignment="Center" Content="Name"></Label>
<TextBox x:Name="txt_Name" Margin="5,0,0,0" Width="80" Height="25" VerticalContentAlignment="Center" Text="{Binding Name}"></TextBox>
<Label Margin="30,0,0,0" Width="60" Height="25" HorizontalContentAlignment="Right" VerticalContentAlignment="Center" Content="Camp"></Label>
<ComboBox Margin="5,0,0,0" Width="100" Height="25" VerticalContentAlignment="Center"
ItemsSource="{StaticResource al_CampList}" SelectedIndex="{Binding Camp}"></ComboBox>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="10,10,0,0">
<Label Width="60" Height="25" HorizontalContentAlignment="Right" VerticalContentAlignment="Center" Content="Health"></Label>
<ProgressBar Margin="5,0,0,0" Width="200" Height="25" Value="{Binding HealthValue}"></ProgressBar>
<TextBox x:Name="txt_Health" Margin="5,0,0,0" Width="80" Height="25" VerticalContentAlignment="Center" Text="{Binding HealthValue}" KeyDown="txt_Health_KeyDown"></TextBox>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="10,10,0,0">
<Label Width="60" Height="25" HorizontalContentAlignment="Right" VerticalContentAlignment="Center" Content="Magic"></Label>
<ProgressBar Margin="5,0,0,0" Width="200" Height="25" Value="{Binding MagicValue}" Foreground="Blue"></ProgressBar>
<TextBox x:Name="txt_Magic" Margin="5,0,0,0" Width="80" Height="25" VerticalContentAlignment="Center" Text="{Binding MagicValue}" KeyDown="txt_Magic_KeyDown"></TextBox>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="10,10,0,0">
<Label Width="60" Height="25" HorizontalContentAlignment="Right" VerticalContentAlignment="Center" Content="Age"></Label>
<TextBox Margin="5,0,0,0" Width="80" Height="25" VerticalContentAlignment="Center" Text="{Binding Age}"></TextBox>
<Label Margin="30,0,0,0" Width="60" Height="25" HorizontalContentAlignment="Right" VerticalContentAlignment="Center" Content="Gender"></Label>
<ComboBox Margin="5,0,0,0" Width="80" Height="25" VerticalContentAlignment="Center"
ItemsSource="{StaticResource al_GenderList}" SelectedIndex="{Binding Gender}"></ComboBox>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="10,10,0,0">
<Label Width="60" Height="25" HorizontalContentAlignment="Right" VerticalContentAlignment="Center" Content="Attack"></Label>
<TextBox Margin="5,0,0,0" Width="80" Height="25" VerticalContentAlignment="Center" Text="{Binding AttackValue}"></TextBox>
<Label Margin="30,0,0,0" Width="60" Height="25" HorizontalContentAlignment="Right" VerticalContentAlignment="Center" Content="Defense"></Label>
<TextBox Margin="5,0,0,0" Width="80" Height="25" VerticalContentAlignment="Center" Text="{Binding DefenseValue}"></TextBox>
</StackPanel>
</StackPanel>
</Grid>
</Window>
与以往的xaml文件不同之处在于,一些控件增添了Binding,目的就是为了建立我们所说的界面与数据直接绑定机制。
界面效果图
逻辑代码部分
接下来是核心,也就是后台的逻辑代码编写,我们分几步来思考。
首先,我们创建需要的全局变量,这里我们只需要两个,即所有的玩家数据和当前的玩家数据变量。
private GameData gameData = null; //所有的玩家数据
private PlayerData curPlayer = new PlayerData(); //当前玩家数据
然后,当界面初始化后,我们首先要拿到所有的玩家数据,这里命名成GameData.data文件。
private void LoadGameData()
{
gameData = new GameData();
gameData.PlayerDataList = new List<PlayerData>();
try
{
FileStream fsRead = new FileStream(Global.strFilePath, FileMode.Open);
byte[] dataRead = new byte[fsRead.Length];
fsRead.Read(dataRead, 0, (int)fsRead.Length);
string message = Encoding.UTF8.GetString(dataRead);
fsRead.Close();
gameData = Global.JsonToClass<GameData>(message);
InitPlayerData();
}
catch (Exception ex)
{
CheckLog.WriteLog("MainWindow-LoadGameData Error: " + ex.Message);
return;
}
}
这样我们就拿到了所有的数据,然后我们要做的是,把所有的玩家列表显示到我们界面左侧的ListBox中,也就是上面代码中引用到的InitPlayerData函数。
private void InitPlayerData()
{
lb_PlayerList.Items.Clear();
for (int i = 0; i < gameData.PlayerDataList.Count; i++)
{
ListBoxItem listItem = new ListBoxItem();
listItem.Tag = gameData.PlayerDataList[i].Id;
listItem.Content = gameData.PlayerDataList[i].Name;
lb_PlayerList.Items.Add(listItem);
}
lb_PlayerList.SelectedIndex = 0;
}
接下来是重点,就是当我们点击玩家列表中不同的项时,界面上要切换成当前选中的玩家数据。
private void lb_PlayerList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (lb_PlayerList.SelectedItem == null) return;
for (int i = 0; i < gameData.PlayerDataList.Count; i++)
{
if (gameData.PlayerDataList[i].Id.ToString() == ((ListBoxItem)lb_PlayerList.Items[lb_PlayerList.SelectedIndex]).Tag.ToString())
{
sp_playerData.DataContext = gameData.PlayerDataList[i];
curPlayer = gameData.PlayerDataList[i];
return;
}
}
}
这里的核心代码是sp_playerData.DataContext = gameData.PlayerDataList[i]; 这句代码的作用是将控件的数据上下文绑定到类数据上。
最后,我们需要提供保存功能,也就是将修改后的玩家数据保存到GameData.data文件里。
private void btn_Save_Click(object sender, RoutedEventArgs e)
{
try
{
if (File.Exists(Global.strFilePath))
{
File.Delete(Global.strFilePath); //删除之前的文件
}
FileStream fsWrite = new FileStream(Global.strFilePath, FileMode.Append);
string message = Global.ClassToJson<GameData>(gameData);
byte[] myByte = Encoding.UTF8.GetBytes(message);
fsWrite.Write(myByte, 0, myByte.Length);
fsWrite.Close();
MessageBox.Show("保存成功", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
LoadGameData(); //重新刷新数据
}
catch (Exception ex)
{
CheckLog.WriteLog("MainWindow-btn_Save_Click Error: " + ex.Message);
MessageBox.Show("保存失败", "警告", MessageBoxButton.OK, MessageBoxImage.Warning);
}
}
后续
我们主要的绑定技术已经介绍完了,本人的这个示例程序还有一些其他功能,比如增删玩家数据,不过这里就不一一列出了。想参考的朋友可以点击下方链接下载查看:
链接:百度网盘
提取码:7vzs。
如果不能下载,可以私信我获取源码。
也可以点此直接在CSND下载源码。