好的。删除所有代码并重新开始。
如果你正在使用 WPF,你真的需要The WPF Mentality
作为一般规则,您几乎从不在 WPF 的过程代码中创建或操作 UI 元素。
这就是 XAML 的用途。
这是在 WPF 中执行您所要求的正确方法(在一个完整的工作示例中):
<Window x:Class="MiscSamples.ItemsControlSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dxe="http://schemas.devexpress.com/winfx/2008/xaml/editors"
Title="ItemsControlSample" Height="300" Width="300">
<DockPanel>
<Button Content="Add New Row" Command="{Binding AddNewRowCommand}"
DockPanel.Dock="Bottom"/>
<ItemsControl ItemsSource="{Binding Data}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Black" Background="Gainsboro" BorderThickness="1" Margin="2">
<!-- This is the Inner Grid for each element, which is represented in Brown color in your picture -->
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width=".2*"/>
<ColumnDefinition Width=".2*"/>
</Grid.ColumnDefinitions>
<Label Content="{Binding Label1Text}"
Margin="2"/>
<Button Content="Button1"
Command="{Binding DataContext.Command1, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding}"
Grid.Column="1" Margin="2"/>
<Button Content="Button2"
Command="{Binding DataContext.Command2, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding}"
Grid.Column="2" Margin="2"/>
<dxe:TextEdit Text="{Binding Text}"
Grid.Row="1" Grid.ColumnSpan="3"
Margin="2"/>
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.Template>
<ControlTemplate TargetType="ItemsControl">
<ScrollViewer CanContentScroll="True">
<ItemsPresenter/>
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</DockPanel>
</Window>
代码背后:
public partial class ItemsControlSample : Window
{
public ItemsControlSample()
{
InitializeComponent();
DataContext = new ItemsControlSampleViewModel();
}
}
视图模型:
public class ItemsControlSampleViewModel
{
public ObservableCollection<ItemsControlSampleData> Data { get; set; }
public Command AddNewRowCommand { get; set; }
public Command<ItemsControlSampleData> Command1 { get; set; }
public Command<ItemsControlSampleData> Command2 { get; set; }
public ItemsControlSampleViewModel()
{
var sampledata = Enumerable.Range(0, 10)
.Select(x => new ItemsControlSampleData()
{
Label1Text = "Label1 " + x.ToString(),
Text = "Text" + x.ToString()
});
Data = new ObservableCollection<ItemsControlSampleData>(sampledata);
AddNewRowCommand = new Command(AddNewRow);
Command1 = new Command<ItemsControlSampleData>(ExecuteCommand1);
Command2 = new Command<ItemsControlSampleData>(ExecuteCommand2);
}
private void AddNewRow()
{
Data.Add(new ItemsControlSampleData() {Label1Text = "Label 1 - New Row", Text = "New Row Text"});
}
private void ExecuteCommand1(ItemsControlSampleData data)
{
MessageBox.Show("Command1 - " + data.Label1Text);
}
private void ExecuteCommand2(ItemsControlSampleData data)
{
MessageBox.Show("Command2 - " + data.Label1Text);
}
}
数据项:
public class ItemsControlSampleData
{
public string Label1Text { get; set; }
public string Text { get; set; }
}
助手类:
public class Command : ICommand
{
public Action Action { get; set; }
public string DisplayName { get; set; }
public void Execute(object parameter)
{
if (Action != null)
Action();
}
public bool CanExecute(object parameter)
{
return IsEnabled;
}
private bool _isEnabled = true;
public bool IsEnabled
{
get { return _isEnabled; }
set
{
_isEnabled = value;
if (CanExecuteChanged != null)
CanExecuteChanged(this, EventArgs.Empty);
}
}
public event EventHandler CanExecuteChanged;
public Command(Action action)
{
Action = action;
}
}
public class Command<T>: ICommand
{
public Action<T> Action { get; set; }
public void Execute(object parameter)
{
if (Action != null && parameter is T)
Action((T)parameter);
}
public bool CanExecute(object parameter)
{
return IsEnabled;
}
private bool _isEnabled = true;
public bool IsEnabled
{
get { return _isEnabled; }
set
{
_isEnabled = value;
if (CanExecuteChanged != null)
CanExecuteChanged(this, EventArgs.Empty);
}
}
public event EventHandler CanExecuteChanged;
public Command(Action<T> action)
{
Action = action;
}
}
结果:
- 请注意,我不是在过程代码中处理 UI,而是使用DataBinding 和简单、简单的属性。这就是您在 WPF 中编程的方式。这就是 WPF 的理念所在。
- 我使用在 XAML 中定义的 ItemsControl 和 DataTemplate 让 WPF 为我的每个数据项创建 UI。
- 还要注意我的代码除了公开数据和定义可重用的Commands 之外什么都不做,这些Commands 用作对用户操作(例如按钮单击)的抽象。这样一来,您就可以专注于编写业务逻辑,而不是纠结于如何让 UI 正常工作。
- 每个项目内的按钮都使用RelativeSource 绑定绑定到命令,以便在可视树中向上导航并找到 ItemsControl 的 DataContext,其中实际定义了命令。
- 当您需要添加新项目时,只需将新项目添加到包含您的数据的
ObservableCollection,WPF 就会自动创建绑定到该项目的新 UI 元素。
- 虽然这看起来像是“代码过多”,但我在此处发布的大部分代码都是高度可重用的,并且可以在通用
ViewModel<T> 中实现,然后可重用于任何类型的数据项。 Command 和 Command<T> 也是一次性可重用类,可以在任何 MVVM 框架中找到,例如 Prism、MVVM Light 或 Caliburn.Micro。
- 这种方法在 WPF 中非常受欢迎,因为它在 UI 和业务逻辑之间实现了极大的可扩展性和独立性,并且还实现了 ViewModel 的可测试性。
- 我建议您阅读帖子中链接的所有材料,最重要的是 Rachel 的 WPF Mentality 和相关博客文章。如果您需要进一步的帮助,请告诉我。
-
WPF 摇滚。只需将我的代码复制并粘贴到
File -> New Project -> WPF Application 中,然后自己查看结果。