亲爱的开发者,美好的一天。我叫丹尼。
这是我在 Stackoverflow 上发表的第一篇文章...因为我对 .NET Framework 还很陌生。 我确实通过几个论坛进行了非常彻底的搜索,但显然是用 Nose 在看。
我的问题是: 我正在编写一段脚本,用于读取一个目录中存在多少个 .txt 文件。
然后它通过“foreach”命令创建与 .txt 一样数量的 GroupBox(每个都有一个网格)。 在同一个 foreach 命令中,我使用:Grid.Children.Add(control)。 每次 foreach 迭代都有 2 个控件要添加到生成的网格中。
问题:
它不会那样做……有点。
它只进行 2 次迭代,无论有多少 .txt。
在输出对话框中它说:A first chance exception of type 'System.ArgumentException' occurred in PresentationCore.dll
。
如果我的解释没有我想的那么清楚,下面是我的脚本,感谢阅读:
using System;
using System.IO;
using System.Linq;
using System.Windows;
using System.Threading;
using System.Windows.Media;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Threading;
using System.Windows.Controls.Primitives;
namespace Notes
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
// Create Objects
public TextBox tBox = new TextBox();
public Label fLabel = new Label();
public GroupBox group = new GroupBox();
public Grid groupGrid = new Grid();
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// Global Variables
int Increment = 1;
string Dir = "DIRECTORY_HERE";
int leftMargin = 10;
int betweenMargin = 10;
// GroupBox Initials
group.Height = this.Height - 125;
group.Margin = new Thickness(0, 75, 0, 0);
group.HorizontalAlignment = HorizontalAlignment.Left;
group.VerticalAlignment = VerticalAlignment.Top;
// Grid Initials
groupGrid.Width = leftMargin;
groupGrid.Height = group.Height;
groupGrid.Margin = new Thickness(0, 0, 0, 0);
groupGrid.HorizontalAlignment = HorizontalAlignment.Left;
groupGrid.VerticalAlignment = VerticalAlignment.Top;
// Label Initials
fLabel.Width = 260;
fLabel.Height = 28;
fLabel.HorizontalAlignment = HorizontalAlignment.Left;
fLabel.VerticalAlignment = VerticalAlignment.Top;
fLabel.Margin = new Thickness(0, 0, 0, 0);
// TextBox Intitials
tBox.Width = 100;
tBox.Height = groupGrid.Height;
tBox.VerticalScrollBarVisibility = ScrollBarVisibility.Hidden;
tBox.HorizontalAlignment = HorizontalAlignment.Left;
tBox.VerticalAlignment = VerticalAlignment.Top;
tBox.Foreground = new SolidColorBrush(Colors.DarkCyan);
tBox.Background = new SolidColorBrush(Colors.Red);
tBox.TextWrapping = TextWrapping.NoWrap;
// Get paths of all files within given directory.
string[] Notes = Directory.GetFiles(Dir, "*.*", SearchOption.TopDirectoryOnly).ToArray();
foreach (string Note in Notes)
{
Console.WriteLine("NOTE: " + Note);
// Text settings
FileInfo fi = new FileInfo(Note);
TextReader tr = new StreamReader(fi.ToString());
tBox.Name = "tBox" + Increment.ToString();
tBox.Text = tr.ReadToEnd();
tBox.Margin = new Thickness(betweenMargin, 0, 0, 0);
// FileLabel settings
fLabel.Name = "fLabel" + Increment.ToString();
// GroupGrid settings
groupGrid.Name = "groupGrid" + Increment.ToString();
groupGrid.Children.Add(tBox); //error
groupGrid.Children.Add(fLabel); //error
// group settings
group.Width = leftMargin + (tBox.Width * Increment) + (betweenMargin * Increment);
group.Content = groupGrid;
// Increment
Increment++;
}
NotesDoc.Children.Add(group);
}
最佳答案
您正在尝试像编写 Windows 窗体应用程序一样编写 WPF 应用程序。相信我,如果您真正学习了一些 WPF 开发模式(例如 MVVM),您可以更轻松地实现您的目标。
例如,这可以使用 ObservableCollection 轻松完成持有所有(不知道你是怎么做到的)FileInfo实例(每个文件一个)。
然后你bind一个ItemsControl的 ItemsSource此集合的属性。现在,只要您向 ObservableCollection 添加一个新的 FileInfo 实例,ItemsControl 就会添加一个新行并将该行绑定(bind)到该实例。
当然,每一行的默认模板只是在每个实例上调用 .ToString(),所以你会得到一堆包含 "System.IO.FileInfo"的行强>。要显示有关每个 FileInfo 的信息,您可以添加 DataTemplate至 ItemsSource.ItemTemplate并添加绑定(bind)到 FileInfo 的公共(public)属性的控件。
在开发 WPF 应用程序时,了解其中一些基本模式非常重要。尝试以您尝试的方式从代码隐藏与 UI 交互是非常困难的。 WPF 应用程序的许多 UI 模式都针对在 XAML 中的使用进行了优化;尝试从代码隐藏中与这些(例如附加属性)交互可能会非常困惑和违反直觉。
这是我更新的一个简单的 MVVM 示例。要使用它,请创建一个名为 SimpleMVVM 的新解决方案(3.5 或更高版本)。在项目的根目录中创建一个名为 ViewModel 的新类,并将以下代码添加到文件中:
/// <summary>
/// A simple ViewModel to demonstrate MVVM
/// </summary>
public sealed class ViewModel : DependencyObject, IDataErrorInfo
{
#region Properties
#region DirectoryName
/// <summary>
/// The <see cref="DependencyProperty"/> for <see cref="DirectoryName"/>.
/// </summary>
public static readonly DependencyProperty DirectoryNameProperty =
DependencyProperty.Register(
DirectoryNameName,
typeof(string),
typeof(ViewModel),
new UIPropertyMetadata(null));
/// <summary>
/// The name of the <see cref="DirectoryName"/> <see cref="DependencyProperty"/>.
/// </summary>
public const string DirectoryNameName = "DirectoryName";
/// <summary>
///
/// </summary>
public object DirectoryName
{
get { return (object)GetValue(DirectoryNameProperty); }
set { SetValue(DirectoryNameProperty, value); }
}
#endregion
#region SelectedFile
/// <summary>
/// The <see cref="DependencyProperty"/> for <see cref="SelectedFile"/>.
/// </summary>
public static readonly DependencyProperty SelectedFileProperty =
DependencyProperty.Register(
SelectedFileName,
typeof(FileInfo),
typeof(ViewModel),
new UIPropertyMetadata(null));
/// <summary>
/// The name of the <see cref="SelectedFile"/> <see cref="DependencyProperty"/>.
/// </summary>
public const string SelectedFileName = "SelectedFile";
/// <summary>
///
/// </summary>
public FileInfo SelectedFile
{
get { return (FileInfo)GetValue(SelectedFileProperty); }
set { SetValue(SelectedFileProperty, value); }
}
#endregion
/// <summary>
/// The files found under <see cref="DirectoryName"/>.
/// </summary>
public ObservableCollection<FileInfo> Files { get; private set; }
/// <summary>
/// Holds the last filename error for IDataErrorInfo
/// </summary>
private string _lastDirectoryNameError = null;
#endregion
#region ctor
/// <summary>
/// Initializes a new instance of the <see cref="ViewModel"/> class.
/// </summary>
public ViewModel()
{
Files = new ObservableCollection<FileInfo>();
}
#endregion
#region methods
/// <summary>
/// Invoked whenever the effective value of any dependency property on this <see cref="T:System.Windows.DependencyObject"/> has been updated. The specific dependency property that changed is reported in the event data.
/// </summary>
/// <param name="e">Event data that will contain the dependency property identifier of interest, the property metadata for the type, and old and new values.</param>
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (e.Property == DirectoryNameProperty)
UpdateFiles(e.OldValue as string, e.NewValue as string);
}
/// <summary>
/// Updates <see cref="Files"/> when <see cref="DirectoryName"/> changes.
/// </summary>
/// <param name="oldDirectoryName">The old value of <see cref="DirectoryName"/></param>
/// <param name="newDirectoryName">The new value of <see cref="DirectoryName"/></param>
private void UpdateFiles(string oldDirectoryName, string newDirectoryName)
{
if (string.IsNullOrWhiteSpace(newDirectoryName))
{
Files.Clear();
return;
}
if (!string.IsNullOrEmpty(oldDirectoryName) &&
oldDirectoryName.Equals(newDirectoryName, StringComparison.OrdinalIgnoreCase))
return;
try
{
var di = new DirectoryInfo(Directory.Exists(newDirectoryName) ? newDirectoryName : Path.GetDirectoryName(newDirectoryName));
// dirty hack
if (di.ToString().Equals(".", StringComparison.OrdinalIgnoreCase))
{
_lastDirectoryNameError = "Not a valid directory name.";
return;
}
Files.Clear();
foreach (var file in di.GetFiles())
Files.Add(file);
_lastDirectoryNameError = null;
}
catch (Exception ioe)
{
_lastDirectoryNameError = ioe.Message;
}
}
#endregion
#region IDataErrorInfo
/// <summary>
/// Gets an error message indicating what is wrong with this object.
/// </summary>
/// <returns>An error message indicating what is wrong with this object. The default is an empty string ("").</returns>
string IDataErrorInfo.Error
{
get
{
return _lastDirectoryNameError;
}
}
/// <summary>
/// Gets the error message for the property with the given name.
/// </summary>
/// <returns>The error message for the property. The default is an empty string ("").</returns>
string IDataErrorInfo.this[string columnName]
{
get
{
if (columnName.Equals(DirectoryNameName, StringComparison.OrdinalIgnoreCase))
return _lastDirectoryNameError;
return null;
}
}
#endregion
}
接下来,打开 MainWindow.xaml 并将 xaml 替换为以下内容:
<Window
x:Class="SimpleMVVM.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:t="clr-namespace:SimpleMVVM"
Title="MainWindow"
Height="350"
Width="525">
<Window.DataContext>
<t:ViewModel />
</Window.DataContext>
<Window.Resources>
<Style
x:Key="alternatingListViewItemStyle"
TargetType="{x:Type ListViewItem}">
<Style.Triggers>
<Trigger
Property="ItemsControl.AlternationIndex"
Value="1">
<Setter
Property="Background"
Value="LightGray"></Setter>
</Trigger>
<Trigger
Property="ItemsControl.AlternationIndex"
Value="2">
<Setter
Property="Background"
Value="White"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition
Height="auto" />
<RowDefinition />
<RowDefinition
Height="auto" />
</Grid.RowDefinitions>
<TextBox
Margin="4"
Text="{Binding DirectoryName, ValidatesOnDataErrors=True, ValidatesOnExceptions=True,UpdateSourceTrigger=PropertyChanged}">
<TextBox.Style>
<Style
TargetType="TextBox">
<Setter
Property="ToolTip"
Value="Please enter a directory name" />
<Style.Triggers>
<Trigger
Property="Validation.HasError"
Value="true">
<Setter
Property="ToolTip"
Value="{Binding (Validation.Errors)[0].ErrorContent, RelativeSource={x:Static RelativeSource.Self}}" />
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
<ListView
Margin="4"
Grid.Row="1"
AlternationCount="2"
ItemsSource="{Binding Files}"
ItemContainerStyle="{StaticResource alternatingListViewItemStyle}"
SelectedItem="{Binding SelectedFile}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition
Width="100" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label
Grid.Row="0">Name</Label>
<Label
Grid.Row="1">Size</Label>
<Label
Grid.Row="2">Read Only</Label>
<Label
Grid.Row="3">Type</Label>
<TextBlock
Grid.Row="0"
Grid.Column="1"
Text="{Binding Name}" />
<TextBlock
Grid.Row="1"
Grid.Column="1"
Text="{Binding Length}" />
<CheckBox
Grid.Row="2"
Grid.Column="1"
IsChecked="{Binding IsReadOnly}"
IsEnabled="False" />
<TextBlock
Grid.Row="3"
Grid.Column="1"
Text="{Binding Extension}" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<StatusBar
Grid.Row="2">
<StatusBarItem
Content="{Binding SelectedFile.FullName, StringFormat='Selected: {0}', FallbackValue='Please enter a directory and select a file.'}" />
</StatusBar>
</Grid>
</Window>
(请大家原谅代码转储!)编译、修复错误并运行它。
关于c# - 在运行时通过 foreach 命令向网格添加多个控件 (C# WPF),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5476506/