在列表框的 ItemTemplate 中包含来自 itemsource 本身的项目
Including an item from the itemsource itself in the ItemTemplate of listbox
我的 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>
在我的视图模型中,我有一个按钮集合,一个名为 ButtonsCollection 的 属性。
让我们说:
内容为 "a" 的按钮,
内容为 "b" 的按钮,
内容为 "c".
的按钮
现在我希望列表框显示每个带有边框的按钮,正如我在 ItemTemplate 中声明的那样。
DataTemplate 是为 ItemsControl(在您的例子中是 ListBox)中的每个项目实例化的。它的唯一作用是描述您的项目在呈现时的外观。
DataContext 应该 包含描述您的 UI 状态的对象。
这就是关注点分离。这样 UI 和后端可以由几个人独立开发,DataContext 就是契约。
当然,正如 Thomas Christof 指出的那样,框架不会以任何方式强制您那样做。
如果你这样做:
<ListBox ItemsSource="{Binding ButtonsCollection}">
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="2" >
<ContentControl Content={Binding}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
它可能会起作用,但是只要您将同一个集合绑定到另一个 ItemsControl,
它会崩溃。为什么?因为 WPF 中的每个 FrameworkElement 都必须只有一个父级。 WPF 将抛出异常 "Element is already the child of another element."
如果您遵循 MVVM 模式并将按钮的逻辑表示封装在 class:
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 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。一个好的解决方案是在保留的 space 中放置一个 ContentControl 并为开发人员提供一个接口来实现,其中包含一个 object GetUI();
方法并进行如下调用: ContentControl.Content = ActivePlugin.GetUI();
我的 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>
在我的视图模型中,我有一个按钮集合,一个名为 ButtonsCollection 的 属性。 让我们说: 内容为 "a" 的按钮, 内容为 "b" 的按钮, 内容为 "c".
的按钮现在我希望列表框显示每个带有边框的按钮,正如我在 ItemTemplate 中声明的那样。
DataTemplate 是为 ItemsControl(在您的例子中是 ListBox)中的每个项目实例化的。它的唯一作用是描述您的项目在呈现时的外观。
DataContext 应该 包含描述您的 UI 状态的对象。
这就是关注点分离。这样 UI 和后端可以由几个人独立开发,DataContext 就是契约。
当然,正如 Thomas Christof 指出的那样,框架不会以任何方式强制您那样做。
如果你这样做:
<ListBox ItemsSource="{Binding ButtonsCollection}">
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="2" >
<ContentControl Content={Binding}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
它可能会起作用,但是只要您将同一个集合绑定到另一个 ItemsControl, 它会崩溃。为什么?因为 WPF 中的每个 FrameworkElement 都必须只有一个父级。 WPF 将抛出异常 "Element is already the child of another element."
如果您遵循 MVVM 模式并将按钮的逻辑表示封装在 class:
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 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。一个好的解决方案是在保留的 space 中放置一个 ContentControl 并为开发人员提供一个接口来实现,其中包含一个 object GetUI();
方法并进行如下调用: ContentControl.Content = ActivePlugin.GetUI();