Привязка ContentControl к ObservableCollection, если количество == 1

как я могу связать Содержание ContentControl к ObservableCollection. Управление должно показать объект содержанием, только если ObservableColelction содержит точно один объект (объект, который покажут).

Спасибо, Walter

6
задан Walter 12 June 2010 в 07:25
поделиться

2 ответа

Это просто. Просто используйте этот 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>

Он используется как ContentTemplate вашего ContentControl. Например:

<Button Content="{Binding observableCollection}"
        ContentTemplate="{StaticResource ShowItemIfExactlyOneItem}" />

Это все, что вам нужно сделать.

Как это работает: шаблон обычно содержит ItemsControl без элементов, который невидим и не имеет размера. Но если ObservableCollection, установленный как Content, когда-либо имеет в себе ровно один элемент (Count == 1), триггер срабатывает и устанавливает ItemsSource для ItmesControl, в результате чего отдельный элемент отображается с использованием Grid для панели. Шаблон сетки необходим, поскольку панель по умолчанию (StackPanel) не позволяет своему содержимому расширяться, чтобы заполнить доступное пространство.

Примечание. Если вы также хотите указать DataTemplate для самого элемента, а не использовать шаблон по умолчанию, установите свойство «ItemTemplate» элемента ItemsControl.

6
ответ дан 16 December 2019 в 21:34
поделиться

+1, хороший вопрос :)

Вы можете привязать ContentControl к ObservableCollection , и WPF достаточно умен, чтобы знать что вас интересует только рендеринг одного элемента из коллекции («текущий» элемент)

(Кроме того: это основа коллекций основных деталей в WPF, привяжите ItemsControl и ContentControl к одной и той же коллекции и установите IsSynchronizedWithCurrentItem = True в ItemsControl)

Ваш вопрос, однако, спрашивает, как отображать контент только , если коллекция содержит единственный элемент ... для этого нам нужно использовать тот факт, что ObservableCollection содержит общедоступное свойство 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; }
    }

Установите DataContext в Window как экземпляр ВМ ...

    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>

UPDATE

To Чтобы это динамическое поведение работало, есть еще один класс, который нам поможет ... CollectionViewSource

Обновите свою виртуальную машину, чтобы открыть 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);
    }
}

И в окне подключите событие нажатия кнопки до новый метод 'Add' (вы можете использовать Commanding, если хотите, это так же эффективно на данный момент)

    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 , и вы можете сообщить WPF, что является текущим элементом, используя метод MoveCurrentTo () . Обратите внимание, что, хотя ICollectionView сам по себе не содержит свойств, называемых «Count» или «Name», платформа достаточно умен, чтобы использовать базовый источник данных из CollectionView в наших привязках ...

2
ответ дан 16 December 2019 в 21:34
поделиться
Другие вопросы по тегам:

Похожие вопросы: