绑定ComboBox TwoWay到属性

时间:2015-01-16 07:32:21

标签: c# wpf xaml binding combobox

我试图让我的TwoWay-Binding工作。

这些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"/>

VM:

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是&#34; CategoryLinkCollection&#34;其中包含&#34;类别&#34; -Property和Obs.Collection&#34; CategoryLinkCollection&#34;。通过SelectedValuePath&#34; Category&#34;获得&#34;保存&#34;在VM中的属性中。

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

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

更新1:

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

XAML:

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

VM: 见上文。

更新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)将操作(散列= 64100268)替换为0级的项目

     

System.Windows.Data警告:101:BindingExpression(hash = 43478430):使用RuntimePropertyInfo(MVM)从Manipulation(hash = 64100268)获取0级GetValue:ManipulationViewModel(hash = 11088040)

     

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

     

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

     

System.Windows.Data警告:101:BindingExpression(hash = 43478430):使用RuntimePropertyInfo(SelectedManufacturer)从ManipulationViewModel(hash = 11088040)获取1级GetValue:制造商(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(Type:Entity)并将其交给我的Manipulation-View,它实例化了它的ViewModel。在此ViewModel中,实体被分成它的部分(例如制造商)并且属性(例如,SelectedManufacturer)被设置。所有这些步骤都在之前执行构建View(在Initializecomponent之前)。我想通过这种方式,View必须在初始化时获取所选值 - 或者我错了?

更新5:

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

3 个答案:

答案 0 :(得分:0)

在绑定中使用update source trigger属性并将其设置为PropertyChanged:

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

答案 1 :(得分:0)

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。

答案 2 :(得分:0)

好像我已经解开了这个谜团。

<强>解释

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

<强>解决方案:

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