當前位置: 妍妍網 > 碼農

C# WPF後台程式碼動態添加控制項

2024-01-28碼農

在wpf開發中,雖然可以透過XMAL編寫炫酷的界面,但是有時候需要動態定義控制項即:前台界面控制項數量或者型別需要解析的數據或者其它條件確認再生成,這時候我們就需要透過後台cs中編寫程式碼實作這一功能。

01

功能演示

02


功能說明

以上演示部份我們可以看到我前台的部份界面在表單載入後並沒有顯示,而是選擇檔解析後自動產生的,這種場景有時候也挺常用,特別是有大量同型別的數據顯示到同型別的控制項中時,我們就可以透過匯入txt、Xml等檔的形式然後自動生成. 本地主要是舉例演示實作這一功能,使用場景造得可能並不恰當,大家忍受下。

03


源碼實作

前台程式碼:

<UserControlx: class="Caliburn.Micro.Hello.DynamicalView"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:Caliburn.Micro.Hello"xmlns:cal="http://www.caliburnproject.org"xmlns:dxlc="http://schemas.devexpress.com/winfx/2008/xaml/layoutcontrol"mc:Ignorable="d"d:DesignHeight="450"d:DesignWidth="800"><Grid><Grid.RowDefinitions><RowDefinitionHeight="1.5*" /><RowDefinitionHeight="8.5*" /></Grid.RowDefinitions><Grid.ColumnDefinitions><ColumnDefinitionWidth="*" /><ColumnDefinitionWidth="*" /></Grid.ColumnDefinitions><StackPanelOrientation="Horizontal"VerticalAlignment="Center"Grid.Row="0"Grid.ColumnSpan="2"><TextBoxWidth="500"Height="30"Margin="3"Text="{Binding FilePath}"FontSize="14"Font style="Normal"IsReadOnly="True" /><ButtonContent="..."Margin="3"MinWidth="50"cal:Message.Attach="[Event Click] = [Action SelectFile()]" /></StackPanel><GroupBoxGrid.Column="0"Grid.Row="1"Margin="3"><GroupBox.Header><dxlc:LayoutItemLabel="Student"Foreground ="Green" /></GroupBox.Header><dxlc:LayoutControl><GridHorizontalAlignment="Left"VerticalAlignment="Top"Grid.Row="0"cal:Message.Attach="[Event Loaded] = [Action StudentGridLoaded($source)]" /></dxlc:LayoutControl></GroupBox><GroupBoxGrid.Column="1"Grid.Row="1"Margin="3"><GroupBox.Header><dxlc:LayoutItemLabel="Teacher"Foreground ="Blue" /></GroupBox.Header><dxlc:LayoutControl><GridHorizontalAlignment="Left"VerticalAlignment="Top"Grid.Row="0"cal:Message.Attach="[Event Loaded] = [Action TeacherGridLoaded($source)]" /></dxlc:LayoutControl></GroupBox></Grid></UserControl>

這裏使用了Caliburn.Micro框架,所以需要參照名稱空間

xmlns:cal="http://www.caliburnproject.org"

因為控制項數量不確定,需要顯示不全時行列可以拖動,實作這一功能只需要把控制項包裹進: < dxlc:LayoutControl >就可以。

後台程式碼:

  • using DevExpress.Xpf.Editors;using PropertyChanged;using System;using System.Collections.Generic;using System.IO;using System.Linq;using System.Threading;using System.Threading.Tasks;using System.Windows;using System.Windows.Controls;using System.Windows.Data;using System.Windows.Forms;using Binding = System.Windows.Data.Binding;using HorizontalAlignment = System.Windows.HorizontalAlignment;using Label = System.Windows.Controls.Label;namespaceCaliburn.Micro.Hello{ [AddINotifyPropertyChangedInterface]public classDynamicalViewModel : Screen, IViewModel {privatereadonly AutoResetEvent StudentGridLoad = new AutoResetEvent(false);privatereadonly AutoResetEvent TeacherGridLoad = new AutoResetEvent(false);publicstring FilePath { get; set; } = @"D:\test.txt";public List<PersonInfoDTO> PersonInfoList = new List<PersonInfoDTO>();public PersonInfo PersonInfo { get; set; }publicDynamicalViewModel() { DisplayName = "DynamicalControls"; }publicvoidDispalyReuslt() { Task.Run(() => { ParseData(); Execute.OnUIThread(() => { AddGridControl(); }); }); }privatevoidParseData() {var lines = File.ReadAllLines(FilePath);foreach (string line in lines) {var strs = line.Split(':');if (strs.Count() > 1) {var infos = strs[1].Split(','); PersonInfoList.Add(new PersonInfoDTO() { InfoType = strs[0], PersonInfo = new PersonInfo() { Name = infos[0], Sex = infos[1], Age = Convert.ToInt32(infos[2]) } }); } } }publicvoidSelectFile() {string defaultInputFolder = @"D:\test.txt"; OpenFileDialog fileDialog = new OpenFileDialog();if (defaultInputFolder != null) { fileDialog.InitialDirectory = defaultInputFolder; } fileDialog.Multiselect = false;//該值確定是否可以選擇多個檔 fileDialog.Title = "請選擇ReportFile檔"; fileDialog.Filter = "文字檔案(*.txt)|*.txt";if (fileDialog.ShowDialog() == DialogResult.OK) { FilePath = fileDialog.FileName; } DispalyReuslt(); }private Grid StudentGrid { get; set; }private Grid TeacherGrid { get; set; }publicvoidStudentGridLoaded(object sender) { StudentGrid = (Grid)sender; }publicvoidTeacherGridLoaded(object sender) { TeacherGrid = (Grid)sender; }int studentRowIndex = 0;int studentColumnIndex = 0;int teacherRowIndex = 0;int teacherColumnIndex = 0;privatevoidAddGridControl() {int StudentConut = 0;int TeacherCount = 0;foreach (var item in PersonInfoList) {if (item.InfoType == "老師") { TeacherCount++; }else { StudentConut++; } } StudentGrid.Children.Clear(); StudentGrid.ColumnDefinitions.Clear(); StudentGrid.RowDefinitions.Clear(); TeacherGrid.Children.Clear(); TeacherGrid.ColumnDefinitions.Clear(); TeacherGrid.RowDefinitions.Clear();var gridColumns = 4;var successGridRows = Math.Ceiling(StudentConut / 2.0);var failGridRows = Math.Ceiling(TeacherCount / 2.0);//添加grid列for (int i = 0; i < gridColumns; i++) {var successColumnDefinition = new ColumnDefinition(); StudentGrid.ColumnDefinitions.Add(successColumnDefinition);var failedColumnDefinition = new ColumnDefinition(); TeacherGrid.ColumnDefinitions.Add(failedColumnDefinition); }//添加grid行for (int i = 0; i < successGridRows; i++) {var successRowDefinition = new RowDefinition(); StudentGrid.RowDefinitions.Add(successRowDefinition); successRowDefinition.Height = new GridLength(30, GridUnitType.Pixel);//絕對尺寸 }for (int i = 0; i <= failGridRows; i++) {var failedRowDefinition = new RowDefinition(); TeacherGrid.RowDefinitions.Add(failedRowDefinition); failedRowDefinition.Height = new GridLength(30, GridUnitType.Pixel);//絕對尺寸 }int rowIndex = 0;int columnIndex = 0; UIElement uIElement = new UIElement();foreach (var item in PersonInfoList) {if (item.InfoType == "學生") {if (studentColumnIndex / 4 == 1) { studentColumnIndex = 0; studentRowIndex++; } rowIndex = studentRowIndex; columnIndex = studentColumnIndex; }else {if (teacherColumnIndex / 4 == 1) { teacherColumnIndex = 0; teacherRowIndex++; } rowIndex = teacherRowIndex; columnIndex = teacherColumnIndex; }if (columnIndex % 2 == 0) { Label label = new Label(); label.HorizontalAlignment = HorizontalAlignment.Right; label.VerticalAlignment = VerticalAlignment.Center; label.Width = 100; label.Content = item.PersonInfo.Name; label.SetValue(Grid.RowProperty, rowIndex); label.SetValue(Grid.ColumnProperty, columnIndex); uIElement = label;if (item.InfoType == "學生") { StudentGrid.Children.Add(uIElement); studentColumnIndex++; columnIndex = studentColumnIndex; }else { TeacherGrid.Children.Add(uIElement); teacherColumnIndex++; columnIndex = teacherColumnIndex; } } TextEdit textBox = new TextEdit(); textBox.HorizontalAlignment = HorizontalAlignment.Left; textBox.VerticalAlignment = VerticalAlignment.Center; textBox.Name = item.PersonInfo.Name; textBox.Width = 100; textBox.Height = 25; textBox.SetValue(Grid.RowProperty, rowIndex); textBox.SetValue(Grid.ColumnProperty, columnIndex);var path = item.PersonInfo.GetType().GetProperty("Age"); Binding binding = new Binding() { Source = item.PersonInfo, Path = new PropertyPath(path), Mode = BindingMode.TwoWay, UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged }; textBox.SetBinding(TextEdit.TextProperty, binding); uIElement = textBox;if (item.InfoType == "學生") { StudentGrid.Children.Add(uIElement); studentColumnIndex++; columnIndex = studentColumnIndex; }else { TeacherGrid.Children.Add(uIElement); teacherColumnIndex++; columnIndex = teacherColumnIndex; } } } }}

    數據模型:

    public classPersonInfo {publicstring Name { get; set; }publicint Age { get; set; }publicstring Sex { get; set; }publicoverridestringToString() {string report = $"[Name] = [{Name}],[Age] = [{Age}],[Sex] = [{Sex}]";return report; } }public classPersonInfoEven : PersonInfo { }public classPersonInfoDTO {publicstring InfoType { get; set; }public PersonInfo PersonInfo { get; set; } }

    這裏需要註意一些地方:

    ①首先StudentGridLoaded和TeacherGridLoaded是在viewmodel初始化完成後才載入的,所以在建構函式執行完後還是null;

    ②載入控制項和解析數據比較慢我放在了執行緒Task.Run執行,但是執行緒中更新界面又需要用委托實作,這裏CM給我們封裝了方法

    Execute.OnUIThread(() => { });

    ③:grid行列添加:

    var successColumnDefinition = new ColumnDefinition(); StudentGrid.ColumnDefinitions.Add(successColumnDefinition);var successRowDefinition = new RowDefinition(); StudentGrid.RowDefinitions.Add(successRowDefinition);

    ④透過程式碼生成TextEdit,bing數據並添加到grid中:

    TextEdittextBox = new TextEdit();textBox.HorizontalAlignment = HorizontalAlignment.Left;textBox.VerticalAlignment = VerticalAlignment.Center;textBox.Name = item.PersonInfo.Name;textBox.Width = 100;textBox.Height = 25;textBox.SetValue(Grid.RowProperty,rowIndex);textBox.SetValue(Grid.ColumnProperty,columnIndex);varpath = item.PersonInfo.GetType().GetProperty("Age");Bindingbinding = new Binding(){Source = item.PersonInfo,Path = new PropertyPath(path),Mode = BindingMode.TwoWay,UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged};textBox.SetBinding(TextEdit.TextProperty,binding);uIElement = textBox;TeacherGrid.Children.Add(uIElement);

    ⑤遍歷grid中的控制項:

    foreach (UIElement uiElement in failedParsedGrid.Children) {if (uiElement is TextEdit) { TextEdit textBox = uiElement as TextEdit;switch (textBox.Name) {//todo } } }

    ⑥透過反射遍歷內容:

    foreach (PropertyInfo info in PersonInfo.GetType().GetProperties()) {var itemValue = info.GetValue(PersonInfo);// TO DO }