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吧。
