我的WPF应用程序中有一个列表框。
<ListBox ItemsSource="{Binding ButtonsCollection}">
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="2" >
**Here I want to insert the current button**
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
在我的viewmodel中,我有一组按钮,一个名为ButtonsCollection的属性。 让我们说: 内容为“a”的按钮, 内容为“b”的按钮, 内容为“c”的按钮。
现在我想让列表框显示每个带有边框的按钮,就像我在ItemTemplate中声明的一样。
答案 0 :(得分:1)
DataConmplate是为ItemsControl中的每个项目实例化的(在您的情况下为ListBox)。它的唯一作用是描述你的物品在渲染时的样子。
DataContext 应该包含描述UI状态的对象。
这是一个令人担忧的分离。通过这种方式,UI和后端可以由几个人独立开发,DataContext是合同。
当然,正如托马斯·克里斯托夫所指出的那样,框架并没有强迫你以任何方式这样做。
如果你这样做:
<ListBox ItemsSource="{Binding ButtonsCollection}">
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="2" >
<ContentControl Content={Binding}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
它可能有效,但只要将同一个集合绑定到另一个ItemsControl, 它会崩溃。为什么?因为WPF中的每个FrameworkElement都必须只有一个父级。 WPF将抛出异常“元素已经是另一个元素的子元素。”
如果您遵循MVVM模式并将按钮的逻辑表示封装在类中:
public class NamedCommand : ICommand
{
private Action _action;
public string Name { get; private set; }
public NamedCommand(string name, Action action)
{
Name = name;
_action = action;
}
public virtual bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
if (_action != null)
_action();
}
// Call this whenever you need to update the IsEnabled of the binding target
public void Update()
{
if (CanExecuteChanged != null)
CanExecuteChanged(this, EventArgs.Empty);
}
public event EventHandler CanExecuteChanged;
}
您可以将同一个集合绑定到多个控件,例如菜单栏,上下文菜单,侧面板等
在你的情况下,这是一个ListBox:
<ListBox ItemsSource="{Binding Commands}">
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="2" >
<Button Content="{Binding Name}" Command="{Binding}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
使用当前方法遇到的另一个问题是后台线程试图操纵按钮。 UI元素与创建它们的线程(STA线程)相关联。 你最终将所有调用都包装在Dispatcher.Invokes中,可能会在某些时候陷入僵局。 但是,实现INotifyPropertyChanged并在需要时引发PropertyChanged会给WPF框架带来负担(更新通知将在引擎盖下的主线程上调度)。
最后请注意,在后面的代码中创建UI并不总是一个坏主意。想象一下,您希望在应用程序中实现一个插件系统,并在布局中保留一个可折叠区域,该区域将托管未知插件的UI。你不能强迫插件的开发者拥有正好2个按钮和一个文本框,这样它就能很好地适应你的DataTemplate。一个很好的解决方案是将ContentControl放在保留空间中,并为开发人员提供一个实现的接口,包含object GetUI();
方法并进行如下调用:ContentControl.Content = ActivePlugin.GetUI();