WPF MVVM模式一直没怎么用过,.net5正式版就要出来了,趁这个时间看看各个微软的前后端.netCore的功能,使用.netCore下WPF实现一个简单的从数据库读取数据显示功能,
示例主要用到了按钮,编辑框,树控件,列表控件, 代码东拼西凑的,只贴几个片段。
1、XAML
<Page x:Class="NEasyCode.PageDataBase" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:NEasyCode" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800" Title="PageDataBase" > <Page.DataContext> <local:DataBaseViewModel /> </Page.DataContext> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="300" /> <ColumnDefinition Width="5" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <DockPanel > <Grid DockPanel.Dock="Top"> <Grid.ColumnDefinitions> <ColumnDefinition Width="100" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="50" /> </Grid.ColumnDefinitions> <Label Name="LabelFilter" Grid.Column="0">表名过滤:</Label> <TextBox Margin="0,0,0,0" Grid.Column="1" Name="TextFilter" Text="{Binding Path=SearchText, Mode=TwoWay}"/> <Button Name="ButtonRefresh" Content="刷新" Grid.Column="2" Width="40" DockPanel.Dock="Right" Command="{Binding Path=QueryCommand}"></Button> </Grid> <TreeView Margin="0,0,0,0" Name="TreeDataBase" ScrollViewer.VerticalScrollBarVisibility="Auto" ItemsSource="{Binding TreeViewItems}"> </TreeView> </DockPanel> <GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Left" /> <DockPanel Grid.Column="2" > <DataGrid Name="TableProp" ItemsSource="{Binding Path=TableInfoList}"> <DataGrid.Columns> <DataGridTextColumn Header="编号" Width="90" Binding="{Binding ColumnId}"/> <DataGridTextColumn Header="列名" Width="90" Binding="{Binding ColumnName}"/> <DataGridTextColumn Header="主键" Width="90" Binding="{Binding ColumnIsPrimaryKey}"/> <DataGridTextColumn Header="标识列" Width="90" Binding="{Binding ColumnIsIdentity}"/> <DataGridTextColumn Header="列类型" Width="90" Binding="{Binding ColumnType}"/> <DataGridTextColumn Header="长度" Width="90" Binding="{Binding ColumnLength}"/> <DataGridTextColumn Header="精度" Width="90" Binding="{Binding ColumnPrecision}"/> <DataGridTextColumn Header="小数点" Width="90" Binding="{Binding ColumnScale}"/> <DataGridTextColumn Header="允许空" Width="90" Binding="{Binding ColumnIsNullAble}"/> <DataGridTextColumn Header="默认值" Width="90" Binding="{Binding ColumnDefaultValue}"/> <DataGridTextColumn Header="列描述" Width="90" Binding="{Binding ColumnDesc}"/> <DataGridTextColumn Header="索引" Width="90" Binding="{Binding ColumnIndexName}"/> <DataGridTextColumn Header="排序" Width="90" Binding="{Binding ColumnIndexSort}"/> </DataGrid.Columns> </DataGrid > <DataGrid Name="TableData" ItemsSource="{Binding}"> <DataGrid.Columns> </DataGrid.Columns> </DataGrid> </DockPanel> </Grid> </Page>
2、DataBaseModel.cs
using NEasyCode.Entity; using NEasyCode.Util; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Text; using System.Windows.Controls; using System.Windows.Input; namespace NEasyCode { public class DataBaseViewModel : INotifyPropertyChanged { #region Fields private string _searchText; private ObservableCollection<TableInfo> _resultList; #endregion #region Properties private ObservableCollection<TableInfo> tableInfoList; public ObservableCollection<TableInfo> TableInfoList { get { return tableInfoList; } set { tableInfoList = value; RaisePropertyChanged("TableInfoList"); } } public ObservableCollection<string> TableList { get; set; } = new ObservableCollection<string>(); public ObservableCollection<TreeViewItem> TreeViewItems { get; set; } = new ObservableCollection<TreeViewItem>(); // 查询关键字 public string SearchText { get { return _searchText; } set { _searchText = value; RaisePropertyChanged("SearchText"); } } public ICommand QueryCommand { get { return new QueryCommand(SearchTable, CanSearching); } } #endregion #region Construction public DataBaseViewModel() { SearchTable(); ViewTableInfo("dghr_userinfo"); } #endregion #region Command Handler /// <summary> /// 查看表信息 /// </summary> /// <param name="tableName"></param> public void ViewTableInfo(string tableName) { //TableInfoList.Clear(); TableInfoList = DbUtil.GetTableProp(tableName).ToObservableCollection(); //_resultList = TableInfoList; } /// <summary> /// 查找表 /// </summary> public void SearchTable() { TableList.Clear(); var tableList= DbUtil.GetAllDataBase().ToObservableCollection(); if (string.IsNullOrWhiteSpace(SearchText)) { TableList = tableList; } else { foreach (string p in tableList) { if (p.Contains(SearchText)) { TableList.Add(p); } } } TreeViewItems.Clear(); for (int i = 0; i < TableList.Count; i++) { TreeViewItem item = new TreeViewItem(); item.Header = TableList[i]; item.Selected += Item_Selected; item.MouseDoubleClick += Item_MouseDoubleClick; TreeViewItems.Add(item); } } private void Item_Selected(object sender, System.Windows.RoutedEventArgs e) { var v= ((TreeViewItem)(e.Source)).Header.ToString(); ViewTableInfo(v); } private void Item_MouseDoubleClick(object sender, MouseButtonEventArgs e) { //ViewTableInfo(e.ToString) } public bool CanSearching() { return true; } #endregion #region INotifyPropertyChanged Members public event PropertyChangedEventHandler PropertyChanged; #endregion #region Methods private void RaisePropertyChanged(string propertyName) { // take a copy to prevent thread issues PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(propertyName)); } } #endregion } }
3、Command
using System; using System.Collections.Generic; using System.Text; using System.Windows.Input; namespace NEasyCode { class QueryCommand : ICommand { #region Fields private Action _execute; private Func<bool> _canExecute; #endregion public QueryCommand(Action execute) : this(execute, null) { } public QueryCommand(Action execute, Func<bool> canExecute) { if (execute == null) throw new ArgumentNullException("execute"); _execute = execute; _canExecute = canExecute; } #region ICommand Member public event EventHandler CanExecuteChanged { add { if (_canExecute != null) { CommandManager.RequerySuggested += value; } } remove { if (_canExecute != null) { CommandManager.RequerySuggested -= value; } } } public bool CanExecute(object parameter) { return _canExecute == null ? true : _canExecute(); } public void Execute(object parameter) { _execute(); } #endregion } }
4、TableInfo实体
using System; using System.Collections.Generic; using System.Text; namespace NEasyCode.Entity { public class TableInfo { public string TableName { get; set; } public string TableDesc { get; set; } public int ColumnId { get; set; } public string ColumnName { get; set; } public bool ColumnIsPrimaryKey { get; set; } public bool ColumnIsIdentity { get; set; } public string ColumnType { get; set; } public int ColumnLength { get; set; } public int ColumnPrecision { get; set; } public int ColumnScale { get; set; } public bool ColumnIsNullAble { get; set; } public string ColumnDefaultValue { get; set; } public string ColumnDesc { get; set; } public string ColumnIndexName { get; set; } public string ColumnIndexSort { get; set; } public DateTime ColumnCreateDate { get; set; } public DateTime ColumnModifyDate { get; set; } } }
5、运行效果
6、有几点问题,研究的不深,可能因为使用的不算完全纯正的mvvm的原因,有几点疑惑。
6.1 树控件数据源不需要调用RaisePropertyChanged,但是DataGrid需要,没有搞明白为什么这样。
6.2 树控件的绑定事件按网上的无法使用,.netCore下无法引入NuGet下的System.Windows.Interactivity,其它引入方式未试验,直接使用事件绑定的方式了。
通过Command绑定事件 在项目中引用 System.Windows.Interactivity.WPF(简单来说该插件可以将页面控件的Event转为ViewModel中的Command) 在窗体中添加引用 xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" 绑定Command到SelectedItemChanged事件 <TreeView x:Name="treeView" ItemsSource="{Binding TypeList}"> <i:Interaction.Triggers> <i:EventTrigger EventName="SelectedItemChanged"> <i:InvokeCommandAction Command="{Binding SelectItemChangeCommand}" CommandParameter="{Binding ElementName=treeView,Path=SelectedItem}"/> </i:EventTrigger> </i:Interaction.Triggers> </TreeView>
后续测试:在.net5 预览版下是可以的,.netCore下不行,
7、在xp下无法运行,传家项目没有必须升级的需求还是用.net4.0吧。