经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » ASP.net » 查看文章
WPF应用开发之控件动态内容展示
来源:cnblogs  作者:伍华聪  时间:2023/12/1 9:10:27  对本文有异议

在我们开发一些复杂信息的时候,由于需要动态展示一些相关信息,因此我们需要考虑一些控件内容的动态展示,可以通过动态构建控件的方式进行显示,如动态选项卡展示不同的信息,或者动态展示一个自定义控件的内容等等,目的就是能够减少一些硬编码的处理方式,以及能够灵活的展示数据。本篇随笔通过实际案例介绍WPF应用开发之控件动态内容展示。

1、选项卡TabControl的动态内容展示

在我们客户关系管理模块中,往往需要展示一个客户相关的很多数据,我们可以把它们放在多个选项卡中进行统一展示,如下界面所示。

由于客户的相关模块信息比较多,因此我们通过选项卡的展示是比较合理的一种界面组织方式,这里由于不同的客户信息,他们展示的内容不同(但结构相同),因此可以考虑动态的刷新选项卡项目TabItem的内容数据进行。

因此我们这里引入一个自定义的控件AllRelatedListControl,用来承载所有需要展示的模块一个组合。

因此在主页面上,我们可以通过一个Divider分隔控件隔开,展示客户相关的数据,如下XAML 代码所示。

  1. <hc:Divider
  2. Margin="0"
  3. LineStroke="{DynamicResource DarkPrimaryBrush}"
  4. LineStrokeThickness="2" />
  5. <Grid Margin="0,5,0,0" Background="{DynamicResource BackgroundBrush}">
  6. <!-- 客户相关数据 -->
  7. <local:AllRelatedListControl x:Name="allRelatedList" CustomerId="{Binding SelectedItem.Id, ElementName=grid}" />
  8. </Grid>

这个自定义的控件,主要的作用是组合多个选项卡项目,减少主界面的代码,并增加一些共同的属性和方法来控制数据的更新显示的。

  1. <UserControl
  2. x:Class="WHC.SugarProject.CRM.WpfUI.Views.Pages.AllRelatedListControl"
  3. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  4. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  5. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  6. xmlns:local="clr-namespace:WHC.SugarProject.CRM.WpfUI.Views.Pages"
  7. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  8. x:Name="allList"
  9. d:DesignHeight="450"
  10. d:DesignWidth="800"
  11. Background="{DynamicResource RegionBrush}"
  12. mc:Ignorable="d">
  13. <Grid>
  14. <!-- 客户相关数据 -->
  15. <TabControl
  16. x:Name="tabControl"
  17. Height="auto"
  18. HorizontalAlignment="Left"
  19. HorizontalContentAlignment="Left"
  20. Background="LightCyan"
  21. Style="{StaticResource TabControlCapsuleSolid}"
  22. TabStripPlacement="Top">
  23. <TabItem Header="客户跟进" Tag="FollowControl">
  24. <local:FollowListControl CustomerId="{Binding CustomerId, ElementName=allList}" />
  25. </TabItem>
  26. <TabItem Header="联系人资料" Tag="ContactControl">
  27. <local:ContactListControl CustomerId="{Binding CustomerId, ElementName=allList}" />
  28. </TabItem>
  29. <TabItem Header="客户拜访" Tag="VisitControl">
  30. <local:VisitListControl CustomerId="{Binding CustomerId, ElementName=allList}" />
  31. </TabItem>
  32. <TabItem Header="销售机会" Tag="ChanceControl">
  33. <local:ChanceListControl CustomerId="{Binding CustomerId, ElementName=allList}" />
  34. </TabItem>
  35. <TabItem Header="客户文档" Tag="FileDataControl">
  36. <local:FileDataListControl CustomerId="{Binding CustomerId, ElementName=allList}" />
  37. </TabItem>
  38. <TabItem Header="合同文档" Tag="ContractControl">
  39. <local:ContractListControl CustomerId="{Binding CustomerId, ElementName=allList}" />
  40. </TabItem>
  41. <TabItem Header="产品报价" Tag="QuotationControl">
  42. <local:QuotationListControl CustomerId="{Binding CustomerId, ElementName=allList}" />
  43. </TabItem>
  44. <TabItem Header="客户来电" Tag="ComingCallControl">
  45. <local:ComingCallListControl CustomerId="{Binding CustomerId, ElementName=allList}" />
  46. </TabItem>
  47. <TabItem Header="发票记录" Tag="InvoiceControl">
  48. <local:InvoiceListControl CustomerId="{Binding CustomerId, ElementName=allList}" />
  49. </TabItem>
  50. <TabItem Header="维护记录" Tag="SupplierControl">
  51. <local:MaintenaceListControl CustomerId="{Binding CustomerId, ElementName=allList}" />
  52. </TabItem>
  53. <TabItem Header="售后服务" Tag="MaintenaceControl">
  54. <local:AfterSellListControl CustomerId="{Binding CustomerId, ElementName=allList}" />
  55. </TabItem>
  56. <TabItem Header="客户投诉" Tag="ComplaintControl">
  57. <local:ComplaintListControl CustomerId="{Binding CustomerId, ElementName=allList}" />
  58. </TabItem>
  59. <TabItem Header="客户活动" Tag="ActivityControl">
  60. <local:ActivityListControl CustomerId="{Binding CustomerId, ElementName=allList}" />
  61. </TabItem>
  62.  
  63. <TabItem Header="收货地址" Tag="">
  64. <local:ShippingListControl CustomerId="{Binding CustomerId, ElementName=allList}" />
  65. </TabItem>
  66. </TabControl>
  67. </Grid>
  68. </UserControl>

上面的用户控件的界面效果如下所示,它的作用就是组合多个不同的页面。

 和其他自定义控件一样,我们增加一些自定义的属性,如这里是客户的ID,用于赋值后刷新相关的数据的。

  1. /// <summary>
  2. /// 客户ID
  3. /// </summary>
  4. public string? CustomerId
  5. {
  6. get { return (string?)GetValue(CustomerIdProperty); }
  7. set { SetValue(CustomerIdProperty, value); }
  8. }
  9. public static readonly DependencyProperty CustomerIdProperty = DependencyProperty.Register(
  10. nameof(CustomerId), typeof(string), typeof(AllRelatedListControl),
  11. new FrameworkPropertyMetadata("-1", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnCustomerIdPropertyChanged)));
  12. private static async void OnCustomerIdPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  13. {
  14. if (d is not AllRelatedListControl control)
  15. return;
  16. if (control != null)
  17. {
  18. var oldValue = (string?)e.OldValue; // 旧的值
  19. var newValue = (string?)e.NewValue; // 更新的新的值
  20. //更新数据源
  21. await control.InitData(newValue);
  22. }
  23. }

而我们每个子控件里面,其实也是已经根据父控件的客户ID进行了绑定属性了,如下所示。

  1. <TabItem Header="客户跟进" Tag="FollowControl">
  2. <local:FollowListControl CustomerId="{Binding CustomerId, ElementName=allList}" />
  3. </TabItem>

如果我们要根据数据库的配置信息,用来判断哪个选项卡显示或者隐藏,那么可以进一步进行处理每个TabItem的Visibility即可

  1. //判断是否显示
  2. foreach (TabItem item in this.tabControl.Items)
  3. {
  4. if (!item.Tag.IsEmpty())
  5. {
  6. item.Visibility = CustomerTabItems.Contains(item.Tag.ToString()!) ? Visibility.Visible : Visibility.Collapsed;
  7. }
  8. }

在DataGrid的鼠标键按下左键的时候,我们刷新对应自定义控件的属性CustomerId,就可以刷新相关的客户数据了。

  1. /// <summary>
  2. /// 选中每个客户记录的时候,触发更新客户相关信息
  3. /// </summary>
  4. private async void grid_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
  5. {
  6. if (this.grid.SelectedItem is CustomerInfo selectItem)
  7. {
  8. allRelatedList.CustomerId = selectItem.Id;
  9. await allRelatedList.InitData(selectItem.Id);
  10. }
  11. }

以上就是对于复杂的客户信息内容,使用选项卡动态组合的方式,实现内容的动态展示处理操作。

 

2、动态自定义表单内容的展示

有时候,我们在工作流表单中,需要展示一些固定的申请单信息,以及根据不同流程的表单数据(结构也不同)进行展示,如工作流中,不同的表单数据结构是不同的。

 明细展示效果如下所示。

或者

可以看出不同的流程类型表单,它们的表单结构是不同的,如果我们在一个固定的页面里面展示数据,那么这块表单的数据展示,就需要用到动态控件的展示方式。

我们采用Grid布局排版方式,grid.row=0为固定表单(这里不再赘述),grid.row=1为动态表单内容,如下所示。

  1. <StackPanel Grid.Row="1" Margin="0,20,0,0">
  2. <TextBlock
  3. HorizontalAlignment="Center"
  4. Style="{StaticResource TextBlockSubTitleBold}"
  5. Text="表单信息" />
  6. <Frame x:Name="formContent" />
  7. </StackPanel>

其中里面<Frame x:Name="formContent" /> 就是我们根据实际表单的内容进行动态展示的地方。

  1. /// <summary>
  2. /// 该事件在loaded之后执行,也是在所有元素渲染结束之后执行
  3. /// </summary>
  4. /// <param name="e"></param>
  5. protected override async void OnContentRendered(EventArgs e)
  6. {
  7. base.OnContentRendered(e);
  8. //动态构建表单内容展示 formContent
  9. var formId = this.ViewModel.Item.FormId;
  10. if(!formId.IsNullOrEmpty())
  11. {
  12. var formInfo = await BLLFactory<IFormService>.Instance.GetAsync(formId);
  13. if(formInfo != null && !formInfo.ApplyWpfview.IsNullOrEmpty())
  14. {
  15. var control = ReflectionUtil.CreateInstance(formInfo.ApplyWpfview);
  16. var data = control as IApplyInit;
  17. if(data != null)
  18. {
  19. await data.InitData(this.ViewModel.Item.Id);
  20. }
  21. formContent.Content = control;
  22. }
  23. }
  24. //初始化工具栏
  25. await InitToolBar();
  26. }

我们根据表单ID获取对应的formInfo.ApplyWpfview 属性配置,他就是一个具体的控件的名称路径,因此我们根据这个来进行反射构建一个实例,并把它转换为 IApplyInit 的接口实例,进行调用控件初始化即可。

生成的控件当做Frame控件的Content,从而实现动态内容的展示了。

我们以第一个表单【故障维修】的自定义控件定义来看看

  1. public partial class MaintenanceViewControl : INavigableView<MaintenanceEditViewModel>, IApplyInit

它实现了 IApplyInit 接口,因此我们可以在动态控件的时候,把它转换为接口实例进行调用,这也是我们约束动态控件实例的一个规则。

不同的控件,他们的数据模型肯定不同,因此由它们自己本身实现具体的获取数据即可。

  1. /// <summary>
  2. /// 初始化相关业务表单数据
  3. /// </summary>
  4. /// <param name="applyId">申请单Id</param>
  5. /// <returns></returns>
  6. public async Task InitData(string applyId)
  7. {
  8. this.ViewModel.Item = await BLLFactory<IMaintenanceService>.Instance.FindByApplyId(applyId);
  9. this.ViewModel.NotifyChanged();
  10. }

这样我们自定义控件的XAML就可以顺利绑定对应的数据展示了,这些控件的内容,可以根据数据库接口,使用我们的代码生成工具进行快速生成,然后进行一定的裁剪调整即可,不必要一个个来编写代码。

  1. <StackPanel hc:TitleElement.TitleWidth="100">
  2. <hc:Row>
  3. <hc:Col Span="12">
  4. <TextBox
  5. x:Name="txtDeviceName"
  6. Margin="5"
  7. hc:TitleElement.Title="故障设备名称"
  8. hc:TitleElement.TitlePlacement="Left"
  9. IsReadOnly="True"
  10. Text="{Binding ViewModel.Item.DeviceName}" />
  11. </hc:Col>
  12. <hc:Col Span="12">
  13. <DatePicker
  14. x:Name="txtRepairDate"
  15. Margin="5"
  16. hc:InfoElement.Title="报修日期"
  17. hc:InfoElement.TitlePlacement="Left"
  18. IsEnabled="False"
  19. SelectedDate="{Binding ViewModel.Item.RepairDate, StringFormat='yyyy-MM-dd'}"
  20. Style="{StaticResource DatePickerExtend}" />
  21.  
  22. </hc:Col>
  23. </hc:Row>
  24. <hc:Row>
  25. <hc:Col>
  26. <TextBox
  27. x:Name="txtFaultDescription"
  28. Margin="5"
  29. hc:TitleElement.Title="故障描述"
  30. hc:TitleElement.TitlePlacement="Left"
  31. IsReadOnly="True"
  32. Text="{Binding ViewModel.Item.FaultDescription}" />
  33. </hc:Col>
  34. </hc:Row>
  35. <hc:Row>
  36. <hc:Col Span="12">
  37. <TextBox
  38. x:Name="txtRepairFee"
  39. Margin="5"
  40. hc:TitleElement.Title="预计维修费用"
  41. hc:TitleElement.TitlePlacement="Left"
  42. IsReadOnly="True"
  43. Text="{Binding ViewModel.Item.RepairFee}" />
  44. </hc:Col>
  45. <hc:Col Span="12" />
  46. </hc:Row>
  47.  
  48. <hc:Row>
  49. <hc:Col Span="24">
  50. <TextBox
  51. x:Name="txtNote"
  52. Height="50"
  53. Margin="5"
  54. VerticalAlignment="Top"
  55. VerticalContentAlignment="Top"
  56. hc:TitleElement.Title="备注信息"
  57. hc:TitleElement.TitlePlacement="Left"
  58. AcceptsReturn="True"
  59. IsReadOnly="True"
  60. Text="{Binding ViewModel.Item.Note}" />
  61. </hc:Col>
  62. </hc:Row>
  63. <hc:Row>
  64. <hc:Col>
  65. <control:AttachmentControl AttachmentGUID="{Binding ViewModel.Item.AttachGUID}" Text="相关附件" />
  66. </hc:Col>
  67. </hc:Row>
  68. </StackPanel>

可以看到这部分的代码,和我们生成的编辑界面的内容是相似的,只是进行了适量的裁剪处理,以及增加一些自定义控件,统一界面效果(如附件控件的展示)。

 

以上就是两种不同方式,动态构建不同的内容展示的处理,动态内容可以带来很好的处理便利,不过也不要滥用,比较反射太多还是会牺牲一些UI的性能,不过总体来说肯定是值得的,而且这也是一种UI的处理模式。

原文链接:https://www.cnblogs.com/wuhuacong/p/17866762.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号