Master-slave structure list for wpf enterprise applications
The master-slave structure is quite common in enterprise-level applications. Here we will talk about the common practices of displaying the master-slave structure list in wpf in combination with my examples. For specific results, see several common business scenarios in enterprise-level development of wpf.
First, there are two types of models: the primary table corresponds to the Model (assumed as modelA), the table corresponds to the model (assumed as modelB), and the two models are used to bind the list respectively, is the binding of a normal list.
Secondly, to achieve the association effect (that is, to select a record in the master table to display records from the table), we must design a SelectedModelA in our ViewModel to bind the selected item, when SelectedModelA changes, update the data source of the modelB list (usually SelectedModelA contains a set, but I have made a set separately for other reasons, and the logic is similar ).
The following is my code. Because it is mixed with some services and is for reference only, the reader only needs to understand the selected items of the master table as the data source from the table UI.
UI, you can look at the specific effect of several common business scenarios in enterprise-level development of wpf, binding part mainly depends on two DataGrid.
<Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="200"/> <ColumnDefinition/> </Grid.ColumnDefinitions> <HeaderedContentControl> <HeaderedContentControl.Header> <DockPanel> <TextBlock VerticalAlignment="Center" Text="{DynamicResource ProductClassify_Header_Classify}" DockPanel.Dock="Left"/> <StackPanel VerticalAlignment="Center" HorizontalAlignment="Right" Orientation="Horizontal"> <Button Name="Button_RefreshClassify" Style="{StaticResource RefreshIconButton}" Content="{DynamicResource Common_Refresh}" Command="{Binding TreeVM.RefreshCommand}"/> </StackPanel> </DockPanel> </HeaderedContentControl.Header> <local:ClassifyTreeControl DataContext="{Binding TreeVM}"/> </HeaderedContentControl> <HeaderedContentControl Name="Header_Product" Grid.Column="1"> <HeaderedContentControl.Header> <DockPanel> <TextBlock VerticalAlignment="Center" Text="{DynamicResource ProductList_Header_Product}" DockPanel.Dock="Left"/> <StackPanel VerticalAlignment="Center" HorizontalAlignment="Right" Orientation="Horizontal"> <Button Name="Button_RefreshClassify2" Style="{StaticResource RefreshIconButton}" Content="{DynamicResource Common_Refresh}" Command="{Binding RefreshCommand}"/> <Button Name="Button_AddProduct" Visibility="{Binding AddButtonVisibility}" IsEnabled="{Binding CanAdd}" Style="{StaticResource AddIconButton}" Content="{DynamicResource Common_Add}" Click="Button_AddProduct_Click"/> <Button Name="Button_ModifyProduct" Visibility="{Binding ModifyButtonVisibility}" IsEnabled="{Binding CanModify}" Style="{StaticResource ModifyIconButton}" Content="{DynamicResource Common_Modify}" Click="Button_ModifyProduct_Click"/> <Button Name="Button_DeleteProduct" Visibility="{Binding DeleteButtonVisibility}" Style="{StaticResource DeleteIconButton}" Content="{DynamicResource Common_Delete}" Command="{Binding DeleteCommand}"/> </StackPanel> </DockPanel> </HeaderedContentControl.Header> <DataGrid Name="DataGrid_Product" ItemsSource="{Binding Products}" SelectedItem="{Binding SelectedProduct}" SelectionMode="Single" AutoGenerateColumns="False" IsReadOnly="True"> <DataGrid.Columns> <DataGridTextColumn Header="{DynamicResource Product_Num}" Binding="{Binding Num}" Width="120"/> <DataGridTextColumn Header="{DynamicResource Product_Name_CH}" Binding="{Binding Name_CH}" Width="100"/> <DataGridTextColumn Header="{DynamicResource Product_Name_EN}" Binding="{Binding Name_EN}" Width="100"/> <DataGridTextColumn Header="{DynamicResource Product_Unit}" Binding="{Binding Unit}" Width="50"/> <DataGridTextColumn Header="{DynamicResource Product_Weight}" Binding="{Binding Weight}" Width="100"/> <DataGridTextColumn Header="{DynamicResource Product_Size}" Binding="{Binding Size}" Width="100"/> <DataGridTemplateColumn Header="{DynamicResource Product_CanSale}"> <DataGridTemplateColumn.CellTemplate> <DataTemplate> <CheckBox IsThreeState="False" IsChecked="{Binding CanSale}" IsEnabled="False" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" HorizontalAlignment="Center" Width="50"/> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn> <DataGridTextColumn Header="{DynamicResource Product_ReferencePrice}" Binding="{Binding ReferencePrice}" Width="100"/> <DataGridTemplateColumn Header="{DynamicResource Product_StopProduction}"> <DataGridTemplateColumn.CellTemplate> <DataTemplate> <CheckBox IsThreeState="False" IsChecked="{Binding StopProduction}" IsEnabled="False" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" HorizontalAlignment="Center" Width="50"/> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn> <DataGridTextColumn Header="{DynamicResource Product_MinSaleCount}" Binding="{Binding MinSaleCount}" Width="80"/> <DataGridTextColumn Header="{DynamicResource Product_PackageCount}" Binding="{Binding PackageCount}" Width="80"/> <DataGridTextColumn Header="{DynamicResource Product_Remark}" Binding="{Binding Remark}" Width="200"/> </DataGrid.Columns> <DataGrid.RowStyle> <Style TargetType="DataGridRow" BasedOn="{StaticResource {x:Type DataGridRow}}"> <Setter Property="ToolTip"> <Setter.Value> <Border Width="100" Height="100" BorderBrush="#FF7DB6D8" BorderThickness="1" Padding="1"> <Border.Resources> <product:PicUrlConverter x:Key="urlConverter"/> </Border.Resources> <Image Source="{Binding ProductPic, Converter={StaticResource urlConverter}}"/> </Border> </Setter.Value> </Setter> <EventSetter Event="MouseDoubleClick" Handler="Button_ModifyProduct_Click"/> </Style> </DataGrid.RowStyle> </DataGrid> </HeaderedContentControl> </Grid>
Related code in ViewModel
public class ProductListVM : ViewModelBase { public ProductListVM() { LoadData(); TreeVM.SelectedChanged += (s, e) => { LoadData(); if (TreeVM.SelectedModel != null && !string.IsNullOrEmpty(TreeVM.SelectedModel.ID)) CanAdd = true; else CanAdd = false; }; } private ClassifyTreeVM _treeVM; public ClassifyTreeVM TreeVM { get { return _treeVM ?? (_treeVM = new ClassifyTreeVM()); } } private tb_product _selectedProduct; public tb_product SelectedProduct { get { return _selectedProduct; } set { _selectedProduct = value; OnPropertyChanged("SelectedProduct"); if (value != null) CanModify = true; else CanModify = false; RaiseCanExecute(); } } private List<tb_product> _products; public List<tb_product> Products { get { return _products; } set { _products = value; OnPropertyChanged("Products"); } } protected override void LoadData() { if (TreeVM.SelectedModel != null && !string.IsNullOrEmpty(TreeVM.SelectedModel.ID)) Products = XDBContext.tb_product.Where(p => p.ClassifyID == TreeVM.SelectedModel.ID).AsNoTracking().ToList(); else Products = XDBContext.tb_product.AsNoTracking().ToList(); } }
public class ClassifyTreeVM : ViewModelBase { public ClassifyTreeVM() { LoadData(); } private BindingList<TB_ClassifyTreeModel> _classifyModels; public BindingList<TB_ClassifyTreeModel> ClassifyModels { get { return _classifyModels; } set { _classifyModels = value; OnPropertyChanged("ClassifyModels"); } } public event EventHandler SelectedChanged; private TB_ClassifyTreeModel _selectedModel; public TB_ClassifyTreeModel SelectedModel { get { return _selectedModel; } set { _selectedModel = value; OnPropertyChanged("SelectedModeld"); if (SelectedChanged != null) SelectedChanged(SelectedModel, null); } } protected override void LoadData() { ClassifyModels = new BindingList<TB_ClassifyTreeModel>(); ClassifyModels.Add(new TB_ClassifyTreeModel(XDBContext) { ClassifyName = "All", ID = string.Empty }); } }
For the storage of master-slave structure editing, UI binding is a master table model (including a slave table set), which is nothing special. You only need to save the information of the slave table when saving the table. (adding, deleting, and modifying the slave table may exist. The specific operation depends on the data operation layer, when data is less lazy, you can first delete all related records from the table and then add them, in this way, you do not need to distinguish which records are deleted, which are modified, and which are newly added. Generally, a state attribute is designed in the model to distinguish the state of the model)