主要讲一下如何通过点击菜单,实现动态加载TabControl的功能,准确来说应该是动态加载TabItem,要实现这个功能,我们需要解决两个问题:
-
点击菜单的时候,需要传进来一个你要加载的UserControl控件的名称,让程序知道你要加载的是哪个控件。
-
通过传进来的UserControl控件名实例化UserControl控件(利用反射来实例化UserControl控件)。
关于第一点,我通过在数据库菜单表加一个字段Code,用来保存UserControl控件的名称,然后在加载菜单的时候,给菜单的Name赋上Code的值,关于动态加载菜单可以看我上一篇博客。
在xaml添加一个
Name
="TabControl"
的
TabControl
CS后台代码:
我们在添加菜单的时候,根据菜单的Name值是否为空,给菜单添加点击事件
/// <summary>
/// 添加菜单的点击事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Item_Click(object sender, RoutedEventArgs e)
MenuItem item = sender as MenuItem;
bool isExist = false;
foreach (TabItem tab in this.TabControl.Items)
//如果已经存在这个TabItem,就把焦点设置到这个TabItem上
if (tab.Header == item.Header)
isExist = true;
tab.Focus();
//如果不存就添加一个TabItem
if (!isExist)
//获取命名空间名称(这个名称一定要完整)
string str = string.Format("WPFTest.{0}", item.Name);
UserControl userControl = ReflectionHelper.CreateInstance<UserControl>(str, "WPFTest");//程序集名称
TabItem tabItem = new TabItem()
Header = item.Header,
Name = item.Name
tabItem.Content = userControl;
this.TabControl.Items.Add(tabItem);
tabItem.Focus();
添加一个反射帮助类ReflectionHelper,在里面添加一个创建对象实例的方法CreateInstance:
/// <summary>
/// 创建对象的实例
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="fullName">命名空间.类名</param>
/// <param name="assemblyName">程序集名称</param>
/// <returns></returns>
public static T CreateInstance<T>(string fullName, string assemblyName)
//命名空间.类名,程序集名称
string path = fullName + "," + assemblyName;
//获取加载类型
Type o = Type.GetType(path);
//根据类型创建实例
object obj = Activator.CreateInstance(o, true);
//类型转换并返回
return (T)obj;
这样我们就可以动态的实例化UserControl控件对象,实现动态加载TabItem的功能了。
为了方便理解,补上完整代码:
public MainWindow()
InitializeComponent();
LoadSystemMenu();
/// <summary>
/// 加载菜单
/// </summary>
public void LoadSystemMenu()
//查询出数据库所有的菜单数据
DataTable dataTable = model.GetSystemMenu();
//筛选出上级ID为空的数据(一级菜单)
DataRow[] dataRows = dataTable.Select("SuperiorID is null");
//用来存放一级菜单
List<MenuItem> menuItems = new List<MenuItem>();
foreach (DataRow item in dataRows)
MenuItem menu = new MenuItem();
menu.Header = item["Name"].ToString();
menu.Name = item["Code"].ToString();//UserControl控件名称
menu.RenderSize = new Size(100, 25);
//如果Code不为空,就添加点击事件
if (!string.IsNullOrWhiteSpace(item["Code"].ToString())) menu.Click += Item_Click;
//添加子菜单
AddChildMenu(menu, item["ID"].ToString(), dataTable);
menuItems.Add(menu);
//把一级菜单绑定到菜单栏
foreach (MenuItem item in menuItems)
this.SystemMenu.Items.Add(item);
/// <summary>
/// 添加子菜单
/// </summary>
/// <param name="menu"></param>
/// <param name="superiorId"></param>
/// <param name="dataTable"></param>
private void AddChildMenu(MenuItem menu, string superiorId, DataTable dataTable)
//筛选出上级菜单ID等于传进来的菜单ID的数据(子菜单)
DataRow[] dataRows = dataTable.Select("SuperiorID =" + superiorId);
foreach (DataRow item in dataRows)
MenuItem menuItem = new MenuItem();
menuItem.Header = item["Name"].ToString();
menuItem.Name = item["Code"].ToString();//UserControl控件名称
menuItem.RenderSize = new Size(100, 25);
if (!string.IsNullOrWhiteSpace(item["Code"].ToString())) menuItem.Click += Item_Click;
//绑定子菜单
menu.Items.Add(menuItem);
AddChildMenu(menuItem, item["ID"].ToString(), dataTable);
/// <summary>
/// 添加菜单的点击事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Item_Click(object sender, RoutedEventArgs e)
MenuItem item = sender as MenuItem;
bool isExist = false;
foreach (TabItem tab in this.TabControl.Items)
//如果已经存在这个TabItem,就把焦点设置到这个TabItem上
if (tab.Header == item.Header)
isExist = true;
tab.Focus();
//如果不存就添加一个TabItem
if (!isExist)
//获取命名空间名称(这个名称一定要完整)
string str = string.Format("WPFTest.{0}", item.Name);
UserControl userControl = ReflectionHelper.CreateInstance<UserControl>(str, "WPFTest");//程序集名称
TabItem tabItem = new TabItem()
Header = item.Header,
Name = item.Name
tabItem.Content = userControl;
this.TabControl.Items.Add(tabItem);
tabItem.Focus();
这些控件都是用于在WPF应用程序中呈现数据的控件。它们之间的主要区别在于其用途、功能、视觉外观和自定义选项。可以根据的需求和数据类型来选择适当的控件:如果需要简单地显示一个数据项集合,则可以使用ItemsControl或ListView;如果需要支持选择功能,则可以使用ListBox;如果需要以表格形式查看或编辑数据,则应该使用DataGrid;如果需要显示具有层次结构的数据,则应该使用TreeView;如果需要将不同视图放在不同选项卡页面上,则应该使用TabControl。
<DataGrid x:Name="dataGrid" Margin="0,52,0,65" AutoGenerateColumns="False" LoadingRow="dataGrid_LoadingRow"...
原因:切换选项卡时会卸载TabItem内的用户控件,再次点击时会重新加载用户控件,如果主要的逻辑代码位于用户控件的Loaded事件中,将会再执行一遍,导致缓慢。
解决方案:将Loaded事件内的代码移至初始化控件中
言归正传,所谓动态加载DataGrid就是说表头和数据都是动态生成出来的,不是预先设置好的值。
在网上也找了很久这方面的资料,然后功夫不负有心人还是找到了,然后写一下自己在做的过程中的一些方法和心得。
先说明我没有用WPF的MVVM模式做。
首先需要先在页面上放一个空的DataGrid
<DataGrid x:Name="DataGrids" >
<DataGrid.Columns>
//tCLayerParaSet为tableControl
string pageTitle = "工艺" + showlayerIDs[iLayer].ToString();
TabPage curTabPage = new TabPage(pageTitle);
curTabPage.Name = pageTitle;
tCLayerParaSet.TabPages.Add(curTabPage);
//要添加的TAB页页面form,需在设计窗口设置formbodorStyle为None..