我可以以编程方式将元素添加到 XAML 吗? WPF c# [关闭]

Posted

技术标签:

【中文标题】我可以以编程方式将元素添加到 XAML 吗? WPF c# [关闭]【英文标题】:Can i add elements programatically to XAML? WPF c# [closed] 【发布时间】:2021-12-29 10:46:42 【问题描述】:

我想通过用户输入添加画布元素。类似于单击按钮时,新的<Ellipse/> 元素会添加到画布内的 XAML 文件中。

<Canvas x:Name="GraphDisplayFrame" Grid.Column="1" Grid.Row="0" Grid.ColumnSpan="3" Grid.RowSpan="4">
            <Ellipse
                Width="50"
                Height="50"
                Stroke="Black"
                StrokeThickness="2"
                Canvas.Left="100"
                Canvas.Top="100" />
</Canvas>

我是 WPF 新手,我不确定这是否是正确的方法。

我正在尝试的另一件事是 System.Windows.Media,但操作 XAMl 文件看起来更容易和更好,因为那时绘图的位置被锚定在画布上。我不确定是否可以使用 System.Windows.Media 实现类似的功能。

所以我的问题在标题中,但我愿意接受其他建议。

【问题讨论】:

您可以在运行时向其他人添加元素,这不是问题。考虑到这一点,首选的方法是使用 wpf 的 mvvm 模式,这样您就可以实现关注点的绑定和分离。如果你走这条路,你可以使用可以为你完成工作的数据模板。 虽然你可以写 GraphDisplayFrame.Children.Add(new Ellipse ... ),但最好使用 ItemsControl,例如喜欢this 或this。 【参考方案1】:

您可能想了解 WPF 中的绑定。假设您希望您的Ellipses 通过用户的输入(例如在Button 点击)添加到您的Canvas。我不确定 Canvas 是否用于此目的(它没有子元素的自动对齐),所以我使用 WrapPanel 代替(以允许它对齐项目)。我们需要 2 个Buttons(用于添加删除Ellipses)。我添加了一个Label 来显示我们当前拥有的椭圆的数量。

XAML

<Window x:Class="WpfApp2.MainWindow"
        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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp2"
        mc:Ignorable="d"
        Name ="mainWindow"
        Title="Main Window"
        Width="800"
        MaxWidth="800"
        Height="450"
        MaxHeight="450">
    <Grid x:Name="MainGrid">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="50*"/>
            <ColumnDefinition Width="50*"/>
            <ColumnDefinition Width="50*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="50*"/>
            <RowDefinition Height="50*"/>
            <RowDefinition Height="50*"/>
            <RowDefinition Height="50*"/>
        </Grid.RowDefinitions>
        <Label Content="Binding ElementName=mainWindow, Path=EllipsesCount, UpdateSourceTrigger=PropertyChanged" 
               HorizontalContentAlignment="Center"
               VerticalContentAlignment="Center" 
               Grid.Row="0" 
               Background="DimGray" 
               Foreground="White"
               Margin="15,35" />
        <Button x:Name="BtnAddEllipse" 
                        Content="ADD ELLIPSE" 
                        Grid.Row="1" 
                        Margin="10, 25" FontSize="22" FontWeight="Bold"
                        Background="LightGreen"/>
        <Button x:Name="BtnRemoveEllipse" 
                        Content="REMOVE ELLIPSE" 
                        Grid.Row="2" 
                        Margin="10, 25" FontSize="22" FontWeight="Bold"
                        Background="IndianRed"/>
        <WrapPanel Orientation="Horizontal" 
                   Background="Gainsboro"
                   HorizontalAlignment="Stretch"
                   VerticalAlignment="Stretch"
                   Grid.Column="1" 
                   Grid.Row="0" 
                   Grid.ColumnSpan="3"
                   Grid.RowSpan="4" >
            <ItemsControl ItemsSource="Binding ElementName=mainWindow, Path=Ellipses, UpdateSourceTrigger=PropertyChanged">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <WrapPanel />
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
            </ItemsControl>
        </WrapPanel>
    </Grid>
</Window>

在这里,您可以看到 Label.Content 属性绑定到某个 EllipsesCount 属性(您将在下面的代码隐藏中看到它)。同样WrapPanel 绑定到 Ellipses 属性。

代码隐藏:(用于复制粘贴)

using System;
using System.Linq;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;

namespace WpfApp2

    public partial class MainWindow : Window, INotifyPropertyChanged
    
        // Text for Label about Ellipses amount in collection
        private object _ellipsesCount = "Current ellipses count: 0";
        public object EllipsesCount
        
            get => _ellipsesCount;
            set
            
                _ellipsesCount = "Current ellipses count: " + value;
                // When we set new value to this property - 
                // we call OnPropertyChanged notifier, so Label
                // would be "informed" about this change and will get new value
                OnPropertyChanged(nameof(EllipsesCount));
            
        

        // Collection for Ellipses
        private ObservableCollection<Ellipse> _ellipses;
        public ObservableCollection<Ellipse> Ellipses
        
            get => _ellipses;
            set
            
                _ellipses = value;                   
                OnPropertyChanged(nameof(Ellipses));
            
        

        // Hanlder, which would notify our Controls about property changes, so they will "update" itself with new values
        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged([CallerMemberName] string propertyName = "") =>
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

        // Just for random colors
        private readonly Random random = new Random();

        public MainWindow()
        
            InitializeComponent();

            // Initialize collection of Ellipses
            Ellipses = new ObservableCollection<Ellipse>();
            // Handle when collection is changed to update Label
            // with a new amount of Ellipses
            Ellipses.CollectionChanged += delegate 
            
                // Update counter of ellipses when new one added or existing removed
                EllipsesCount = Ellipses.Count;
            ;

            BtnAddEllipse.Click += delegate
            
                // Create an Ellipse with random stroke color
                var ellipse = new Ellipse
                
                    Width = 50,
                    Height = 50,
                    Margin = new Thickness(3),
                    Stroke = new SolidColorBrush(Color.FromRgb((byte)random.Next(255), (byte)random.Next(255), (byte)random.Next(255))),
                    StrokeThickness = 3
                ;
                // Add to collection of ellipses
                Ellipses.Add(ellipse);
            ;
            BtnRemoveEllipse.Click += delegate
            
                // Check, that Ellipses collection isn't null and empty,
                // so we can remove something from it
                if (Ellipses?.Count > 0)
                    Ellipses.Remove(Ellipses.Last()); // Removing last element
            ;
        
    

因此,实际上,您会看到“椭圆集合的内容”,而无需将椭圆直接添加到窗口中。绑定使WrapPanel 使用Ellipse 集合作为子元素的来源,这应该在WrapPanel 中(而不是我原来的答案,我们将Ellipse 作为子元素添加到Canvas)。


原始答案。

是的,你可以。例如(基于您的 XAML):

XAML(空窗口):

<Window x:Class="WPFApp.MainWindow"
        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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WPFApp"
        mc:Ignorable="d">
    <!-- No even Grid here -->
</Window>

代码隐藏(也检查 cmets):


    public partial class MainWindow : Window
    
        public MainWindow()
        
            InitializeComponent();
            // Setting Window properties (they not exists in XAML)
            // XAML: <Window ... Title="Main Window" Height="450" Width="800">...
            this.Title = "Main Window";
            this.Height = 450;
            this.Width = 800;

            // Create main Grid and register some its name
            // XAML: ...
            var mainGrid = new System.Windows.Controls.Grid();
            this.RegisterName("MainGrid", mainGrid);

            // Add row and column definitions (as Canvas below needs, at least 4 rows and 3 columns)
            for (int i = 0; i < 4; i++)
            
                mainGrid.RowDefinitions.Add(new System.Windows.Controls.RowDefinition  Height = new GridLength(50, GridUnitType.Star) );

                if (i < 3) // Needn't 4th column
                    mainGrid.ColumnDefinitions.Add(new System.Windows.Controls.ColumnDefinition  Width = new GridLength(50, GridUnitType.Star) );
            

            // Create Canvas and register its name too
            // XAML: ...
            var canvas = new System.Windows.Controls.Canvas
            
                // Just to be able see it at Window
                Background = System.Windows.Media.Brushes.LightGray
            ;
            this.RegisterName("GraphDisplayFrame", canvas);
            canvas.SetValue(System.Windows.Controls.Grid.ColumnProperty, 1);
            canvas.SetValue(System.Windows.Controls.Grid.RowProperty, 0);
            canvas.SetValue(System.Windows.Controls.Grid.ColumnSpanProperty, 3);
            canvas.SetValue(System.Windows.Controls.Grid.RowSpanProperty, 4);

            // Create Ellipse (child canvas element)
            // XAML: ...
            var ellipse = new System.Windows.Shapes.Ellipse
            
                Width = 50,
                Height = 50,
                Stroke = System.Windows.Media.Brushes.Black,
                StrokeThickness = 2
            ;
            ellipse.SetValue(System.Windows.Controls.Canvas.LeftProperty, 100D);
            ellipse.SetValue(System.Windows.Controls.Canvas.TopProperty, 100D);

            // Add child Ellipse to Canvas
            canvas.Children.Add(ellipse);
            // or you already can find Canvas by its name:
            (this.FindName("GraphDisplayFrame") as System.Windows.Controls.Canvas).Children.Add(ellipse);

            // Add Canvas to MainGrid. Find Grid by its registered name too
            (this.FindName("MainGrid") as System.Windows.Controls.Grid).Children.Add(canvas);

            // Set main Grid as window content
            this.Content = mainGrid;
        
    
 

因此,如您所见,XAML 标记非常紧凑,即代码隐藏标记。

【讨论】:

当您在 XAML 中没有任何内容时,如何实现使用 XAML 而不是代码隐藏的目标? @Piotr Golacki,它只是显示代码隐藏使用的可能性。

以上是关于我可以以编程方式将元素添加到 XAML 吗? WPF c# [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

可以以编程方式使用新的WP7剪切和粘贴功能吗?

C#/XAML 将图像添加到按钮

Mailchimp for WP 以编程方式添加订阅者

我可以以编程方式创建 WKInterfaceButton 吗?

以编程方式将系列添加到 OxyPlot 图表:没有显示

如何以编程方式将元素添加到 ConfigurationElementCollection?