如何将ContentControl的内容绑定到ObservableCollection?
只有当ObservableCollection恰好包含一个对象(要显示的对象)时,该控件才应将其作为内容显示。
谢谢, 沃尔特
这很简单。只需使用此DataTemplate:
<DataTemplate x:Key="ShowItemIfExactlyOneItem">
<ItemsControl x:Name="ic">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate><Grid/></ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Count}" Value="1">
<Setter TargetName="ic" Property="ItemsSource" Value="{Binding}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
这将被用作您的ContentControl的ContentTemplate。例如:
<Button Content="{Binding observableCollection}"
ContentTemplate="{StaticResource ShowItemIfExactlyOneItem}" />
+1,好问题 :)
您可以将ContentControl
绑定到一个 ObservableCollection<T>
,WPF 足够聪明,知道您只对集合中的一个项(“当前”项)进行呈现。
(顺便说一句:这是 WPF 中主从集合的基础,将 ItemsControl 和 ContentControl 绑定到同一集合,并将 IsSynchronizedWithCurrentItem=True 设置为 ItemsControl)
不过,您的问题是如何仅在集合包含单个项时呈现内容...... 为此,我们需要利用 ObservableCollection<T>
包含一个公共的 Count
属性,以及一些恰当使用 DataTriggers
...
试试这个...
首先,这是我的微不足道的模型对象,“Customer”
public class Customer
{
public string Name { get; set; }
}
public class ViewModel
{
public ViewModel()
{
MyCollection = new ObservableCollection<Customer>();
// Add and remove items to check that the DataTrigger fires correctly...
MyCollection.Add(new Customer { Name = "John Smith" });
//MyCollection.Add(new Customer { Name = "Mary Smith" });
}
public ObservableCollection<Customer> MyCollection { get; private set; }
}
在窗口中将DataContext设置为VM的实例...
public Window1()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
以下是有趣的部分:XAML模板化Customer对象,并设置数据触发器,仅在Count等于1时才移除“Invalid Count”部分。
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<Style TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate x:Name="template">
<Grid>
<Grid Background="AliceBlue">
<TextBlock Text="{Binding Name}" />
</Grid>
<Grid x:Name="invalidCountGrid" Background="LightGray" Visibility="Visible">
<TextBlock
VerticalAlignment="Center" HorizontalAlignment="Center"
Text="Invalid Count" />
</Grid>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Count}" Value="1">
<Setter TargetName="invalidCountGrid" Property="Visibility" Value="Collapsed" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<ContentControl
Margin="30"
Content="{Binding MyCollection}" />
</Window>
更新
为了使得动态行为生效,我们需要使用另一个类——CollectionViewSource
将您的VM更新,以公开一个ICollectionView
,例如:
public class ViewModel
{
public ViewModel()
{
MyCollection = new ObservableCollection<Customer>();
CollectionView = CollectionViewSource.GetDefaultView(MyCollection);
}
public ObservableCollection<Customer> MyCollection { get; private set; }
public ICollectionView CollectionView { get; private set; }
internal void Add(Customer customer)
{
MyCollection.Add(customer);
CollectionView.MoveCurrentTo(customer);
}
}
在窗口中,将按钮的Click事件与新的“Add”方法绑定(如果您喜欢,可以使用命令,但现在这样做同样有效)。
private void Button_Click(object sender, RoutedEventArgs e)
{
_viewModel.Add(new Customer { Name = "John Smith" });
}
然后在XAML中,不需要改变资源,将这个作为你的窗口主体:
<StackPanel>
<TextBlock Height="20">
<TextBlock.Text>
<MultiBinding StringFormat="{}Count: {0}">
<Binding Path="MyCollection.Count" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<Button Click="Button_Click" Width="80">Add</Button>
<ContentControl
Margin="30" Height="120"
Content="{Binding CollectionView}" />
</StackPanel>
现在,您的ContentControl的内容是ICollectionView
,您可以使用MoveCurrentTo()
方法告诉WPF当前项目是什么。
请注意,即使ICollectionView
本身没有名为“Count”或“Name”的属性,平台也足够智能,可以在我们的绑定中使用CollectionView中的底层数据源...