轉(zhuǎn)帖|使用教程|編輯:龔雪|2023-12-15 10:51:31.877|閱讀 103 次
概述:本文主要通過實際案例介紹WPF應(yīng)用開發(fā)之控件動態(tài)內(nèi)容展示,希望對大家有所幫助~
# 界面/圖表報表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
相關(guān)鏈接:
在我們開發(fā)一些復(fù)雜信息的時候,由于需要動態(tài)展示一些相關(guān)信息,因此我們需要考慮一些控件內(nèi)容的動態(tài)展示,可以通過動態(tài)構(gòu)建控件的方式進(jìn)行顯示,如動態(tài)選項卡展示不同的信息,或者動態(tài)展示一個自定義控件的內(nèi)容等等,目的就是能夠減少一些硬編碼的處理方式,以及能夠靈活的展示數(shù)據(jù)。本文主要通過實際案例介紹WPF應(yīng)用開發(fā)之控件動態(tài)內(nèi)容展示,希望對大家有所幫助~
PS:給大家推薦一個C#開發(fā)可以用到的界面組件——DevExpress WPF,它擁有120+個控件和庫,將幫助您交付滿足甚至超出企業(yè)需求的高性能業(yè)務(wù)應(yīng)用程序。通過DevExpress WPF能創(chuàng)建有著強大互動功能的XAML基礎(chǔ)應(yīng)用程序,這些應(yīng)用程序?qū)W⒂诋?dāng)代客戶的需求和構(gòu)建未來新一代支持觸摸的解決方案。
DevExpress技術(shù)交流群9:909157416 歡迎一起進(jìn)群討論
在我們客戶關(guān)系管理模塊中,往往需要展示一個客戶相關(guān)的很多數(shù)據(jù),可以把它們放在多個選項卡中進(jìn)行統(tǒng)一展示,如下界面所示。
由于客戶的相關(guān)模塊信息比較多,因此我們通過選項卡的展示是比較合理的一種界面組織方式,這里由于不同的客戶信息,他們展示的內(nèi)容不同(但結(jié)構(gòu)相同),因此可以考慮動態(tài)的刷新選項卡項目TabItem的內(nèi)容數(shù)據(jù)進(jìn)行。
因此我們這里引入一個自定義的控件AllRelatedListControl,用來承載所有需要展示的模塊一個組合。
在主頁面上,我們可以通過一個Divider分隔控件隔開,展示客戶相關(guān)的數(shù)據(jù),如下XAML 代碼所示。
<hc:Divider Margin="0" LineStroke="{DynamicResource DarkPrimaryBrush}" LineStrokeThickness="2" /> <Grid Margin="0,5,0,0" Background="{DynamicResource BackgroundBrush}"> <!-- 客戶相關(guān)數(shù)據(jù) --> <local:AllRelatedListControl x:Name="allRelatedList" CustomerId="{Binding SelectedItem.Id, ElementName=grid}" /> </Grid>
這個自定義的控件,主要的作用是組合多個選項卡項目,減少主界面的代碼,并增加一些共同的屬性和方法來控制數(shù)據(jù)的更新顯示的。
<UserControl x:Class="WHC.SugarProject.CRM.WpfUI.Views.Pages.AllRelatedListControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:WHC.SugarProject.CRM.WpfUI.Views.Pages" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" x:Name="allList" d:DesignHeight="450" d:DesignWidth="800" Background="{DynamicResource RegionBrush}" mc:Ignorable="d"> <Grid> <!-- 客戶相關(guān)數(shù)據(jù) --> <TabControl x:Name="tabControl" Height="auto" HorizontalAlignment="Left" HorizontalContentAlignment="Left" Background="LightCyan" Style="{StaticResource TabControlCapsuleSolid}" TabStripPlacement="Top"> <TabItem Header="客戶跟進(jìn)" Tag="FollowControl"> <local:FollowListControl CustomerId="{Binding CustomerId, ElementName=allList}" /> </TabItem> <TabItem Header="聯(lián)系人資料" Tag="ContactControl"> <local:ContactListControl CustomerId="{Binding CustomerId, ElementName=allList}" /> </TabItem> <TabItem Header="客戶拜訪" Tag="VisitControl"> <local:VisitListControl CustomerId="{Binding CustomerId, ElementName=allList}" /> </TabItem> <TabItem Header="銷售機會" Tag="ChanceControl"> <local:ChanceListControl CustomerId="{Binding CustomerId, ElementName=allList}" /> </TabItem> <TabItem Header="客戶文檔" Tag="FileDataControl"> <local:FileDataListControl CustomerId="{Binding CustomerId, ElementName=allList}" /> </TabItem> <TabItem Header="合同文檔" Tag="ContractControl"> <local:ContractListControl CustomerId="{Binding CustomerId, ElementName=allList}" /> </TabItem> <TabItem Header="產(chǎn)品報價" Tag="QuotationControl"> <local:QuotationListControl CustomerId="{Binding CustomerId, ElementName=allList}" /> </TabItem> <TabItem Header="客戶來電" Tag="ComingCallControl"> <local:ComingCallListControl CustomerId="{Binding CustomerId, ElementName=allList}" /> </TabItem> <TabItem Header="發(fā)票記錄" Tag="InvoiceControl"> <local:InvoiceListControl CustomerId="{Binding CustomerId, ElementName=allList}" /> </TabItem> <TabItem Header="維護(hù)記錄" Tag="SupplierControl"> <local:MaintenaceListControl CustomerId="{Binding CustomerId, ElementName=allList}" /> </TabItem> <TabItem Header="售后服務(wù)" Tag="MaintenaceControl"> <local:AfterSellListControl CustomerId="{Binding CustomerId, ElementName=allList}" /> </TabItem> <TabItem Header="客戶投訴" Tag="ComplaintControl"> <local:ComplaintListControl CustomerId="{Binding CustomerId, ElementName=allList}" /> </TabItem> <TabItem Header="客戶活動" Tag="ActivityControl"> <local:ActivityListControl CustomerId="{Binding CustomerId, ElementName=allList}" /> </TabItem> <TabItem Header="收貨地址" Tag=""> <local:ShippingListControl CustomerId="{Binding CustomerId, ElementName=allList}" /> </TabItem> </TabControl> </Grid> </UserControl>
上面的用戶控件的界面效果如下所示,它的作用就是組合多個不同的頁面。
和其他自定義控件一樣,我們增加一些自定義的屬性,如這里是客戶的ID,用于賦值后刷新相關(guān)的數(shù)據(jù)的。
/// <summary> /// 客戶ID /// </summary> public string? CustomerId { get { return (string?)GetValue(CustomerIdProperty); } set { SetValue(CustomerIdProperty, value); } } public static readonly DependencyProperty CustomerIdProperty = DependencyProperty.Register( nameof(CustomerId), typeof(string), typeof(AllRelatedListControl), new FrameworkPropertyMetadata("-1", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnCustomerIdPropertyChanged))); private static async void OnCustomerIdPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is not AllRelatedListControl control) return; if (control != null) { var oldValue = (string?)e.OldValue; // 舊的值 var newValue = (string?)e.NewValue; // 更新的新的值 //更新數(shù)據(jù)源 await control.InitData(newValue); } }
而我們每個子控件里面,其實也是已經(jīng)根據(jù)父控件的客戶ID進(jìn)行了綁定屬性了,如下所示。
<TabItem Header="客戶跟進(jìn)" Tag="FollowControl"> <local:FollowListControl CustomerId="{Binding CustomerId, ElementName=allList}" /> </TabItem>
如果我們要根據(jù)數(shù)據(jù)庫的配置信息,用來判斷哪個選項卡顯示或者隱藏,那么可以進(jìn)一步進(jìn)行處理每個TabItem的Visibility即可。
//判斷是否顯示 foreach (TabItem item in this.tabControl.Items) { if (!item.Tag.IsEmpty()) { item.Visibility = CustomerTabItems.Contains(item.Tag.ToString()!) ? Visibility.Visible : Visibility.Collapsed; } }
在DataGrid的鼠標(biāo)鍵按下左鍵的時候,我們刷新對應(yīng)自定義控件的屬性CustomerId,就可以刷新相關(guān)的客戶數(shù)據(jù)了。
/// <summary> /// 選中每個客戶記錄的時候,觸發(fā)更新客戶相關(guān)信息 /// </summary> private async void grid_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e) { if (this.grid.SelectedItem is CustomerInfo selectItem) { allRelatedList.CustomerId = selectItem.Id; await allRelatedList.InitData(selectItem.Id); } }
以上就是對于復(fù)雜的客戶信息內(nèi)容,使用選項卡動態(tài)組合的方式,實現(xiàn)內(nèi)容的動態(tài)展示處理操作。
有時候,我們在工作流表單中,需要展示一些固定的申請單信息,以及根據(jù)不同流程的表單數(shù)據(jù)(結(jié)構(gòu)也不同)進(jìn)行展示,如工作流中,不同的表單數(shù)據(jù)結(jié)構(gòu)是不同的。
明細(xì)展示效果如下所示。
或者
可以看出不同的流程類型表單,它們的表單結(jié)構(gòu)是不同的,如果我們在一個固定的頁面里面展示數(shù)據(jù),那么這塊表單的數(shù)據(jù)展示,就需要用到動態(tài)控件的展示方式。
我們采用Grid布局排版方式,grid.row=0為固定表單(這里不再贅述),grid.row=1為動態(tài)表單內(nèi)容,如下所示。
<StackPanel Grid.Row="1" Margin="0,20,0,0"> <TextBlock HorizontalAlignment="Center" Style="{StaticResource TextBlockSubTitleBold}" Text="表單信息" /> <Frame x:Name="formContent" /> </StackPanel>
其中里面Frame x:Name="formContent" 就是我們根據(jù)實際表單的內(nèi)容進(jìn)行動態(tài)展示的地方。
/// <summary> /// 該事件在loaded之后執(zhí)行,也是在所有元素渲染結(jié)束之后執(zhí)行 /// </summary> /// <param name="e"></param> protected override async void OnContentRendered(EventArgs e) { base.OnContentRendered(e); //動態(tài)構(gòu)建表單內(nèi)容展示 formContent var formId = this.ViewModel.Item.FormId; if(!formId.IsNullOrEmpty()) { var formInfo = await BLLFactory<IFormService>.Instance.GetAsync(formId); if(formInfo != null && !formInfo.ApplyWpfview.IsNullOrEmpty()) { var control = ReflectionUtil.CreateInstance(formInfo.ApplyWpfview); var data = control as IApplyInit; if(data != null) { await data.InitData(this.ViewModel.Item.Id); } formContent.Content = control; } } //初始化工具欄 await InitToolBar(); }
我們根據(jù)表單ID獲取對應(yīng)的formInfo.ApplyWpfview 屬性配置,他就是一個具體的控件的名稱路徑,因此我們根據(jù)這個來進(jìn)行反射構(gòu)建一個實例,并把它轉(zhuǎn)換為 IApplyInit 的接口實例,進(jìn)行調(diào)用控件初始化即可。
生成的控件當(dāng)做Frame控件的Content,從而實現(xiàn)動態(tài)內(nèi)容的展示了。
我們以第一個表單【故障維修】的自定義控件定義來看看:
public partial class MaintenanceViewControl : INavigableView<MaintenanceEditViewModel>, IApplyInit
它實現(xiàn)了 IApplyInit 接口,因此我們可以在動態(tài)控件的時候,把它轉(zhuǎn)換為接口實例進(jìn)行調(diào)用,這也是約束動態(tài)控件實例的一個規(guī)則。
不同的控件,他們的數(shù)據(jù)模型肯定不同,因此由它們自己本身實現(xiàn)具體的獲取數(shù)據(jù)即可。
/// <summary> /// 初始化相關(guān)業(yè)務(wù)表單數(shù)據(jù) /// </summary> /// <param name="applyId">申請單Id</param> /// <returns></returns> public async Task InitData(string applyId) { this.ViewModel.Item = await BLLFactory<IMaintenanceService>.Instance.FindByApplyId(applyId); this.ViewModel.NotifyChanged(); }
這樣我們自定義控件的XAML就可以順利綁定對應(yīng)的數(shù)據(jù)展示了,這些控件的內(nèi)容,可以根據(jù)數(shù)據(jù)庫接口,使用我們的代碼生成工具進(jìn)行快速生成,然后進(jìn)行一定的裁剪調(diào)整即可,不必要一個個來編寫代碼。
<StackPanel hc:TitleElement.TitleWidth="100"> <hc:Row> <hc:Col Span="12"> <TextBox x:Name="txtDeviceName" Margin="5" hc:TitleElement.Title="故障設(shè)備名稱" hc:TitleElement.TitlePlacement="Left" IsReadOnly="True" Text="{Binding ViewModel.Item.DeviceName}" /> </hc:Col> <hc:Col Span="12"> <DatePicker x:Name="txtRepairDate" Margin="5" hc:InfoElement.Title="報修日期" hc:InfoElement.TitlePlacement="Left" IsEnabled="False" SelectedDate="{Binding ViewModel.Item.RepairDate, StringFormat='yyyy-MM-dd'}" Style="{StaticResource DatePickerExtend}" /> </hc:Col> </hc:Row> <hc:Row> <hc:Col> <TextBox x:Name="txtFaultDescription" Margin="5" hc:TitleElement.Title="故障描述" hc:TitleElement.TitlePlacement="Left" IsReadOnly="True" Text="{Binding ViewModel.Item.FaultDescription}" /> </hc:Col> </hc:Row> <hc:Row> <hc:Col Span="12"> <TextBox x:Name="txtRepairFee" Margin="5" hc:TitleElement.Title="預(yù)計維修費用" hc:TitleElement.TitlePlacement="Left" IsReadOnly="True" Text="{Binding ViewModel.Item.RepairFee}" /> </hc:Col> <hc:Col Span="12" /> </hc:Row> <hc:Row> <hc:Col Span="24"> <TextBox x:Name="txtNote" Height="50" Margin="5" VerticalAlignment="Top" VerticalContentAlignment="Top" hc:TitleElement.Title="備注信息" hc:TitleElement.TitlePlacement="Left" AcceptsReturn="True" IsReadOnly="True" Text="{Binding ViewModel.Item.Note}" /> </hc:Col> </hc:Row> <hc:Row> <hc:Col> <control:AttachmentControl AttachmentGUID="{Binding ViewModel.Item.AttachGUID}" Text="相關(guān)附件" /> </hc:Col> </hc:Row> </StackPanel>
可以看到這部分的代碼,和我們生成的編輯界面的內(nèi)容是相似的,只是進(jìn)行了適量的裁剪處理,以及增加一些自定義控件,統(tǒng)一界面效果(如附件控件的展示)。
以上就是兩種不同方式,動態(tài)構(gòu)建不同的內(nèi)容展示的處理,動態(tài)內(nèi)容可以帶來很好的處理便利,不過也不要濫用,比較反射太多還是會犧牲一些UI的性能,不過總體來說肯定是值得的,而且這也是一種UI的處理模式。
本文轉(zhuǎn)載自:
本站文章除注明轉(zhuǎn)載外,均為本站原創(chuàng)或翻譯。歡迎任何形式的轉(zhuǎn)載,但請務(wù)必注明出處、不得修改原文相關(guān)鏈接,如果存在內(nèi)容上的異議請郵件反饋至chenjj@fc6vip.cn
文章轉(zhuǎn)載自:慧都網(wǎng)