以编程方式创建的控件不呈现
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
、本身以及所有 Children
。 ActualHeight
和 ActualWidth
确实会更改为 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,则必须学习绑定。
因为我需要对文本框中的掩码输入进行排序,所以我决定构建自己的控件来处理这个问题。
许多模板之一可能是 "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
、本身以及所有 Children
。 ActualHeight
和 ActualWidth
确实会更改为 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,则必须学习绑定。