Привязка к CurrentItem в ItemsControl

Приведенный ниже XAML в основном пытается составить список Button (выведенный из свойства Name объектов в коллекции Views в текущем DataContext.

Когда я нажимаю кнопку, свойство CurrentItem для CollectionViewSource должно измениться, и связанное с ним View должно отображаться в презентаторе контента.

ХОРОШО. Если я нажму на ListBox в XAML ниже, он будет работать именно так, как хотелось бы.

Но если я нажму кнопку в UniformGrid (созданном элементом управления), свойство CurrentItem не обновится.

Как сделать так, чтобы CurrentItem обновлялось при выборе элемента в ItemsControl?

Спасибо

<UserControl x:Class="Pos.Features.Reservation.ReservationView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:product="clr-namespace:Pos.Features.ProductBrowser"
             xmlns:activity="clr-namespace:Pos.Features.ActivityBrowser"
             xmlns:addbysku="clr-namespace:Pos.Features.AddBySku"
             xmlns:client="clr-namespace:Pos.Features.ClientBrowser"
             xmlns:notes="clr-namespace:Pos.Features.Notes"
             xmlns:controls="clr-namespace:Pos.Views"
             xmlns:res="clr-namespace:Pos.Core;assembly=Pos.Core"
             Height="300" Width="300">
    <UserControl.Resources>
        <DataTemplate DataType="{x:Type product:ProductBrowserViewModel}">
            <product:ProductBrowserView/>
        </DataTemplate>
        <DataTemplate DataType="{x:Type activity:ActivityBrowserViewModel}">
            <activity:ActivityBrowserView/>
        </DataTemplate>

        <CollectionViewSource x:Name="x" x:Key="ViewsCollection" Source="{Binding Views}"  />
    </UserControl.Resources>

    <StackPanel>
        <ListBox Name="ListBoxMenu" Grid.Column="0" Margin="5" ItemsSource="{Binding Source={StaticResource ViewsCollection}}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Name}" Padding="10"/>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        <ContentControl Grid.Column="1" Content="{Binding ElementName=ListBoxMenu, Path=SelectedItem}"/>
        <ItemsControl  Grid.Column="2" Name="ViewList" ItemsSource="{Binding Source={StaticResource ViewsCollection}}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Button>
                        <TextBlock Text="{Binding Path=Name}" Name="txtButtonLabel" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="Black"/>
                    </Button>
                </DataTemplate>                
            </ItemsControl.ItemTemplate>
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <UniformGrid Rows="1" Columns="{Binding Views.Count}"/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
        <ContentControl Grid.Column="3" Content="{Binding Source={StaticResource ViewsCollection}, Path=CurrentItem}"/>
        <Button Grid.Column="4" Click="Button_Click">dsadsd</Button>
    </StackPanel>
</UserControl>

person thrag    schedule 21.12.2009    source источник


Ответы (2)


Ваша кнопка ничего не делает. Обычно ваша ViewModel будет иметь ICommand с именем Select (или что-то подобное), с которым будет связана кнопка.

Command="{Binding Select, ElementName="root"}"

и вы передадите экземпляр ICommand, который хотите выбрать

CommandParameter="{Binding}"

Это будет выглядеть примерно так (c#/XAML как псевдокод):

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

public class MyViewModel
{
  public ObservableCollection<MyModel> Models {get;set;}
  public ICommand Select {get;set;}
  /* configure Models and Select etc */
}

<UserControl DataContext="{StaticResource MyViewModelInstance}" x:Name="root">
<ItemsControl ItemsSource="{Binding Models}">
  <ItemsControl.ItemTemplate>
    <ItemsPanelTemplate>
      <Button Text="{Binding Name}" 
      Command="{Binding Select, ElementName="root"}"
      CommandParameter="{Binding}"/>
   </ItemsPanelTemplate>
  </ItemsControl.ItemTemplate>
</ItemsControl>
</UserControl>

ItemsControl привязывается к Models, поэтому каждая MyModel в Models получает кнопку. Текст кнопки привязан к свойству Name. Команда кнопки привязана к свойству Select в ViewModel. Когда кнопка нажата, она вызывает ICommand, отправляя экземпляр MyModel, с которым связана кнопка.

Обратите внимание, что использование ViewModels в UserControl — это запах кода. UserControl должны отображаться для пользователей так же, как и все остальные элементы управления — они должны иметь привязываемые общедоступные свойства, которые привязаны к пользовательской ViewModel, а не к вашей. Затем вы привязываетесь к значениям этих свойств в файле UserControl. В этом примере у вас будет свойство ItemsSource, определенное для вашего UserControl, и ItemsControl будет привязано к этому свойству, а не напрямую к ViewModel.

person Community    schedule 21.12.2009
comment
Да, это то, что я делаю (в моем приложении, а не в образце), но тогда мне нужно установить свойство, такое как IsActiveView, в каждой модели представления и синхронизировать другие модели представлений, установив IsActiveView=false для каждой из них. . Начинается снежный ком. ListBox в моем примере делает то, что мне нужно. Нет ли способа заставить элемент управления Items оставаться синхронизированным с созданными им элементами? Единственная проблема со списком заключается в том, что он не является горизонтальным и не создает области одинакового размера. В любом случае, я ценю эту мысль. Я просто подожду, пока не появятся другие идеи. Спасибо - person thrag; 21.12.2009
comment
Немного сложно понять, что вы пытаетесь сделать в своем приложении. Если бы я хотел показать детали конкретной MyModel, у меня было бы общедоступное свойство MyModel Selected {get;set;}, и я бы привязал к нему ContentControl. У вас могут быть разные DataTemplates для каждого типа, поэтому любой тип объекта, к которому вы привязываетесь, будет отображаться с использованием другого DataTemplate... - person ; 21.12.2009
comment
Хорошо, это работает. Но, следовать за вопросом; если у меня есть пять кнопок на экране. Каждый активирует представление, связанное с присутствующим контентом (как вы предлагаете). Я хочу, чтобы кнопка, представляющая активный вид, была выделена. Есть ли способ использовать триггер для изменения цвета кнопок на основе свойства выбранного представления. Таким образом, только одна кнопка всегда окрашена (выделена как активная). Она будет вести себя как элемент управления вкладками, но мне нужно, чтобы кнопки были равномерно расположены сверху. (Что заставляет меня думать, что я должен посмотреть, можно ли изменить шаблон управления вкладками, чтобы использовать единую сетку - person thrag; 22.12.2009
comment
Может быть. Вероятно, это было бы намного сложнее, чем просто установить общедоступное свойство в вашей ViewModel, чтобы указать, какая кнопка должна быть выделена, а затем использовать Style DataTrigger, привязанный к этому общедоступному свойству, чтобы включать и выключать выделение... public enum Highlight { ViewA, ViewB, ViewC, ViewD } - person ; 22.12.2009

Я думаю, вам нужно сделать это вручную в коде программной части.

XAML

<Button Click="ViewListButton_Click">
    <TextBlock Text="{Binding Path=Name}" Name="txtButtonLabel" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="Black"/>
</Button>

Код

private void ViewListButton_Click(object sender, RoutedEventArgs e)
{
    Button button = sender as Button;
    ICollectionView view = CollectionViewSource.GetDefaultView(ViewList.ItemsSource);
    view.MoveCurrentTo(button.DataContext);
}

Если вы используете шаблон MVVM и/или не хотите использовать код программной части, вы можете привязать кнопку Command к команде в вашей ViewModel, как предложил Уилл

person Thomas Levesque    schedule 21.12.2009