WPF 组合框 - 更改的数据源导致空 ItemsSource

WPF Combobox - changed Datasource results in empty ItemsSource

我正在对页面中的组合框进行简单绑定:

XAML:

 <ComboBox x:Name="Cmb_test"  Grid.Column="2" Grid.Row="2" HorizontalAlignment="Left"  ItemsSource="{Binding}" />

后面的代码:

private void Page_Loaded(object sender, RoutedEventArgs e)
{
     //Binding of label works fine everytime
     My_label.Content = dt.Rows[0]["Column1"];

     Cmb_test.DataContext = dt.DefaultView;
     Cmb_test.SelectedValuePath = dt.Columns[3].ToString();
     Cmb_test.DisplayMemberPath = dt.Columns[4].ToString();

     //Just a check to see whether DataTable really has changed
     Console.WriteLine(dt.Rows.Count.ToString());
 }

但是每当我的数据表“dt”发生变化时,我的组合框就不再显示项目。我知道已经有很多关于这个问题的问题,但我所能找到的只是与 运行 时间刷新相关的问题。在我的例子中,当 DataTable 更改时,我关闭了一个页面并重新打开它,但结果是空的 Combobox。

关闭我的页面的代码,补充:

与组合框在同一页面中的代码:

private void BtnClose_Click(object sender, RoutedEventArgs e)
{
      Cmb_test.ItemsSource = null;
      Cmb_test.DataContext = null;

      var main_window = Application.Current.MainWindow;
      var frame = (main_window as MainWindow).My_Frame;
      frame.Content = null;

}

主窗口中的代码:

private void My_Frame_Navigated(object sender, NavigationEventArgs e)
{
     if (My_Frame.Content == null)
     {
        My_Frame.RemoveBackEntry();
     }
}

编辑 - 另一次尝试:

XAML:

  <Page.Resources>
        <CollectionViewSource x:Key="My_source"/>
    </Page.Resources>

  <ComboBox x:Name="Cmb_test" ItemsSource="{Binding Source={StaticResource My_source}}" DisplayMemberPath="Column1"/>

后面的代码:

 private void Page_Loaded(object sender, RoutedEventArgs e)
        {

            var combo_datasource = new CollectionViewSource();
            combo_datasource = (CollectionViewSource)this.FindResource("seznamVrstEvidenc");
            combo_datasource.Source = Tabele.dt_Sifrant.DefaultView;

        }

这是怎么回事,我怎样才能修复组合框以每次都显示它的项目?

您没有将 ComboBox 绑定到任何东西

<ComboBox x:Name="Cmb_test" [...] ItemsSource="{Binding}" />

你应该有一些 collection

<ComboBox x:Name="Cmb_test" [...] ItemsSource="{Binding MyList}" />

查看其余代码,您似乎是 "manually binding" 数据表的组合框。然后,您可以创建一个绑定 programmatically,将 ComboBox ItemsSource 链接到 DataTable DefaultView。

但是有一个问题,如果你对 "DataTable is changed" 的意思是

dt = new DataTable();

dt = Db.GetTable();

你可能会再次陷入同样的​​问题,因为绑定是在两个实例之间完成的,所以当创建一个新的 dt 时,你必须将它重新绑定到 ComboBox。

另一种解决问题的方法是在每次有新的 DataTable 时设置 ComboBox ItemsSource。

希望对您有所帮助。

--------更新--------

根据我的评论,我会在存储 DataTable dt 的 class 上实施 INotifyPropertyChanged。

public class ThatParticulareClass : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
    {  
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    // [...] all the other stuff

    public void MethodThatUpdateDataTable()
    {
        // Update DataTable
        NotifyPropertyChanged(nameof(dt));
    }
}

应该可以。如果您对 DataTable object 的更改仅来自用户(来自视图),那么您应该在公开 DataTable 的控件上注册一个结束编辑事件(类似于 DataGrid.RowEditEnding)。那时你调用 NotifyPropertyChanged(nameof(dt)); (确保此调用来自包含 DataTable dt 的同一个 class)

Implementation

我找到了解决办法。在您熟悉 WPF 和绑定控件之前,我必须说一件非常令人沮丧的事情...

为了理解 在 WPF 中你应该使用 MVVM 模式来正确绑定你的控件,我阅读了大量文章。然而,有很多不同的方法可以做到这一点,所以我最终得出了一些我现在至少无法理解的东西:

1.) 创建模型 class。您在这里定义 DataTable 列并将它们公开为属性。这个class需要继承自INotifyPropertyChanged:

 class Test_Model : INotifyPropertyChanged
 {
        private string col1;
        private string col2;

        public string Col1
        {
            get
            {
                return col1;
            }
            set
            {
                col1 = value;
                OnPropertyChanged("Col1");
            }
        }
        public string Col2
        {
            get
            {
                return col2;
            }
            set
            {
                col2 = value;
                OnPropertyChanged("Col2");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
   }

2.) 创建Viev_Model class。这里创建一个IList(Test_Model类型)并用DataTable填充它,为此,我使用了 LINQ——这也是一个挑战。接下来,您再次公开 IList 类型的 属性,以便将其用作您的数据源:

 class Test_ViewModel
 {
        private IList<Test_Model> _comboData;

        public Test_ViewModel()
        {
            _comboData = dt.AsEnumerable().
                Select(row => new Test_Model
                {
                    Col1 = row.Field<string>(0),
                    Col2 = row.Field<string>(1)
                }).ToList();
        }

        public IList<Test_Model> ComboData
        {
            get { return _comboData; }
            set { _comboData = value; }
        }
  }

3.) 在您的 Window 或页面中将 DataContext 分配给 View_Model class。这是在构造函数中完成的,如下所示:

 public partial class My_Page: Page
 {
        public My_Page()
        {
            InitializeComponent();
            this.DataContext = new Test_ViewModel(); //assign ViewModel class
        }

        //...
  }

4.) 将组合框绑定到 View_Model class:

  <ComboBox x:Name="Cmb_Test" ItemsSource="{Binding Path= ComboData}" 
  DisplayMemberPath="Col1" SelectedValuePath="Col1" SelectedIndex="0"/>

然后终于成功了。虽然我对解决方案不满意,但我必须想出更简单的方法来进行正确的绑定。我通常有很多控件要绑定 & 如果我对我需要的每个 DataTable 都这样做,我最终会在 classes.

中得到大量代码