简体   繁体   中英

WPF MVVM code for button binding with a parameter

I have a WPF application that has multiple comboboxes and buttons. I am learning the MVVM model with this application. The first combobox will display a list of database instances. This is done at the start of the application. This works fine.

There is a button object next to the database instances combobox. When the user clicks this button I need to get the contents of the database instance combobox and use it in a call to get all the databases in that instance. I am using a RelayCommand (ICommand) for the actions. The action for the button is getting setup correctly. I have a method SelectedDatabase in the DBInstance class but it is null when I click the button.

In the LoadDBInfo method below the selectedItem parameter is null.

Here is my XAML:

<ComboBox x:Name="cbxRLFDBInstances" ItemsSource="{Binding DBInstances}" 
                  SelectedValue="{Binding SelectedDBInstance}" SelectedValuePath="value" 
                  HorizontalAlignment="Left" Height="28" Margin="189,87,0,0" VerticalAlignment="Top" 
                  Width="250" FontFamily="Arial" FontSize="14.667" 
                  IsEditable="True"/>
        <Button x:Name="btnRLFDBLoadDBInfo" Content="Load DB Info" Command="{Binding LoadDBInfoCommand}" 
                CommandParameter="{Binding SelectedDBInstance}"  HorizontalAlignment="Left" Height="26" Margin="475,89,0,0" VerticalAlignment="Top" 
                Width="101" FontFamily="Arial" FontSize="14.667" Background="#FFE8F9FF" 
                ToolTip="Click here after choosing or typing in the datbase instance.  This will populate the database list."/>
        <ComboBox x:Name="cbxRLFDBName" HorizontalAlignment="Left" Height="28" Margin="189,132,0,0" 
                  ItemsSource="{Binding DBDatabases}" SelectedValue="{Binding SelectedDBDatabase}" 
                  SelectedValuePath="value" VerticalAlignment="Top" Width="250" FontFamily="Arial" 
                  FontSize="14.667" IsEditable="True" IsReadOnly="True"
                  ToolTip="Once a database is choosen the table list will automatically be populated."/>

Here is my ViewModel:

namespace DatabaseTest.ViewModel
{

    class RLFDatabaseTableViewModel
    {

    Utilities dbtUtilities = new Utilities();


    public RelayCommand LoadDBInfoCommand
    {
        get;
        set;
    }

    public RLFDatabaseTableViewModel()
    {
        LoadDBInstances();

        LoadDBInfoCommand = new RelayCommand(LoadDBInfo);
    }


    #region Database Instance

    public IList<DBInstance> DBInstances
    {
        get;
        set;
    }


    public void LoadDBInstances()
    {
        IList<DBInstance> dbInstances = nList<DBInstance>();
        DataTable dt = SmoApplication.EnumAvailableSqlServers(false);

        dbInstances.Add(new DBInstance { DBInstanceName = "fal-conversion\\mun2012ci" });
        dbInstances.Add(new DBInstance { DBInstanceName = "fal-conversion\\mun2014ci" });

        if (dt.Rows.Count > 0)
        {
            foreach (DataRow dr in dt.Rows)
            {
                dbInstances.Add(new DBInstance { DBInstanceName = dr["Name"].ToString() });
            }
        }

        DBInstances = dbInstances;
    }


    #endregion Database Instance


    #region Database Names

    public IList<DBDatabase> DBDatabases
    {
        get;
        set;
    }


    public void LoadDBDatabases()
    {
        IList<DBDatabase> dbDatabases = new List<DBDatabase>();

        dbDatabases.Add(new DBDatabase { DBDatabaseName = "DB - A" });
        dbDatabases.Add(new DBDatabase { DBDatabaseName = "DB - B" });



        DBDatabases = dbDatabases;
    }

    #endregion Database Names


    #region Button Cammands

    void LoadDBInfo(object selectedItem)
    {
        SqlConnection sqlConn = null;
        IList<DBDatabase> dbDatabaseNames = new List<DBDatabase>();


       // string selectedItem = dbInstances.
        //Setting the PUBLIC property 'TestText', so PropertyChanged event is fired 
        if (selectedItem == null)
            dbDatabaseNames = null;
        else
        {
            SelectedDBInstance = selectedItem as DBInstance;
            dbDatabaseNames = dbtUtilities.GetDBNames(sqlConn, _selectedDBInstance.ToString(),
                _selectedDBDatabase.ToString());
        }

        DBDatabases = dbDatabaseNames;
    }

    #endregion Button Commands
}

Here is my Model:

namespace DatabaseTest.Model
{
    public class RLFDatabaseTableModel { }


    public class DBInstance : INotifyPropertyChanged
    {
        private string strDBInstance;


        public override string ToString()
        {
            return strDBInstance;
        }


        public string DBInstanceName
        {
            get
            {
                return strDBInstance;
            }

            set
            {
                if (strDBInstance != value)
                {
                    strDBInstance = value;
                    RaisePropertyChanged("DBInstanceName");
                }
            }
        }


        public event PropertyChangedEventHandler PropertyChanged;

        private void RaisePropertyChanged(string property)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(property));
            }
        }
    }

    public class DBDatabase : INotifyPropertyChanged
    {
        private string strDBDatabase;


        public override string ToString()
        {
            return strDBDatabase;
        }


        public string DBDatabaseName
        {
            get
            {
                return strDBDatabase;
            }

            set
            {
                if (strDBDatabase != value)
                {
                    strDBDatabase = value;
                    RaisePropertyChanged("DBDatabaseName");
                }
            }
        }


        public event PropertyChangedEventHandler PropertyChanged;

        private void RaisePropertyChanged(string property)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(property));
            }
        }
    }
}

EDIT: This is my code to load the 2nd combobox, cbxRLFDBName, The DBDatabase has the values but the combobox is not loaded.

public void LoadDatabases(string strDBInstanceName)
    {
        string strQuery;
        IList<DBDatabase> dbDatabases = new List<DBDatabase>();
        SqlConnection sqlUtilDBConn = null;


        try
        {
            if (sqlUtilDBConn != null)
            {
                sqlUtilDBConn.Close();
            }

            sqlUtilDBConn = dbtUtilities.LoginToDatabase(strDBInstanceName, "master");

            strQuery = "select name from sys.databases order by 1";

            using (SqlCommand sqlCmd = new SqlCommand(strQuery, sqlUtilDBConn))
            {
                SqlDataReader sqlDataRead = sqlCmd.ExecuteReader();

                while (sqlDataRead.Read())
                {
                    string strDBNme = sqlDataRead.GetString(0);

                    dbDatabases.Add(new DBDatabase { DBDatabaseName = strDBNme });
                }

                sqlDataRead.Close();
                sqlCmd.Dispose();
            }
        }
        catch (Exception exQuery)
        {
            string strMsg;


            strMsg = "GetNumRows: Error, '" + exQuery.Message + "', has occurred.";
            System.Windows.MessageBox.Show(strMsg);
        }

        DBDatabases = dbDatabases;
}

EDIT: I have removed some of the code that is not needed in the hopes that this will be easier to read. My issue is that combobox "cbxRLFDBInstances" with ItemsSource="{Binding DBInstances}" loads the combobox fine. I also have another combobox, "cbxRLFDBName" with ItemsSource="{Binding DBDatabases}". When I choose the appropriate database instance and click the Load DB Info button, LoadDatabases runs and I can see that DBDatabases has the information needed in it. However the combobox is not loaded and I do not have a failure. Why does one ItemsSource data binding work and the other does not? I believe I am setting the class correctly but it seems lo=ike the binding is not happening? What have I missed?

您是否尝试过通过组合框将命令参数作为选定的izem传递,例如:

CommandParameter="{Binding SelectedItem,ElementName=yourComboBoxName}"

Your code look fine to me, except for the SelectedValuePath="value" on the ComboBoxes. SelectedValuePath specifies a property on the selected item that is to be bound to the SelectedValue . SelectedDBInstance is of type DBInstance and DBInstance class does not define a value property, so I'd say you just have to remove SelectedValuePath="value" from the ComboBoxes.

Edit:
You need your ViewModel to implement INotifyPropertyChanged :

class RLFDatabaseTableViewModel : INotifyPropertyChanged
{
  public event PropertyChangedEventHandler PropertyChanged;

  private void RaisePropertyChanged(string property)
  {
    if (PropertyChanged != null) {
      PropertyChanged(this, new PropertyChangedEventArgs(property));
    }
  }

  // the rest of RLFDatabaseTableViewModel implementation ...
}

And then every time you change a property value inside ViewModel, you also need to call RaisePropertyChanged immediately after. For example:

DBDatabases = dbDatabaseNames;
RaisePropertyChanged("DBDatabases");

It is helpful to define your properties like so:

public string StringProperty
{
  get { return this.stringProperty; }
  set {
    this.stringProperty = value;
    this.RaisePropertyChanged("StringProperty");
  }
}
private string stringProperty;

Then you can just write

this.StringProperty = "new value";

and the new value will be set and a change notification sent.

You have to send the notifications because the View (XAML) and ViewModel are different classes and the View has no way of knowing that a property on the ViewModel has changed. If ViewModel implements INotifyPropertyChanged , WPF will listen for property changes through the PropertyChanged event and update the View accordingly.

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