【问题标题】:WPF MVVM code for button binding with a parameter带有参数的按钮绑定的 WPF MVVM 代码
【发布时间】:2017-08-10 11:59:27
【问题描述】:

我有一个包含多个组合框和按钮的 WPF 应用程序。我正在使用此应用程序学习 MVVM 模型。第一个组合框将显示数据库实例列表。这是在应用程序开始时完成的。这工作正常。

数据库实例组合框旁边有一个按钮对象。当用户单击此按钮时,我需要获取数据库实例组合框的内容并在调用中使用它来获取该实例中的所有数据库。我正在使用 RelayCommand (ICommand) 来执行这些操作。按钮的操作正在正确设置。我在 DBInstance 类中有一个 SelectedDatabase 方法,但是当我单击按钮时它为空。

在LoadDBInfo方法下面的selectedItem参数为null。

这是我的 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."/>

这是我的 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
}

这是我的模型:

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

编辑:这是我加载第二个组合框 cbxRLFDBName 的代码,DBDatabase 具有值但未加载组合框。

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

编辑:我删除了一些不需要的代码,希望这将更容易阅读。我的问题是带有 ItemsSource="{Binding DBInstances}" 的组合框“cbxRLFDBInstances”可以很好地加载组合框。我还有另一个组合框,“cbxRLFDBName”,ItemsSource="{Binding DBDatabases}"。当我选择适当的数据库实例并单击 Load DB Info 按钮时,LoadDatabases 运行,我可以看到 DBDatabases 中包含所需的信息。但是组合框没有加载,我没有失败。为什么一个 ItemsSource 数据绑定有效而另一个无效?我相信我正确设置了课程,但似乎 lo=ike 绑定没有发生?我错过了什么?

【问题讨论】:

  • 到底是什么问题?这很容易阅读和理解:D
  • 当我进入 LoadDBInfo 方法时,传递的 selectedItem 对象为空。我需要获取 databaseinstance 组合框的内容。
  • 您的 Sql 连接是否应该为空? SqlConnection sqlConn = null;
  • 在获得 selectedItem 集或获得数据库实例组合框的内容后,我可以处理 else 代码。由于 selectedItem 为空,因此代码没有实际意义。我认为 CommandPaeameter = "{Binding SelectedDBInstance"} 会触发 SelectedDBInstance 方法,并且会填充 _selectedDBInstance。我对绑定很陌生。但我真正需要帮助的是获取当前在 Database Instance 组合框中的值。一旦我有了它,并且希望对绑定有更好的理解,我就可以修复代码。
  • 这给了我我需要的东西。谢谢!

标签: c# button mvvm combobox binding


【解决方案1】:

您的代码在我看来很好,除了 ComboBoxes 上的 SelectedValuePath="value"SelectedValuePath 指定要绑定到SelectedValue 的选定项的属性。 SelectedDBInstanceDBInstance 类型,而 DBInstance 类没有定义 value 属性,所以我想说你只需要从 ComboBoxes 中删除 SelectedValuePath="value"

编辑:
你需要你的 ViewModel 来实现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 ...
}

然后每次更改 ViewModel 中的属性值时,还需要立即调用 RaisePropertyChanged。例如:

DBDatabases = dbDatabaseNames;
RaisePropertyChanged("DBDatabases");

这样定义属性会很有帮助:

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

那你就可以写了

this.StringProperty = "new value";

将设置新值并发送更改通知。

您必须发送通知,因为 View (XAML) 和 ViewModel 是不同的类,并且 View 无法知道 ViewModel 上的属性已更改。如果 ViewModel 实现了INotifyPropertyChanged,WPF 将通过PropertyChanged 事件监听属性变化,并相应地更新 View。

【讨论】:

  • Hromadik——它确实有效。我试图弄清楚,但我不知道。但是对于 cbxRLFDBInstances 组合框,所选名称在 value 参数中
  • @Cass 我刚刚意识到您正在使用 SelectedValue 和 SelectedValuePath,所以应该可以。我一直使用 SelectedItem 而不是 SelectedValue 和 SelectedValuePath。检查是否设置了 SelectedDBInstance,如果没有,请尝试使用 SelectedItem,这对我一直有效。
  • @Hromadik -- 你能看看我的最新编辑吗?对我的问题有什么想法吗?
  • @Cass 我认为问题在于最后一行DBDatabases = dbDatabases; 设置了属性,但视图(XAML)不知道发生了这种情况。必须通知视图属性的值已更改。你实际上已经在你的模型中这样做了——通过实现INotifyPropertyChanged 并调用RaisePropertyChanged(property)。每次更改数据绑定属性的值时,只需在视图模型中执行相同的操作即可。这会通知 WPF 引擎某个属性已更改,它将获取其当前值。
  • 对不起,我有点困惑。我不确定我需要在我的视图模型中做什么?如果我已经在模型中这样做了,为什么不通知 UI?在 ViewModel 中,什么需要是 INotifyPropertyChanged 类?
【解决方案2】:

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

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

【讨论】:

  • 刚刚发现
  • @iviica.moke -- 你能看看我最新的编辑吗?对我的问题有什么想法吗?
  • 我不确定,但可能是因为您将 DBDatabase 设置为列表,并在单击按钮后初始化,列表填充但您的 UI 不会收到通知。尝试将该列表更改为 ObservableCollection,因为它已经实现了集合更改,并且您的 UI 将在集合更改时收到通知。当我绑定到集合时,我总是使用 ObservableCollection。并且您的第一个列表在构造函数中初始化,这就是它显示项目的原因,但它不会显示列表上的任何更改,因为列表没有实现 INotifyCollectionChanged。
  • 我不能在 hromadik 上写字,但是使用 ObservableCollection 属性,你的 UI 会收到通知。像这样。 ObservableCollection DBDatabases {get; set;} 并在构造函数中初始化该属性,例如: DBDatabases = new ObservableCollection();它会起作用。当您希望通知您的 UI 时,我建议始终使用 ObservableCollection。
猜你喜欢
  • 1970-01-01
  • 2013-07-01
  • 2019-03-04
  • 2015-06-07
  • 1970-01-01
  • 1970-01-01
  • 2014-06-19
  • 1970-01-01
  • 2017-10-29
相关资源
最近更新 更多