【问题标题】:ComboBox - select object from two propertiesComboBox - 从两个属性中选择对象
【发布时间】:2012-01-23 02:00:51
【问题描述】:

我希望我的用户能够从 WPF 中的 ComboBox 中选择一个客户端(自定义对象)。 ComboBox 将按其 FirstName(字符串)和 LastName(字符串)显示所有客户端。

所以基本上我的 ViewModel 公开了一个客户端,这是用户将做出的选择,以及用于填充 ComboBox 的所有客户端的列表。 ComboBox 的声明如下所示:

<ComboBox Grid.Row="3" Grid.Column="1" Text="{Binding Client}" ItemsSource="{Binding Clients}" IsEditable="True">
    <ComboBox.ItemTemplate>
        <DataTemplate>
            <TextBlock>
                <TextBlock.Text>
                    <MultiBinding StringFormat="{}{0} {1}">
                        <Binding Path="FirstName"/>
                        <Binding Path="LastName"/>
                    </MultiBinding>
                </TextBlock.Text>
            </TextBlock>
        </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>

当然这不起作用,因为 Text 属性显示一个原始客户端(显示类型名称),并且如果用户键入名称,则无法将其转换为客户端。这似乎是一件非常简单的事情,但是通过在线搜索我似乎找到了几十种不同的解决方案:创建一个具有正确格式名称的包装器类型,使用值转换器,使用数据模板......我不熟悉所有那些 WPF 技术,所以请帮我找出最好的(希望是简单的!)解决方案。

谢谢!

【问题讨论】:

    标签: c# wpf xaml data-binding combobox


    【解决方案1】:

    还有一种更简单的方法可以做到这一点。就像你说的,XAML 绑定到对象的类型名,这来自 ToString() 方法。这允许您覆盖该方法并返回所需的格式,而无需复杂的多个绑定。

    protected override ToString()
    {
       return String.Format("{0} {1}", FirstName, LastName);
    }
    

    也可能是您使用了Text 属性错误并且您需要SelectedItem?

    【讨论】:

    • 是的,但如果您想做更复杂的事情,这将无济于事。例如,粗体显示名字,正常显示姓氏。为此,您需要数据模板
    • @Dr.AndrewBurnett-Thompson 啊,这是真的,没想到。
    • 我仍然为它 +1。简单性经常被忽视;)
    • 这非常适合我的需求,并且几乎不需要任何代码。谢谢!
    【解决方案2】:

    你能试试这样的吗?

    <!-- Example assumes DataContext of ComboBox is MasterViewModel below -->
    <ComboBox Grid.Row="3" Grid.Column="1" ItemsSource="{Binding Clients}" IsEditable="True">
        <ComboBox.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text={Binding FirstName} Margin="0,0,10,0"/>
                    <TextBlock Text={Binding LastName}/>
                </StackPanel>
            </DataTemplate>
        </ComboBox.ItemTemplate>
    </ComboBox>
    

    然后直接绑定viewmodel属性

    public class ClientViewModel: INotifyPropertyChanged
    {
        public event EventHandler<PropertyChangedEventArgs> PropertyChanged; 
    
        private string firstName;
        private string lastName;
    
        public string FirstName
        {
            get { return this.firstName; }
            set 
            {
                this.firstName = value; // Dont forget to raise PropertyChanged!
            }
        }
    
        public string LastName
        {
            get { return this.firstName; }
            set 
            {
                this.lastName = value;
            }
        }
    }
    

    像这样在 MasterViewModel 中公开IEnumerable&lt;ClientViewModel&gt;

    public class MasterViewModel : INotifyPropertyChanged
    {
        private IEnumerable<ClientViewModel> _clients = new ClientViewModel[] 
        {
            new ClientViewModel() { FirstName = "Dave", LastName = "Cameron" }, 
            new ClientViewModel() { FirstName = "Ed", LastName = "Miliband" }, 
        }
        public IEnumerable<ClientViewModel> Clients
        {
            get { return _clients; } 
        } 
    }
    

    DataTemplates 的强大功能意味着您可以以任何您喜欢的方式呈现数据。在 DataTemplate 和多个文本框中使用 StackPanel 或 Grid 是最好的方法。

    最好的问候

    【讨论】:

      【解决方案3】:

      这应该可以开箱即用,但您似乎没有绑定组合的选定项。

      <ComboBox Grid.Row="3" Grid.Column="1" IsEditable="True"
                ItemsSource="{Binding Clients}"
                SelectedItem="{Binding Client}" >
      

      我不清楚为什么你的组合是可编辑的。你是说用户可以输入名字/姓氏来选择吗?在这种情况下,您需要转换器。

      【讨论】:

        【解决方案4】:

        我认为最简单的方法是向Client 对象添加一个自定义属性。所以客户最终是这样的。请注意,Client 对象必须在某一方面实现 INotifyPropertyChanged,以便绑定基础架构能够接受更改。

        public class Client : INotifyPropertyChanged
        {
           //other fields/properies
           private string firstName;
           private string lastName;
        
           public string FirstName
           {
              get { return this.firstName; }
              set 
              {
                 this.firstName = value;
                 this.FirePropertyChanged("FirstName");
                 this.FirePropertyChanged("DisplayName");
              }
           }
        
           public string LastName
           {
              get { return this.firstName; }
              set 
              {
                 this.lastName = value;
                 this.FirePropertyChanged("LastName");
                 this.FirePropertyChanged("DisplayName");
              }
           }
        
           public string DisplayName
           {
              get 
              {
                  return string.Format("{0} {1}", this.firstName, this.lastName);
              }
           }
        
           protected void FirePropertyChanged(string propertyName)
           {
              if (this.PropertyChanged != null)
                 this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
           }
        }
        

        那么你只需要一个非常简单的ComboBox 声明,指向新的DisplayName 属性:

        编辑:

        不要使用 Text 属性,而是使用带有双向绑定的 SelectedItem 属性以将其与您的 ViewModel 匹配。

        <ComboBox Grid.Row="3" Grid.Column="1" SelectedItem="{Binding Client, Mode=TwoWay" ItemsSource="{Binding Clients}" DisplayMemberPath="DisplayName" />
        

        【讨论】:

        • 谢谢。这样做的问题是,当我从列表中选择一个客户端时,我得到:“System.Windows.Data 错误:8:无法将值从目标保存回源。(...) 'System.String' 类型的对象无法转换为类型“[MyAppNamespace].Client”。”
        • 啊,我知道问题出在哪里了...假设“客户端”属性的类型为“客户端”,请查看我的编辑...
        • 我为 FirePropertyChanged +1 了这个,为简洁起见,我在回答中省略了这个
        猜你喜欢
        • 1970-01-01
        • 2011-02-01
        • 2021-09-13
        • 2021-09-06
        • 1970-01-01
        • 2015-11-05
        • 2017-08-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多