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

时间:2015-09-16 07:19:36

标签: c# wpf wpf-controls

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

许多模板中的一个可以是"尺寸{输入尺寸}颜色{输入颜色}"我已经分解来创建一系列控件。扩展名为StackPanel CustomTextBox的自定义控件会从构造函数生成以下内容。

// 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为例。我没有看到我的场景和他们的场景之间的区别,除了他们的工作和按钮是可见的。

1 个答案:

答案 0 :(得分:0)

<强> UPDATE3

错误是假设,人们可以通过在代码中为控件的名称(在xaml中指定)指定一个新控件来简单地替换可视树中的控件

<强> Updated2

你的错误在追随。如果你写

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

然后在代码中你会做

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

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

我在谈论你的标题,潜台词和&amp;描述。你没有改变它们

<强>更新

以下是通过指定输入掩码动态创建控件的示例:

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属性不会影响相应的文本框。要实现从Value到文本框的更新,您必须实现INotifyProperty接口

过时

既然你问过。 有几百种可能的实现,取决于你想要实现的目标,你想要如何验证,你想使用MVVM,你想使用绑定等吗?通常有两种方法:创建usercontrol和创建自定义控制。我相信第一个更适合你。

使用以下xaml创建一个usercontrol:

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

在代码隐藏中,您可以按名称访问TextBoxes并执行您想要执行的操作。

您可以在xaml和codebehind中使用usercontrol。 在xaml:

指定usercontrol命名空间的别名(查看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,则必须学习绑定。