Привязка к выбранному элементу в ItemsControl - PullRequest
2 голосов
/ 26 января 2010

Я создал пользовательский ComboBox следующим образом: (обратите внимание, код не верный, но вы должны получить общее представление.) ComboBox содержит 2 свойства зависимости, которые имеют значение: TitleText и DescriptionText.

<Grid>
  <TextBlock x:Name="Title"/>
  <Grid x:Name="CBG">
    <ToggleButton/>
    <ContentPresenter/>
    <Popup/>
  </Grid>
</Grid>

Я хочу использовать это ComboBox для отображения широкого диапазона параметров. Я создал класс с именем Setting, который наследуется от DependencyObject для создания пригодных для использования элементов, я создал DataTemplate для привязки содержимого этого Settings объекта к моему ComboBox и создал UserControl, который содержит ItemsControl который имеет в качестве шаблона мой ранее упомянутый DataTemplate. Я могу заполнить его Setting объектами.

<DataTemplate x:Key="myDataTemplate">
  <ComboBox TitleText="{Binding Title}" DescriptionText="{Binding DescriptionText}"/>
</DataTemplate>

<UserControl>
  <Grid>
    <StackPanel Grid.Column="0">
      <ItemsControl Template="{StaticResource myDataTemplate}">
        <Item>
          <Setting Title="Foo" Description="Bar">
            <Option>Yes</Option><Option>No</Option>
          </Setting>
        </Item>
      </ItemsControl>
    </StackPanel>
    <StackPanel Grid.Column="1">
      <TextBlock x:Name="Description"/>
    </StackPanel>
  </Grid>
</UserControl>

Я бы хотел, чтобы DescriptionText выбранного ComboBox (выбранного либо с помощью IsFocus элемента управления ComboBox, либо с помощью свойства IsOpen всплывающего окна) был помещен в Description TextBlock по моему UserControl.

Один из способов, которым мне удалось этого добиться, - заменить ItemsControl на ListBox, но это вызвало несколько проблем: на нем всегда была полоса прокрутки, даже если я ее отключил, при открытии всплывающего окна она не фокусировалась, но только когда я явно выбрал элемент в моем ListBox, когда я включил свойство OverridesDefaultStyle, содержимое ListBox вообще не показывалось, мне пришлось переназначить элемент управления ListBox, чтобы он соответствовал моему UserControl макет ...

Какой самый лучший и простой способ заставить мой DescriptionText отображаться без использования ListBox или создания пользовательского элемента управления Selector (так как это имело тот же эффект, что и ListBox)?

Цель в конце состоит в том, чтобы пройтись по всем элементам (возможно, получить их в ObservableCollection или что-то в этом роде и сохранить их в моем файле настроек.

Ответы [ 2 ]

1 голос
/ 03 апреля 2010

Мне кажется, я знаю, что вы пытаетесь сделать. Вот возможное решение.

Для использования свойства SelectedItem следует использовать ListBox (или все, что происходит от элемента управления Selector).

<UserControl>
  <Grid>
    <StackPanel Grid.Column="0">
      <ListBox x:Name="SettingListBox" Template="{StaticResource myDataTemplate}">
        <Item>
          <Setting Title="Foo" Description="Bar">
            <Option>Yes</Option><Option>No</Option>
          </Setting>
        </Item>
      </ListBox >
    </StackPanel>
    <StackPanel Grid.Column="1">
      <TextBlock x:Name="Description"
          Text="{Binding SelectedItem.Description, ElementName=SettingListBox}"/>
    </StackPanel>
  </Grid>
</UserControl>

Чтобы решить проблему, когда ваш ListBox не фокусируется на элементе при открытии раскрывающегося меню ComboBox, у меня есть прикрепленное свойство, которое исправит это для вас.

public class ListBoxHelper
{
    #region Dependency Property

    public static bool GetCanFocusParent(DependencyObject obj)
    {
        return (bool)obj.GetValue(CanFocusParentProperty);
    }

    public static void SetCanFocusParent(DependencyObject obj, bool value)
    {
        obj.SetValue(CanFocusParentProperty, value);
    }

    // Using a DependencyProperty as the backing store for CanFocusParent.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty CanFocusParentProperty =
        DependencyProperty.RegisterAttached("CanFocusParent", typeof(bool), typeof(ListBoxHelper), new UIPropertyMetadata(false, OnCanFocusParentChanged));



    #endregion

    private static void OnCanFocusParentChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
    {
        var element = obj as UIElement;
        if(element == null) return;

        if((bool)args.NewValue)
            element.PreviewMouseDown += FocusOnParent;
        else
            element.PreviewMouseDown -= FocusOnParent;
    }

    private static void FocusOnParent(object sender, RoutedEventArgs e)
    {
        var listBoxItem = VisualUpwardSearch<ListBoxItem>(sender as DependencyObject) as ListBoxItem;
        if (listBoxItem != null) listBoxItem.IsSelected = true;
    }

    public static DependencyObject VisualUpwardSearch<T>(DependencyObject source)
    {
        while (source != null && source.GetType() != typeof(T))
            source = VisualTreeHelper.GetParent(source);

        return source;
    }
}

Этот маленький класс помогает вашему элементу управления сосредоточиться на выбранном элементе ListBox, когда вы активируете элемент управления внутри него (т. Е. Ваш ComboBox). Работает при щелчке мышью внутри элемента ListBox.

Теперь все, что вам нужно сделать, это прикрепить его к вашему ComboBox следующим образом:

<DataTemplate x:Key="myDataTemplate">
  <ComboBox
      TitleText="{Binding Title}"
      DescriptionText="{Binding DescriptionText}"
      CotrolHelper:ListBoxHelper.CanFocusParent="true"/>
</DataTemplate>

Где ControlHelper:

xmlns:ControlHelper="clr-namespace:WhereYouPutYour.ListBoxHelperClass"

И, наконец, чтобы отключить (но я рекомендую установить авто) полосу прокрутки, вы можете использовать вложенное свойство ScrollViewer в своем ListBox, например:

<ListBox
    x:Name="SettingListBox"
    Template="{StaticResource myDataTemplate}"
    ScrollViewer.VerticalScrollBarVisibility="Disabled" >
    <Item>
        <Setting Title="Foo" Description="Bar">
            <Option>Yes</Option><Option>No</Option>
        </Setting>
    </Item>
</ListBox >
0 голосов
/ 27 января 2010

Вы можете использовать ListBox, просто измените способ представления данных. Например, этот код по умолчанию не имеет полосы прокрутки. (Мне нужны полосы прокрутки, поэтому я должен явно обернуть все это в ScrollViewer.)

                <ListBox.Template>
                    <ControlTemplate>
                        <StackPanel IsItemsHost="True" >
                        </StackPanel>
                    </ControlTemplate>
                </ListBox.Template>
...