【问题标题】:Programmatically created controls not rendering以编程方式创建的控件未呈现
【发布时间】:2015-12-12 15:45:28
【问题描述】:

由于需要对文本框中的输入进行排序,因此我决定构建自己的控件来处理此问题。

许多模板之一可能是“Size {enter size} Color {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 属性和Measure/Arrange,它本身和所有ChildrenActualHeightActualWidth 确实更改为 0 以外的其他内容,但它们不会呈现/显示/变得可见。

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

我已经尝试为所有内容预先定义尺寸,将背景涂成红色等等,但难以捉摸的控件仍有待捕捉并绑定到我的屏幕上。

这里一定有一个巨大的“哦……”,我就是找不到。我拒绝相信这是不可能的。我的意思是,它是WPF。 WPF 很棒。

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

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

编辑 更适合该问题的新标题。

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

【问题讨论】:

  • 有什么好的理由不创建一个简单的 UserControl?
  • @Clemens 现在最好的理由是,当面具是任意的时,我想不出一种方法来实现我想要的。
  • 没有必要覆盖任何东西。只需将控件添加到面板,例如UserControl 中的顶级水平 StackPanel。您正在谈论的 ItemsSource 属性也不清楚。您在任何地方都使用 ItemsControl 吗?如果没有看到一些代码,很难判断你哪里出错了。
  • @Heki ItemsSource 属性用于数据,而不用于可视元素。您可以创建一个用户控件并在其上放置带有标签的文本框,然后在代码隐藏中处理逻辑。无需实现继承自ItemsControl的自定义控件
  • @netaholic 这并不能解释我在StackPanel 上遇到的问题。但感谢您的澄清。

标签: c# wpf wpf-controls


【解决方案1】:

更新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 中的 Fields 集合表示。字段的值会随着用户键入内容而更新。但只有一种方式,即用户输入更新对应的Value属性,但在运行时改变Value属性不会影响对应的文本框。要实现从值到文本框的更新,您必须实现 INotifyProperty 接口


过时

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

使用以下 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>

在代码隐藏中,您可以通过名称访问 TextBox 并执行您想做的任何事情。

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

为用户控件的命名空间指定别名(查看 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();
        }

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

附:如果您正在学习 WPF,则必须学习绑定。

【讨论】:

  • 嗯,这一切都很好,但它没有涵盖需求的动态部分。我不知道我是否只需要一个标签、一个文本框还是十七对和两个额外的。它确实显示了我在UserControl 中的所有内容,但我以编程方式添加的任何控件都没有显示。
  • @Heki 我不清楚您的要求。您能否准确描述您要归档的行为?
  • 当我们在同一页面上时,我会更新问题。所以,我需要能够根据一些输入配置一个输入字段,比如“a {bla} b {bla} {bla}”,但它实际上可以是任何东西。我需要让用户只能编辑 {} 中的任何内容。有意义吗?
  • @Heki,有道理。现在我明白你为什么要使用 ItemsControl。我稍后会更新我的答案
  • @Heki 你觉得我的回答有帮助吗?还有什么我可以做的吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-07
  • 2010-12-15
  • 1970-01-01
相关资源
最近更新 更多