[英]WPF isn't binding ComboBox SelectedItem
我已经广泛搜索,但我找不到任何解决方案。
我在我的项目中有几个ComboBox,我正在寻找一个自动完成解决方案,然后我发现了一个很好的并应用于我的项目,我也将解决方案的样式应用到我项目中的所有ComboBox。
在那之后,SelectedItem停止了工作,有人可以帮助我吗?
我的组合框:
<ComboBox Name="CbOwnerType" Grid.Column="1" Grid.Row="2" ItemsSource="{Binding Path=OwnerTypes, Mode=OneWay}" SelectedItem="{Binding Owner.OwnerType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" SelectedValuePath="Id" DisplayMemberPath="Name" Margin="5,0,10,0" />
我的风格:
<Style TargetType="{x:Type ComboBox}">
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="Foreground" Value="Black" />
<Setter Property="FontWeight" Value="ExtraBold" />
<Setter Property="IsEditable" Value="False"/>
<Setter Property="IsSynchronizedWithCurrentItem" Value="False" />
<Setter Property="StaysOpenOnEdit" Value="True" />
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBox}">
<Grid>
<ToggleButton Name="ToggleButton" Template="{StaticResource ComboBoxToggleButton}" Grid.Column="2" Focusable="True" IsChecked="{Binding Path=IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" ClickMode="Press" BorderThickness="0" />
<ContentPresenter Name="ContentSite" IsHitTestVisible="False" Content="{TemplateBinding SelectionBoxItem}" ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}" ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}" Margin="5,0,20,0" VerticalAlignment="Center" HorizontalAlignment="Left" />
<TextBox x:Name="PART_EditableTextBox" Style="{x:Null}" Template="{StaticResource ComboBoxTextBox}" HorizontalAlignment="Left" VerticalAlignment="Bottom" 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">
<Themes:SystemDropShadowChrome Margin="4,6,4,6" CornerRadius="4">
<Grid Name="DropDown" SnapsToDevicePixels="True" MinWidth="{TemplateBinding ActualWidth}" MaxHeight="{TemplateBinding MaxDropDownHeight}">
<Border x:Name="DropDownBorder" Background="{StaticResource WindowBackgroundBrush}" BorderThickness="1" BorderBrush="{StaticResource SolidBorderBrush}" />
<ScrollViewer Margin="4,6,4,6" SnapsToDevicePixels="True">
<ItemsPresenter />
</ScrollViewer>
</Grid>
</Themes:SystemDropShadowChrome>
</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>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
UPDATE
我的ToggleButton
<ControlTemplate x:Key="ComboBoxToggleButton" TargetType="{x:Type ToggleButton}" >
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="20" />
</Grid.ColumnDefinitions>
<Border x:Name="Border" Grid.ColumnSpan="2" BorderBrush="{StaticResource LabPetsStandardColor}" BorderThickness="1" CornerRadius="5" />
<Border Grid.Column="0" Margin="1" Background="Transparent" BorderBrush="{StaticResource NormalBorderBrush}" BorderThickness="0" CornerRadius="5,0,0,5" />
<Path x:Name="Arrow" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center" Data="M 0 0 L 4 4 L 8 0 Z">
<Path.Fill>
<SolidColorBrush Color="Black" />
</Path.Fill>
</Path>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="ToggleButton.IsMouseOver" Value="true">
<Setter TargetName="Border" Property="Background" Value="{StaticResource LabPetsStandardColor}" />
</Trigger>
<Trigger Property="ToggleButton.IsChecked" Value="true">
<Setter TargetName="Border" Property="Background" Value="{StaticResource LabPetsPressedStandardColor}" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Border" Property="Background" Value="{StaticResource DisabledBackgroundBrush}" />
<Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource DisabledBorderBrush}" />
<Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}"/>
<Setter TargetName="Arrow" Property="Fill" Value="{StaticResource DisabledForegroundBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
我的TextBox
<Style x:Key="ComboBoxTextBox" TargetType="{x:Type TextBox}">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="AllowDrop" Value="True" />
<Setter Property="MinWidth" Value="0" />
<Setter Property="MinHeight" Value="0" />
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<ScrollViewer HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden" Background="#00FFFFFF" Name="PART_ContentHost" Focusable="False" VerticalAlignment="Center" VerticalContentAlignment="Center" Margin="0">
<ScrollViewer.Style>
<Style TargetType="ScrollViewer">
<Setter Property="OverridesDefaultStyle" Value="True" />
</Style>
</ScrollViewer.Style>
</ScrollViewer>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
请帮助我一些吗?
更新2
发现一个黑客,不是完美的解决方案,但有点工作......
如果我插入属性SelectedValue
和值Owner.OwnerTypeId
,它就像一个魅力...但是,这是正确的吗?
我的Combobox现在:
<ComboBox Name="CbOwnerType" Grid.Column="1" Grid.Row="2" ItemsSource="{Binding Path=OwnerTypes, Mode=OneWay}" SelectedItem="{Binding Owner.OwnerType}" SelectedValue="{Binding Owner.OwnerTypeId}" SelectedValuePath="Id" DisplayMemberPath="Name" Margin="5,0,10,0" />
这是一个解决方案,不是我喜欢它,但它是一个解决方案......有人可以回答为什么SelectedItem
不能正常工作?
Obs。:当我更改选择时, SelectedItem
工作,只是在我加载视图时不起作用。
更新3
好吧,它像我说的那样工作,但问题是WPF在我的ViewModel上打了4次,所以我改变了我的ComboBox
:
<ComboBox Name="CbOwnerType" Grid.Column="1" Grid.Row="2" ItemsSource="{Binding Path=OwnerTypes, Mode=OneWay}" SelectedItem="{Binding Owner.OwnerType}" SelectedValue="{Binding Owner.OwnerTypeId, Mode=OneTime}" SelectedValuePath="Id" DisplayMemberPath="Name" Margin="5,0,10,0" />
所以,现在,WPF只搜索OwnerTypeId
,当我更改项目时,WPF只会命中2次。
更新4
好的,另一个奇怪的发现......在另一个ComboBox中,具有相同的属性,除了SelectedValue
,它的工作完美......我无法理解发生了什么。
更新5
对不起,我忘记发布我的模特了。
型号所有者:
public class Owner
{
public int Id { get; set; }
public int OwnerTypeId { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string FormatedPhone
{
get
{
if (this.Phone == null)
return string.Empty;
switch (this.Phone.Length)
{
case 11:
return Regex.Replace(this.Phone, @"(\d{2})(\d{4})(\d{4})", "($1) $2-$3");
case 12:
return Regex.Replace(this.Phone, @"(\d{2})(\d{5})(\d{4})", "($1) $2-$3");
default:
return this.Phone;
}
}
}
public string Phone { get; set; }
public string CellPhone { get; set; }
public string FormatedCellPhone
{
get
{
if (this.CellPhone == null)
return string.Empty;
switch (this.CellPhone.Length)
{
case 11:
return Regex.Replace(this.Phone, @"(\d{2})(\d{4})(\d{4})", "($1) $2-$3");
case 12:
return Regex.Replace(this.Phone, @"(\d{2})(\d{5})(\d{4})", "($1) $2-$3");
default:
return this.CellPhone;
}
}
}
public string Email { get; set; }
public virtual OwnerType OwnerType { get; set; }
public virtual ICollection<Animal> Animals { get; set; }
public Owner()
{
this.OwnerType = new OwnerType();
this.Animals = new List<Animal>();
this.ErrorList = new StringBuilder();
}
型号所有者类型:
public class OwnerType
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Owner> Owners { get; set; }
public OwnerType()
{
this.Owners = new List<Owner>();
}
}
您的SelectedItem
绑定不起作用,因为WPF通过.Equals()
方法将SelectedItem
与ItemsSource
的项进行比较,默认情况下,通过引用进行比较。 包含SelectedItem
内存中的实例与ItemsSource
中的项目之一在内存中不是同一个实例
有3种方法可以解决这个问题。
首先,您已经发现可以将SelectedValue
绑定到项目的值类型属性,并设置SelectedValuePath
。
<ComboBox ItemsSource="{Binding Path=OwnerTypes}" SelectedValue="{Binding Owner.OwnerTypeId}" SelectedValuePath="Id" DisplayMemberPath="Name" />
这通常是我使用的解决方案,因为它通常是最简单的
其次,您可以确保您的SelectedItem
在内存中设置为与其中一个ItemsSource
项相同的引用。 根据应用程序设计,这也不是一个糟糕的选择。
public class Owner() { public int OwnerTypeId { get; set; } public OwnerType OwnerType { get { return StaticClass.OwnerTypes .FirstOrDefault(p => p.Id == this.OwnerTypeId); } set { if (value != null) OwnerTypeId = value.Id; } } }
最后,您可以覆盖OwnerType
对象上的.Equals()
方法,以便在Id
属性相同时将两个值视为相等。
public override bool Equals(object obj) { if (obj == null || !(obj is OwnerType)) return false; return ((OwnerType)obj).Id == this.Id); }
我通常会尝试避免使用此方法,除非我知道我总是希望仅通过Id
属性来比较此对象的相等性,但有时这是最好的方法。
此外,它的好的做法覆盖.GetHashCode()
每当你覆盖.Equals()
作为旁注,您通常不希望绑定SelectedItem
和SelectedValue
。 它们是设置相同内容的两种不同方式,您可以通过绑定它们来获得意外结果。
你必须对comobox的selectedValue进行模板绑定。 因为您已覆盖控件模板并放置了自己的TextBlock以显示所选值。 现在,当从UI控件中选择值时,模板会负责显示它。 但它没有将值设置为SelectedValue或SelectedItem。
<TextBox x:Name="PART_EditableTextBox" Style="{x:Null}" HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="3,3,23,3" Focusable="True" Background="Transparent" Visibility="Hidden" IsReadOnly="{TemplateBinding IsReadOnly}" Text="{TemplateBinding SelectedValue}"/>
希望这对你有所帮助
让我们看看绑定,并分解他们说的话:
<ComboBox ItemsSource="{Binding Path=OwnerTypes, Mode=OneWay}"
SelectedItem="{Binding Owner.OwnerType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="Id" DisplayMemberPath="Name"
/>
所以这意味着:
由于没有应用于SelectedValue
绑定,因此将完全忽略SelectedValuePath
。 注意SelectedValuePath的定义 :
获取或设置用于从SelectedItem获取SelectedValue的路径。
所以我们现在省略#3。 使用#1和#2,我们假设属性“OwnerTypes”的类型为List<OwnerTypeDef>
。 这意味着OwnerTypeDef
类型与“Owner”属性中属性“OwnerType”的类型相同。 有了这个设置 -
public class OwnerDef : INotifyPropertyChanged // TODO implement INotifyPropertyChanged
{
public OwnerTypeDef OwnerType
{
get { return _ownerType; }
set
{
if (_ownerType == value)
return;
_ownerType = value;
RaisePropertyChanged();
}
}
private OwnerTypeDef _ownerType;
}
public class OwnerTypeDef : INotifyPropertyChanged
{
public string Name
{
get { return _name; }
set
{
if (_name == value)
return;
_name = value;
RaisePropertyChanged();
}
}
private string _name;
public int Id
{
get { return _id; }
set
{
if (_id == value)
return;
_id = value;
RaisePropertyChanged();
}
}
private int _id;
}
public class ViewModel : INotifyPropertyChanged
{
public List<OwnerTypeDef> OwnerTypes
{
get { return _ownerTypes; }
set { _ownerTypes = value; }
}
private List<OwnerTypeDef> _ownerTypes = new List<OwnerTypeDef>
{
new OwnerTypeDef { Name = "foo", Id = 1, },
new OwnerTypeDef { Name = "bar", Id = 2, },
new OwnerTypeDef { Name = "baz", Id = 3, },
};
public OwnerDef Owner
{
get { return _owner; }
set
{
if (_owner == value)
return;
_owner = value;
RaisePropertyChanged();
}
}
private OwnerDef _owner = new OwnerDef();
}
- 绑定对我有用。 当我更改UI中的选择时,“Owner”中的属性“OwnerType”会更新。
编辑
让我们看一下使用SelectedValuePath
和SelectedValue
的其他场景。 在这种情况下,我们将所选值绑定到“Owner.OwnerTypeId”(整数)。 我们将使用SelectedValuePath=Id
,它告诉框架在所选项中查找“Id”属性(即OwnerDef.Id
,一个int
)。 因此,我们必须在OwnerDef
类中添加一个匹配的int
属性,称之为“OwnerTypeId”。 在这种情况下,XAML将是:
<ComboBox ItemsSource="{Binding Path=OwnerTypes, Mode=OneWay}"
SelectedValue="{Binding Owner.OwnerTypeId, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="Id" DisplayMemberPath="Name"
/>
使用此设置,绑定正在更新“OwnerTypeId”。
编辑#2
也可以同时使用SelectedItem
和SelectedValue
,以便更新“OwnerType”和“OwnerTypeID”属性。 确保使用TwoWay
绑定(在上面的Update#3中,它们是默认的OneWay):
<ComboBox ItemsSource="{Binding OwnerTypes}"
SelectedValuePath="Id" SelectedValue="{Binding Owner.OwnerTypeId,Mode=TwoWay}"
SelectedItem="{Binding Owner.OwnerType,Mode=TwoWay}"
DisplayMemberPath="Name"
/>
这种设置既可以更新“OwnerTypeId” 和 “OwnerType”属性,当我选择在组合框中选择一个项目。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.