如果Count == 1,则将ContentControl绑定到ObservableCollection

时间:2010-06-12 07:25:38

标签: wpf binding

如何将ContentControl的内容绑定到ObservableCollection。 仅当ObservableColelction包含一个对象(要显示的对象)时,控件才应将对象显示为内容。

谢谢,  沃尔特

2 个答案:

答案 0 :(得分: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显示。 Grid模板是必需的,因为默认面板(StackPanel)不允许其内容扩展以填充可用空间。

注意:如果您还想为项目本身指定DataTemplate而不是使用默认模板,请设置ItemsControl的“ItemTemplate”属性。

答案 1 :(得分:2)

+1,好问题:)

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

(旁白:这是WPF中主 - 细节集合的基础,将ItemsControl和ContentControl绑定到同一个集合,并在ItemsControl上设置IsSynchronizedWithCurrentItem = True)

但是,您的问题是,如果集合包含单个项目,则询问如何仅呈现内容 ...为此,我们需要利用ObservableCollection<T>包含公众的事实Count属性,以及对DataTriggers ...

的明智使用

试试这个......

首先,这是我的琐碎模型对象,'客户'

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; }
    }

将Window中的DataContext设置为VM的实例...

    public Window1()
    {
        InitializeComponent();

        this.DataContext = new ViewModel();
    }

这里有趣的是:XAML模拟Customer对象,并设置DataTrigger以删除'Invalid Count'部分,如果(且仅当)Count等于1。

<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);
    }
}

并在窗口中点击一个按钮点击事件直到新的“添加”方法(如果您愿意,可以使用命令,这在目前同样有效)

    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”的属性,该平台也足够智能,可以在我们的Bindings中使用CollectionView中的基础数据源...