1
votes

After struggling for about one week with a problem in Silverlight 4 + MVVM-Light toolkit and after searching the web without success I want to present my problem here and hope that somebody could give me some hints.

I want to present the simplified programm:

My model classes:

  1. Person

    public class Person { private decimal _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

    public class PersonInfo { private decimal _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; }
    }
    

    }

Here my 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);
        }
    }
}

In my view (PersonView) I have a combo box, a text box and a button:

<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>

When the button is clicked the first time everything works. The parameter (PersonInfo) passed after the button was clicked contains the selected ComboBox value (Cod_person) and the inserted text(Des_note). In the method SavePerson a new PersonInfo instance is assigned to the ChoosenPerson object. The problem occurs when I click the button a second time. Then as parameter after the button was clicked I get an instance of the PersonInfo class which contains the correct inserted text in the text box but as value selected in the combo box I always get 0, independet of what I choosed in the combo box. This problems occurs just in the case, that I use as combo box items instances of a class. If I use as combo box items just string values this problem does not occur. But I have to use instances of a class in my combo box.

I hope that somebody has a hint. Thanks!

PS:

Interesting is still the fact, that when changing the assignment of the ChoosenPerson from "ChoosenPerson = new Model.PersonInfo();" to _choosenPerson = new Model.PersonInfo(); this means by assignment through use of private members instead of access methods, the second time the button is clicked, the values are written into the parameter of the button correctly. The only thing is that the values that were inserted the last time are not deleted. They are shown after the first button click. But they are not shown when the assignment of a new empty ChoosenPerson is made throuh access methods... I can't explain this behavior. Who can help me?? Thank you.

2

2 Answers

0
votes

Adam was on the right track. I would assign your PersonViewModel to the DataContext instead of assigning it to the 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>

I would also totally get rid of the PersonInfo class. You do not need this class. You can work with the Person object directly. Change your references from PersonInfo to the Person class. Also, if you want to clear out the current selection in the ComboBox, set the ChoosenPerson property to null. Do not new up an instance of a class. The ChoosenPerson property needs to either be null or one of the objects in the AllPerson collection.

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);
        }
    }
}
0
votes

I think you want something like this:

<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>

I updated most of the bindings. Once you set the UserControl's data context, the rest of the controls inside can simply reference the property name, instead of having to use the Locator. I also think you had a few things pointing to the wrong places.