DelegateCommand RefreshCommand 刷新 ObservableCollection 绑定的 DataGrid
DelegateCommand RefreshCommand to refresh ObservableCollection-bound DataGrid
我在向数据库添加新行时尝试更新我的 DataGrid
(名称:dgLicenseholder,绑定到 ObservableCollection
。我正在使用 MVVMLight
)。
为什么这不起作用?
在我的 ViewModel
中,我有一个 public DelegateCommand RefreshCommand {get; private set; }
和一个方法:
private void Refresh() {
licenseHolders.Add(new LicenseHolders());
}
licenseHolders
是一个 ObservableCollection 列表,看起来像这样:
public ObservableCollection<LicenseHolders> licenseHolders { get; }
= new ObservableCollection<LicenseHolders>();
LicenseHolders
是我的 Model
中的一个 class,它保存数据:
public class LicenseHolders {
public int ID { get; set; }
public string Foretaksnavn { get; set; }
// more like the above...
}
在 XAML 中,我已将命令绑定到按钮,如下所示;
Command="{Binding RefreshCommand}"
CommandParameter="{Binding ElementName=dgLicenseHolder}"
我更新数据库的方法放在 ViewModel
中,由持有 DataGrid
的 window 的代码隐藏中的点击事件调用。
AddToDB()
NewLicenseHolder nlh = Application.Current.Windows.OfType<NewLicenseHolder>().FirstOrDefault();
try {
using (SqlConnection sqlCon = new(connectionString))
using (SqlCommand sqlCmd = new(sqlString, sqlCon)) {
sqlCon.Open();
sqlCmd.Parameters.AddWithValue("@Foretaksnavn, nlh.txtForetaksnavn.Text.ToString());
// more of the above...
sqlCmd.ExecuteNonQuery();
}
}
问题 1:
我绑定 RefreshCommand
的方式有什么问题?我添加到数据库的方法工作正常,但我必须为 DataGrid
重新打开 window 以“捕获”更改。
问题 2:
如何绑定我用来更新数据库的方法而不将其放入点击事件中?
更新代码(尝试实施用户 ChrisBD 建议的解决方案)
LicenseHolder.cs - 支架 class(型号)
namespace Ridel.Hub.Model {
public class LicenseHolder {
public int ID { get; set; }
public string Foretaksnavn { get; set; }
public string Foretaksnummer { get; set; }
public string Adresse { get; set; }
public int Postnummer { get; set; }
public string Poststed { get; set; }
public string BIC { get; set; }
public string IBAN { get; set; }
public string Kontakt { get; set; }
public string Epost { get; set; }
public string Tlf { get; set; }
public string Kontonummer { get; set; }
}
}
RidelHubMainViewModel.cs (ViewModel)
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using GalaSoft.MvvmLight.CommandWpf;
using Ridel.Hub.Model;
namespace Ridel.Hub.ViewModel {
public class RidelHubMainViewModel : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
public ICommand RefreshCommand { get; private set; }
public ObservableCollection<LicenseHolder> LicenseHolders { get; set; }
public RidelHubMainViewModel() {
RefreshCommand = new RelayCommand(this.ExecuteRefreshCommand);
LicenseHolders = new ObservableCollection<LicenseHolder>();
FillDataGridLicenseHolders();
}
private void ExecuteRefreshCommand() {
NewLicenseHolder nlh = Application.Current.Windows.OfType<NewLicenseHolder>().FirstOrDefault();
if (LicenseHolders == null) {
LicenseHolders = new ObservableCollection<LicenseHolder>();
} else {
AddToDB();
LicenseHolders.Clear();
LicenseHolders.Add(new LicenseHolder() { Foretaksnavn = nlh.txtForetaksnavn.Text.ToString() });
LicenseHolders.Add(new LicenseHolder() { Foretaksnummer = nlh.txtForetaksnr.Text.ToString() });
LicenseHolders.Add(new LicenseHolder() { Adresse = nlh.txtAdresse.Text.ToString() });
LicenseHolders.Add(new LicenseHolder() { Postnummer = Convert.ToInt32(nlh.txtPostnummer.Text.ToString()) });
LicenseHolders.Add(new LicenseHolder() { Poststed = nlh.txtPoststed.Text.ToString() });
LicenseHolders.Add(new LicenseHolder() { BIC = nlh.txtBIC.Text.ToString() });
LicenseHolders.Add(new LicenseHolder() { IBAN = nlh.txtIBAN.Text.ToString() });
LicenseHolders.Add(new LicenseHolder() { Kontakt = nlh.txtKontakt.Text.ToString() });
LicenseHolders.Add(new LicenseHolder() { Epost = nlh.txtEpost.Text.ToString() });
LicenseHolders.Add(new LicenseHolder() { Tlf = nlh.txtTlf.Text.ToString() });
LicenseHolders.Add(new LicenseHolder() { Kontonummer = nlh.txtKontonr.Text.ToString() });
}
}
public void FillDataGridLicenseHolders() {
// Initially populates my DataGrid with the Sql server table
try {
using (SqlConnection sqlCon = new(ConnectionString.connectionString))
using (SqlCommand sqlCmd = new("select * from tblLicenseHolder", sqlCon))
using (SqlDataAdapter sqlDaAd = new(sqlCmd))
using (DataSet ds = new()) {
sqlCon.Open();
sqlDaAd.Fill(ds, "tblLicenseHolder");
foreach (DataRow dr in ds.Tables[0].Rows) {
LicenseHolders.Add(new LicenseHolder {
ID = Convert.ToInt32(dr[0].ToString()),
Foretaksnavn = dr[1].ToString(),
Foretaksnummer = dr[2].ToString(),
Adresse = dr[3].ToString(),
Postnummer = (int)dr[4],
Poststed = dr[5].ToString(),
BIC = dr[6].ToString(),
IBAN = dr[7].ToString(),
Kontakt = dr[8].ToString(),
Epost = dr[9].ToString(),
Tlf = dr[10].ToString(),
Kontonummer = dr[11].ToString()
});
}
}
} catch (Exception ex) {
MessageBox.Show(ex.Message, "Message", MessageBoxButton.OK, MessageBoxImage.Information);
}
}
private void OnPropertyChanged(string myLicenseHolder) {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(myLicenseHolder));
}
private void OnLicenseHoldersPropertyChanged() {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("LicenseHolders"));
}
#region Slett løyvehaver
// How I delete rows from the DataGrid
private static bool RemoveFromDB(LicenseHolder myLicenseHolder) {
string sqlString = $"Delete from tblLicenseHolder where ID = '{myLicenseHolder.ID}'";
if (MessageBox.Show("Er du sikker på at du ønsker å slette valgt løyvehaver?", "Slett løyvehaver", MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.Yes) {
try {
using (SqlConnection sqlCon = new(ConnectionString.connectionString))
using (SqlCommand sqlCmd = new(sqlString, sqlCon)) {
sqlCon.Open();
sqlCmd.ExecuteNonQuery();
return true;
}
} catch {
return false;
}
} else {
return false;
}
}
private void RemoveLicenseHolderExecute(LicenseHolder myLicenseHolder) {
bool result = RemoveFromDB(myLicenseHolder);
if (result)
LicenseHolders.Remove(myLicenseHolder);
}
private RelayCommand<LicenseHolder> _removeLicenseHoldersCommand;
public RelayCommand<LicenseHolder> RemoveLicenseHoldersCommand => _removeLicenseHoldersCommand
??= new RelayCommand<LicenseHolder>(RemoveLicenseHolderExecute, RemoveLicenseHolderCanExecute);
private bool RemoveLicenseHolderCanExecute(LicenseHolder myLicenseHolder) {
return LicenseHolders.Contains(myLicenseHolder);
}
#endregion
public void AddToDB() {
string sqlString = "insert into tblLicenseHolder (Foretaksnavn, Foretaksnummer, Adresse, Postnummer, Poststed, BIC, IBAN, Kontakt, Epost, Tlf, Kontonummer) " +
"values (@Foretaksnavn, @Foretaksnummer, @Adresse, @Postnummer, @Poststed, @BIC, @IBAN, @Kontakt, @Epost, @Tlf, @Kontonummer)";
NewLicenseHolder nlh = Application.Current.Windows.OfType<NewLicenseHolder>().FirstOrDefault();
try {
using (SqlConnection sqlCon = new(ConnectionString.connectionString))
using (SqlCommand sqlCmd = new(sqlString, sqlCon)) {
sqlCon.Open();
if (string.IsNullOrEmpty(nlh.txtForetaksnavn.Text) || string.IsNullOrEmpty(nlh.txtForetaksnr.Text)
|| string.IsNullOrEmpty(nlh.txtAdresse.Text) || string.IsNullOrEmpty(nlh.txtPostnummer.Text)
|| string.IsNullOrEmpty(nlh.txtPoststed.Text) || string.IsNullOrEmpty(nlh.txtBIC.Text)
|| string.IsNullOrEmpty(nlh.txtIBAN.Text) || string.IsNullOrEmpty(nlh.txtKontakt.Text)
|| string.IsNullOrEmpty(nlh.txtEpost.Text) || string.IsNullOrEmpty(nlh.txtTlf.Text)
|| string.IsNullOrEmpty(nlh.txtKontonr.Text)) {
MessageBox.Show("Vennligst fyll ut alle tekstboksene.");
sqlCon.Close();
} else {
sqlCmd.Parameters.AddWithValue("@Foretaksnavn", nlh.txtForetaksnavn.Text.ToString());
sqlCmd.Parameters.AddWithValue("@Foretaksnummer", nlh.txtForetaksnr.Text.ToString());
sqlCmd.Parameters.AddWithValue("@Adresse", nlh.txtAdresse.Text.ToString());
sqlCmd.Parameters.AddWithValue("@Postnummer", nlh.txtPostnummer.Text);
sqlCmd.Parameters.AddWithValue("@Poststed", nlh.txtPoststed.Text.ToString());
sqlCmd.Parameters.AddWithValue("@BIC", nlh.txtBIC.Text.ToString());
sqlCmd.Parameters.AddWithValue("@IBAN", nlh.txtIBAN.Text.ToString());
sqlCmd.Parameters.AddWithValue("@Kontakt", nlh.txtKontakt.Text.ToString());
sqlCmd.Parameters.AddWithValue("@Epost", nlh.txtEpost.Text.ToString());
sqlCmd.Parameters.AddWithValue("@Tlf", nlh.txtTlf.Text.ToString());
sqlCmd.Parameters.AddWithValue("@Kontonummer", nlh.txtKontonr.Text.ToString());
sqlCmd.ExecuteNonQuery();
MessageBox.Show("Ny løyvehaver lagret. Husk å oppdatere listen.");
}
}
} catch (Exception ex) {
MessageBox.Show(ex.Message, "Message", MessageBoxButton.OK, MessageBoxImage.Information);
}
}
}
}
视图,代码隐藏 - 父级-window (LicenseHoldersWindow)
using System.Windows;
using System.Windows.Controls;
using Ridel.Hub.ViewModel;
namespace Ridel.Hub {
public partial class LicenseHoldersWindow : Window {
public LicenseHoldersWindow() {
InitializeComponent();
DataContext = new RidelHubMainViewModel();
}
private btnNew_Click(object sender, RoutedEventArgs e) {
NewLicenseHolder newLicenseHolder = new();
newLicenseHolder.ShowDialog();
}
}
}
查看,XAML - Parent-window (LicenseHoldersWindow)
<Window
x:Class="Ridel.Hub.LicenseHoldersWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Ridel.Hub"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewmodel="clr-namespace:Ridel.Hub.ViewModel"
Title="License Holders"
d:DataContext="{d:DesignInstance Type=viewmodel:RidelHubMainViewModel}"
mc:Ignorable="d">
<!-- GUI stuff -->
<!-- also button to open new child-window "NewLicenseHolder" -->
<DataGrid
x:Name="dgLicenseHolder"
AutoGenerateColumns="False"
IsReadOnly="True"
ItemsSource="{Binding LicenseHolders, Mode=OneWay}"
SelectionChanged="dgLicenseHolder_SelectionChanged"
SelectionMode="Single">
View, code-behind- child-window (NewLicenseHolder)
using System.ComponentModel;
using System.Windows;
using Prism.Commands;
using Ridel.Hub.ViewModel;
namespace Ridel.Hub {
public partial class NewLicenseHolder : Window, INotifyPropertyChanged {
public NewLicenseHolder() {
InitializeComponent();
btnLogOut.Content = UserInfo.UserName;
DataContext = new RidelHubMainViewModel();
}
public event PropertyChangedEventHandler PropertyChanged;
private void btnLagre_Click(object sender, RoutedEventArgs e) {
// Button: Save new LicenseHolder
((RidelHubMainViewModel)DataContext).RefreshCommand.Execute(null);
this.Close();
}
}
}
查看,XAML- child-window (NewLicenseHolder)
<Window
x:Class="Ridel.Hub.NewLicenseHolder"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:viewmodel="clr-namespace:Ridel.Hub.ViewModel"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
d:DataContext="{d:DesignInstance Type=viewmodel:RidelHubMainViewModel}"
mc:Ignorable="d">
<Button
Name="btnLagre"
Foreground="White"
HorizontalContentAlignment="Center"
Click="btnLagre_Click"
Command="{Binding RefreshCommand}"
Style="{DynamicResource ButtonWithRoundCornersGreen}" >
</Button>
好的,所以,如果您通读了所有这些内容:上帝保佑。
如您所见,我在子表单中添加了新的行信息,单击保存,理想情况下,希望我的 DataGrid 然后自动更新(无需单击任何其他“刷新”或“更新”按钮。
现在发生的事情是,当我点击保存时,我得到一个错误:
System.NullReferenceException: 'Object reference not set to an instance of an object.' Local1 was null.
在我的 AddToDB()
方法中,在 if-else statement
处我检查文本框的 none 是否为空。
所以我在 ViewModel
:
中引用 NewLicenseHolder class
的方式显然有问题
NewLicenseHolder nlh = Application.Current.Window.OfType<NewLicenseHolder>().FirstOrDefault();
当我离开子表单并返回父表单时,如何正确引用它并确保更新 DataGrid?
非常感谢,非常感谢任何指导来纠正我的想法!
好的,关于数据库代码如何链接到您的代码,我在这里没有看到很多内容,但这可能会有所帮助:
public class ViewModel
{
public ViewModel()
{
RefreshCommand = new Command(async()=> await ExecuteRefreshCommand());
LicenceHolders = new ObservableCollection<LicenceHolder>();
}
public ICommand RefreshCommand { get; private set; }
public ObservableCollection<LicenceHolder> LicenceHolders { get; set; }
private async Task ExecuteRefreshCommand()
{
if (LicenceHolders == null)
{
LicenceHolders = new ObservableCollection<LicenceHolder>();
}
LicenceHolders.Clear();
//Code here for async retrieval of licence holder records from database
//and addition of new LicenceHolder instances to the LicenceHolders collection
}
}
在视图中XAML
<Button Text="Refresh" Command="{Binding RefreshCommand}"/>
这将触发 RefreshCommand
使用 ViewModel 注入查看隐藏代码
public partial class SomeView : ContentPage
{
public SomeView(ViewModel viewModel)
{
InitializeComponent();
BindingContext = viewModel;
}
}
否则
public partial class SomeView : ContentPage
{
public SomeView()
{
InitializeComponent();
BindingContext = new ViewModel();
}
}
理想情况下,您会希望在 ViewModel 中使用命令来触发事件或处理。这可以通过在上面的视图中绑定或像这样从后面的代码中触发
private void Button_OnClick(object sender, OnClickEventArgs e)
{
((ViewModel)BindingContext).RefreshCommand.Execute();
}
严格来说,我们应该使用标志来指示异步数据库访问正在进行 - 这可以通过在用作 [=17= 的 ViewModel 中使用 public 布尔值(标志)来实现] 命令实例创建中的条件,这将防止在完成之前多次调用同一代码。
例如
视图模型:
bool IsBusy {get;set;}
在调用您的异步数据访问例程之前将此设置为真,并在完成时设置为假
在构造函数中,命令实例化现在变为
RefreshCommand = new Command(async()=> await ExecuteRefreshCommand(), ()=>!IsBusy);
XAML 绑定会自动拾取它,但如果您使用代码隐藏代码来触发命令,这将变为:
if (((ViewModel)BindingContext).RefreshCommand.CanExecute())
{
((ViewModel)BindingContext).RefreshCommand.Execute();
}
注:
如果您在运行时用新实例替换 LicenceHolders ObservableCollection
,而不是更改其内容,那么您将需要通过调用 OnPropertyChanged 处理程序手动 raise/invoke 和 PropertyChanged
。
例如
protected void OnLicenHoldersPropertyChanged()
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("LicenceHolders"));
}
看来我可能混淆了 MVVM 框架 - 不管怎样,这里有一些 MVVMLite 的工作代码。
public class LicenseHolder
{
public string Name { get; set; }
}
public class ViewModel
{
public ViewModel()
{
RefreshCommand = new GalaSoft.MvvmLight.CommandWpf.RelayCommand(this.ExecuteRefreshCommand);
LicenseHolders = new ObservableCollection<LicenseHolder>();
}
public ICommand RefreshCommand { get; private set; }
public ObservableCollection<LicenseHolder> LicenseHolders { get; set; }
private void ExecuteRefreshCommand(object o)
{
if (LicenseHolders == null)
{
LicenseHolders = new ObservableCollection<LicenseHolder>();
}
LicenseHolders.Clear();
//Code here for async retrieval of licence holder records from database
//and addition of new LicenceHolder instances to the LicenceHolders collection
LicenseHolders.Add(new LicenseHolder(){Name ="Ted"});
LicenseHolders.Add(new LicenseHolder(){Name = "Bill"});
}
}
查看:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<local:ViewModel x:Key="ViewModelAsDataSource" />
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40"/>
<ColumnDefinition/>
<ColumnDefinition Width="40"/>
</Grid.ColumnDefinitions>
<DataGrid Grid.Column="1" Grid.Row="1" ItemsSource="{Binding LicenseHolders }" AutoGenerateColumns="false">
<DataGrid.Columns>
<DataGridTextColumn Header="License Holder Name" Width="Auto" MinWidth="18" Binding="{Binding Name }" />
</DataGrid.Columns>
</DataGrid>
<Button Grid.Column="1" Grid.Row="2" Content="Refresh" Command="{Binding RefreshCommand}" Width="50" Height="30"/>
<Button Grid.Column="1" Grid.Row="3" Content="Refresh" Click="ButtonBase_OnClick" Width="50" Height="30"/>
后面的代码:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
((ViewModel)DataContext).RefreshCommand.Execute(null);
}
}
我在向数据库添加新行时尝试更新我的 DataGrid
(名称:dgLicenseholder,绑定到 ObservableCollection
。我正在使用 MVVMLight
)。
为什么这不起作用?
在我的 ViewModel
中,我有一个 public DelegateCommand RefreshCommand {get; private set; }
和一个方法:
private void Refresh() {
licenseHolders.Add(new LicenseHolders());
}
licenseHolders
是一个 ObservableCollection 列表,看起来像这样:
public ObservableCollection<LicenseHolders> licenseHolders { get; }
= new ObservableCollection<LicenseHolders>();
LicenseHolders
是我的 Model
中的一个 class,它保存数据:
public class LicenseHolders {
public int ID { get; set; }
public string Foretaksnavn { get; set; }
// more like the above...
}
在 XAML 中,我已将命令绑定到按钮,如下所示;
Command="{Binding RefreshCommand}"
CommandParameter="{Binding ElementName=dgLicenseHolder}"
我更新数据库的方法放在 ViewModel
中,由持有 DataGrid
的 window 的代码隐藏中的点击事件调用。
AddToDB()
NewLicenseHolder nlh = Application.Current.Windows.OfType<NewLicenseHolder>().FirstOrDefault();
try {
using (SqlConnection sqlCon = new(connectionString))
using (SqlCommand sqlCmd = new(sqlString, sqlCon)) {
sqlCon.Open();
sqlCmd.Parameters.AddWithValue("@Foretaksnavn, nlh.txtForetaksnavn.Text.ToString());
// more of the above...
sqlCmd.ExecuteNonQuery();
}
}
问题 1:
我绑定 RefreshCommand
的方式有什么问题?我添加到数据库的方法工作正常,但我必须为 DataGrid
重新打开 window 以“捕获”更改。
问题 2:
如何绑定我用来更新数据库的方法而不将其放入点击事件中?
更新代码(尝试实施用户 ChrisBD 建议的解决方案)
LicenseHolder.cs - 支架 class(型号)
namespace Ridel.Hub.Model {
public class LicenseHolder {
public int ID { get; set; }
public string Foretaksnavn { get; set; }
public string Foretaksnummer { get; set; }
public string Adresse { get; set; }
public int Postnummer { get; set; }
public string Poststed { get; set; }
public string BIC { get; set; }
public string IBAN { get; set; }
public string Kontakt { get; set; }
public string Epost { get; set; }
public string Tlf { get; set; }
public string Kontonummer { get; set; }
}
}
RidelHubMainViewModel.cs (ViewModel)
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using GalaSoft.MvvmLight.CommandWpf;
using Ridel.Hub.Model;
namespace Ridel.Hub.ViewModel {
public class RidelHubMainViewModel : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
public ICommand RefreshCommand { get; private set; }
public ObservableCollection<LicenseHolder> LicenseHolders { get; set; }
public RidelHubMainViewModel() {
RefreshCommand = new RelayCommand(this.ExecuteRefreshCommand);
LicenseHolders = new ObservableCollection<LicenseHolder>();
FillDataGridLicenseHolders();
}
private void ExecuteRefreshCommand() {
NewLicenseHolder nlh = Application.Current.Windows.OfType<NewLicenseHolder>().FirstOrDefault();
if (LicenseHolders == null) {
LicenseHolders = new ObservableCollection<LicenseHolder>();
} else {
AddToDB();
LicenseHolders.Clear();
LicenseHolders.Add(new LicenseHolder() { Foretaksnavn = nlh.txtForetaksnavn.Text.ToString() });
LicenseHolders.Add(new LicenseHolder() { Foretaksnummer = nlh.txtForetaksnr.Text.ToString() });
LicenseHolders.Add(new LicenseHolder() { Adresse = nlh.txtAdresse.Text.ToString() });
LicenseHolders.Add(new LicenseHolder() { Postnummer = Convert.ToInt32(nlh.txtPostnummer.Text.ToString()) });
LicenseHolders.Add(new LicenseHolder() { Poststed = nlh.txtPoststed.Text.ToString() });
LicenseHolders.Add(new LicenseHolder() { BIC = nlh.txtBIC.Text.ToString() });
LicenseHolders.Add(new LicenseHolder() { IBAN = nlh.txtIBAN.Text.ToString() });
LicenseHolders.Add(new LicenseHolder() { Kontakt = nlh.txtKontakt.Text.ToString() });
LicenseHolders.Add(new LicenseHolder() { Epost = nlh.txtEpost.Text.ToString() });
LicenseHolders.Add(new LicenseHolder() { Tlf = nlh.txtTlf.Text.ToString() });
LicenseHolders.Add(new LicenseHolder() { Kontonummer = nlh.txtKontonr.Text.ToString() });
}
}
public void FillDataGridLicenseHolders() {
// Initially populates my DataGrid with the Sql server table
try {
using (SqlConnection sqlCon = new(ConnectionString.connectionString))
using (SqlCommand sqlCmd = new("select * from tblLicenseHolder", sqlCon))
using (SqlDataAdapter sqlDaAd = new(sqlCmd))
using (DataSet ds = new()) {
sqlCon.Open();
sqlDaAd.Fill(ds, "tblLicenseHolder");
foreach (DataRow dr in ds.Tables[0].Rows) {
LicenseHolders.Add(new LicenseHolder {
ID = Convert.ToInt32(dr[0].ToString()),
Foretaksnavn = dr[1].ToString(),
Foretaksnummer = dr[2].ToString(),
Adresse = dr[3].ToString(),
Postnummer = (int)dr[4],
Poststed = dr[5].ToString(),
BIC = dr[6].ToString(),
IBAN = dr[7].ToString(),
Kontakt = dr[8].ToString(),
Epost = dr[9].ToString(),
Tlf = dr[10].ToString(),
Kontonummer = dr[11].ToString()
});
}
}
} catch (Exception ex) {
MessageBox.Show(ex.Message, "Message", MessageBoxButton.OK, MessageBoxImage.Information);
}
}
private void OnPropertyChanged(string myLicenseHolder) {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(myLicenseHolder));
}
private void OnLicenseHoldersPropertyChanged() {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("LicenseHolders"));
}
#region Slett løyvehaver
// How I delete rows from the DataGrid
private static bool RemoveFromDB(LicenseHolder myLicenseHolder) {
string sqlString = $"Delete from tblLicenseHolder where ID = '{myLicenseHolder.ID}'";
if (MessageBox.Show("Er du sikker på at du ønsker å slette valgt løyvehaver?", "Slett løyvehaver", MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.Yes) {
try {
using (SqlConnection sqlCon = new(ConnectionString.connectionString))
using (SqlCommand sqlCmd = new(sqlString, sqlCon)) {
sqlCon.Open();
sqlCmd.ExecuteNonQuery();
return true;
}
} catch {
return false;
}
} else {
return false;
}
}
private void RemoveLicenseHolderExecute(LicenseHolder myLicenseHolder) {
bool result = RemoveFromDB(myLicenseHolder);
if (result)
LicenseHolders.Remove(myLicenseHolder);
}
private RelayCommand<LicenseHolder> _removeLicenseHoldersCommand;
public RelayCommand<LicenseHolder> RemoveLicenseHoldersCommand => _removeLicenseHoldersCommand
??= new RelayCommand<LicenseHolder>(RemoveLicenseHolderExecute, RemoveLicenseHolderCanExecute);
private bool RemoveLicenseHolderCanExecute(LicenseHolder myLicenseHolder) {
return LicenseHolders.Contains(myLicenseHolder);
}
#endregion
public void AddToDB() {
string sqlString = "insert into tblLicenseHolder (Foretaksnavn, Foretaksnummer, Adresse, Postnummer, Poststed, BIC, IBAN, Kontakt, Epost, Tlf, Kontonummer) " +
"values (@Foretaksnavn, @Foretaksnummer, @Adresse, @Postnummer, @Poststed, @BIC, @IBAN, @Kontakt, @Epost, @Tlf, @Kontonummer)";
NewLicenseHolder nlh = Application.Current.Windows.OfType<NewLicenseHolder>().FirstOrDefault();
try {
using (SqlConnection sqlCon = new(ConnectionString.connectionString))
using (SqlCommand sqlCmd = new(sqlString, sqlCon)) {
sqlCon.Open();
if (string.IsNullOrEmpty(nlh.txtForetaksnavn.Text) || string.IsNullOrEmpty(nlh.txtForetaksnr.Text)
|| string.IsNullOrEmpty(nlh.txtAdresse.Text) || string.IsNullOrEmpty(nlh.txtPostnummer.Text)
|| string.IsNullOrEmpty(nlh.txtPoststed.Text) || string.IsNullOrEmpty(nlh.txtBIC.Text)
|| string.IsNullOrEmpty(nlh.txtIBAN.Text) || string.IsNullOrEmpty(nlh.txtKontakt.Text)
|| string.IsNullOrEmpty(nlh.txtEpost.Text) || string.IsNullOrEmpty(nlh.txtTlf.Text)
|| string.IsNullOrEmpty(nlh.txtKontonr.Text)) {
MessageBox.Show("Vennligst fyll ut alle tekstboksene.");
sqlCon.Close();
} else {
sqlCmd.Parameters.AddWithValue("@Foretaksnavn", nlh.txtForetaksnavn.Text.ToString());
sqlCmd.Parameters.AddWithValue("@Foretaksnummer", nlh.txtForetaksnr.Text.ToString());
sqlCmd.Parameters.AddWithValue("@Adresse", nlh.txtAdresse.Text.ToString());
sqlCmd.Parameters.AddWithValue("@Postnummer", nlh.txtPostnummer.Text);
sqlCmd.Parameters.AddWithValue("@Poststed", nlh.txtPoststed.Text.ToString());
sqlCmd.Parameters.AddWithValue("@BIC", nlh.txtBIC.Text.ToString());
sqlCmd.Parameters.AddWithValue("@IBAN", nlh.txtIBAN.Text.ToString());
sqlCmd.Parameters.AddWithValue("@Kontakt", nlh.txtKontakt.Text.ToString());
sqlCmd.Parameters.AddWithValue("@Epost", nlh.txtEpost.Text.ToString());
sqlCmd.Parameters.AddWithValue("@Tlf", nlh.txtTlf.Text.ToString());
sqlCmd.Parameters.AddWithValue("@Kontonummer", nlh.txtKontonr.Text.ToString());
sqlCmd.ExecuteNonQuery();
MessageBox.Show("Ny løyvehaver lagret. Husk å oppdatere listen.");
}
}
} catch (Exception ex) {
MessageBox.Show(ex.Message, "Message", MessageBoxButton.OK, MessageBoxImage.Information);
}
}
}
}
视图,代码隐藏 - 父级-window (LicenseHoldersWindow)
using System.Windows;
using System.Windows.Controls;
using Ridel.Hub.ViewModel;
namespace Ridel.Hub {
public partial class LicenseHoldersWindow : Window {
public LicenseHoldersWindow() {
InitializeComponent();
DataContext = new RidelHubMainViewModel();
}
private btnNew_Click(object sender, RoutedEventArgs e) {
NewLicenseHolder newLicenseHolder = new();
newLicenseHolder.ShowDialog();
}
}
}
查看,XAML - Parent-window (LicenseHoldersWindow)
<Window
x:Class="Ridel.Hub.LicenseHoldersWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Ridel.Hub"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewmodel="clr-namespace:Ridel.Hub.ViewModel"
Title="License Holders"
d:DataContext="{d:DesignInstance Type=viewmodel:RidelHubMainViewModel}"
mc:Ignorable="d">
<!-- GUI stuff -->
<!-- also button to open new child-window "NewLicenseHolder" -->
<DataGrid
x:Name="dgLicenseHolder"
AutoGenerateColumns="False"
IsReadOnly="True"
ItemsSource="{Binding LicenseHolders, Mode=OneWay}"
SelectionChanged="dgLicenseHolder_SelectionChanged"
SelectionMode="Single">
View, code-behind- child-window (NewLicenseHolder)
using System.ComponentModel;
using System.Windows;
using Prism.Commands;
using Ridel.Hub.ViewModel;
namespace Ridel.Hub {
public partial class NewLicenseHolder : Window, INotifyPropertyChanged {
public NewLicenseHolder() {
InitializeComponent();
btnLogOut.Content = UserInfo.UserName;
DataContext = new RidelHubMainViewModel();
}
public event PropertyChangedEventHandler PropertyChanged;
private void btnLagre_Click(object sender, RoutedEventArgs e) {
// Button: Save new LicenseHolder
((RidelHubMainViewModel)DataContext).RefreshCommand.Execute(null);
this.Close();
}
}
}
查看,XAML- child-window (NewLicenseHolder)
<Window
x:Class="Ridel.Hub.NewLicenseHolder"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:viewmodel="clr-namespace:Ridel.Hub.ViewModel"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
d:DataContext="{d:DesignInstance Type=viewmodel:RidelHubMainViewModel}"
mc:Ignorable="d">
<Button
Name="btnLagre"
Foreground="White"
HorizontalContentAlignment="Center"
Click="btnLagre_Click"
Command="{Binding RefreshCommand}"
Style="{DynamicResource ButtonWithRoundCornersGreen}" >
</Button>
好的,所以,如果您通读了所有这些内容:上帝保佑。 如您所见,我在子表单中添加了新的行信息,单击保存,理想情况下,希望我的 DataGrid 然后自动更新(无需单击任何其他“刷新”或“更新”按钮。
现在发生的事情是,当我点击保存时,我得到一个错误:
System.NullReferenceException: 'Object reference not set to an instance of an object.' Local1 was null.
在我的 AddToDB()
方法中,在 if-else statement
处我检查文本框的 none 是否为空。
所以我在 ViewModel
:
NewLicenseHolder class
的方式显然有问题
NewLicenseHolder nlh = Application.Current.Window.OfType<NewLicenseHolder>().FirstOrDefault();
当我离开子表单并返回父表单时,如何正确引用它并确保更新 DataGrid?
非常感谢,非常感谢任何指导来纠正我的想法!
好的,关于数据库代码如何链接到您的代码,我在这里没有看到很多内容,但这可能会有所帮助:
public class ViewModel
{
public ViewModel()
{
RefreshCommand = new Command(async()=> await ExecuteRefreshCommand());
LicenceHolders = new ObservableCollection<LicenceHolder>();
}
public ICommand RefreshCommand { get; private set; }
public ObservableCollection<LicenceHolder> LicenceHolders { get; set; }
private async Task ExecuteRefreshCommand()
{
if (LicenceHolders == null)
{
LicenceHolders = new ObservableCollection<LicenceHolder>();
}
LicenceHolders.Clear();
//Code here for async retrieval of licence holder records from database
//and addition of new LicenceHolder instances to the LicenceHolders collection
}
}
在视图中XAML
<Button Text="Refresh" Command="{Binding RefreshCommand}"/>
这将触发 RefreshCommand
使用 ViewModel 注入查看隐藏代码
public partial class SomeView : ContentPage
{
public SomeView(ViewModel viewModel)
{
InitializeComponent();
BindingContext = viewModel;
}
}
否则
public partial class SomeView : ContentPage
{
public SomeView()
{
InitializeComponent();
BindingContext = new ViewModel();
}
}
理想情况下,您会希望在 ViewModel 中使用命令来触发事件或处理。这可以通过在上面的视图中绑定或像这样从后面的代码中触发
private void Button_OnClick(object sender, OnClickEventArgs e)
{
((ViewModel)BindingContext).RefreshCommand.Execute();
}
严格来说,我们应该使用标志来指示异步数据库访问正在进行 - 这可以通过在用作 [=17= 的 ViewModel 中使用 public 布尔值(标志)来实现] 命令实例创建中的条件,这将防止在完成之前多次调用同一代码。 例如
视图模型:
bool IsBusy {get;set;}
在调用您的异步数据访问例程之前将此设置为真,并在完成时设置为假
在构造函数中,命令实例化现在变为
RefreshCommand = new Command(async()=> await ExecuteRefreshCommand(), ()=>!IsBusy);
XAML 绑定会自动拾取它,但如果您使用代码隐藏代码来触发命令,这将变为:
if (((ViewModel)BindingContext).RefreshCommand.CanExecute())
{
((ViewModel)BindingContext).RefreshCommand.Execute();
}
注:
如果您在运行时用新实例替换 LicenceHolders ObservableCollection
,而不是更改其内容,那么您将需要通过调用 OnPropertyChanged 处理程序手动 raise/invoke 和 PropertyChanged
。
例如
protected void OnLicenHoldersPropertyChanged()
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("LicenceHolders"));
}
看来我可能混淆了 MVVM 框架 - 不管怎样,这里有一些 MVVMLite 的工作代码。
public class LicenseHolder
{
public string Name { get; set; }
}
public class ViewModel
{
public ViewModel()
{
RefreshCommand = new GalaSoft.MvvmLight.CommandWpf.RelayCommand(this.ExecuteRefreshCommand);
LicenseHolders = new ObservableCollection<LicenseHolder>();
}
public ICommand RefreshCommand { get; private set; }
public ObservableCollection<LicenseHolder> LicenseHolders { get; set; }
private void ExecuteRefreshCommand(object o)
{
if (LicenseHolders == null)
{
LicenseHolders = new ObservableCollection<LicenseHolder>();
}
LicenseHolders.Clear();
//Code here for async retrieval of licence holder records from database
//and addition of new LicenceHolder instances to the LicenceHolders collection
LicenseHolders.Add(new LicenseHolder(){Name ="Ted"});
LicenseHolders.Add(new LicenseHolder(){Name = "Bill"});
}
}
查看:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<local:ViewModel x:Key="ViewModelAsDataSource" />
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40"/>
<ColumnDefinition/>
<ColumnDefinition Width="40"/>
</Grid.ColumnDefinitions>
<DataGrid Grid.Column="1" Grid.Row="1" ItemsSource="{Binding LicenseHolders }" AutoGenerateColumns="false">
<DataGrid.Columns>
<DataGridTextColumn Header="License Holder Name" Width="Auto" MinWidth="18" Binding="{Binding Name }" />
</DataGrid.Columns>
</DataGrid>
<Button Grid.Column="1" Grid.Row="2" Content="Refresh" Command="{Binding RefreshCommand}" Width="50" Height="30"/>
<Button Grid.Column="1" Grid.Row="3" Content="Refresh" Click="ButtonBase_OnClick" Width="50" Height="30"/>
后面的代码:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
((ViewModel)DataContext).RefreshCommand.Execute(null);
}
}