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)
我找到了解决办法。在您熟悉 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.
中得到大量代码
我正在对页面中的组合框进行简单绑定:
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)
我找到了解决办法。在您熟悉 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.
中得到大量代码