【问题标题】:WPF Bound Editable Combobox not changing ID on Text ChangeWPF Bound Editable Combobox 在文本更改时不更改 ID
【发布时间】:2016-05-13 13:22:09
【问题描述】:

我不确定bindcombobox control 的正确方式是我的view model

我正在使用 MVVM 方法,因此在我的 viewmodel 中,我正在加载所有 CDType 数据,并将它们的源与实际记录属性 model.CDType.idmodel.CDType.name 结合起来。

当我更改文本时会发生什么? - 我保留我的旧 ID(来自加载例程)并从组合框绑定中获取新的文本值,因此总是改写现有记录创建一个新的。

如果文本不在列表中,如何让我的组合框将 id 设置为 0 / undefined? (手动?-有点蹩脚)

任何事情都会有所帮助 - 谢谢!

TL;DR:可编辑组合框不会在文本更改时更新 ID。

Xaml 绑定示例:

<ComboBox x:Name="ddlCDType"    
          IsTextSearchEnabled="True"     
          IsTextSearchCaseSensitive="False"     
          StaysOpenOnEdit="True"    
          TextSearch.TextPath="Name"     
          ItemsSource="{Binding CDTypes}"    
          SelectedValue="{Binding Assignment.CDType.ID}"     
          Text="{Binding Assignment.CDType.Name,     
          UpdateSourceTrigger=PropertyChanged,    
          NotifyOnValidationError=True,     
          ValidatesOnDataErrors=True}"      
          DisplayMemberPath="Name"     
          SelectedValuePath="ID"     
          IsEditable="True"     
          HorizontalAlignment="Left"     
          Margin="98,10,0,0"     
          VerticalAlignment="Top"     
          Width="136" />

【问题讨论】:

  • 不清楚!看起来您正试图通过输入 CBox 来创建新的 Id。
  • 保持简单。 ItemsSource 应该是一个类型的实例。 SelectedValue 应该是绑定到ItemsSource 的实例中的一个实例。两者都应该绑定到您的视图模型。在这种情况下尝试绑定模型的子属性或做任何其他奇怪的事情只会导致心碎和悲伤。如果必须,请将模型包装在简化版本中,以将子属性提升到表面。但绝对远离Binding Assignment.CDType.ID 的废话。
  • @我会在更改此内容以适应您的评论后回复您 - 谢谢。 (代码前问题:不理解 - CDTypes 是一个具有 2 个属性的类 - 用于填充组合框。Assignment.CDType.ID 是我从数据库中加载的模型,保存当前保存的值。这是怎么回事?)
  • 'Wrong' 是主观的,但在这种情况下,Easy 是客观的。 ItemsControls 旨在与实例一起使用,并使用 Object.Equals 来确定 ItemsSource 中的哪个项目在 SelectedValue 中(如果它们不是结构;这只是一个注释,所以我将删除细节)。您可以做各种不同的事情,以使选择与您的设计配合使用,但您只会让自己变得困难。不要让自己难过。

标签: c# wpf xaml mvvm combobox


【解决方案1】:

据我所知,当手动更改所选 ComboBox 项目的名称并且此名称未显示在 ComboBox ItemSource 中时,您需要更新上一个上次选择的值。这是我的建议,它是您的 ComboBox 控件数据绑定和 ViewModel 中定义的逻辑的组合。

绑定说明

  1. 由于 ComboBox ItemsSource 是 Cd 的集合(ComboModels 的集合),因此 TextSearch.TextPath 绑定应定义为 CDType.Name,其中 CdType 是在描述子模型的 ComboModel 中定义的属性,而名称是描述子模型的实际搜索路径。
  2. ComboBox Text 属性绑定到 AssignmentText 属性以在组合失去焦点时触发更新逻辑(如绑定中所定义)。
  3. 如您的示例代码中定义的那样,ItemsSource 很简单。
  4. 选定的值将带来整个模型进行更新(以防我们更改选定的值名称)。
  5. 由于 ComboBox ItemsSource 是 Cd 的集合(我们称之为 ComboModel),DisplayMemberPath 绑定应定义为 CDType.Name,其中 CdType 是在描述子模型的 ComboModel 中定义的属性,Name 是描述子模型的实际搜索路径。

Xaml 代码:

        <Grid VerticalAlignment="Bottom">
        <Grid.DataContext>
            <comboBoxWhenNoAnySelectedHelpAttempt:ComboboxDataContext/>
        </Grid.DataContext>
        <StackPanel Orientation="Vertical">
            <ComboBox x:Name="ddlCDType"    
                      IsTextSearchEnabled="True"     
                      IsTextSearchCaseSensitive="False"     
                      StaysOpenOnEdit="True"    
                      TextSearch.TextPath="CDType.Name"
                      Text="{Binding AssignmentText,     
                      UpdateSourceTrigger=LostFocus, Mode=TwoWay,
                      ValidatesOnDataErrors=True}"
                      ItemsSource="{Binding CDTypes}"    
                      SelectedValue="{Binding Assignment}"     
                      DisplayMemberPath="CDType.Name"     
                      IsEditable="True"     
                      HorizontalAlignment="Left"     
                      Margin="98,10,0,0"     
                      VerticalAlignment="Top"     
                      Width="136" />
            <!--added to see changes in updated combo box item-->
            <TextBlock >
                      <Run Text="Name:"/>
                      <Run Text="{Binding Assignment.CDType.Name, UpdateSourceTrigger=PropertyChanged}"></Run>
                      <Run Text="Id:"/>
                      <Run Text="{Binding Assignment.CDType.ID, UpdateSourceTrigger=PropertyChanged}"></Run>
            </TextBlock>
        </StackPanel>
    </Grid>

虚拟机代码

public class ComboboxDataContext:BaseObservableObject
{
    private ObservableCollection<ComboModel> _cdTypes;
    private ComboModel _assignment;
    private string _assignmentText;
    private string _lastAcceptedName;


    public ComboboxDataContext()
    {
        CDTypes = new ObservableCollection<ComboModel>
        {
            new ComboModel
            {
                CDType = new ComboModelSubModel
                {
                    Name = "Cd-1",
                    ID = "1",
                },
            },
            new ComboModel
            {
                CDType = new ComboModelSubModel
                {
                    Name = "Cd-2",
                    ID = "2",
                }
            },
            new ComboModel
            {
                CDType = new ComboModelSubModel
                {
                    Name = "Cd-3",
                    ID = "3",
                },
            },
            new ComboModel
            {
                CDType = new ComboModelSubModel
                {
                    Name = "Cd-4",
                    ID = "4",
                }
            }
        };
        Assignment = CDTypes.FirstOrDefault();
    }

    public ObservableCollection<ComboModel> CDTypes
    {
        get { return _cdTypes; }
        set
        {
            _cdTypes = value;
            OnPropertyChanged();
        }
    }

    public ComboModel Assignment
    {
        get { return _assignment; }
        set
        {
            if (value == null)
                _lastAcceptedName = _assignment.CDType.Name;
            _assignment = value;
            OnPropertyChanged();
        }
    }

    //on lost focus when edit is done will check and update the last edited value
    public string AssignmentText
    {
        get { return _assignmentText; }
        set
        {
            _assignmentText = value;
            OnPropertyChanged();
            UpDateSourceCollection(AssignmentText);
        }
    }

    //will do the the update on combo lost focus to prevent the 
    //annessasary updates (each property change will make a lot of noice in combo)
    private void UpDateSourceCollection(string assignmentText)
    {
        var existingModel = CDTypes.FirstOrDefault(model => model.CDType.Name == assignmentText);
        if (existingModel != null) return;
        if (_lastAcceptedName == null)
        {
            CDTypes.Add(new ComboModel{CDType = new ComboModelSubModel{ID = string.Empty, Name = assignmentText}});
        }
        else
        {
            var existingModelToEdit = CDTypes.FirstOrDefault(model => model.CDType.Name == _lastAcceptedName);
            if(existingModelToEdit == null) return;
            existingModelToEdit.CDType.Name = assignmentText;
            existingModelToEdit.CDType.ID = string.Empty;
        }

    }
}

public class ComboModel:BaseObservableObject
{
    private ComboModelSubModel _cdType;

    public ComboModelSubModel CDType
    {
        get { return _cdType; }
        set
        {
            _cdType = value;
            OnPropertyChanged();
        }
    }
}

public class ComboModelSubModel:BaseObservableObject
{
    private string _id;
    private string _name;

    public string ID
    {
        get { return _id; }
        set
        {
            _id = value;
            OnPropertyChanged();
        }
    }

    public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
            OnPropertyChanged();
        }
    }
}

BaseObservableObject 代码

    /// <summary>`enter code here`
/// implements the INotifyPropertyChanged (.net 4.5)
/// </summary>
public class BaseObservableObject : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }

    protected virtual void OnPropertyChanged<T>(Expression<Func<T>> raiser)
    {
        var propName = ((MemberExpression)raiser.Body).Member.Name;
        OnPropertyChanged(propName);
    }

    protected bool Set<T>(ref T field, T value, [CallerMemberName] string name = null)
    {
        if (!EqualityComparer<T>.Default.Equals(field, value))
        {
            field = value;
            OnPropertyChanged(name);
            return true;
        }
        return false;
    }
}

问候。

【讨论】:

  • 有趣 - 所以基本上组合框在可编辑时,添加新文本时不会自动知道它不是集合的一部分,因此将分配的值(id)保留在内存中并附加新文本而不是将其更新为 (0/string.Empty()) - 因此迫使我们手动处理它?感谢您的回答,我将在今天晚些时候实施!
猜你喜欢
  • 2016-11-02
  • 2022-11-19
  • 2015-09-14
  • 2011-12-05
  • 2011-02-26
  • 1970-01-01
  • 2010-10-12
  • 2019-09-11
  • 2013-03-03
相关资源
最近更新 更多