【问题标题】:Problem with DataBinding a ComboBox in Silverlight with MVVM Light使用 MVVM Light 在 Silverlight 中对 ComboBox 进行数据绑定的问题
【发布时间】:2010-11-02 13:24:03
【问题描述】:

在 Silverlight 4 + MVVM-Light 工具包中的一个问题挣扎了大约一周后,在没有成功搜索网络之后,我想在这里提出我的问题,希望有人能给我一些提示。

我要介绍简化程序:

我的模型类:

  1. 公共类人 { 私人小数_cod_Person;

    public decimal Cod_Person
    {
        get { return _cod_Person; }
        set { _cod_Person = value; }
    }
    private string _des_Person;
    
    
    public string Des_Person
    {
        get { return _des_Person; }
        set { _des_Person = value; }
    }
    

    }

  2. 人物信息

    公共类 PersonInfo { 私人小数_cod_person;

    public decimal Cod_person
    {
        get { return _cod_person; }
        set { _cod_person = value; }
    }
    private string _des_note;
    
    
    public string Des_note
    {
        get { return _des_note; }
        set { _des_note = value; }
    }
    

    }

这是我的 ViewModel:

public class PersonViewModel : ViewModelBase
{
    public RelayCommand<Model.PersonInfo> save_Click { get; private set; }

    public PersonViewModel()
    {
        save_Click = new RelayCommand<Model.PersonInfo>(personInfo =>
        {
            SavePerson(personInfo);
        });
        //the content of the combo box is defined
        AllPerson = new ObservableCollection<Model.Person>
        {
            new Model.Person(){
                Cod_Person = 1,
                Des_Person = "Name 1"
            },
            new Model.Person(){
                Cod_Person = 2,
                Des_Person = "Name 2"
            }
        };

        //an empty PersonInfo is created, which the UI will work on
        ChoosenPerson = new Model.PersonInfo();
    }

    private void SavePerson(Model.PersonInfo personInfo)
    {
        //here some safing processing could be done...
        //but is omitted here


        //and here a new PersonInfo is assigned the ChoosenPerson
        ChoosenPerson = new Model.PersonInfo();
    }

    public const string AllPersonPropertyName = "AllPerson";
    private ObservableCollection<Model.Person> _allPersons = null;
    public ObservableCollection<Model.Person> AllPerson
    {
        get
        {
            return _allPersons;
        }

        set
        {
            if (_allPersons == value)
            {
                return;
            }

            var oldValue = _allPersons;
            _allPersons = value;
            RaisePropertyChanged(AllPersonPropertyName, oldValue, value, true);
        }
    }

    public const string ChoosenPersonPropertyName = "ChoosenPerson";
    private Model.PersonInfo _choosenPerson = null;
    public Model.PersonInfo ChoosenPerson
    {
        get
        {
            return _choosenPerson;
        }

        set
        {
            if (_choosenPerson == value)
            {
                return;
            }

            var oldValue = _choosenPerson;
            _choosenPerson = value;

            // Update bindings and broadcast change using GalaSoft.MvvmLight.Messenging
            RaisePropertyChanged(ChoosenPersonPropertyName, oldValue, value, true);
        }
    }
}

在我看来(PersonView)我有一个组合框、一个文本框和一个按钮:

<UserControl x:Class="TEST_STACKOVERFLOW.PersonView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         mc:Ignorable="d"
         DataContext="{Binding Source={StaticResource Locator}, Path=PersonViewModel.ChoosenPerson}" d:DesignHeight="258" d:DesignWidth="341">
<Grid>
    <ComboBox Height="23" HorizontalAlignment="Left" Margin="84,51,0,0" Name="comboBox1" VerticalAlignment="Top" Width="120" ItemsSource="{Binding Source={StaticResource Locator}, Path=PersonViewModel.AllPerson}" DisplayMemberPath="Des_Person" SelectedValuePath="Cod_Person" SelectedValue="{Binding Path=Cod_person, Mode=TwoWay}" />
    <Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="159,199,0,0" Name="button1" VerticalAlignment="Top" Width="75" Command="{Binding Source={StaticResource Locator}, Path=PersonViewModel.save_Click}" CommandParameter="{Binding}" />
    <TextBox Height="23" HorizontalAlignment="Left" Margin="94,107,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" Text="{Binding Path=Des_note, Mode=TwoWay}" />
</Grid>

当第一次单击按钮时,一切正常。单击按钮后传递的参数 (PersonInfo) 包含选定的 ComboBox 值 (Cod_person) 和插入的文本 (Des_note)。在方法 SavePerson 中,将一个新的 PersonInfo 实例分配给 ChoosenPerson 对象。当我第二次单击该按钮时会出现问题。然后作为单击按钮后的参数,我得到一个 PersonInfo 类的实例,它在文本框中包含正确插入的文本,但作为在组合框中选择的值,我总是得到 0,与我无关在组合框中选择。这个问题只是在我用作类的组合框项实例的情况下发生。如果我仅将字符串值用作组合框项,则不会发生此问题。但我必须在组合框中使用类的实例。

希望有人能指点一下。 谢谢!

PS:

有趣的事实仍然是,当从 "ChoosenPerson = new Model.PersonInfo();" 更改 ChoosenPerson 的分配时到 _choosenPerson = new Model.PersonInfo(); 这意味着通过使用私有成员而不是访问方法进行分配,第二次单击按钮时,值将正确写入按钮的参数.唯一的问题是上次插入的值没有被删除。它们在第一次单击按钮后显示。但是当通过访问方法分配新的空 ChoosenPerson 时,它们不会显示... 我无法解释这种行为。谁能帮我??谢谢。

【问题讨论】:

    标签: silverlight mvvm-light


    【解决方案1】:

    亚当走在了正确的轨道上。我会将您的PersonViewModel 分配给DataContext,而不是分配给ChoosenPerson

    <UserControl
        x:Class="TEST_STACKOVERFLOW.PersonView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        DataContext="{Binding Source={StaticResource Locator}, Path=PersonViewModel}"
        mc:Ignorable="d" d:DesignHeight="258" d:DesignWidth="341">
    
        <Grid>
            <ComboBox Height="23" HorizontalAlignment="Left" Margin="84,51,0,0" Name="comboBox1" VerticalAlignment="Top" Width="120" ItemsSource="{Binding AllPerson}" DisplayMemberPath="Des_Person" SelectedValuePath="Cod_Person" SelectedItem="{Binding Path=ChoosenPerson, Mode=TwoWay}" />
            <Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="159,199,0,0" Name="button1" VerticalAlignment="Top" Width="75" Command="{Binding save_Click}" CommandParameter="{Binding SelectedItem, ElementName=comboBox1}" />
            <TextBox Height="23" HorizontalAlignment="Left" Margin="94,107,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" Text="{Binding ChoosenPerson.Des_Person, Mode=TwoWay}" />
        </Grid>
    </UserControl>
    

    我也将完全摆脱 PersonInfo 类。你不需要这个类。您可以直接使用Person 对象。将您的引用从 PersonInfo 更改为 Person 类。另外,如果要清除ComboBox 中的当前选择,请将ChoosenPerson 属性设置为null。不要新建一个类的实例。 ChoosenPerson 属性必须为 null 或 AllPerson 集合中的对象之一。

    public class PersonViewModel : ViewModelBase
    {
        public RelayCommand<Person> save_Click { get; private set; }
    
        public PersonViewModel()
        {
            save_Click = new RelayCommand<Person>(personInfo =>
            {
                SavePerson(personInfo);
            });
    
            //the content of the combo box is defined
            AllPerson = new ObservableCollection<Person>
            {
                new Person(){
                    Cod_Person = 1,
                    Des_Person = "Name 1"
                },
                new Person(){
                    Cod_Person = 2,
                    Des_Person = "Name 2"
                }
            };
    
            //an empty PersonInfo is created, which the UI will work on
            ChoosenPerson = new Person();
        }
    
        private void SavePerson(Person personInfo)
        {
            //here some safing processing could be done...
            //but is omitted here
    
    
            //and here a new PersonInfo is assigned the ChoosenPerson
            ChoosenPerson = null;
        }
    
        public const string AllPersonPropertyName = "AllPerson";
        private ObservableCollection<Person> _allPersons = null;
        public ObservableCollection<Person> AllPerson
        {
            get
            {
                return _allPersons;
            }
    
            set
            {
                if (_allPersons == value)
                {
                    return;
                }
    
                var oldValue = _allPersons;
                _allPersons = value;
                RaisePropertyChanged(AllPersonPropertyName, oldValue, value, true);
            }
        }
    
        public const string ChoosenPersonPropertyName = "ChoosenPerson";
        private Person _choosenPerson = null;
        public Person ChoosenPerson
        {
            get
            {
                return _choosenPerson;
            }
    
            set
            {
                if (_choosenPerson == value)
                {
                    return;
                }
    
                var oldValue = _choosenPerson;
                _choosenPerson = value;
    
                // Update bindings and broadcast change using GalaSoft.MvvmLight.Messenging
                RaisePropertyChanged(ChoosenPersonPropertyName, oldValue, value, true);
            }
        }
    }
    

    【讨论】:

    • 感谢您的帮助。现在一切正常。问题一方面是正确的 DataBinding,另一方面是填充 ComboBox 的类必须实现 IEquatable 接口....
    【解决方案2】:

    我想你想要这样的东西:

    <UserControl x:Class="TEST_STACKOVERFLOW.PersonView" 
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             mc:Ignorable="d" 
             DataContext="{Binding Source={StaticResource Locator}, Path=PersonViewModel}" d:DesignHeight="258" d:DesignWidth="341"> 
    <Grid> 
        <ComboBox Height="23" HorizontalAlignment="Left" Margin="84,51,0,0" Name="comboBox1" VerticalAlignment="Top" Width="120" ItemsSource="{Binding AllPerson}" DisplayMemberPath="Des_Person" SelectedValuePath="Cod_Person" SelectedValue="{Binding Path=ChoosenPerson, Mode=TwoWay}" /> 
        <Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="159,199,0,0" Name="button1" VerticalAlignment="Top" Width="75" Command="{Binding save_Click}" CommandParameter="{Binding ChoosenPerson}" /> 
        <TextBox Height="23" HorizontalAlignment="Left" Margin="94,107,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" Text="{Binding Path=Des_note, Mode=TwoWay}" /> 
    </Grid>
    

    我更新了大部分绑定。一旦设置了 UserControl 的数据上下文,内部的其余控件就可以简单地引用属性名称,而不必使用定位器。我也认为你有一些东西指向了错误的地方。

    【讨论】:

    • 这不起作用。首先将文本框设置为上面设置的 DataContext 中不存在的 Path Des_note。其次,组合框的 SelectedValue 设置为具有 PersonInfo 类型的路径 ChoosenPerson,但 SelectedValuePath 设置为具有十进制类型的 Cod_Person。问题一定是别的。
    • 我现在明白了。看起来您需要修改 ViewModel 以跟踪当前的 Person 以及相应的 PersonInfo。您真的想将 UserControl 的 DataContext 设置为 ViewModel 本身——这是我在原始答案中试图提出的要点。这将真正简化事情。 ViewModel 是 View 的 Model - 其中的属性和方法应该与 View 中的控件非常接近。
    • 好吧,你想到这样的事情: ...DataContext="{Binding Source={StaticResource Locator}, Path=PersonViewModel}.. 但这会产生与以前相同的错误,在第二次单击按钮后...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-09
    • 1970-01-01
    • 2012-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多