我有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个元素是一个月。
答案 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
属性