简体   繁体   中英

MVP Winforms and textbox combobox values

I have a combobox with a list as datasource. This list contains objects(customers) with their properties (name, address, ...). When i select an item of the combobox, i want to pass the information (address, zipcode...) to some textboxes on my form. In my test 1tier application this works correct. But the main application im working on, is based on MVP (with my own touch on it). The problem that im facing is the casting. As my view does not know my model, i should not be allowed to use the (Customers). string address = ((Customers)comboBox1.SelectedItem).CustomerAddress;

1Tier testing code:

private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
    //getCustomers((int)comboBox1.SelectedValue);
    //txtAddress.Text =Convert.ToString( comboBox1.SelectedValue);
    Customers p = (Customers)comboBox1.SelectedItem;
    string s = comboBox1.SelectedItem.ToString();
    string address = ((Customers)comboBox1.SelectedItem).CustomerAddress;
    txtAddress1.Text = address;
}

private void Form3_Load(object sender, EventArgs e)
{
    using (var emp = new EmployerEFEntities())
    {
        var query = from customers in emp.Customers
                    select customers;

        comboBox1.DisplayMember = "CustomerName";
        comboBox1.ValueMember = "CustomerID";
        comboBox1.DataSource = query.ToList();
    }
}

I have been looking in to this for a few days now, but haven't come to a success. I hope someone could give me the right direction.

The code of the real application:

View:

private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
    txtName.Text = comboBox1.SelectedValue.ToString();
}

private void CustomerView_Load(object sender, EventArgs e)
{
    comboBox1.DataSource = customerPresenter.getCustomers();
    comboBox1.DisplayMember = "CustomerName";
    comboBox1.ValueMember = "CustomerId";
}

Presenter:

public List<tbl_customer> getCustomers()
{
    using (var customers = new DBCrownfishEntities())
    {
        var customer = from c in customers.tbl_customer
                       select c;

        return customer.ToList();
    }
}

This is just one way to implement it. Your MVP pattern might look different. In this implementation the View knows the Presenter. For more information about MVP you can take a look here

You can use the Presenter as a Wrapper for your Customer:

public interface IPresenter
{
    void Init();
    void SetSelectedCustomer(int customerId);
    IEnumerable GetCustomers();
    string FirstName { get; set; }
    string LastName { get; set; }
    string Address { get; set; }
}

The Presenter must implement INotifyPropertyChanged (and call OnPropertyChanged in the Property setters).

public class Presenter : IPresenter, INotifyPropertyChanged
{
    private readonly Repository _repository;
    private string _firstName;
    private string _lastName;
    private string _address;
    private Customer _currentCustomer;

    public Presenter(Repository repository)
    {
        _repository = repository;
    }

    public string FirstName
    {
        get { return _firstName; }
        set
        {
            if (_firstName == value) return;
            _firstName = value;
            OnPropertyChanged();
        }
    }

    public string LastName
    {
        get { return _lastName; }
        set
        {
            if (_lastName == value) return;
            _lastName = value;
            OnPropertyChanged();
        }
    }

    public string Address
    {
        get { return _address; }
        set
        {
            if (_address == value) return;
            _address = value;
            OnPropertyChanged();
        }
    }

    public IEnumerable GetCustomers()
    {
        return _repository.GetAllCustomers();
    }

    public void Init()
    {
        var result = _repository.GetAllCustomers();
        SetSelectedCustomer(result[0].Id);
    }

    public void SetSelectedCustomer(int customerId)
    {
        var customer = _repository.GetCustomerById(customerId);
        FirstName = customer.FirstName;
        LastName = customer.LastName;
        Address = customer.Address;
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

This is what the View looks like:

public partial class Form1 : Form
{
    private IPresenter _presenter;
    private bool _initialized;

    public Form1(IPresenter presenter)
    {
        InitializeComponent();           
        _presenter = presenter;
        _presenter.Init();
        SetComboBoxData(_presenter.GetCustomers());
        _initialized = true;
    }

    public void SetComboBoxData(IEnumerable data)
    {
        comboBox1.DataSource = data;
        comboBox1.ValueMember = "Id";
        comboBox1.DisplayMember = "FirstName";
    }

    private void comboBox1_SelectedIndexChanged(object sender, System.EventArgs e)
    {
        if (!_initialized) return;
        _presenter.SetSelectedCustomer((int)comboBox1.SelectedValue);
    }

    private void Form1_Load(object sender, System.EventArgs e)
    {
        textBox1.DataBindings.Add(new Binding("Text", _presenter, nameof(_presenter.FirstName)));
        textBox2.DataBindings.Add(new Binding("Text", _presenter, nameof(_presenter.LastName)));
        textBox3.DataBindings.Add(new Binding("Text", _presenter, nameof(_presenter.Address)));
    }
}

You can set the selected CustomerId at the Presenter in your SelectedIndexChanged event from your combobox:

_presenter.SetSelectedCustomer((int)comboBox1.SelectedValue);

The SetSelectedCustomer method in your Presenter (or the EventHandler for the SelectedCustomerChanged event) selects the Customer with the given CustomerId and sets the FirstName, LastName and Address:

public void SetSelectedCustomer(int customerId)
{
    var customer = _repository.GetCustomerById(customerId);
    FirstName = customer.FirstName;
    LastName = customer.LastName;
    Address = customer.Address;
}

You should do your Binding for the TextBoxes in Form_Load:

textBox1.DataBindings.Add(new Binding("Text", _presenter, nameof(_presenter.FirstName)));
textBox2.DataBindings.Add(new Binding("Text", _presenter, nameof(_presenter.LastName)));
textBox3.DataBindings.Add(new Binding("Text", _presenter, nameof(_presenter.Address)));

If you're absolutely against allowing your view access to your domain objects--those that represent data table rows (which, depending on how heavy they are, might be okay in some situations)--you might want to look into using DTOs. Check this out for a good description of considerations and one approach to doing it. Note the mention of Automapper at the bottom. It's a great tool.

EDIT: I just realized you're interested in a Winforms solution, and not one for ASP.NET. Though the link above concerns ASP.NET, the idea is the same for Winforms apps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM