Con risposta Binding 2 classes to a single user control

  • lunedì 9 aprile 2012 08:30
     
      Contiene codice

    Hi everyone. I have a class called Inventory which in it has a property called GivenToContact. Then I have another class called Contact Which has an Id, first name and last name properties. In my xaml I have a combobox. What I want is for the combobox to show the Contact names but save the ContactId in the database. So here is the property:

    public Contact Zaduzio
            {
                get { return _zaduzio; }
                set { _zaduzio = value; OnPropertyChanged("Zaduzio"); }
            }

    And then in my class contact I get only these 3 values from the database

    _contactStore.Add(new Contact()
                        {
    
                            Prezime = Reader["PREZIME"].ToString(),
    
                            Ime = Reader["IME"].ToString(),
    
                            Id = Convert.ToInt32(Reader["UPOSLENIKID"])

    Problem is that when I try to do this it is giving me an error because I am trying to save all of contact (the name, last name and ID, instead of just id) into a number field in the database. I was wondering if there was a way to get past this?

    Here is the XAML

    <ComboBox x:Name="givenToContact"
                                
                                 Grid.Column="2"
                                  Grid.Row="2"
                                  
                                 SelectedValue="{Binding Path=Inventory.GivenToContact}" IsEditable="True"
                                  
                                  DisplayMemberPath="LookupName">
                                  </ComboBox>

    • Spostato Rudedog2MVP martedì 10 aprile 2012 21:55 : move to more appropriate forum : (From:Visual C# General)
    • Spostato Mike Dos ZhangMicrosoft Contingent Staff, Moderator giovedì 12 aprile 2012 03:12 This is a WPF DataBinding question, not a WinForm one (From:Windows Forms Data Controls and Databinding)
    •  

Tutte le risposte

  • lunedì 9 aprile 2012 13:22
     
     
    I have a feeling I did not explain this well
  • lunedì 9 aprile 2012 13:41
     
     

    Problem is that when I try to do this it is giving me an error because I am trying to save all of contact (the name, last name and ID, instead of just id) into a number field in the database. I was wondering if there was a way to get past this?

    To answer on your last question: I guess you didnt.

    Back to your issue. 

    Why do you want to save all of the content into a numeric field (or property)? Inside numeric field, it only can be numbers, no strings, objects, or what ever.


    Mitja

  • lunedì 9 aprile 2012 20:48
     
     

    Hey Mitja thanks for answering.

    I don't want to save all of the content into a numeric field. That is just the error that happens.

    Here is the deal. I have a primary key for table Contacts, Contact.Id then I have a field that should have been a foreign key of that key called Zaduzio in the table Inventory but isn't. This is all database now so it's not really relavant because I don't use datasets I use adapters for connecting and querying the database.

    Here is what I want to happen. I want to have an Edit Inventory View, in which I will have a drop down box which will show the names of all the contacts in the contacts table, BUT when a name is selected will store that contact name's Contact.Id in the field Zaduzio.

    Here is what I have accomplished. Using Item source in code behind I have set it to the contacts collection, so the drop down box will show all the contact names. However when I try to set the Selected Value Binding Path to Contact.Id I get a binding error, in the output window (checking for binding errors is actually something you showed me).

    So if I set the zaduzio property to type Contact (a class I have) then I get the above error, and the problem is that I don't know how to limit it to just store the Contact.Id in the zaduzio field.

    If I try to set Zaduzio as type Int32 I get a null value stored every time in the database no matter what Contact name I select from drop down box.

    Does this make it any clearer? Can you help me?

  • lunedì 9 aprile 2012 21:25
     
     

    You're unfortunately going to have to put in some more work.

    The best option is really to separate what you're displaying on the screen (your "model") from what is actually in the database (your "domain object"). The field in the database should be an int field, set up as a foreign key to the Contact table. Your Zaduzio property in the domain object should also be type int.

    The model which you use for display should be different. You should have a separate class, called InventoryModel (or whatever you want to call it). You'll also want a ContactModel class, which contains the contact Id and Name. Inside your view model class, you should also have a Zaduzio property, but this one is a ComtactModel instance.

    You can probably see where I'm going with this. In between the UI and the database, you'll want a translation layer. It should take an InventoryModel and copy it to an Inventory, just copying over the ContactId (and ignoring the name and whatever other data). Going the other direction, you can take an Inventory and return an InventoryModel, by loading the Contact from the database using the ContactId and using it to populate the name of your ContactModel instance.

    Or, you can skip all of this work and use something like Entity Framework or NHibernate to do all this work for you...


    Check out My Blog. Now updated to actually work!

  • lunedì 9 aprile 2012 21:25
     
      Contiene codice

    It seems like you wanna bind data to comboBox. The name of contact and its appropriate id.

    What you can do, it to set DisplayMember (this will be a contact name) and ValueMember (this will be a contact id - it will be in the so called "background" of contact name).

    Then when you are retreiving selectedItem, you do:

    DataRowView view = comboBox1.SelectedItem as DataRowView;
    if (item != null)
    {
        string contactname = item["ContactName"].ToString();
        int contactID = int.Parse(view["ContactId"].ToString());
    }

    But before that bind data from dataSet (or dataTable) to comboBox: comboBox1.DataCource = table; //or ds.Tables[0];

    and define

    DisplayMember (comboBox1.DisplayMember = "ContantName";) and

    ValueMember (comboBox1.ValueMember = "ContantId";)

    Will this be in any help?


    Mitja

  • martedì 10 aprile 2012 06:28
     
      Contiene codice

    To Tim

    I use the MVP pattern so the presenters are my transition layer. And I do have them. I also have the models. One for contact. One for inventory.

    Here is the Inventory class (it's called adress but it's really just inventory ignore the name). You can also see the zaduzio field. Right now it is contact but I also tried making it int 32 with no luck.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Inventar.Model;
    using System.Data.OleDb;
    using System.Data;
    
    
    namespace Inventar.Model
    {
        [Serializable]
        public class Adress : Notifier
        {
            OleDbConnection oleCon;
            OleDbTransaction transaction = null;
            OleDbCommand oleComd;
            private Int32 _inventurniBroj;
            private string _naziv;
            private string _dobavljac;
            private Contact _zaduzio;
            private string _jedinicaMjere;
            private Int32 _vrijednost;
            private Int32 _kolicinaNabavna;
            private Int32 _kolicinaOtpisana;
            private Int32 _kolicinaTrenutna;
            private string _status;
            private DateTime? _datumNabavke;
            private DateTime? _datumZaduzenja;
            bool m_IsNew = true;
           
    
            public bool IsNew
            {
                get { return m_IsNew; }
                set { m_IsNew = value; OnPropertyChanged("IsNew"); }
            }
    
    
            public Int32 InventurniBroj
            {
                get { return _inventurniBroj; }
                set { _inventurniBroj = value; OnPropertyChanged("InventurniBroj"); OnPropertyChanged("LookupAdress");}
            }
    
           
    
            public string Naziv
            {
                get { return _naziv; }
                set { _naziv = value; OnPropertyChanged("Naziv"); OnPropertyChanged("LookupAdress"); }
            }
    
            public string Dobavljac
            {
                get { return _dobavljac; }
                set { _dobavljac = value; OnPropertyChanged("Dobavljac"); }
            }
    
            public Contact Zaduzio
            {
                get { return _zaduzio; }
                set { _zaduzio = value; OnPropertyChanged("Zaduzio"); }
            }
           
            public string JedinicaMjere
            {
                get { return _jedinicaMjere; }
                set { _jedinicaMjere = value; OnPropertyChanged("JedinicaMjere"); }
            }
            public Int32 Vrijednost
            {
                get { return _vrijednost; }
                set { _vrijednost = value; OnPropertyChanged("Vrijednost"); }
    
            }
            public Int32 KolicinaNabavna
            {
                get { return _kolicinaNabavna; }
                set { _kolicinaNabavna = value; OnPropertyChanged("KolicinaNabavna"); CalculateKolicinaTrenutna(); }
    
            }
            public Int32 KolicinaOtpisana
            {
                get { return _kolicinaOtpisana; }
                set { _kolicinaOtpisana = value; OnPropertyChanged("KolicinaOtpisana"); CalculateKolicinaTrenutna(); }
    
            }
            //public Int32 KolicinaTrenutna
            //{
            //    get { return _kolicinaTrenutna; }
            //     set { _kolicinaTrenutna = ((_kolicinaNabavna > _kolicinaOtpisana) ? (_kolicinaNabavna-_kolicinaOtpisana) : 0); }
    
            public Int32 KolicinaTrenutna
            {
                get { return _kolicinaTrenutna; }
                set { _kolicinaTrenutna = value; OnPropertyChanged("KolicinaTrenutna"); CalculateStatus(); }
    
            }
            
            public string Status
            {
                get { return _status; }
                set { _status = value; OnPropertyChanged("Status"); }
    
            }
            public DateTime? DatumNabavke
            {
                get { return _datumNabavke; }
                set { _datumNabavke = value; OnPropertyChanged("DatumNabavke"); }
    
            }
            public DateTime? DatumZaduzenja
            {
                get { return _datumZaduzenja; }
                set { _datumZaduzenja = value; OnPropertyChanged("DatumZaduzenja"); }
    
            }
            public string LookupAdress
            {
                get { return string.Format("{0}", _naziv, "{1}", _inventurniBroj); }
            }
            public override string ToString()
            {
                return LookupAdress;
            }
            private void CalculateKolicinaTrenutna()
            {
                KolicinaTrenutna = (_kolicinaNabavna > _kolicinaOtpisana) ? (_kolicinaNabavna - _kolicinaOtpisana) : 0;
            }
            private void CalculateStatus()
            {
                Status = ((_kolicinaTrenutna > 0) ? ("Aktivno") : "Otpisano");
            }
            public void fillTable()
            {
                DataTable _dobavljac = new DataTable();
                string connectionString = "provider=ORAOLEDB.ORACLE; data source=ORCL; password=****; user id=****;";
                oleCon = new OleDbConnection(connectionString);
                
                {
                    using (OleDbCommand oleComd = oleCon.CreateCommand())
                    {
                        oleComd.CommandText = "select * from Dobavljaci";
                        oleComd.CommandType = System.Data.CommandType.Text;
                        using (OleDbDataReader reader = oleComd.ExecuteReader())
                        {
                            if (reader.HasRows)
                            {
                                _dobavljac = new DataTable();
                                _dobavljac.Load(reader);
                            }
                        }
                    }
                }
            }
            
        }
    }


    Here is the contact class

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace Inventar.Model
    {
        [Serializable]
        public class Contact : Notifier
        {
            private string _prezime;
            private string _ime;
            private string _jmbg;
            private string _titula;
            private Int32 _id;
            private string _funkcija;
            private DateTime _datumRodjenja;
            private string _telefon;
            private string _zanimanje;
            private string _sektorID;
            private string _fotografija;
            private string _mobitel;
            private string _email;
    
            public string Titula
            {
                get { return _titula; }
                set { _titula = value; OnPropertyChanged("Titula"); }
            }
    
            public DateTime DatumRodjenja
            {
                get { return _datumRodjenja; }
                set { _datumRodjenja = value; OnPropertyChanged("DatumRodjenja"); }
            }
    
            public string Funkcija
            {
                get { return _funkcija; }
                set { _funkcija = value; OnPropertyChanged("Funkcija"); }
            }
            public Int32 Id
            {
                get { return _id; }
                set { _id = value; OnPropertyChanged("Id"); }
            }
            public string Telefon
            {
                get { return _telefon; }
                set { _telefon = value; OnPropertyChanged("Telefon"); }
            }
            public string Jmbg
            {
                get { return _jmbg; }
                set { _jmbg = value; OnPropertyChanged("Jmbg"); }
            }
            public string Prezime
            {
                get { return _prezime; }
                set
                {
                    _prezime = value; OnPropertyChanged("Prezime");
                    OnPropertyChanged("LookupName");
                }
            }
            public string Ime
            {
                get { return _ime; }
                set
                {
                    _ime = value; OnPropertyChanged("Ime");
                    OnPropertyChanged("LookupName");
                }
            }
            public string Fotografija
            {
                get { return _fotografija; }
                set { _fotografija = value; OnPropertyChanged("Fotografija"); }
            }
            public string Zanimanje
            {
                get { return _zanimanje; }
                set { _zanimanje = value; OnPropertyChanged("Zanimanje"); }
            }
        
            public string SektorID
            {
                get { return _sektorID; }
                set { _sektorID = value; OnPropertyChanged("SektorID"); }
            }
       
            public string Mobitel
            {
                get { return _mobitel; }
                set { _mobitel = value; OnPropertyChanged("Mobitel"); }
            }
    
            public string Email
            {
                get { return _email; }
                set { _email = value; OnPropertyChanged("Email"); }
            }
    
            public string LookupName
            {
                get { return string.Format("{0} {1}", _ime, _prezime); }
            }
            public override string ToString()
            {
                return LookupName;
            }
            public override bool Equals(object obj)
            {
                Contact other = obj as Contact;
                return other != null && other.Id == Id;
            }
    
        }
    }

    Now I also have repository classes and presenter classes. The repository gets the data from the database. Here is the contact repository.

    public List<Contact> FindAll()
            
            {
                string connectionString = "provider=ORAOLEDB.ORACLE; data source=ORCL; password=fin; user id=fin;";
                oleCon = new OleDbConnection(connectionString);
                List<Contact> _contactStore = new List<Contact>();
    
    
                try
                {
                    oleComd = new OleDbCommand();
                    oleComd.Connection = oleCon;
                    oleComd.CommandText = "Select * from UPOSLENICI";
                    oleCon.Open();
                    OleDbDataReader Reader = oleComd.ExecuteReader();
    
                    while (Reader.Read())
                    {
    
                        _contactStore.Add(new Contact()
                        {
    
                            Prezime = Reader["PREZIME"].ToString(),
    
                            Ime = Reader["IME"].ToString(),
    
                            Id = Convert.ToInt32(Reader["UPOSLENIKID"])
    
                            //Telefon = Reader["TELEFON"].ToString(),
    
                            //Titula = Reader["TITULA"].ToString()
    
    
    
                        });
                    }
    
                    oleCon.Close();
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
    
                return _contactStore;
            }


    And here is the presenter for the view that I have.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Inventar.Model;
    using Inventar.Views;
    using System.Collections.ObjectModel;
    
    namespace Inventar.Presenters
    {
       public class EditOpremaPresenter : PresenterBase<EditOpremaView>
      
       
        {
            private readonly ApplicationPresenter _applicationPresenter;
            private readonly Adress _adress;
            
            
            
            private ObservableCollection<Adress> _currentAdress;
            private ObservableCollection<Contact> _currentContacts;
            public EditOpremaPresenter(
                ApplicationPresenter applicationPresenter,
                EditOpremaView view,
                Adress adress,
               
               
                 ObservableCollection<Adress> currentAdress,
                ObservableCollection<Contact> currentContacts
               )
                : base(view, "Adress.Naziv")
            {
                _applicationPresenter = applicationPresenter;
                _adress = adress;
               
                _currentAdress = _applicationPresenter.CurrentAdress;
                _currentContacts = _applicationPresenter.CurrentContacts;
    
    
    
                this.View.zaduzio.ItemsSource = CurrentContacts;
                
    
                
            }
            public ObservableCollection<Adress> CurrentAdress
            {
                get { return _currentAdress; }
    
            }
    
            public ObservableCollection<Contact> CurrentContacts
            {
                get { return _currentContacts; }
    
            }
    
           
    
            public Adress Adress
            {
                get { return _adress; }
            }
    
           
            public void Save()
            {   
               
                _applicationPresenter.SaveAdress(Adress);
            }
            public void Delete()
            {
                _applicationPresenter.CloseTab(this);
                _applicationPresenter.DeleteAdress(Adress);
            }
            public void Close()
            {
                _applicationPresenter.CloseTab(this);
            }
            public override bool Equals(object obj)
            {
                EditOpremaPresenter presenter = obj as EditOpremaPresenter;
                return presenter != null && presenter.Adress.Equals(Adress);
            }
           
        }
    }
    

    To Mitja

    My problem is that I don't use a datatable or dataset. I tried using this method to populate a datatable but when I try to run it, it gives me an error like Execute Reader requires an open and available connection, the connection's current state is closed. I put the filltable method in the inventory class here it is

     public void fillTable()
            {
                DataTable _dobavljac = new DataTable();
                string connectionString = "provider=ORAOLEDB.ORACLE; data source=ORCL; password=****; user id=****;";
                oleCon = new OleDbConnection(connectionString);
                
                {
                    using (OleDbCommand oleComd = oleCon.CreateCommand())
                    {
                        oleComd.CommandText = "select * from Dobavljaci";
                        oleComd.CommandType = System.Data.CommandType.Text;
                        using (OleDbDataReader reader = oleComd.ExecuteReader())
                        {
                            if (reader.HasRows)
                            {
                                _dobavljac = new DataTable();
                                _dobavljac.Load(reader);
                            }
                        }

    And then I call the method in the dobavljac property (same thing like with zaduzio, a combobox that shows a title but stores the id).


    • Modificato dino2dy martedì 10 aprile 2012 06:49
    •  
  • martedì 10 aprile 2012 12:54
     
      Contiene codice

    Open connection before instanitating a reader class:

    oleComd.CommandType = System.Data.CommandType.Text;
    oleCon.Open(); //open connection here
    using (OleDbDataReader reader = oleComd.ExecuteReader())
    {
     //rest of code...
    }

    and this code aboce is not gonna work.

    If you wanna fill datatable, you cannot use DataReader class (since its only executing one by one operation). If you wanna fill datatable you should use dataAdapter class:

            public void fillTable()
            {
                DataTable _dobavljac = new DataTable();
                string connectionString = "provider=ORAOLEDB.ORACLE; data source=ORCL; password=****; user id=****;";
                oleCon = new OleDbConnection(connectionString);            
                {
                    using (OleDbCommand oleComd = oleCon.CreateCommand())
                    {
                        oleComd.CommandText = "select * from Dobavljaci";
                        oleComd.CommandType = System.Data.CommandType.Text;
                        using (OleDbDataAdapter da = new OleDbDataAdapter(cmd))
                        {
                            da.Fill(_dobavoljac);
                        }//and rest of brackets (and code)...


    Mitja


  • martedì 10 aprile 2012 13:22
     
      Contiene codice

    Well that fixed the error but it isn't what I needed. When I run it now the combobox comes out empty, even the ones that have a value in the database.

    Thing is when i debug it, it is showing the correct id values for the _dobavljac property. They just don't show up on screen (and I am guessing that is the least of my problems as they still have to be saved into the database).

    Here is my Xaml code for the dobavljac combobox.

    <ComboBox x:Name="dobavljac"
                                 
                                 Grid.Column="1"
                                 SelectedValuePath="{Binding Path=Adress.Dobavljac}"
                                 SelectedValue="{Binding Path=Adress.Dobavljac}" IsEditable="True"
                                  DisplayMemberPath="">

    Also when I run the output, strangely, it's reporting no binding errors for the dobavljac field.

  • martedì 10 aprile 2012 13:28
     
      Contiene codice

    You have to set DisplayMember  property (and /or valuemember - in case if you wanna use some background id column for particulat item:

    //..
    using (OleDbDataAdapter da = new OleDbDataAdapter(cmd))
        da.Fill(_dobavoljac);
    comboBox1.DataSouce = _dobavljaci.DefalutView;
    comboBox1.DispayMember = "columnNameToShow";
    comboBox1.ValueMemher = "someIDColumn"; //if you wanna use id  - but this is not needed


    Mitja

  • martedì 10 aprile 2012 19:32
     
     

    I already show the names of the column, I use itemsource for that, and combine it with an observable collection of contacts. The problem is that I can't seperate just the ID from the observable collection and that is what I want. To show the names and store the id.

    Can I do it somehow without setting the DataSource because since I use the Model View Presenter pattern my Data Context is set by the presenters and I have a feeling if I do it like that it will mess up my application.

    Also where should I put that code? In my Adress class?

  • mercoledì 11 aprile 2012 11:47
     
      Contiene codice

    public void fillTable() { DataTable _dobavljac = new DataTable(); string connectionString = "provider=ORAOLEDB.ORACLE; data source=ORCL; password=****; user id=****;"; oleCon = new OleDbConnection(connectionString); { using (OleDbCommand oleComd = oleCon.CreateCommand()) { oleComd.CommandText = "select * from Dobavljaci"; oleComd.CommandType = System.Data.CommandType.Text; oleCon.Open(); using (OleDbDataReader reader = oleComd.ExecuteReader()) { if (reader.HasRows) { _dobavljac = new DataTable(); _dobavljac.Load(reader); } } using (OleDbDataAdapter da = new OleDbDataAdapter(oleComd)) da.Fill(_dobavljac); this.View.dobavljac.DataContext = _dobavljac.DefaultView; this.View.dobavljac.DisplayMemberPath = "Naziv"; this.View.dobavljac.SelectedValue = "Id"; } }

    This is the best interpretation of your code I could get. But in order to use it I put it in the Presenter and then instantiated a presenter in the Adress class and called the method.

     EditOpremaPresenter presenter;
    
    
    
    
    public string Dobavljac
            {
                get { return _dobavljac; }
                set { _dobavljac = value; OnPropertyChanged("Dobavljac"); presenter.fillTable(); }
            }

    But I am getting an error     

        Message    "Object reference not set to an instance of an object."    string


    • Modificato dino2dy mercoledì 11 aprile 2012 11:48
    •  
  • mercoledì 11 aprile 2012 12:01
     
     

    What actually represents "EditOpremaPresenter"? And what is "fillTable()? A method of EditOprema class or something?

    You know, its hard to help you out, becuase of some many unknown names (classes, propeties) insid your code - for you its all logical, but for me its a complete mistery. And until I wont know what Iam dealing with, I surely cannot help you further.


    Mitja

  • mercoledì 11 aprile 2012 12:06
     
     

    EditOpremaPresenter is the presenter for the editOpremaView, which is a xaml file where I have all the comboboxes and what not. And then you have something like Adress which is just a class and where all the getters and setters are.

    fillTable() is a method that would fill the datatable. I put it in the editOpremaPresenter, because in that class I can see the view objects, like combobox. So then I called it from the Adress class, or to be more precise from that property's (dobavljac) setter. Cos I figure that is when the method should run.

  • lunedì 16 aprile 2012 12:39
     
     Con risposta Contiene codice

    Here is the solution. It was quite simple really.In WPF all I had to do was this:

    <ComboBox x:Name="zaduzio"  
                                 Grid.Column="2"
                                  Grid.Row="2"
                                  
                                 SelectedValue="{Binding Path=Adress.Zaduzio}" IsEditable="True"
                                  ItemsSource="{Binding Path=CurrentContacts}"
                                  SelectedValuePath="Id"
                                   DisplayMemberPath="LookupName"
                                 >
                                  </ComboBox>

    And now everything works. Full credit goes to Christoph Basedau, here is the link for the thread where he helped me.

    • Contrassegnato come risposta dino2dy lunedì 16 aprile 2012 12:39
    •