我对C#很陌生,并且遇到了以下问题:我的程序在运行时创建了一个对象字典(大小未知)。对于我想创建一个内部有两个TextBox的StackPanel元素的每个对象,我的操作如下:
foreach (string name in a.Keys){
StackPanel SP = new StackPanel();
TextBox TB1 = new TextBox();
TextBox TB2 = new TextBox();
SP.Children.Add(TB1);
SP.Children.Add(TB2);
// ... Content to TextBoxes
TB2.Name = name;
}
现在,在程序的其他地方,我放了一个Checkbox,如果上面每个StackPanel中的第二个文本框可见,应该切换,例如,通过以下方式:
void MainCheckBox_Clicked(object sender, RoutedEventArgs e){
if (MainCheckBox.IsChecked == true){XXX.Height = 300;}
else {XXX.Height = 0;}
}
这里的XXX应该是对名为'TB2'的TextBox的引用。但是使用列表中的任何“名称”(应该对应于TB2的名称)都不起作用,编译器说'name'不存在,即使我确实使用列表中的具体名称,例如'约翰'(假设他在名单中)。
我认为我在做一些概念错误的事情,那么根据数据创建此类事物的更好方法是什么?
提前致谢!
答案 0 :(得分:3)
您需要保留对您创建的TextBox
个对象的引用。例如。您可以创建Dictionary<string, TextBox>
来存储引用:
private Dictionary<string, TextBox> myTextBoxes = new Dictionary<string, TextBox>();
创建TextBox
对象时,您可以将它们添加到字典中:
myTextBoxes.Add(name, TB2);
然后,您可以像这样访问TextBox
:
myTextBoxes["TextBoxName"].Height = 300;
答案 1 :(得分:1)
执行此操作的简单方法是遍历所有控件,然后检查类型和名称(或您为控件提供的标记)。然后你可以正常处理控制。对于迭代,我将使用以下内容:
foreach(TextBox tb in this.Controls.OfType<TextBox>().Where(t => t.Name.Equals("SEARCHEDNAME")))
{
//Do something with the control and then u can break
}
答案 2 :(得分:1)
你可以搜索WPF的VisualTree来查找带有你名字的对象(我有一些你可能觉得有用的VisualTreeHelpers
on my blog,并且它们会显示一些Visual Tree Navigation示例)
var tb = VisualTreeHelpers.FindChild<TextBox>(this, tbName);
if (tb != null)
tb.Height = (MainCheckBox.IsChecked ? 300 : 0);
或者您可以利用WPF的绑定系统为您执行此操作。就个人而言,如果您在代码隐藏中进行控制,我会使用Style执行此操作
<Style x:Key="SecondTextBoxStyle" TargetType="{x:Type TextBox}">
<Setter Property="Height" Value="0" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=MainCheckBox, Path=IsChecked}" Value="True">
<Setter Property="Height" Value="300" />
</DataTrigger>
</Style.Triggers>
</Style>
然后当您创建TextBoxes时:
TB2.Style = this.FindResource("SecondTextBoxStyle") as Style;
在理想的世界中,我甚至不会使用代码隐藏来制作对象。相反,我会使用绑定到ItemsControl
ObservableCollection的Keys
,并将ItemTemplate
设置为您想要的任何内容。
<ItemsControl ItemsSource="{Binding Keys}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBox ... />
<TextBox Style="{StaticResource SecondTextBoxStyle}" ... />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
答案 3 :(得分:1)
简单的方法是获取动态创建的对象 - 将它们放在一个集合中 - 创建一个datatemplate,以便wpf知道如何渲染 - 并将这些集合放到itemscontrol中。有些人首先用viewmodel调用这个mvvm;)
动态对象包装器
public class MyObject //should implement INotifyPropertyChanged
{
//your public properties goes here
}
用于处理集合和检索集合数据的viewmodel
public class MyViewmodelWithDynamicCollection //should implement INotifyPropertyChanged
{
public ObservableCollection<MyObject> MyItems {get;set;}//just init once and add, remove, clear like you want
}
在您的视图资源中
<DataTemplate DataType="{x:Type local:MyObject}">
<StackPanel>
<TextBlock Text="{Binding MyReadonlyProperty}"/>
<TextBox Text="{Binding MyEditableProperty}"/>
</StackPanel>
</DataTemplate>
在您的视图中
<ItemsControl ItemsSource="{Binding MyItems}"/>
那就是
答案 4 :(得分:0)
将对象存储在字典中后,名称就是字典中的键。这对编译器没有任何影响;该名称不是编译器的已知标识符。
这同样适用于您为控件的Name
属性赋值的值。对于编译器来说,这只是一个普通的字符串,也可以是一个帮助文本;编译器认为它没有任何特殊之处,因为该属性被称为Name
。
您可以使用a["myName"]
等表达式从字典中检索对象;并且您可以通过迭代所有控件并比较其Name
属性的值来找到具有特定名称的控件(根据Name
属性)。
如果您经常访问这些控件,则可能需要设置一个附加字典,以便按名称存储控件(例如Dictionary<string, TextBox>
)。
答案 5 :(得分:0)
其他答案中提供了很多变通方法;他们或多或少地解决了你的问题。还有一个解决方法:
foreach (string name in a.Keys){
StackPanel SP = new StackPanel();
TextBox TB1 = new TextBox();
TextBox TB2 = new TextBox();
SP.Children.Add(TB1);
SP.Children.Add(TB2);
// ... Content to TextBoxes
TB2.Name = name;
MainCheckBox.Clicked += (sender, e) => {
TB2.Height = MainCheckBox.IsChecked ? 300 : 0;
}
}
但是让我们看看应该采用什么方法。
首先,您需要以可绑定的形式呈现数据项。这意味着从DependencyObject
派生或实施INotifyPropertyChanged
。其次,您需要将对象放入ObservableCollection
。然后,您可以使用不同的容器(如ListView
或ListBox
)来显示数据集合。对于表示数据项的类型,您需要创建一个DataTemplate
,您可以在其中放置所需的任何自定义逻辑。
你看,这是相当多的工作,所以我不推荐这种方法用于真正的小应用程序;然而,随着您的应用程序的增长,这种方法将显示其优势。