以编程方式创建的控件不呈现

Programmatically created controls not rendering

因为我需要对文本框中的掩码输入进行排序,所以我决定构建自己的控件来处理这个问题。

许多模板之一可能是 "Size {enter size} Colour {enter colour}",我已将其分解以创建一系列控件。我命名为 CustomTextBox 的扩展 StackPanel 的自定义控件从构造函数生成以下内容。

// Pseudo
Children = {
    Label = { Content = "Size" },
    TextBox = { Text = "enter size" },
    Label = { Content = "Colour" },
    TextBox = { Text = "enter colour" }
    // .. and an arbitrary amount of more Labels and TextBoxes in no particular order
}

到目前为止一切顺利。但是当我想让它渲染时……这就是我头疼的地方。 我已尝试将控件添加到 Children 属性 和 parent 上的 Measure/Arrange、本身以及所有 ChildrenActualHeightActualWidth 确实会更改为 0 以外的其他内容,但它们不会 render/display/become 可见。

我也尝试过使用 ItemsControl 并将控件添加到 ItemsSource 属性 但无济于事。

我已尝试预先定义所有内容的大小,将背景颜色设为红色等等,但难以捉摸的控件仍未被捕获并绑定到我的屏幕上。

一定有一个巨大的 "Oooh..." 这里我找不到。我拒绝相信这是不可能的。我的意思是,它是 WPF。 WPF 真棒。

编辑 更新到我目前拥有的似乎最有可能工作的内容 - 但仍然没有。

我在设计器中所做的一切都会显示出来,但我在 CustomTextBox 中所做的任何事情都不会产生任何明显的差异。

编辑 更符合问题的新标题。

另外,我发现了几个以编程方式添加控件的示例。以 this article 为例。我看不出我的场景和他们的场景之间的区别,除了他们的工作和按钮是可见的。

更新3

错误的假设是,通过在代码隐藏中将新控件分配给它的名称(在 xaml 中指定),可以简单地替换可视化树中的控件

已更新2

你的错误如下。如果你写

    <TextBlock Name="tb" Text="tb"/>

然后在代码中你会做

tb = new TextBlock() { Text = "Test" };

然后你将有一个新的文本块作为变量,xaml中的任何内容都不会改变。您要么必须更改现有控件,要么删除旧控件并添加新控件。

我说的是您的标题、潜台词和说明。你不改变它们

更新:

下面是一个通过指定输入掩码动态创建控件的例子:

MainWindow.xaml

<Window x:Class="WpfApplication35.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525" xmlns:local="clr-namespace:WpfApplication35">
    <Grid>
        <local:UserControl1 x:Name="myUserControl"/>
    </Grid>
</Window>

MainWindow.cs

 public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            myUserControl.BuildControls("a {enter a} b {enter b1}{enter c2}");
        }
    }

UserControl1.xaml

  <UserControl x:Class="WpfApplication35.UserControl1"
                 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" 
                 mc:Ignorable="d" 
                 d:DesignHeight="30" d:DesignWidth="300">
        <WrapPanel Name="root" Orientation="Horizontal"/>
    </UserControl>

UserControl1.cs

    public partial class UserControl1 : UserControl
    {
        public List<CustomField> Fields = new List<CustomField>();
        public UserControl1()
        {
            InitializeComponent();
        }

        public UserControl1(string mask)
        {
            InitializeComponent();
            BuildControls(mask);
        }

        public void BuildControls(string mask)
        {
            //Parsing Input
            var fields = Regex.Split(mask, @"(.*?\}\s)");
            foreach (var item in fields)
            {
                if (item != "")
                {
                    int index = item.IndexOf('{');
                    string namestring = item.Substring(0, index).Trim();
                    var field = new CustomField() { Name = namestring };

                    string valuesstring = item.Substring(index, item.Length - index).Trim();
                    var values = valuesstring.Split(new char[] { '{', '}' }, StringSplitOptions.RemoveEmptyEntries);
                    foreach (var val in values)
                    {
                        var valuewrapper = new FieldValue() { Value = val };
                        field.Values.Add(valuewrapper);
                    }
                    Fields.Add(field);
                }
            }

            foreach (var field in Fields)
            {
                var stackPanel = new StackPanel() { Orientation = Orientation.Horizontal };
                var label = new Label() { Content = field.Name, Margin = new Thickness(4) };
                stackPanel.Children.Add(label);
                foreach (var item in field.Values)
                {
                    var tb = new TextBox() { Margin = new Thickness(4), Width = 200 };
                    tb.SetBinding(TextBox.TextProperty, new Binding() { Path = new PropertyPath("Value"), Source = item, Mode = BindingMode.TwoWay });
                    stackPanel.Children.Add(tb);
                }
                root.Children.Add(stackPanel);
            }
        }


    }

    public class CustomField
    {
        public string Name { get; set; }
        public List<FieldValue> Values = new List<FieldValue>();
    }
    public class FieldValue
    {
        public string Value { get; set; }
    }

这样,字段和值将由 UserControl1 中的字段 collection 表示。字段的值会随着用户键入内容而更新。但只有 one-way,即用户输入更新相应的值 属性,但在运行时更改值 属性 不会影响相应的文本框。要实现从值到文本框的更新,您必须实现 INotifyProperty 接口


已过时

既然你问了。 有数百种可能的实现,具体取决于您要实现的目标、验证方式、是否要使用 MVVM、是否要使用绑定等。通常有 2 种方法:创建用户控件和创建自定义控制。我相信第一个更适合你。

使用以下 xaml 创建一个用户控件:

  <Grid Height="24">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition/>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Label Content="Size: " Grid.Column="0"/>
        <TextBox Name="tbSize"  Grid.Column="1"/>
        <Label Content="Colour:"  Grid.Column="2"/>
        <TextBox Name="tbColour"  Grid.Column="3"/>
    </Grid>

在 code-behind 中,您可以通过名称访问文本框并执行任何您想执行的操作。

您可以在 xaml 和代码隐藏中使用用户控件。 在 xaml:

为用户控件的命名空间指定别名(查看 xmlns:local)

<Window x:Class="WpfApplication35.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525" 
        xmlns:local="clr-namespace:WpfApplication35">
    <Grid>
        <local:UserControl1/>
    </Grid>
</Window>

在代码隐藏中你可以像这样使用它:

 public MainWindow()
        {
            InitializeComponent();
            var myUserControl = new UserControl1();
        }

有很多话要说,这些都是基本的东西,所以请查看教程并提出问题。

P.S。如果您正在学习 WPF,则必须学习绑定。