【问题标题】:Binding ComboBox TwoWay to Property将 ComboBox TwoWay 绑定到属性
【发布时间】:2015-03-14 18:36:30
【问题描述】:

我正在尝试让我的双向绑定工作。

这些 ComboBox 应该将它们自己(TwoWay)绑定到它们在 VM 中的属性:

<ComboBox x:Name="_cmbCatT1" Margin="5,1,5,10" 
          ItemsSource="{Binding MVM.CategoryLinkCollection}"
          DisplayMemberPath="Category.Name" 
          SelectedValue="{Binding MVM.SelectedTier1, Mode=TwoWay}" 
          SelectedValuePath="Category"/>
<ComboBox x:Name="_cmbCatT2" Margin="5,1,5,10" 
          DataContext="{Binding SelectedItem, ElementName=_cmbCatT1}" 
          ItemsSource="{Binding CategoryLinkCollection}" 
          DisplayMemberPath="Category.Name" 
          SelectedValue="{Binding MVM.SelectedTier2, ElementName=_vManipulation, Mode=TwoWay}" 
          SelectedValuePath="Category"/>
<ComboBox x:Name="_cmbCatT3" Margin="5,1,5,10" 
          DataContext="{Binding SelectedItem, ElementName=_cmbCatT2}" 
          ItemsSource="{Binding CategoryLinkCollection}" 
          DisplayMemberPath="Category.Name" 
          SelectedValue="{Binding MVM.SelectedTier3, ElementName=_vManipulation, Mode=TwoWay}" 
          SelectedValuePath="Category"/>

虚拟机:

private string selectedName;
public string SelectedName {
    get {
        return this.selectedName;
    }
    set {
        this.selectedName = value;
        OnPropertyChanged("SelectedName");
    }
}
private string selectedDescription;
public string SelectedDescription {
    get {
        return this.selectedDescription;
    }
    set {
        this.selectedDescription = value;
        OnPropertyChanged("SelectedDescription");
    }
}
private string selectedRemark;
public string SelectedRemark {
    get {
        return this.selectedRemark;
    }
    set {
        this.selectedRemark = value;
        OnPropertyChanged("SelectedRemark");
    }
}
private string selectedValue; 
public string SelectedValue {
    get {
        return this.selectedValue;
    }
    set {
        this.selectedValue = value;
        OnPropertyChanged("SelectedValue");
    }
}
private Model.Room selectedRoom;
public Model.Room SelectedRoom {
    get {
        return this.selectedRoom;
    }
    set {
        this.selectedRoom = value;
        OnPropertyChanged("SelectedRoom");
    }
}
private Model.Locker selectedLocker;
public Model.Locker SelectedLocker {
    get {
        return this.selectedLocker;
    }
    set {
        this.selectedLocker = value;
        OnPropertyChanged("SelectedLocker");
    }
}
private Model.Category selectedTier1;
public Model.Category SelectedTier1 {
    get {
        return this.selectedTier1;
    }
    set {
        this.selectedTier1 = value;
        OnPropertyChanged("SelectedTier1");
    }
}
private Model.Category selectedTier2;
public Model.Category SelectedTier2 {
    get {
        return this.selectedTier2;
    }
    set {
        this.selectedTier2 = value;
        OnPropertyChanged("SelectedTier2");
    }
}
private Model.Category selectedTier3;
public Model.Category SelectedTier3 {
    get {
        return this.selectedTier3;
    }
    set {
        this.selectedTier3 = value;
        OnPropertyChanged("SelectedTier3");
    }
}
private Model.Manufacturer selectedManufacturer;
public Model.Manufacturer SelectedManufacturer {
    get {
        return this.selectedManufacturer;
    }
    set {
        this.selectedManufacturer = value;
        OnPropertyChanged("SelectedManufacturer");
    }
}

请注意,ItemsSource 的 DataType 是“CategoryLinkCollection”,其中包含“Category”-Property 和“CategoryLinkCollection”的 Obs.Collection。通过 SelectedValuePath 将“类别”“保存”在 VM 的属性中。

只要我在 ComboBox 中选择一个项目,一切都可以正常工作,但是 当我手动设置 VM-Properties 时,ComboBoxes 不会从包含已定义类别的 ItemsSource 中预先选择项目。正常的字符串值只适用于 finde(文本框)

我认为由于类型不同(ComboBox = CategoryLinkCollection with ValuePath to Category,Property = Category),我认为它永远不会起作用,但也许你们中的一些人可能会证明我错了。如果您需要更多信息,请告诉我。

更新 1:

我只记得主要原因应该在其他地方,因为模型“制造商”没有包装器 - 所以 ItemsSource 包含与属性相同数据类型的集合。

XAML:

<ComboBox x:Name="_cmbManufacturer" 
          ItemsSource="{Binding MVM.ManufacturerCollection}" 
          DisplayMemberPath="Name" 
          SelectedItem="{Binding MVM.SelectedManufacturer, Mode=TwoWay}"/>

虚拟机: 见上文。

更新 2:

首先,请远离那些糟糕的类别并前往更新 1 中提到的制造商,因为这是同样的问题,但更容易进入。

我现在手动设置后检查了 SelectedManufacturer-Property,以防在设置时出现问题。 SelectedManufacturer-Property 包含制造商(非空),但 UI 仍未更新。

更新 3:

使用诊断 NS 后,我从输出窗口中得到以下输出:

System.Windows.Data 警告:60:BindingExpression (hash=43478430):默认模式解析为 TwoWay

System.Windows.Data 警告:61:BindingExpression (hash=43478430):默认更新触发器解析为 PropertyChanged

System.Windows.Data 警告:62:BindingExpression (hash=43478430):附加到 System.Windows.Controls.ComboBox.SelectedItem (hash=10372298)

System.Windows.Data 警告:67:BindingExpression (hash=43478430):解析源

System.Windows.Data 警告:70:BindingExpression (hash=43478430):找到数据上下文元素:ComboBox (hash=10372298) (OK)

System.Windows.Data 警告:78:BindingExpression (hash=43478430):使用根项操作激活 (hash=64100268)

System.Windows.Data 警告:107:BindingExpression (hash=43478430):在级别 0 使用缓存访问器进行 Manipulation.MVM:RuntimePropertyInfo(MVM)

System.Windows.Data 警告:104:BindingExpression (hash=43478430):使用访问器 RuntimePropertyInfo(MVM) 将级别 0 的项目替换为 Manipulation (hash=64100268)

System.Windows.Data 警告:101 : BindingExpression (hash=43478430): GetValue at level 0 from Manipulation (hash=64100268) using RuntimePropertyInfo(MVM): ManipulationViewModel (hash=11088040)

System.Windows.Data 警告:108:BindingExpression (hash=43478430):在级别 1 - 对于 ManipulationViewModel.SelectedManufacturer 找到访问器 RuntimePropertyInfo(SelectedManufacturer)

System.Windows.Data 警告:104:BindingExpression (hash=43478430):使用访问器 RuntimePropertyInfo(SelectedManufacturer) 将级别 1 的项目替换为 ManipulationViewModel (hash=11088040)

System.Windows.Data 警告:101 : BindingExpression (hash=43478430): GetValue at level 1 from ManipulationViewModel (hash=11088040) using RuntimePropertyInfo(SelectedManufacturer): Manufacturer (hash=14500136)

System.Windows.Data 警告:80 : BindingExpression (hash=43478430): TransferValue - 获得原始值制造商 (hash=14500136)

System.Windows.Data 警告:84 : BindingExpression (hash=43478430): TransferValue - 隐式转换器产生的制造商 (hash=14500136)

System.Windows.Data 警告:89:BindingExpression (hash=43478430): TransferValue - 使用最终值制造商 (hash=14500136)

更新 4:

制造商类:

public class Manufacturer : Base.SqlBase {
    public Manufacturer(int id, string name) {
        this.SqlID = id;
        this.Name = name;
    }
}

SqlBase 类:

public abstract class SqlBase : INotifyPropertyChanged {
    public int SqlID { get; set; }
    private string _name;
    public string Name {
        get {
            return _name;
        }
        protected set {
            this._name = value;
            PropertyChangedHandler("Name");
        }
    }

    public void SetId(int id) {
        this.SqlID = id;
        PropertyChangedHandler("SqlID");
    }

    private void PropertyChangedHandler(string propertyName) {
        PropertyChangedEventHandler temp = PropertyChanged;
        if (temp != null) {
            temp(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

实体类:

public class Entity : Base.SqlBase {

    public string Description { get; private set; }
    public string Remark { get; private set; }
    public int Value { get; private set; }
    public Model.CategoryWrapper Categories { get; private set; }
    public Model.Manufacturer Manufacturer { get; private set; }
    public Model.Locker Locker { get; private set; }

    public Entity(string name, string desc, string remark, int value, Model.CategoryWrapper cat, Model.Manufacturer manuf, Model.Locker locker) {           
        this.Name = name;
        this.Description = desc;
        this.Remark = remark;
        this.Value = value;
        this.Categories = cat;
        this.Manufacturer = manuf;
        this.Locker = locker;
    }

    public Entity(int id, string name, string desc, string remark, int value, Model.CategoryWrapper cat, Model.Manufacturer manuf, Model.Locker locker) {
        this.SqlID = id;
        this.Name = name;
        this.Description = desc;
        this.Remark = remark;
        this.Value = value;
        this.Categories = cat;
        this.Manufacturer = manuf;
        this.Locker = locker;
    }
}

我得到了一个 GridView(类型:实体)的 SelectedItem 并将它交给我的 Manipulation-View 实例化它的 ViewModel。在这个 ViewModel 中,实体被分为它的部分(例如制造商)和属性(例如 SelectedManufacturer)被设置。所有这些步骤都在视图构建之前执行(在 Initializecomponent 之前)。我认为通过这种方式,视图必须在初始化时获取选定的值 - 还是我错了?

更新 5:

我真的不知道为什么,但是当窗口初始化时设置制造商时,制造商对象的 PropertyChanged-Property 为空 - 当我通过选择一个项目来设置制造商时,该属性不为空。

【问题讨论】:

  • 您尝试过使用转换器吗?我不是 100% 确定,但由于您使用的是不匹配的类型,这可能是问题所在。
  • 我只记得“Manufacturer”是同一类型(除了Collection)。我会更新问题。

标签: c# wpf xaml binding combobox


【解决方案1】:

在绑定中使用更新源触发器属性并将其设置为 PropertyChanged:

<ComboBox x:Name="_cmbManufacturer" ItemsSource="{Binding MVM.ManufacturerCollection}" DisplayMemberPath="Name" SelectedItem="{Binding MVM.SelectedManufacturer, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>

【讨论】:

  • 两者,设置 UpdateSourceTrigger 和使用相同的数据类型都没有解决问题。
【解决方案2】:

Howsit Chill-X,我是否正确理解核心问题是您希望组合框在更新 viewModel 时反映更改的选择?如果是这样,这里有一个淡化的版本来说明如何做到这一点。

需要注意的重要事项(将所选项目绑定到另一个名为 SelectedCategory 的属性。当您在 VM 中更改 SelectedCategory 时,UI 会发生变化,如果您选择新的组合框中的项目,SelectedCategory 属性将被更新。

这是视图模型:

public class ViewModel : INotifyPropertyChanged
{
    private ObservableCollection<Category> _categoryLinkCollection;
    public ObservableCollection<Category> CategoryLinkCollection
    {
        get
        {
            return this._categoryLinkCollection;
        }
        set
        {
            if (value != this._categoryLinkCollection)
            {
                this._categoryLinkCollection = value;
                OnPropertyChanged("CategoryLinkCollection");
            }
        }
    }

    private Category _selectedCategory;
    public Category SelectedCategory
    {
        get
        {
            return this._selectedCategory;
        }
        set
        {
            this._selectedCategory = value;
            OnPropertyChanged("SelectedCategory");
        }
    }
}

这是 xaml:

<ComboBox ItemsSource="{Binding CategoryLinkCollection, Mode=TwoWay}" DisplayMemberPath="Name" SelectedItem="{Binding SelectedCategory, Mode=TwoWay}" />

我只是使用页面后面的代码将虚拟数据添加到集合中(在 window_loaded 上并单击按钮以显示您可以更新 VM 属性和 UI 更新。

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        // Set the View's DataContext to our ViewModel
        var vm = new ViewModel();
        this.DataContext = new ViewModel();
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        // Populate Category Collection with dummy data.
        var vm = ((ViewModel)this.DataContext);
        vm.CategoryLinkCollection = new ObservableCollection<Category>()
        {
            new Category("Cat 1"),
            new Category("Cat 2"),
            new Category("Cat 3"),
            new Category("Cat 4"),
            new Category("Cat 5"),
            new Category("Cat 6"),
        };
        vm.SelectedCategory = vm.CategoryLinkCollection[0];
    }

    private void btn_Click(object sender, RoutedEventArgs e)
    {
        var vm = ((ViewModel)this.DataContext);
        vm.SelectedCategory = vm.CategoryLinkCollection[3];
    }
}

现在你有了一个从 VM 和 UI 更新的 ComboBox。

【讨论】:

  • 我能看到的唯一区别是“ItemsSource”中的附加模式还是我错了?让我们远离那些类别,因为它们看起来更难一些。让我们以我在更新中提到的制造商为例。当我通过 ComboBox 选择一个新的制造商时,一切正常 - OneWay = 选中。但是,当我将 SelectedManufacturer-Property 手动更改为另一个制造商时,UI 不会更新(ComboBox 为空)。 (SelectedManufacturer 是ManufacturerCollection 中包含的Item,仅提一下)。
  • 制造商组合框看起来正确。我会检查以下内容: 1) 在 SelectedManufacturer-Property 的集合上放置一个断点。当您通过 UI 更新 ComBox 时,是否会遇到断点? 2) 当您从/VM 后面的代码中设置 SelectedManufacturer-Property 时,它是否使用正确拼写的属性名称调用 OnPropertyChanged? 3) 您的 ManufacturerCollection 是 ObservableCollection 吗? 4)如果以上都正确,我会在 OnPropertyChanged 方法中放置一个断点,看看是否实际调用调用 PropertyChanged 事件。
  • 您是否将视图的 dataContext 设置为您的 VM?例如。在 Window_Loaded 后面的代码中,放置一个断点并添加一个监视/快速监视以查看 this.DataContext 是什么。如果为 null,则在 Window_Loaded 方法中添加 this.DataContext = myVMinstance; 并查看 PropertyChanged 现在是否有值。 myVMinstance 将是您的 ViewModel 类的一个实例。
  • 老兄,但谜团已经解开了。问题是由相等检查引起的 - 覆盖 Equals-Method 解决了它。顺便说一句,在我看来,Inline-XAML-Datasource 定义比在后面的代码中定义更好:)
  • 太棒了,干得好!是的,我同意你的看法,在代码隐藏中定义上下文仅仅是为了调试目的。干杯
【解决方案3】:

看来我已经解开了谜团。

说明:

“操作”- 视图,应该预先选择实体给出的 ComboBox-Value 将接收它自己的 ObservableCollection 实例 - 但制造商对象被传递给该表单 - 所以它不包含在集合中.

解决方案:

重写 Equals-Method 来检查 SQLID 属性而不是默认方法来确定相等性解决了问题。

【讨论】:

    猜你喜欢
    • 2023-04-10
    • 2015-10-15
    • 2012-05-12
    • 2013-03-23
    • 1970-01-01
    • 2016-09-25
    • 2011-04-17
    • 2015-09-29
    • 1970-01-01
    相关资源
    最近更新 更多