Значения ComboBox исчезают после выбора, когда объекты используются для отображения - PullRequest
4 голосов
/ 14 мая 2010

У меня есть выпадающий список, в котором я хочу отобразить объекты и получить возвращенные значения перечисления. При первом открытии выпадающий список отображает элементы, как предполагалось, но после выбора значения он, похоже, исчезает из списка. Но если выпадающий список активен, я могу использовать клавиатуру для перемещения вверх и вниз между другими значениями, поэтому они находятся в списке, но только невидимы.

Я создал небольшое тестовое приложение, чтобы показать мою проблему. При запуске приложение отображает выпадающий список со всеми вариантами выбора (два первых - это тип объекта, третий - строка):

Все варианты показаны при запуске http://i42.tinypic.com/ak7rll.png

После того, как выделена синяя линия и когда снова открыт комбинированный список, эта строка отсутствует:

Синяя линия выделена и отсутствует во всплывающем окне http://i41.tinypic.com/24awgth.png

Когда выбрана строка с текстом «Зеленый», эта строка все еще показывает:

Зеленая линия выделена и все еще отображается во всплывающем окне http://i41.tinypic.com/2en3g2r.png

Если бы я выбрал красную линию, единственное, что осталось бы в списке, - это тест "Зеленый".

Я использую .NET Framework 3.5.

Любые подсказки или советы, почему элементы исчезают?


Вот весь код, необходимый после запуска пустого проекта в Visual Studio.

MainWindow.xaml.cs:

using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace Test
{
   public partial class MainWindow
   {
      public MainWindow()
      {
         InitializeComponent();
      }

      private ColorComboBoxValue _activeColor;
      public ColorComboBoxValue ActiveColor
      {
         get { return _activeColor; }
         set
         {
            _activeColor = value;
            Debug.WriteLine("ActiveColor: " + _activeColor.Color);
         }
      }
   }

   public class ColorList : List<ColorComboBoxValue> { }

   public class ColorComboBoxValue
   {
      public Color Color { get; set; }
      public Object Object { get; set; }
   }

   public enum Color
   {
      Red,
      Blue,
      Green
   }
}

MainWindow.xaml:

<Window x:Class="Test.MainWindow" x:Name="window"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:System="clr-namespace:System;assembly=mscorlib"
        xmlns:local="clr-namespace:Test"
        Title="ComboBoxTest" Height="100" Width="200">

    <Window.Resources>
        <local:ColorList x:Key="ColorList">
            <local:ColorComboBoxValue Color="Red">
                <local:ColorComboBoxValue.Object>
                    <Path Data="M0,0 L0,30 60,30 60,0 Z" Fill="Red"/>
                </local:ColorComboBoxValue.Object>
            </local:ColorComboBoxValue>
            <local:ColorComboBoxValue Color="Blue">
                <local:ColorComboBoxValue.Object>
                    <Path Data="M0,0 L0,30 60,30 60,0 Z" Fill="Blue"/>
                </local:ColorComboBoxValue.Object>
            </local:ColorComboBoxValue>
            <local:ColorComboBoxValue Color="Green">
                <local:ColorComboBoxValue.Object>
                    <System:String>Green</System:String>
                </local:ColorComboBoxValue.Object>
            </local:ColorComboBoxValue>
        </local:ColorList>
    </Window.Resources>

    <ComboBox ItemsSource="{Binding Source={StaticResource ColorList}}"
              SelectedItem="{Binding ActiveColor, ElementName=window}">
        <ComboBox.ItemTemplate>
            <DataTemplate>
                <ContentPresenter Content="{Binding Path=Object}"/>
            </DataTemplate>
        </ComboBox.ItemTemplate>
    </ComboBox>
</Window>

1 Ответ

3 голосов
/ 18 мая 2010

Это довольно просто на самом деле.

Путь - это объект WPF, и поэтому каждый объект WPF может иметь только 1 родительский объект. Если у объекта WPF установлен родительский объект, его нельзя использовать в другом родительском объекте.

Что происходит, DataTemplate загружается и показывает ваши элементы. Вы выбираете один элемент с помощью Path, и он устанавливается в ContentPresenter Selected Item вашего Combobox (он должен быть показан). Это отсоединяет Путь от вашего исходного объекта, в результате чего ваши предметы «исчезают». Ваши предметы все еще присутствуют, но вы не можете их видеть, потому что у них больше нет видимого объекта, поскольку Путь был отсоединен от вашего исходного списка. В случае строки это работает, потому что строка не является объектом WPF.

Надеюсь, это немного прояснит ситуацию.

Итак, теперь для решения:

Если вы хотите сохранить зеленый как текст, вы можете сделать следующее:

Создайте свой ColorList типа Color enum:

public class ColorList : List<Color> { }

Выбросьте некоторые вещи:

public partial class Window1 : Window
{
       public Window1()
    {
        this.Resources["ColorList"] = new[] { Color.Red, Color.Blue, Color.Green };
        InitializeComponent();
    }

       private Color _activeColor;
    public Color ActiveColor
    {
        get { return _activeColor; }
        set
        {
            _activeColor = value;
        }
    }
}

public class ColorList : List<Color> { }


public enum Color
{
    Red,
    Blue,
    Green
}

И разверните свой DataTemplate, чтобы установить конкретную матрицу данных для красного и синего, используя объекты Trigger в вашем DataTemplate:

<Window x:Class="WpfApplication6.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:WpfApplication6="clr-namespace:WpfApplication6"
Title="ComboBoxTest" Height="100" Width="200">


<ComboBox ItemsSource="{Binding Source={StaticResource ColorList}}"
          SelectedItem="{Binding ActiveColor, ElementName=ComboBoxTest}">
    <ComboBox.ItemTemplate>
        <DataTemplate>
            <ContentControl Content="{Binding}" x:Name="content" />
            <DataTemplate.Triggers>
                <DataTrigger Binding="{Binding}" Value="{x:Static WpfApplication6:Color.Red}">
                    <Setter TargetName="content" Property="ContentTemplate">
                        <Setter.Value>
                            <DataTemplate>
                                <Path Data="M0,0 L0,30 60,30 60,0 Z" Fill="Red"/>
                            </DataTemplate>
                        </Setter.Value>
                    </Setter>
                </DataTrigger>
                <DataTrigger Binding="{Binding}" Value="{x:Static WpfApplication6:Color.Blue}">
                    <Setter TargetName="content" Property="ContentTemplate">
                        <Setter.Value>
                            <DataTemplate>
                                <Path Data="M0,0 L0,30 60,30 60,0 Z" Fill="Blue"/>
                            </DataTemplate>
                        </Setter.Value>
                    </Setter>
                </DataTrigger>
            </DataTemplate.Triggers>
        </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>
</Window>

Чистый подход:

Если вы хотите, чтобы все элементы были цветными объектами, вам потребуется объект-конвертер для преобразования значения перечисления Color в цвет, который вы хотите показать:

<ComboBox ItemsSource="{Binding Source={StaticResource ColorList}}"
          SelectedItem="{Binding ActiveColor, ElementName=ComboBoxTest}">
    <ComboBox.ItemTemplate>
          <DataTemplate>
              <Path Data="M0,0 L0,30 60,30 60,0 Z" Fill="{Binding Converter={StaticResource ColorConverter}}"/>
          </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>

И хороший конвертер, который нужно добавить в ресурсы:

public class ColorConverter : IValueConverter
{
    #region IValueConverter Members

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        switch ((Color)value)
        {
            case Color.Red:
                return Colors.Red;
            case Color.Blue:
                return Colors.Blue;
            case Color.Green:
                return Colors.Green;
            default:
                throw new ArgumentOutOfRangeException("value");
        }
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion
}

Намного чище;) Надеюсь, это поможет .. если у вас есть какие-либо вопросы, я отвечу на них в комментариях!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...