ComboBox默认文本显示项类型的typeName,而不是我自己的文本属性

时间:2011-08-12 08:22:08

标签: wpf text user-controls combobox

我有ComboBox的模板。取自here。假设每个项目都是Node类型。

这一切都很完美,但有一件事让我大吃一惊。当我在ComboBoxItem(这个区域,我认为是一个像素的行)之间点击时,ComboBox的文本更改为项目的TypeName(带有命名空间)。

我已覆盖Node类的 ToString()方法,但 CallStack 显示该程序来自[External code]

我应该如何ComboBox.Text显示我的字符串属性而不是ComboBoxItem的TypeName?

如果我错过了一些细节,请指出是什么。

修改

代码来自上面的超链接此处,并进行了一些更改。

用法:

<vm:ComboCheckBox Grid.Column="1" ItemsSource="{Binding Path=Months}"
                  DefaultText="Select months"/>

月份是ObservableNodeCollection。

模板[我不在这里放置资源。它们是相同的,如链接]:

<ComboBox   ItemsSource="{Binding ElementName=UserControl, Path=ItemsSource}"
            DataContext="{Binding ElementName=UserControl, Path=DataContext}"
            Text="{Binding Path=Text, Mode=OneWay, ElementName=UserControl, UpdateSourceTrigger=PropertyChanged}"
            Focusable="False"
            IsEditable="False"
            Loaded="CheckableCombo_Loaded"

            x:Name="CheckableCombo"
            SnapsToDevicePixels="True"
            OverridesDefaultStyle="True"
            ScrollViewer.HorizontalScrollBarVisibility="Auto"
            ScrollViewer.VerticalScrollBarVisibility="Auto"
            ScrollViewer.CanContentScroll="True"
            IsSynchronizedWithCurrentItem="True"
            MinWidth="120"
            MinHeight="20"
            >
    <ComboBox.ItemTemplate>
        <HierarchicalDataTemplate>
                <CheckBox Margin="0" IsChecked="{Binding Path=IsSelected}"
                          Command="{Binding Path=CheckBoxStateChanged, ElementName=UserControl}" CommandParameter="{Binding}"
                          Content="{Binding Path=Title}" />
        </HierarchicalDataTemplate>
    </ComboBox.ItemTemplate>
    <ComboBox.Template>
        <ControlTemplate TargetType="ComboBox">
            <Grid>
                <ToggleButton 
                                    Name="ToggleButton" 
                                    Template="{StaticResource ComboBoxToggleButton}" 
                                    Grid.Column="2" 
                                    Focusable="false"
                                    IsChecked="{Binding Path=IsDropDownOpen,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}"
                                    ClickMode="Press">
                </ToggleButton>
                <ContentPresenter
                                    x:Name="Presenter"
                                    IsHitTestVisible="False" 
                                    Margin="3,3,23,3"
                                    VerticalAlignment="Center"
                                    HorizontalAlignment="Left">
                    <ContentPresenter.Content>
                        <TextBlock TextTrimming="CharacterEllipsis"
                                            Text="{Binding Path=Text,Mode=OneWay,RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ComboBox}}" />
                    </ContentPresenter.Content>
                </ContentPresenter>
                <TextBox x:Name="EditableTextBox"
                         Style="{x:Null}" 
                         Template="{StaticResource ComboBoxTextBox}" 
                         HorizontalAlignment="Left" 
                         VerticalAlignment="Center" 
                         Margin="3,3,23,3"
                         Focusable="True"
                         Background="Transparent"
                         Visibility="Hidden"
                         IsReadOnly="{TemplateBinding IsReadOnly}"/>
                <Popup Name="Popup"
                       Placement="Bottom"
                       IsOpen="{TemplateBinding IsDropDownOpen}"
                       AllowsTransparency="True" 
                       Focusable="False"
                       PopupAnimation="Slide">
                    <Grid Name="DropDown"
                          ShowGridLines="True"
                          SnapsToDevicePixels="True"                
                          MinWidth="{TemplateBinding ActualWidth}"
                          MaxHeight="{TemplateBinding MaxDropDownHeight}">
                        <Border Focusable="False"
                                x:Name="DropDownBorder"
                                Background="{StaticResource WindowBackgroundBrush}"
                                BorderThickness="1"
                                BorderBrush="{StaticResource SolidBorderBrush}"/>
                        <ScrollViewer Margin="4,6,4,6" SnapsToDevicePixels="True" DataContext="{Binding}">
                            <StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Cycle" KeyboardNavigation.ControlTabNavigation="Cycle" />
                        </ScrollViewer>
                    </Grid>
                </Popup>
            </Grid>
            <ControlTemplate.Triggers>
                <Trigger Property="HasItems" Value="false">
                    <Setter TargetName="DropDownBorder" Property="MinHeight" Value="95"/>
                </Trigger>
                <Trigger Property="IsEnabled" Value="false">
                    <Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}"/>
                </Trigger>
                <Trigger Property="IsGrouping" Value="true">
                    <Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
                </Trigger>
                <Trigger SourceName="Popup" Property="Popup.AllowsTransparency" Value="true">
                    <Setter TargetName="DropDownBorder" Property="CornerRadius" Value="4"/>
                    <Setter TargetName="DropDownBorder" Property="Margin" Value="0,2,0,0"/>
                </Trigger>
                <Trigger Property="IsEditable" Value="true">
                    <Setter Property="IsTabStop" Value="false"/>
                    <Setter TargetName="EditableTextBox" Property="Visibility" Value="Visible"/>
                    <Setter TargetName="Presenter" Property="Visibility" Value="Hidden"/>
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>
    </ComboBox.Template>
</ComboBox>

代码隐藏:

/// <summary>
/// Interaction logic for ComboCheckBox.xaml
/// </summary>
public partial class ComboCheckBox : UserControl
{
    public ObservableNodeCollection ItemsSource 
    {
        get 
        { 
            return (ObservableNodeCollection)GetValue(ItemsSourceProperty); 
        } 
        set 
        {
            SetValue(ItemsSourceProperty, value);
            SetText();
        }
    }

    public static readonly DependencyProperty ItemsSourceProperty =
        DependencyProperty.Register("ItemsSource", typeof(ObservableNodeCollection), typeof(ComboCheckBox), new UIPropertyMetadata(null)); 

    /// <summary> 
    /// Gets or sets the text displayed in the ComboBox 
    /// </summary> 
    public string Text 
    { 
        get
        { 
            return(string)GetValue(TextProperty); 
        }
        set 
        { 
            SetValue(TextProperty, value);
        } 
    } 

    public static readonly DependencyProperty TextProperty = 
        DependencyProperty.Register("Text", typeof(string), typeof(ComboCheckBox), new UIPropertyMetadata(string.Empty)); 


    /// <summary> 
    /// Gets or sets the text displayed in the ComboBox if there are no selected items 
    /// </summary> 
    public string DefaultText 
    { 
        get
        {
            return(string)GetValue(DefaultTextProperty);
        }
        set
        {
            SetValue(DefaultTextProperty, value);
            SetText();
        }
    }

    public static readonly DependencyProperty DefaultTextProperty = 
        DependencyProperty.Register("DefaultText", typeof(string), typeof(ComboCheckBox), new UIPropertyMetadata(string.Empty)); 

    public ComboCheckBox()
    {
        InitializeComponent();
        this.SetText();
    }

    public ICommand CheckBoxStateChanged
    {
        get
        {
            return new Helpers.DelegateCommand<Node>((Node parameter) =>
            {
                if (parameter.IsSelectAllNode)
                {
                    bool? isSelected = this.ItemsSource[0].IsSelected;
                    ItemsSource.ToList().ForEach(item => item.IsSelected = isSelected);
                }
                else
                {
                    if (this.ItemsSource[0].IsSelectAllNode)
                    {
                        bool isAllTrue = true, isAllFalse = true;
                        for (int i = 1; i < this.ItemsSource.Count; i++)
                        {
                            isAllTrue = isAllTrue && this.ItemsSource[i].IsSelected.Value;
                            isAllFalse = isAllFalse && !this.ItemsSource[i].IsSelected.Value;
                        }
                        if (isAllTrue)
                            this.ItemsSource[0].IsSelected = true;
                        else if (isAllFalse)
                            this.ItemsSource[0].IsSelected = false;
                        else
                            this.ItemsSource[0].IsSelected = null;
                    }
                }
                this.SetText();
            });
        }
    }

    private void SetText()
    {
        string answer = String.Empty;
        if (ItemsSource != null)
            answer = ItemsSource.ToString();
        if (String.IsNullOrWhiteSpace(answer))
            answer = this.DefaultText;
        this.Text = answer;
    }

    private void CheckableCombo_Loaded(object sender, RoutedEventArgs e)
    {
        this.SetText();
    }
}

节点类:

public class Node : ObservableObject
{
    public Node(string title, string header = "",  bool isSelectAllNode = false, bool? isSelected = false)
    {
        this.Title = title;
        if (String.IsNullOrEmpty(header))
            this.Header = title;
        else
            this.Header = header;
        this.IsSelectAllNode = isSelectAllNode;
        this.IsSelected = isSelected;
    }
    public string Header { get; set; }
    public string Title { get; set; }
    private bool? isSelected;
    private const string IsSelectedPropertyName = "IsSelected";
    public bool? IsSelected
    {
        get
        {
            return this.isSelected;
        }
        set
        {
            this.isSelected = value;
            this.OnPropertyChanged(IsSelectedPropertyName);
        }
    }
    public bool IsSelectAllNode { get; set; }
    public override string ToString()
    {
        return "123";//if miss it, the typeName will return.
    }
}

ObservableNodeCollection:

public class ObservableNodeCollection : ObservableCollection<Node>
{
    public ObservableNodeCollection()
    {
    }
    public ObservableNodeCollection(List<Node> list)
        : base(list)
    {
    }
    public ObservableNodeCollection(IEnumerable<Node> collection)
        : base(collection)
    {
    }

    public override string ToString()
    {
        string answer = String.Empty;
        if (this.Items != null)
        {
            StringBuilder outString = new StringBuilder();
            foreach (Node node in this.Items)
                if (node.IsSelected == true && !node.IsSelectAllNode)
                {
                    outString.Append(node.Header);
                    outString.Append(", ");
                }
            answer = outString.ToString().TrimEnd(new char[] { ',', ' ' });
        }
        return answer;
    }
}

编辑2:

主窗口的ViewModel构造函数内部存在此代码

        int i;
        //Initialize months for combobox
        this.Months = new ObservableNodeCollection(new List<Node>() { new Node("SelectAll", isSelectAllNode:true)});

        for (i=0; i < DateTimeFormatInfo.CurrentInfo.MonthNames.Length; i++)
        {
            if (!String.IsNullOrEmpty(DateTimeFormatInfo.CurrentInfo.MonthNames[i]))
                this.Months.Add(
                    new Node
                        (   
                            DateTimeFormatInfo.CurrentInfo.MonthNames[i], 
                            DateTimeFormatInfo.CurrentInfo.AbbreviatedMonthNames[i]
                        ));
        }

所以,this.Months - 是一个ObservableNodeCollection,它的第一个元素是SelectAll,其他12个元素是一个月。

2 个答案:

答案 0 :(得分:1)

所以你说如果删除Node.ToString()方法,它会显示节点的TypeName吗?

这是预期的行为。您的ComboBox项目绑定到ObservableCollection中的每个项目,并且在将对象绑定到Text属性时,始终使用ToString方法。

您可以手动设置ComboBox.Text属性,但只要所选的ComboBox项更改,它就会更改为SelectedItem.ToString()。默认情况下,这会解析为ComboBox数据项的TypeName,但您可以通过覆盖数据项的ToString()方法或将ComboBox.DisplayMemberPath设置为数据项上存在的属性来更改此值

<ComboBox ItemsSource="{Binding Months}"
          DisplayMemberPath="Header" />

答案 1 :(得分:0)

尝试使用ComboBox的DisplayMemberPath属性