我们会完成这样一个程序,输入一系列的词语:
然后选择OK:
程序会统计每一个词的首字母出现次数。然后显示在图表和列表中。
下面是正文。
目录
返回目录
首先建立一个.NET 4.5的WPF工程。我们会用到async/await,所以必须是.NET 4.5和Visual Studio 2012 IDE。
接着需要引用WPF Toolkit的相关类库。分别是:WPFToolkit.dll和System.Windows.Controls.DataVisualization.Toolkit.dll。读者也可以直接下载示例程序源代码,这两个DLL已经包含在示例程序工程中了。
返回目录
由于最后的分析数据要显示在图表和列表中的。所以我们要准备两套数据,然后分别绑定到图表和列表上。
数据类型全部是用的Tuple,对于列表数据。第一个数据项是组的名称,第二个数据项是组的成员,组的成员是拼接后的字符串,最终会显示在界面的TextBox中。
对于图表数据的Tuple类型。第一个数据项是图块的名称,第二个数据项是int类型,代表图块所占的比例,注意这个比例不需要自己换算成总数为100的,WPF Toolkit会自己按比例分配的。
我们这些数据统一写到一个ViewModel中,命名为ChartViewModel类型。这个类型是这样定义的:
class ChartViewModel
{
public ChartViewModel(Tuple<string, string>[] groups, Tuple<string, int>[] chartData)
{
Groups = groups;
ChartData = chartData;
}
/// <summary>
/// 列表的数据源
/// </summary>
public Tuple<string, string>[] Groups { get; private set; }
/// <summary>
/// 图表的数据源
/// </summary>
public Tuple<string, int>[] ChartData { get; private set; }
}
OK,然后就是数据处理的过程,我们定义一个新的类型:ChartOperation类。这个类型专门负责数据的处理。不过在介绍ChartOperation类前,需要介绍下使用LINQ来对数据的分组和排序问题。
整个过程是这样的:我们需要首先提取每一个词的首字母,然后按照首字母分组,接着根据每个组的成员个数降序排列。最后的结果就是我们想要的。
具体如下:
1. 调用GroupBy方法按照提取的数据分组。
2. 调用OrderByDescending方法按照分组返回的IGrouping对象的Count方法分组。(IGrouping接口继承自IEnumerable接口)
我们可以在控制台下练习一下,这里提取的数据就是数据项本身。代码:
var ints = new int[] { 1, 2, 3, 3, 3, 2, 2, 3 };
foreach (var group in ints.GroupBy(i => i).OrderByDescending(g => g.Count()))
{
Console.WriteLine("{0}出现了{1}次:", group.Key, group.Count());
Console.WriteLine("========================");
foreach (var item in group)
Console.WriteLine(item);
}
输出:
3出现了4次:
========================
3
3
3
3
2出现了3次:
========================
2
2
2
1出现了1次:
========================
1
ChartOperation类型就是利用上面的方法对数据进行分类和排序,然后生成图表数据和列表数据,也就是返回上面讲的ChartViewModel类型。
下面是ChartOperation类型的代码:
//+ using System.Threading.Tasks;
static class ChartOperation
{
/// <summary>
/// DooSync方法的异步执行。
/// </summary>
/// <param name="strs">需要处理的词列表</param>
/// <param name="maxCount">图表中的最大图块数量</param>
/// <returns></returns>
public static async Task<ChartViewModel> DooAsync(string[] strs, int maxCount)
{
return await Task.Run(() => DooSync(strs, maxCount));
}
/// <summary>
/// 同步处理数据方法。
/// </summary>
/// <param name="strs">需要处理的词列表</param>
/// <param name="maxCount">图表中的最大图块数量</param>
/// <returns></returns>
public static ChartViewModel DooSync(string[] strs, int maxCount)
{
//分组,按照出现次数降序排列。
var res = strs.GroupBy(s => s[0].ToString()).OrderByDescending(g => g.Count());
//创建列表数据
var groups = res.Select(g => Tuple.Create(g.Key, String.Join(Environment.NewLine, g))).ToArray();
//创建图表数据
var chartData = new List<Tuple<string, int>>();
//对于最大值的处理
foreach (var item in res.Take(maxCount))
chartData.Add(new Tuple<string, int>(item.Key, item.Count()));
if (groups.Length > maxCount)
{
var sum = res.Skip(maxCount).Max(i => i.Count());
chartData.Add(Tuple.Create("其他", sum));
}
return new ChartViewModel(groups, chartData.ToArray());
}
}
返回目录
数据处理逻辑完成后,就可以界面定义了。从最上方的程序截图可以看到,程序只有两个窗体。窗体具体定义就不需要讲了。挑重点讲下数据结果显示时的图表和列表定义。
图表控件在System.Windows.Controls.DataVisualization.Charting命名空间内,所以我们需要先在XAML中引用他。
xmlns:chart="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit"
然后就可以定义Chart控件,设置Series属性,创建PieSeries对象。注意设置ItemsSource到ChartViewModel类型的ChartData属性。然后设置IndependentValuePath属性,代表图块的显示名称,对应Tuple类型的Item1。最后设置DependentValuePath属性,代表图块的大小,对应Tuple中的Item2。
所以Chart控件的XAML定义如下:
<chart:Chart>
<chart:Chart.Series>
<chart:PieSeries ItemsSource="{Binding ChartData}"
IndependentValuePath="Item1"
DependentValuePath="Item2"
IsSelectionEnabled="True"/>
</chart:Chart.Series>
</chart:Chart>
对于列表显示控件,就更简单了,需要注意的是,正确绑定ChartViewModel中的数据项就OK了:
<ListBox ItemsSource="{Binding Groups}" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Expander Header="{Binding Item1}" IsExpanded="False">
<TextBox IsReadOnly="True" Text="{Binding Item2,Mode=OneWay}" BorderThickness="0"/>
</Expander>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
然后为窗体增加一个构造函数,并在Loaded事件后调用ChartOperation类型返回的ChartViewModel对象,最后设置在DataContext属性上。注意Loaded事件执行需要加入async关键字,因为我们需要await ChartOperation类型中的Task执行。
窗体代码(ChartWindow类型代表数据显示的窗体类型):
public partial class ChartWindow : Window
{
/// <summary>
/// 存储词列表的字段
/// </summary>
string[] _strs;
/// <summary>
/// 默认构造函数(用在Designer中)
/// </summary>
public ChartWindow()
{
InitializeComponent();
}
/// <summary>
/// 程序用到的构造函数
/// </summary>
/// <param name="strs">词列表</param>
public ChartWindow(string[] strs)
: this()
{
_strs = strs;
}
/// <summary>
/// Loaded事件后,开始分析数据,注意加async关键字。
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
async private void Window_Loaded(object sender, RoutedEventArgs e)
{
if (_strs != null)
{
//工作中,设置IsEnabled为false。
IsEnabled = false;
//异步设置DataContext。
DataContext = await ChartOperation.DooAsync(_strs, 4);
//await后回到UI线程,设置IsEnabled为true。
IsEnabled = true;
}
}
}
最后在主窗体的按钮Click执行上,创建并显示ChartWindow就可以了!如下代码:
//textBox变量是主窗体的TextBox控件。
if (String.IsNullOrWhiteSpace(textBox.Text))
{
MessageBox.Show("文本不能为空");
}
new ChartWindow(textBox.Text.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)).Show();
返回目录
当前版本的程序和源代码下载
下载页面
注意:链接是微软SkyDrive页面,下载时请用浏览器直接下载,用某些下载工具可能无法下载
程序环境:.NET Framework 4.5
源代码环境:Microsoft Visual Studio Express 2012 for Windows Desktop
ASP.NET MVC+EF框架+EasyUI实现权限管理系
ASP.NET MVC+EF框架+EasyUI实现权限管理系
ASP.NET MVC+EF框架+EasyUI实现权限管理系
热门源码