如果Count等于1,将ContentControl绑定到ObservableCollection。

6

如何将ContentControl的内容绑定到ObservableCollection?

只有当ObservableCollection恰好包含一个对象(要显示的对象)时,该控件才应将其作为内容显示。

谢谢, 沃尔特

2个回答

6

这很简单。只需使用此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}" />

这就是你需要做的全部。
它的工作原理:模板通常包含一个没有项的ItemsControl,该控件不可见且没有大小。但是,如果设置为Content的ObservableCollection恰好有一个项(Count==1),则触发器将触发并设置ItmesControl的ItemsSource,从而使用网格显示单个项作为面板。必须使用Grid模板,因为默认面板(StackPanel)不允许其内容扩展以填充可用空间。
注意:如果您还想为项本身指定DataTemplate而不是使用默认模板,请设置ItemsControl的“ItemTemplate”属性。

现在还没有人提升过这个吗? - Nick Daniels

2

+1,好问题 :)

您可以将ContentControl绑定到一个 ObservableCollection<T>,WPF 足够聪明,知道您只对集合中的一个项(“当前”项)进行呈现。

(顺便说一句:这是 WPF 中主从集合的基础,将 ItemsControl 和 ContentControl 绑定到同一集合,并将 IsSynchronizedWithCurrentItem=True 设置为 ItemsControl)

不过,您的问题是如何仅在集合包含单个项时呈现内容...... 为此,我们需要利用 ObservableCollection<T> 包含一个公共的 Count 属性,以及一些恰当使用 DataTriggers...

试试这个...

首先,这是我的微不足道的模型对象,“Customer”

public class Customer
{
    public string Name { get; set; }
}

现在,一个ViewModel暴露了这些对象的集合...
    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中的底层数据源...


到目前为止,这看起来不错,但如果集合最初是空的,我稍后添加一个元素(可能通过单击按钮),则InvalidCountGrid将被删除,但不会显示Colection中单个元素的数据。我认为OC的CurrentItem仍然为空? - Walter
对我来说,这似乎是一种非常复杂的方法。请查看我的答案,以了解同样问题的简单解决方案。 - Ray Burns

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接