下图为OpenExpressApp的系统架构图,其中在UI层支持WPF和ASP.NET MVC,目前首先实现了对WPF的支持。在中的ObjectView的生成控件功能都是委托给AutoUI静态类库来完成的,本篇将讲解AutoUI功能。
应用模型贯穿于整个架构层
哪些地方调用了AutoUI静态类
-
ListObjectView中树形列表视图的TreeListEditor的CreateControl,调用了AutoUI.CreateTreeListControl生成TreeLi st控件
public class TreeListEditor : ListEditor { public override object CreateControl() { Control = AutoUI.CreateTreeListControl(BOType, View); ... } } - ListObjectView中Grid列表视图的ListEditor的CreateControl,调用了AutoUI.CreateListControl生成Grid控件
public class ListEditor { public virtual object CreateControl() { Control = AutoUI.CreateListControl(BOType, View); ... } } - DetailObjectView的CreateControl调用AutoUI.CreateDetailView生成对象编辑详细面板
public class DetailObjectView : ObjectView { public override object CreateControl() { var control = AutoUI.CreateDetailView(BOType, this , true ); return control; } } - NavigateQueryObjectView的CreateControl调用AutoUI.CreateDetailView生成对象导航详细面板
public class DetailObjectView : ObjectView { public override object CreateControl() { var control = AutoUI.CreateDetailView(BOType, this , true ); return control; } } - 模块和View通过CreateMainToolBar、CreateChildToolBar自动生成工具条
AutoUI静态类方法介绍
CreateDetailView
CreateDetailView生成详细信息视图,生成结果是一个DockPanel,分为上下两部分,上面部分是AutoGrid,用来显示详细属性编辑器控件,下面部分是子对象列表信息区域。
详细信息区域
通过对象标识【ShowInDetail】来判断是否需要显示该属性,如果需要显示,则通过属性编辑器来生成具体的控件,然后布局在AutoGrid中
Code AutoGrid detailGrid = new AutoGrid(); detailGrid.SetValue(DockPanel.DockProperty, Dock.Top); //一般加四列,ConditionQuery/NavigateQuery加两列 detailGrid.ColumnDefinitions.Add(new ColumnDefinition() { Width = GridLength.Auto }); detailGrid.ColumnDefinitions.Add(new ColumnDefinition()); if (RegionType.Data == detailView.RegionType) { detailGrid.ColumnDefinitions.Add(new ColumnDefinition() { Width = GridLength.Auto }); detailGrid.ColumnDefinitions.Add(new ColumnDefinition()); } //加入所有标记了ShowInDetail的属性 var typeInfo = ApplicationModel.GetBusinessObjectInfo(boType); foreach (var p in typeInfo.BOPropertyInfos) { if (p.ShowInDetail) { //detailGrid.RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto }); PropertyEditor editor = PropertyEditorsFactory.GetDefaultEditor(p.EditorName); editor.CreateControl(p, detailView); detailGrid.Children.Add(editor.LabelControl as UIElement); detailGrid.Children.Add(editor.Control as UIElement); } } 子对象区域
如果有子对象,则生成一个子对象区域,现在是生成页签样式,每个子对象分别一个Tab页面,子对象又可以层级生成子对象。
Code //生成子UIElement UIElement child = CreateChildObjectView(boType, detailView, detailGrid, recur); if (null != child) { detailPanel.Children.Add(child); } else { detailPanel.Children.Add(new Label()); } 在生成子对象时,需要考虑生成子对象工具条、导航面板、布局等
Code /// <summary> /// 生成子控件 /// </summary> /// <param name="parentType">根据这个对象的孩子属性生成</param> /// <param name="parentView">生成这个视图的子控件</param> /// <param name="parentControl">已经生成好的父控件</param> /// <param name="recur">是否递归生成下层的子控件</param> /// <returns></returns> private static UIElement CreateChildObjectView( Type parentType, ObjectView parentView, FrameworkElement parentControl, bool recur ) { //生成的控件都放在这个TabControl里面 TabControl tab = null; BusinessObjectInfo parentBOInfo = ApplicationModel.GetBusinessObjectInfo(parentType); IList<BusinessObjectsPropertyInfo> bosPropertyInfo = parentBOInfo.BOsPropertyInfos; if ((bosPropertyInfo.Count == 1) && (null != parentBOInfo.TreeChildPropertyInfo)) { //递归在父对象的View对应的Toolbar上生成按钮 BusinessObjectInfo treeChildBoInfo = parentBOInfo; while (null != treeChildBoInfo.TreeChildPropertyInfo) { treeChildBoInfo = treeChildBoInfo.TreeChildBoInfo; AutoUI.CreateChildToolBar(parentView.ToolBar, treeChildBoInfo.BOType, parentView); } } else if (((bosPropertyInfo.Count > 1) && (null != parentBOInfo.TreeChildPropertyInfo)) || ((bosPropertyInfo.Count > 0) && (null == parentBOInfo.TreeChildPropertyInfo))) { if (Direction.Horizontal == parentBOInfo.BusinessObjectAttribute.Direction) { parentControl.SetValue(DockPanel.DockProperty, Dock.Left); parentControl.SetValue(ResizingPanel.ResizeWidthProperty, new GridLength(200)); } //多个细表时设定细表高度 else if (parentControl.GetType() != typeof(AutoGrid)) { parentControl.SetValue(DockPanel.DockProperty, Dock.Top); parentControl.SetValue(ResizingPanel.ResizeHeightProperty, new GridLength(100)); } tab = new TabControl(); foreach (var o in bosPropertyInfo) { DockPanel rootDck = new DockPanel();//包含当前属性对应控件以及子属性对应控件 ToolBar childTb = null; //创建子Toolbar if (parentView.RegionType != RegionType.NavigateQuery) { childTb = new ToolBar(); childTb.SetValue(DockPanel.DockProperty, Dock.Top); rootDck.Children.Add(childTb); } ResizingPanel rootResizingPanel = new ResizingPanel(); rootDck.Children.Add(rootResizingPanel); if (Direction.Horizontal == ApplicationModel.GetBusinessObjectInfo(o.BOType).BusinessObjectAttribute.Direction) { rootResizingPanel.Orientation = Orientation.Horizontal; } else { rootResizingPanel.Orientation = Orientation.Vertical; } //创建列表 ListObjectView lv = new ListObjectView(o); lv.PropertyName = o.Name; lv.RegionType = parentView.RegionType; parentView.AddChildView(lv); DockPanel dck = new DockPanel(); //添加导航面板 if (null != lv.NavigateQueryView) { //增加一个ListViewController,以便导航查询数据 UIElement navigateControl = lv.NavigateQueryView.Control as UIElement; //初始化导航面板对象 lv.NavigateQueryView.Data = Activator.CreateInstance(lv.NavigateQueryView.BOType); (lv.NavigateQueryView.CurrentObject as BusinessBase).BeginEdit(); ResizingPanel pnlNavigate = new ResizingPanel(); navigateControl.SetValue(ResizingPanel.ResizeWidthProperty, new GridLength(200)); pnlNavigate.Children.Add(navigateControl); ListViewController lvController = new ListViewController(lv, null); //添加列表 pnlNavigate.Children.Add(lvController.Control as UIElement); dck.Children.Add(pnlNavigate); } else //添加列表 dck.Children.Add(lv.Control as UIElement); rootResizingPanel.Children.Add(dck); StackPanel tabHeader = new StackPanel() { Orientation = Orientation.Horizontal }; tabHeader.Children.Add(new TextBlock() { Text = o.Label }); Button btnMax = new Button(); btnMax.CommandParameter = lv; ButtonCommand.SetCommand(btnMax, CommandRepository.Commands[CommandNames.MaxShowView]); tabHeader.Children.Add(btnMax); TabItem ti = new TabItem() { Header = tabHeader, Content = rootDck, }; tab.Items.Add(ti); if (null != childTb) { AutoUI.CreateChildToolBar(childTb, o.BOType, lv); } //递归查找子子对象 if (recur) { UIElement childchild = CreateChildObjectView(o.BOType, lv, dck, recur); if (null != childchild) { rootResizingPanel.Children.Add(childchild); } } } } return tab; } CreateListControl
CreateListControl生成DataGrid列表控件,通过对象类属性标识为【ShowInList】或者【ShowInLookup】来动态生成列,具体列编辑和显示控件由属性对应的GridColumn来完成。
Code public static object CreateListControl(Type boType, ListObjectView view) { DataGrid dg = new SelectionDataGrid() { CanUserAddRows = false, AutoGenerateColumns = false, VerticalGridLinesBrush = new SolidColorBrush(Colors.Gray), HorizontalGridLinesBrush = new SolidColorBrush(Colors.Gray), }; BusinessObjectInfo boInfo = ApplicationModel.GetBusinessObjectInfo(boType); if (0 != boInfo.BusinessObjectAttribute.Height) { dg.Height = (int)boInfo.BusinessObjectAttribute.Height; } //生成每一列 foreach (var item in boInfo.BOPropertyInfos) { bool generate; if (view.RegionType == RegionType.LookupList) { generate = item.ShowInLookup; } else { generate = item.ShowInList; } if (generate) { DataGridColumn c = item.CreateDefaultGridColumn(view); dg.Columns.Add(c); } } return dg; } CreateTreeListControl
CreateTreeListControl生成树形控件。由于树形控件支持多对象显示,现在生成时是采用一种简便的算法,如果各对象的属性Name一样,则认为显示在同一列中;父对象的属性排在前面。
Code /// <summary> /// 自动生成树形列表UI /// </summary> /// <param name="boType"></param> /// <returns></returns> public static object CreateTreeListControl(Type boType, ListObjectView view) { MultiObjectTreeView otv = new MultiObjectTreeView(); otv.SelectNodesOnRightClick = true; //都按懒加载关系处理 //if (ApplicationModel.GetBusinessObjectInfo(boType).LazyTreeNodeRelation) // otv.LazyTreeNodeRelation = true; //装载多个对象的属性,按照 BusinessObjectInfo boInfo = ApplicationModel.GetBusinessObjectInfo(boType); IList<BusinessObjectPropertyInfo> propInfos = new List<BusinessObjectPropertyInfo>(); foreach (var item in boInfo.BOPropertyInfos) { propInfos.Add(item); } //把模型下的需要一同显示在树中的子模型的列,加入List中 while (null != boInfo.TreeChildPropertyInfo) { boInfo = boInfo.TreeChildBoInfo; foreach (var item in boInfo.BOPropertyInfos) { bool exist = false; foreach (var propInfo in propInfos) { if (propInfo.Name == item.Name) { exist = true; break; } } if (!exist) { propInfos.Add(item); } } } //使用list里面的属性生成每一列 foreach (var item in propInfos) { bool generate; if (view.RegionType == RegionType.LookupList) { generate = item.ShowInLookup; } else { generate = item.ShowInList; } if (generate) { GridViewColumn c = item.CreateDefaultTreeColumn(view); (otv.Tree as TreeListView).Columns.Add(c); } } return otv; } CreateMainToolBar、CreateChildToolBar
CreateMainToolBar生成根对象工具条,系统根据当前对象类型的相关设置在Command注册库中查找并自动添加
Code /// <summary> /// 生成主工具栏 /// </summary> /// <param name="mainToolbar"></param> /// <param name="boType"></param> /// <param name="view"></param> /// <param name="moduleType"></param> public static void CreateMainToolBar(ToolBar mainToolbar, Type boType, ObjectView view, ModuleType moduleType) { view.ToolBar = mainToolbar; //找到对应这个toolbar的所有command var commands = ApplicationModel.Commands.Where( c => ((c.TargetObjectType == boType) || (c.TargetObjectType == null)) && ((c.ModuleType == ModuleType.Unspecified) || (c.ModuleType == moduleType)) && ((c.ToolbarType == ToolbarType.Any) || (c.ToolbarType == ToolbarType.Main))); //CommandCategory.Filter类型下的命令需要在一个下拉列表中显示 Panel filterPanel = null; //ComboBox cb = null; foreach (var c in commands) { bool notVisible = ApplicationModel.IsNotVisibleCommand(c.Name, ApplicationModel.GetBusinessObjectInfo(boType)); if (notVisible == false && (c.CanVisible(view))) { Button btn = new Button() { Name = "btn" + c.Name, }; //第一个 if ((null == filterPanel) && (CommandCategory.Filter == c.CommandCategory)) { filterPanel = new StackPanel() { Orientation = Orientation.Horizontal }; mainToolbar.Items.Add(filterPanel); } btn.CommandParameter = view; ButtonCommand.SetCommand(btn, CommandRepository.Commands[c.Name]); if (CommandCategory.Filter == c.CommandCategory) { filterPanel.Children.Add(btn); } else { mainToolbar.Items.Add(btn); } } } if (0 == mainToolbar.Items.Count) { mainToolbar.Visibility = Visibility.Collapsed; } } CreateChildToolBar生成子对象工具条,这个方法一般在前面介绍的CreateDetailView方法中生成子对象区域时使用。
具体编辑器控件生成
AutoUI静态类方法主要是生成Grid、TreeGrid、Panel等大的控件,具体针对每个属性的编辑和显示控件是由属性编辑器、GridColumn和TreeColumn来生成的,而GridColumn和TreeColumn的具体编辑控件又是由属性编辑器来生成的,所以属性编辑器是AutoUI内部的重要部分。
属性编辑器
在《》讲过支持的属性编辑器类型,每种类型都对应一个编辑器类。由于每个属性编辑器都比较类似,下面简单说明一下string属性编辑器。
每个属性编辑器都有一个方法CreateControlCore来生成编辑控件,StringPropertyEditor 生成了一个TextBox并绑定到对象属性上。还有一个方法SetControlReadOnly来控制只读时控件的状态改变。
Code public class StringPropertyEditor : WPFPropertyEditor { private TextBox tb; public override void SetControlReadOnly() { tb.IsReadOnly = ReadOnly; } protected override object CreateControlCore() { tb = new TextBox() { Name = PropertyInfo.Name, }; // 绑定TextBox到对象属性 Binding textBinding = new Binding(PropertyInfo.Name); // 如果是只读属性,则设置binding mode if ( ! PropertyInfo.PropertyInfo.CanWrite) textBinding.Mode = BindingMode.OneWay; tb.SetBinding(TextBox.TextProperty, textBinding); return tb; } } 为了重用这些属性编辑器,GridColumn和TreeColumn的列的具体编辑控件都是由属性编辑器来生成,例如StringTreeColumn内部使用了StringPropertyEditor
Code public class StringTreeColumn : TreeColumn { private StringPropertyEditor _editor; public StringTreeColumn(BusinessObjectPropertyInfo info, ListObjectView view) : base (info, view) { this ._editor = new StringPropertyEditor(); } public override PropertyEditor Editor { get { return this ._editor; } } } 自定义对象编辑界面
前面讲的生成控件都是按照对象标识以及对象属性标识,以及相关的命令设置来生成。实际开发中,可能存在一些业务对象需要其他界面样式来显示(如使用报表控件ReportView来显示列表信息),而AutoUI是处理共性要求的界面表现,这时我们框架就需要提供一种扩展机制,允许用户自定义编辑界面来对应到业务对象。AutoUI生成各类UI时先判断业务对象该类型下的UI有没有自定义,如果有则直接生成对应的Type指定的UserControl,否则按照上面的AutoUI自定生成。
Code /// <summary> /// 业务对象模型 /// </summary> public class BusinessObjectInfo { /// <summary> /// AutoUI不满足应用需要时,通过挂接此属性来自定义明细UI /// </summary> public Type ModuleUIType { get; private set; } public Type ListViewUIType { get; private set; } public Type DetailViewUIType { get; private set; } } /// <summary> /// 业务对象模型 /// </summary> public class BusinessObjectInfo { /// <summary> /// AutoUI不满足应用需要时,通过挂接此属性来自定义明细UI /// </summary> public Type ModuleUIType { get; private set; } public Type ListViewUIType { get; private set; } public Type DetailViewUIType { get; private set; } }
更多内容:
欢迎转载,转载请注明:转载自 [ ]