我可以以编程方式将元素添加到 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 中的绑定。假设您希望您的Ellipse
s 通过用户的输入(例如在Button
点击)添加到您的Canvas
。我不确定 Canvas 是否用于此目的(它没有子元素的自动对齐),所以我使用 WrapPanel
代替(以允许它对齐项目)。我们需要 2 个Button
s(用于添加和删除Ellipse
s)。我添加了一个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# [关闭]的主要内容,如果未能解决你的问题,请参考以下文章