【问题标题】:WPF isn't binding ComboBox SelectedItemWPF 未绑定 ComboBox SelectedItem
【发布时间】:2013-12-13 22:48:26
【问题描述】:

我已经进行了广泛的搜索,但找不到任何解决方案。

我的项目中有几个 ComboBox,我正在寻找一个 AutoComplete 解决方案,然后我找到了一个好的并应用到我的项目中,我将解决方案的样式以及我项目中的所有 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>

更新

我的切换按钮

<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>

我的文本框

<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

找到了一个 hack,不是完美的解决方案,但有点工作......

如果我插入属性SelectedValue 和值Owner.OwnerTypeId,它就像一个魅力......但是,是这样吗?

我现在的组合框:

<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>();
    }
}

【问题讨论】:

  • 在您更改模板后它停止工作?只是注意到一些东西,尝试设置IsSynchronizedWithCurrentItem = True
  • 另一件事:尝试使用ItemsPresenter x:Name="ItemsPresenter"。 (我注意到 MSDN 的 Named Parts 文档充满了错误。)
  • 谢谢 - 我是不是应该假设你已经把所有的 INofityPropertyChanged 都放在里面了,为了简洁起见而忽略了它?
  • 还有一个错字:我想你一定是Style="{StaticResource ComboBoxTextBox}"而不是Style="{x:Null}" Template="{StaticResource ComboBoxTextBox}"
  • SelectedItem 在内存中的引用是否与ItemsSource 中的项目之一相同?我最近回答了this similar question,所以这是阅读您的问题时首先想到的。

标签: c# wpf combobox styles selecteditem


【解决方案1】:

您的 SelectedItem 绑定不起作用,因为 WPF 通过 .Equals() 方法将 SelectedItemItemsSource 中的项目进行比较,默认情况下通过引用进行比较。并且内存中包含您的 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 属性来比较这个对象的相等性,但有时这是最好的方法。

    此外,每当您覆盖 .Equals() 时,覆盖 .GetHashCode() 是一种很好的做法。

附带说明,您通常不希望同时绑定SelectedItemSelectedValue。它们是设置同一事物的两种不同方式,将它们都绑定可以得到意想不到的结果。

【讨论】:

  • 感谢您的回答,但我不确定 WPF 的工作方式,因为我在整个项目中以相同的方式使用 ItemsSourceSelectedItem 在不同的内存实例中。 ..当我更新问题时,在项目的其他部分,SelectedItem 有效,只是在这个不...
  • 除非我看到您正在工作的其他代码,否则我无法确定,但最常见的情况是您绑定到值类型而不是引用类型,或者您的 @ 987654345@ 被设置为ItemsSource 集合中的现有项目,而不是创建新项目。我确信这是 WPF 的工作方式,因为我过去曾多次遇到过这个问题 :)
  • 将 IEquatable 添加到对我有用的数据中。
【解决方案2】:

您必须对组合框的 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}"/> 

希望对你有帮助

【讨论】:

    【解决方案3】:

    让我们看看绑定,并分解它们所说的内容:

    <ComboBox ItemsSource="{Binding Path=OwnerTypes, Mode=OneWay}" 
              SelectedItem="{Binding Owner.OwnerType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" 
              SelectedValuePath="Id" DisplayMemberPath="Name"
              />
    

    所以这意味着:

    1. 列表中的项目是从属性“OwnerTypes”中检索的。
    2. 选中的项目将绑定到属性“OwnerType”内部属性“Owner”的实例。

    由于没有绑定应用于SelectedValueSelectedValuePath 将被完全忽略。注意definition of SelectedValuePath

    获取或设置用于从 SelectedItem 中获取 SelectedValue 的路径。

    所以让我们暂时省略#3。使用#1 和#2,假设属性“OwnerTypes”的类型为List&lt;OwnerTypeDef&gt;。这意味着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”属性会更新。

    编辑

    让我们看看另一个场景,您使用SelectedValuePathSelectedValue。在这种情况下,我们将选定的 value 绑定到“Owner.OwnerTypeId”(一个整数)。我们将使用SelectedValuePath=Id,它告诉框架在选定项(即OwnerDef.Idint)中查找“Id”属性。因此,我们必须在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

    也可以同时使用SelectedItemSelectedValue,这样“OwnerType”和“OwnerTypeID”属性都会得到更新。确保使用 TwoWay 绑定(在上面的更新 #3 中,它们是默认的 OneWay):

    <ComboBox ItemsSource="{Binding OwnerTypes}" 
              SelectedValuePath="Id" SelectedValue="{Binding Owner.OwnerTypeId,Mode=TwoWay}"
              SelectedItem="{Binding Owner.OwnerType,Mode=TwoWay}"
              DisplayMemberPath="Name"
              />
    

    当我在组合框中选择一个项目时,此设置更新“OwnerTypeId”“OwnerType”属性。

    【讨论】:

    • 正如我在更新 4 中所说:**UPDATE 4** Ok, another strange finding... In another ComboBox, with the same properties, except SelectedValue, it's working perfect... I can't understand what is happening. 所以,我理解你的回答,但如果可能的话,我想继续这样工作。
    • 这种方式可行,但不是正确的方式,因为我需要分配或加载OwnerType,当我提交到 DB 和 SelectedValue 之前我没有设置任何东西提交。
    • @BetaSystems-RodrigoDuarte 但在更新 #3 中,SelectedValue 绑定是OneTime,所以当然不会更新。您只需要使用SelectedValueSelectedItem,而不需要同时使用它们。只需从Owner 设置器中设置OwnerTypeId(可能最简单),反之亦然。
    • @BetaSystems-RodrigoDuarte 我尝试同时使用SelectedValueSelectedItem,它似乎有效——参见上面的编辑#2。问题可能只是您的更新 #3 使用单向绑定吗?
    • 这样工作,即使在OneWay绑定中,但我的疑问是为什么没有SelectedValue就不能工作?因为所有其他 ComboBox 的作品都只有 SelectedItem
    猜你喜欢
    • 2015-06-28
    • 2011-11-01
    • 2010-10-24
    • 2017-11-09
    • 2012-06-23
    • 2021-10-18
    • 1970-01-01
    • 2019-11-10
    • 1970-01-01
    相关资源
    最近更新 更多