C# WPF后台代码动态添加控件

Posted dotNET跨平台

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C# WPF后台代码动态添加控件相关的知识,希望对你有一定的参考价值。

在wpf开发中,虽然可以通过XMAL编写炫酷的界面,但是有时候需要动态定义控件即:前台界面控件数量或者类型需要解析的数据或者其它条件确认再生成,这时候我们就需要通过后台cs中编写代码实现这一功能。

01

功能演示

02


功能说明

以上演示部分我们可以看到我前台的部分界面在窗体加载后并没有显示,而是选择文件解析后自动产生的,这种场景有时候也挺常用,特别是有大量同类型的数据显示到同类型的控件中时,我们就可以通过导入txt、Xml等文件的形式然后自动生成. 本地主要是举例演示实现这一功能,使用场景造得可能并不恰当,大家忍受下。

03


源码实现

前台代码:

<UserControl x: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>
            <RowDefinition Height="1.5*" />
            <RowDefinition Height="8.5*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <StackPanel Orientation="Horizontal" VerticalAlignment="Center" Grid.Row="0" Grid.ColumnSpan="2">
            <TextBox Width="500" Height="30"  Margin="3" Text="Binding FilePath" FontSize="14" 
                             FontStyle="Normal" IsReadOnly="True" />
            <Button  Content="..." Margin="3" MinWidth="50"
                    cal:Message.Attach="[Event Click] = [Action SelectFile()]" />
        </StackPanel>
        <GroupBox Grid.Column="0" Grid.Row="1" Margin="3">
            <GroupBox.Header>
                <dxlc:LayoutItem Label="Student" Foreground ="Green" />
            </GroupBox.Header>
            <dxlc:LayoutControl>
                <Grid  HorizontalAlignment="Left" VerticalAlignment="Top" Grid.Row="0"
                                     cal:Message.Attach="[Event Loaded] = [Action StudentGridLoaded($source)]" />
            </dxlc:LayoutControl>
        </GroupBox>
        <GroupBox Grid.Column="1" Grid.Row="1" Margin="3">
            <GroupBox.Header>
                <dxlc:LayoutItem Label="Teacher" Foreground ="Blue" />
            </GroupBox.Header>
            <dxlc:LayoutControl>
                <Grid  HorizontalAlignment="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;


namespace Caliburn.Micro.Hello

    [AddINotifyPropertyChangedInterface]
    public class DynamicalViewModel : Screen, IViewModel
    
        private readonly AutoResetEvent StudentGridLoad = new AutoResetEvent(false);


        private readonly AutoResetEvent TeacherGridLoad = new AutoResetEvent(false);
        public string FilePath  get; set;  = @"D:\\test.txt";
        public List<PersonInfoDTO> PersonInfoList = new List<PersonInfoDTO>();
        public PersonInfo PersonInfo  get; set; 
        public DynamicalViewModel()
        
            DisplayName = "DynamicalControls";
        


        public void DispalyReuslt()
        
            Task.Run(() =>
                        
                            ParseData();
                            Execute.OnUIThread(() =>
                            
                                AddGridControl();
                            );
                        );


        


        private void ParseData()
        
            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])
                        
                    );
                
            
        




        public void SelectFile()
        
            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; 
        public void StudentGridLoaded(object sender)
        
            StudentGrid = (Grid)sender;
        
        public void TeacherGridLoaded(object sender)
        
            TeacherGrid = (Grid)sender;
        


        int studentRowIndex = 0;
        int studentColumnIndex = 0;
        int teacherRowIndex = 0;
        int teacherColumnIndex = 0;
        private void AddGridControl()
        
            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 class PersonInfo
    
        public string Name  get; set; 
        public int Age  get; set; 
        public string Sex  get; set; 


        public override string ToString()
        
            string report = $"[Name] = [Name],[Age] = [Age],[Sex] = [Sex]";
            return report;
        


    
    public class PersonInfoEven : PersonInfo
    


    


    public class PersonInfoDTO
    
        public string 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中:

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;
                 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 


            

以上是关于C# WPF后台代码动态添加控件的主要内容,如果未能解决你的问题,请参考以下文章

C# WPF后台动态添加控件(经典)

c#怎样动态判断wpf窗口某一区域是不是有控件存在

WPF中TreeView控件数据绑定和后台动态添加数据

WPF C# 如何在动态添加的grid控件中添加某个网格中的image控件的单击事件?

C# WPF从后台代码生成行列可变的表格

C# WPF从后台代码生成行列可变的表格