INotifyPropertyChanged 在 Resettable 视图模型中不起作用
INotifyPropertyChanged does not work in Resettable view model
我尝试使用最初由 Nish Nishant 在 https://www.codeproject.com/script/Articles/ViewDownloads.aspx?aid=158591 中发布的可重置视图模型
但是不知何故,当我执行 DeleteBahanExecute 方法时,INotifyPropertyChanged 不适用于 LabelContent 和 OkCancelButtonVis 属性。有人知道为什么吗?这是 MainWindow.xaml
<Window x:Class="XLMPIS.MainWindow"
xmlns:local="clr-namespace:XLMPIS"
xmlns:vm="clr-namespace:XLMPIS.ViewModel"
mc:Ignorable="d"
Loaded="Window_Loaded"
Title="MainWindow" Height="800" Width="800">
<TabControl>
<TabItem Header="Inventory">
<StackPanel DataContext="{Binding Source={x:Static vm:ViewModels.BahanViewModel}}">
<Grid Margin="0 10 0 0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="250"/>
<ColumnDefinition Width="250"/>
</Grid.ColumnDefinitions>
<ComboBox Grid.Column="0" ItemsSource="{Binding TipeBahans}" Width="250" HorizontalAlignment="left" SelectedItem="{Binding SelTipeBahan, Mode=TwoWay}" DisplayMemberPath="NamaTipeBahanGsm" Padding="2"/>
<ComboBox Grid.Column="1" ItemsSource="{Binding Suppliers}" Width="250" HorizontalAlignment="left" SelectedItem="{Binding SelSupplier, Mode=TwoWay}" DisplayMemberPath="NamaSupplier" Padding="2"/>
</Grid>
<Grid Margin="0 10 0 0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="90"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="40"/>
<ColumnDefinition Width="40"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="40"/>
<ColumnDefinition Width="40"/>
<ColumnDefinition Width="40"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Huruf Kode Bahan" HorizontalAlignment="Right" Padding="2"/>
<TextBox Text="{Binding HurufKodeBahan, Mode=TwoWay}" Grid.Column="1" Background="{Binding TBBackground}" IsReadOnly="{Binding IsReadOnly}" IsReadOnlyCaretVisible="{Binding IsReadOnly}" PreviewTextInput="LetterValidationTextBox" Padding="2"/>
<TextBlock Grid.Column="2" Text="No. Awal" HorizontalAlignment="Right" Padding="2"/>
<TextBox Text="{Binding NoAwal, Mode=TwoWay}" Grid.Column="3" Background="{Binding TBBackground}" IsReadOnly="{Binding IsReadOnly}" IsReadOnlyCaretVisible="{Binding IsReadOnly}" PreviewTextInput="IntegerValidationTextBox" Padding="2"/>
<TextBlock Grid.Column="4" Text="No. Akhir" HorizontalAlignment="Right" Padding="2"/>
<TextBox Text="{Binding NoAkhir, Mode=TwoWay}" Grid.Column="5" Background="{Binding TBBackground}" IsReadOnly="{Binding IsReadOnly}" IsReadOnlyCaretVisible="{Binding IsReadOnly}" PreviewTextInput="IntegerValidationTextBox" Padding="2"/>
<TextBlock Grid.Column="6" Text="Lebar" HorizontalAlignment="Right" Padding="2"/>
<TextBox Text="{Binding NewLebar, Mode=TwoWay}" PreviewTextInput="NumberValidationTextBox" Grid.Column="7" Background="{Binding TBBackground}" IsReadOnly="{Binding IsReadOnly}" IsReadOnlyCaretVisible="{Binding IsReadOnly}" Padding="2"/>
<TextBlock Grid.Column="8" Text="Panjang" HorizontalAlignment="Right" Padding="2"/>
<TextBox Text="{Binding NewPanjang, Mode=TwoWay}" PreviewTextInput="NumberValidationTextBox" Grid.Column="9" Background="{Binding TBBackground}" IsReadOnly="{Binding IsReadOnly}" IsReadOnlyCaretVisible="{Binding IsReadOnly}" Padding="2"/>
<TextBlock Grid.Column="10" Text="Sisa" HorizontalAlignment="Right" Padding="2"/>
<TextBox Text="{Binding NewSisa, Mode=TwoWay}" PreviewTextInput="NumberValidationTextBox" Grid.Column="11" Background="{Binding TBBackground}" IsReadOnly="{Binding IsReadOnly}" IsReadOnlyCaretVisible="{Binding IsReadOnly}" Padding="2"/>
</Grid>
<Grid Margin="0 10 0 0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="80"/>
<ColumnDefinition Width="120"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Surat Jalan" HorizontalAlignment="Right" Padding="2"/>
<TextBox Text="{Binding NewSuratJalan, Mode=TwoWay}" Grid.Column="1" Background="{Binding TBBackground}" IsReadOnly="{Binding IsReadOnly}" IsReadOnlyCaretVisible="{Binding IsReadOnly}" Padding="2"/>
<TextBlock Grid.Column="2" Text="Tanggal Masuk" Padding="2"/>
<DatePicker SelectedDate="{Binding NewTanggalMasuk}" Grid.Column="3" Height="25" HorizontalAlignment="Left" VerticalAlignment="Top" Width="115"/>
</Grid>
<Grid Margin="0 10 0 0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="80"/>
<ColumnDefinition Width="70"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Content="Add New Bahan" Width="100" Command="{Binding AddNewBahanCommand}" Padding="2"/>
<Button Grid.Column="1" Margin="10 0 0 0" Content="Delete Bahan" Width="90" Command="{Binding DeleteBahanCommand}" Padding="2"/>
<Button Grid.Column="2" Margin="10 0 0 0" Content="Refresh" Width="90" Command="{Binding ResetCommand}" Padding="2"/>
<Button Grid.Column="3" Margin="20 0 0 0" Content="Ok" Visibility="{Binding OkCancelButtonVis,Mode=TwoWay}" HorizontalAlignment="Left" Width="60" Command="{Binding OkCommand}" Padding="2"/>
<Button Grid.Column="4" Margin="10 0 0 0" Content="Cancel" Visibility="{Binding OkCancelButtonVis,Mode=TwoWay}" HorizontalAlignment="Left" Width="60" Command="{Binding CancelCommand}" Padding="2"/>
</Grid>
<Grid Margin="0 10 0 0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="250"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Nama Tipe Bahan" Padding="2"/>
<TextBox Text="{Binding NewNamaTipeBahan, Mode=TwoWay}" Grid.Column="1" Background="{Binding TBBackground}" IsReadOnly="{Binding IsReadOnly}" IsReadOnlyCaretVisible="{Binding IsReadOnly}" Padding="2"/>
<TextBlock Grid.Column="2" Text="Gsm" HorizontalAlignment="Right" Padding="2"/>
<TextBox Text="{Binding NewGsm, Mode=TwoWay}" PreviewTextInput="NumberValidationTextBox" Grid.Column="3" Background="{Binding TBBackground}" IsReadOnly="{Binding IsReadOnly}" IsReadOnlyCaretVisible="{Binding IsReadOnly}" Padding="2"/>
</Grid>
<Button Margin="0 10 0 0" Content="Add New Tipe Bahan" HorizontalAlignment="Left" Width="120" Command="{Binding AddNewTipeBahanCommand}" Padding="2"/>
<Label Margin="0 10 0 0" Content="{Binding LabelContent, Mode=TwoWay}"/>
</StackPanel>
</TabItem>
这是视图模型代码。我删除了一些 "using" 行。
using XLMPIS.Model;
using XLMPIS.Command;
namespace XLMPIS.ViewModel
{
public class BahanViewModel : INotifyPropertyChanged
{
public BahanViewModel()
{
SelTipeBahan = new TipeBahan();
TipeBahans = new ObservableCollection<TipeBahan>();
Suppliers = new ObservableCollection<Supplier>();
SelSupplier = new Supplier();
SqlDbConnect sdc = new SqlDbConnect();
DataSet ds = new DataSet();
DataTable dt = new DataTable();
sdc.SqlQuery("select * from TTipeBahan");
ds = sdc.QueryEx("TTipeBahan");
dt = ds.Tables["TTipeBahan"];
int tmpIndex = 0;
foreach (DataRow dr in dt.Rows)
{
TipeBahans.Add(new TipeBahan());
TipeBahans[tmpIndex].TipeBahanID = (int)dr["TipeBahanID"];
TipeBahans[tmpIndex].NamaTipeBahan = dr["NamaTipeBahan"].ToString();
TipeBahans[tmpIndex].Gsm = (int)dr["Gsm"];
tmpIndex++;
}
SelTipeBahan = TipeBahans[SELECTED_INDEX];
sdc.SqlQuery("select NamaSupplier,SupplierID from TSupplier");
ds = sdc.QueryEx("TSupplier");
dt = ds.Tables["TSupplier"];
tmpIndex = 0;
foreach (DataRow dr in dt.Rows)
{
Suppliers.Add(new Supplier());
Suppliers[tmpIndex].SupplierID = (int)dr["SupplierID"];
Suppliers[tmpIndex].NamaSupplier= dr["NamaSupplier"].ToString();
tmpIndex++;
}
SelSupplier = Suppliers[SELECTED_INDEX];
sdc.SqlDbClose();
NewTanggalMasuk = DateTime.Today;
IsReadOnly = false;//not read only
TBBackground = new SolidColorBrush(Colors.White); //white textbox
OkCancelButtonVis = "Hidden";
LabelContent = "test";
}
private const int SELECTED_INDEX = 0;
private ObservableCollection<Supplier> _suppliers;
public ObservableCollection<Supplier> Suppliers
{
get { return _suppliers; }
set { _suppliers = value; }
}
private string _noawal;
public string NoAwal
{
get { return _noawal; }
set { _noawal = value; }
}
private string _noakhir;
public string NoAkhir
{
get { return _noakhir; }
set { _noakhir = value; }
}
private string _okcancelbuttonvis;
public string OkCancelButtonVis
{
get { return _okcancelbuttonvis; }
set { _okcancelbuttonvis = value; FirePropertyChanged("OkCancelButtonVis"); }
}
private string _labelcontent;
public string LabelContent
{
get { return _labelcontent; }
set { _labelcontent = value; FirePropertyChanged("LabelContent"); }
}
private string _hurufkodebahan;
public string HurufKodeBahan
{
get { return _hurufkodebahan; }
set { _hurufkodebahan = value; }
}
private string _newsuratjalan;
public string NewSuratJalan
{
get { return _newsuratjalan; }
set { _newsuratjalan = value; }
}
private string _newgsm;
public string NewGsm
{
get { return _newgsm; }
set { _newgsm = value; }
}
private string _newnamatipebahan;
public string NewNamaTipeBahan
{
get { return _newnamatipebahan; }
set { _newnamatipebahan = value; }
}
private string _newlebar;
public string NewLebar
{
get { return _newlebar; }
set { _newlebar = value; }
}
private string _newpanjang;
public string NewPanjang
{
get { return _newpanjang; }
set { _newpanjang = value; }
}
private string _newsisa;
public string NewSisa
{
get { return _newsisa; }
set { _newsisa = value; }
}
private DateTime _newtanggalmasuk;
public DateTime NewTanggalMasuk
{
get { return _newtanggalmasuk; }
set { _newtanggalmasuk = value; }
}
private Supplier _selsupplier;
public Supplier SelSupplier
{
get { return _selsupplier; }
set { _selsupplier = value; }
}
private TipeBahan _seltipebahan;
public TipeBahan SelTipeBahan
{
get { return _seltipebahan; }
set
{
_seltipebahan = value;
FirePropertyChanged("SelTipeBahan");
}
}
private ObservableCollection<TipeBahan> _tipebahans;
public ObservableCollection<TipeBahan> TipeBahans
{
get { return _tipebahans; }
set { _tipebahans = value; }
}
private SolidColorBrush _tbbackground;
public SolidColorBrush TBBackground
{
get { return _tbbackground; }
set { _tbbackground = value; FirePropertyChanged("TBBackground"); }//NotifyPropertyChanged("TBBackground"); }
}
private bool _isreadonly;
public bool IsReadOnly
{
get { return _isreadonly; }
set { _isreadonly = value; FirePropertyChanged("IsReadOnly"); }
//NotifyPropertyChanged("IsReadOnly"); }
}
private ICommand _addnewbahancommand;
public ICommand AddNewBahanCommand
{
get
{
if (_addnewbahancommand == null)
{
_addnewbahancommand = new RelayCommand(AddNewBahanExecute, CanAddNewBahanExecute, false);
}
return _addnewbahancommand;
}
}
private void AddNewBahanExecute(object parameter)
{
}
private bool CanAddNewBahanExecute(object parameter)
{
if (string.IsNullOrEmpty(NewLebar)|| string.IsNullOrEmpty(NewPanjang)|| string.IsNullOrEmpty(HurufKodeBahan)|| string.IsNullOrEmpty(NoAwal) || string.IsNullOrEmpty(NoAkhir) || string.IsNullOrEmpty(NewSuratJalan))
return false;
else
return true;
}
private ICommand _deletebahancommand;
public ICommand DeleteBahanCommand
{
get
{
if (_deletebahancommand == null)
{
_deletebahancommand = new RelayCommand(DeleteBahanExecute, CanDeleteBahanExecute, false);
}
return _deletebahancommand;
}
}
private void DeleteBahanExecute(object parameter)
{
OkCancelButtonVis = "Visible"; //this is where the user interface failed to show the Ok and Cancel Button
LabelContent = "Delete " + HurufKodeBahan + "-" + NoAwal + " to " + NoAkhir +" ?";//This is where the Label Content failed to show up
}
private bool CanDeleteBahanExecute(object parameter)
{
if (string.IsNullOrEmpty(HurufKodeBahan) || string.IsNullOrEmpty(NoAwal) || string.IsNullOrEmpty(NoAkhir))
return false;
else
return true;
}
private ICommand _addnewtipebahancommand;
public ICommand AddNewTipeBahanCommand
{
get
{
if (_addnewtipebahancommand == null)
{
_addnewtipebahancommand = new RelayCommand(AddNewTipeBahanExecute, CanAddNewTipeBahanExecute, false);
}
return _addnewtipebahancommand;
}
}
private void AddNewTipeBahanExecute(object parameter)
{
}
private bool CanAddNewTipeBahanExecute(object parameter)
{
if (string.IsNullOrEmpty(NewNamaTipeBahan) || string.IsNullOrEmpty(NewGsm) )
return false;
else
return true;
}
private ICommand _okcommand;
public ICommand OkCommand
{
get
{
if (_okcommand == null)
{
_okcommand = new RelayCommand(OkExecute, CanOkExecute, false);
}
return _okcommand;
}
}
private void OkExecute(object parameter)
{
SqlDbConnect sdc = new SqlDbConnect();
DataSet ds = new DataSet();
DataTable dt = new DataTable();
DataRow dr;
string tmpKodeBahan;
int tmpNo, tmpCount;
tmpNo = int.Parse(NoAwal);
while (tmpNo <= int.Parse(NoAkhir))
{
tmpKodeBahan = HurufKodeBahan + "-" + tmpNo.ToString();
sdc.SqlQuery("select TmpCount=count(*) from TBahan where Replace(Replace(KodeBahan,'-',''),' ','')= Replace(Replace('" + tmpKodeBahan + "','-',''),' ','')");
ds = sdc.QueryEx("TBahan");
dt = ds.Tables["TBahan"];
dr = dt.Rows[0];
tmpCount = (int)dr["TmpCount"];
if (tmpCount > 0)
{
sdc.SqlQuery("delete from TBahan where Replace(Replace(KodeBahan,'-',''),' ','')=Replace(Replace('" + tmpKodeBahan + "','-',''),' ','')");
sdc.NonQueryEx();
}
tmpNo++;
}
LabelContent = "Delete is done";
sdc.SqlDbClose();
OkCancelButtonVis = "Hidden";
}
private bool CanOkExecute(object parameter)
{
return true;
}
private ICommand _cancelcommand;
public ICommand CancelCommand
{
get
{
if (_cancelcommand == null)
{
_cancelcommand = new RelayCommand(CancelExecute, CanCancelExecute, false);
}
return _cancelcommand;
}
}
private void CancelExecute(object parameter)
{
//hide ok cancel button
OkCancelButtonVis = "Hidden";
LabelContent = "";
}
private bool CanCancelExecute(object parameter)
{
return true;
}
public event PropertyChangedEventHandler PropertyChanged;
private void FirePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
这是 ViewModels Class
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
namespace XLMPIS.ViewModel
{
public class ViewModels
{
public ViewModels()
{
}
private static object inventoryViewModel = new NSViewModelExtensions.ResettableViewModel(new InventoryViewModel());
public static object InventoryViewModel
{
get
{
return ViewModels.inventoryViewModel;
}
}
private static object bahanViewModel = new NSViewModelExtensions.ResettableViewModel(new BahanViewModel());
public static object BahanViewModel
{
get
{
return ViewModels.bahanViewModel;
}
}
private static object spkdetailViewModel = new NSViewModelExtensions.ResettableViewModel(new SPKDetailViewModel());
public static object SPKDetailViewModel
{
get
{
return ViewModels.spkdetailViewModel;
}
}
}
}
这是 ResettableViewModel Class
using System;
using System.ComponentModel;
using System.Windows.Input;
namespace NSViewModelExtensions
{
[TypeDescriptionProvider(typeof(ResettableViewModelTypeDescriptionProvider))]
sealed class ResettableViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void FirePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
private static string ErrorViewModelTypeHasToMatch = "The type of the new View Model has to match that of the old View Model.";
private Func<object> creatorMethod;
public ResettableViewModel(object innerViewModel, Func<object> creatorMethod = null)
{
this.InnerViewModel = innerViewModel;
this.creatorMethod = creatorMethod;
}
public ResettableViewModel(Func<object> creatorMethod)
{
this.InnerViewModel = (this.creatorMethod = creatorMethod)();
}
public ResettableViewModel(Type innerViewModelType)
{
this.InnerViewModel = Activator.CreateInstance(innerViewModelType);
}
internal object InnerViewModel { get; private set; }
private ICommand resetCommand;
public ICommand ResetCommand
{
get
{
return resetCommand ?? (resetCommand = new InternalDelegateCommand(() =>
{
if (creatorMethod == null)
{
this.InnerViewModel = Activator.CreateInstance(this.InnerViewModel.GetType());
}
else
{
var newViewModel = creatorMethod();
if (this.InnerViewModel.GetType() != newViewModel.GetType())
{
throw new InvalidOperationException(ResettableViewModel.ErrorViewModelTypeHasToMatch);
}
this.InnerViewModel = newViewModel;
}
FirePropertyChanged(String.Empty);
}));
}
}
class InternalDelegateCommand : ICommand
{
private readonly Action executeMethod;
public InternalDelegateCommand(Action executeMethod)
{
this.executeMethod = executeMethod;
}
public void Execute(object parameter)
{
if (this.executeMethod != null)
{
this.executeMethod();
}
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
}
}
}
还有 Nish 使用的其他 4 个 类,可以从上面的 link 中找到:
- ResettableViewModelCustomField.cs
- ResettableViewModelCustomFieldPropertyDescriptor.cs
- ResettableViewModelCustomTypeDescriptor.cs
- ResettableViewModelTypeDescriptionProvider.cs
我最终放弃了 ResettableViewModel 并在我的构造函数中使用了 Initialize 方法。每当我需要重置视图模型时,我就调用这个 Initialize 方法。
我尝试使用最初由 Nish Nishant 在 https://www.codeproject.com/script/Articles/ViewDownloads.aspx?aid=158591 中发布的可重置视图模型 但是不知何故,当我执行 DeleteBahanExecute 方法时,INotifyPropertyChanged 不适用于 LabelContent 和 OkCancelButtonVis 属性。有人知道为什么吗?这是 MainWindow.xaml
<Window x:Class="XLMPIS.MainWindow"
xmlns:local="clr-namespace:XLMPIS"
xmlns:vm="clr-namespace:XLMPIS.ViewModel"
mc:Ignorable="d"
Loaded="Window_Loaded"
Title="MainWindow" Height="800" Width="800">
<TabControl>
<TabItem Header="Inventory">
<StackPanel DataContext="{Binding Source={x:Static vm:ViewModels.BahanViewModel}}">
<Grid Margin="0 10 0 0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="250"/>
<ColumnDefinition Width="250"/>
</Grid.ColumnDefinitions>
<ComboBox Grid.Column="0" ItemsSource="{Binding TipeBahans}" Width="250" HorizontalAlignment="left" SelectedItem="{Binding SelTipeBahan, Mode=TwoWay}" DisplayMemberPath="NamaTipeBahanGsm" Padding="2"/>
<ComboBox Grid.Column="1" ItemsSource="{Binding Suppliers}" Width="250" HorizontalAlignment="left" SelectedItem="{Binding SelSupplier, Mode=TwoWay}" DisplayMemberPath="NamaSupplier" Padding="2"/>
</Grid>
<Grid Margin="0 10 0 0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="90"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="40"/>
<ColumnDefinition Width="40"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="40"/>
<ColumnDefinition Width="40"/>
<ColumnDefinition Width="40"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Huruf Kode Bahan" HorizontalAlignment="Right" Padding="2"/>
<TextBox Text="{Binding HurufKodeBahan, Mode=TwoWay}" Grid.Column="1" Background="{Binding TBBackground}" IsReadOnly="{Binding IsReadOnly}" IsReadOnlyCaretVisible="{Binding IsReadOnly}" PreviewTextInput="LetterValidationTextBox" Padding="2"/>
<TextBlock Grid.Column="2" Text="No. Awal" HorizontalAlignment="Right" Padding="2"/>
<TextBox Text="{Binding NoAwal, Mode=TwoWay}" Grid.Column="3" Background="{Binding TBBackground}" IsReadOnly="{Binding IsReadOnly}" IsReadOnlyCaretVisible="{Binding IsReadOnly}" PreviewTextInput="IntegerValidationTextBox" Padding="2"/>
<TextBlock Grid.Column="4" Text="No. Akhir" HorizontalAlignment="Right" Padding="2"/>
<TextBox Text="{Binding NoAkhir, Mode=TwoWay}" Grid.Column="5" Background="{Binding TBBackground}" IsReadOnly="{Binding IsReadOnly}" IsReadOnlyCaretVisible="{Binding IsReadOnly}" PreviewTextInput="IntegerValidationTextBox" Padding="2"/>
<TextBlock Grid.Column="6" Text="Lebar" HorizontalAlignment="Right" Padding="2"/>
<TextBox Text="{Binding NewLebar, Mode=TwoWay}" PreviewTextInput="NumberValidationTextBox" Grid.Column="7" Background="{Binding TBBackground}" IsReadOnly="{Binding IsReadOnly}" IsReadOnlyCaretVisible="{Binding IsReadOnly}" Padding="2"/>
<TextBlock Grid.Column="8" Text="Panjang" HorizontalAlignment="Right" Padding="2"/>
<TextBox Text="{Binding NewPanjang, Mode=TwoWay}" PreviewTextInput="NumberValidationTextBox" Grid.Column="9" Background="{Binding TBBackground}" IsReadOnly="{Binding IsReadOnly}" IsReadOnlyCaretVisible="{Binding IsReadOnly}" Padding="2"/>
<TextBlock Grid.Column="10" Text="Sisa" HorizontalAlignment="Right" Padding="2"/>
<TextBox Text="{Binding NewSisa, Mode=TwoWay}" PreviewTextInput="NumberValidationTextBox" Grid.Column="11" Background="{Binding TBBackground}" IsReadOnly="{Binding IsReadOnly}" IsReadOnlyCaretVisible="{Binding IsReadOnly}" Padding="2"/>
</Grid>
<Grid Margin="0 10 0 0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="80"/>
<ColumnDefinition Width="120"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Surat Jalan" HorizontalAlignment="Right" Padding="2"/>
<TextBox Text="{Binding NewSuratJalan, Mode=TwoWay}" Grid.Column="1" Background="{Binding TBBackground}" IsReadOnly="{Binding IsReadOnly}" IsReadOnlyCaretVisible="{Binding IsReadOnly}" Padding="2"/>
<TextBlock Grid.Column="2" Text="Tanggal Masuk" Padding="2"/>
<DatePicker SelectedDate="{Binding NewTanggalMasuk}" Grid.Column="3" Height="25" HorizontalAlignment="Left" VerticalAlignment="Top" Width="115"/>
</Grid>
<Grid Margin="0 10 0 0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="80"/>
<ColumnDefinition Width="70"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Content="Add New Bahan" Width="100" Command="{Binding AddNewBahanCommand}" Padding="2"/>
<Button Grid.Column="1" Margin="10 0 0 0" Content="Delete Bahan" Width="90" Command="{Binding DeleteBahanCommand}" Padding="2"/>
<Button Grid.Column="2" Margin="10 0 0 0" Content="Refresh" Width="90" Command="{Binding ResetCommand}" Padding="2"/>
<Button Grid.Column="3" Margin="20 0 0 0" Content="Ok" Visibility="{Binding OkCancelButtonVis,Mode=TwoWay}" HorizontalAlignment="Left" Width="60" Command="{Binding OkCommand}" Padding="2"/>
<Button Grid.Column="4" Margin="10 0 0 0" Content="Cancel" Visibility="{Binding OkCancelButtonVis,Mode=TwoWay}" HorizontalAlignment="Left" Width="60" Command="{Binding CancelCommand}" Padding="2"/>
</Grid>
<Grid Margin="0 10 0 0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="250"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Nama Tipe Bahan" Padding="2"/>
<TextBox Text="{Binding NewNamaTipeBahan, Mode=TwoWay}" Grid.Column="1" Background="{Binding TBBackground}" IsReadOnly="{Binding IsReadOnly}" IsReadOnlyCaretVisible="{Binding IsReadOnly}" Padding="2"/>
<TextBlock Grid.Column="2" Text="Gsm" HorizontalAlignment="Right" Padding="2"/>
<TextBox Text="{Binding NewGsm, Mode=TwoWay}" PreviewTextInput="NumberValidationTextBox" Grid.Column="3" Background="{Binding TBBackground}" IsReadOnly="{Binding IsReadOnly}" IsReadOnlyCaretVisible="{Binding IsReadOnly}" Padding="2"/>
</Grid>
<Button Margin="0 10 0 0" Content="Add New Tipe Bahan" HorizontalAlignment="Left" Width="120" Command="{Binding AddNewTipeBahanCommand}" Padding="2"/>
<Label Margin="0 10 0 0" Content="{Binding LabelContent, Mode=TwoWay}"/>
</StackPanel>
</TabItem>
这是视图模型代码。我删除了一些 "using" 行。
using XLMPIS.Model;
using XLMPIS.Command;
namespace XLMPIS.ViewModel
{
public class BahanViewModel : INotifyPropertyChanged
{
public BahanViewModel()
{
SelTipeBahan = new TipeBahan();
TipeBahans = new ObservableCollection<TipeBahan>();
Suppliers = new ObservableCollection<Supplier>();
SelSupplier = new Supplier();
SqlDbConnect sdc = new SqlDbConnect();
DataSet ds = new DataSet();
DataTable dt = new DataTable();
sdc.SqlQuery("select * from TTipeBahan");
ds = sdc.QueryEx("TTipeBahan");
dt = ds.Tables["TTipeBahan"];
int tmpIndex = 0;
foreach (DataRow dr in dt.Rows)
{
TipeBahans.Add(new TipeBahan());
TipeBahans[tmpIndex].TipeBahanID = (int)dr["TipeBahanID"];
TipeBahans[tmpIndex].NamaTipeBahan = dr["NamaTipeBahan"].ToString();
TipeBahans[tmpIndex].Gsm = (int)dr["Gsm"];
tmpIndex++;
}
SelTipeBahan = TipeBahans[SELECTED_INDEX];
sdc.SqlQuery("select NamaSupplier,SupplierID from TSupplier");
ds = sdc.QueryEx("TSupplier");
dt = ds.Tables["TSupplier"];
tmpIndex = 0;
foreach (DataRow dr in dt.Rows)
{
Suppliers.Add(new Supplier());
Suppliers[tmpIndex].SupplierID = (int)dr["SupplierID"];
Suppliers[tmpIndex].NamaSupplier= dr["NamaSupplier"].ToString();
tmpIndex++;
}
SelSupplier = Suppliers[SELECTED_INDEX];
sdc.SqlDbClose();
NewTanggalMasuk = DateTime.Today;
IsReadOnly = false;//not read only
TBBackground = new SolidColorBrush(Colors.White); //white textbox
OkCancelButtonVis = "Hidden";
LabelContent = "test";
}
private const int SELECTED_INDEX = 0;
private ObservableCollection<Supplier> _suppliers;
public ObservableCollection<Supplier> Suppliers
{
get { return _suppliers; }
set { _suppliers = value; }
}
private string _noawal;
public string NoAwal
{
get { return _noawal; }
set { _noawal = value; }
}
private string _noakhir;
public string NoAkhir
{
get { return _noakhir; }
set { _noakhir = value; }
}
private string _okcancelbuttonvis;
public string OkCancelButtonVis
{
get { return _okcancelbuttonvis; }
set { _okcancelbuttonvis = value; FirePropertyChanged("OkCancelButtonVis"); }
}
private string _labelcontent;
public string LabelContent
{
get { return _labelcontent; }
set { _labelcontent = value; FirePropertyChanged("LabelContent"); }
}
private string _hurufkodebahan;
public string HurufKodeBahan
{
get { return _hurufkodebahan; }
set { _hurufkodebahan = value; }
}
private string _newsuratjalan;
public string NewSuratJalan
{
get { return _newsuratjalan; }
set { _newsuratjalan = value; }
}
private string _newgsm;
public string NewGsm
{
get { return _newgsm; }
set { _newgsm = value; }
}
private string _newnamatipebahan;
public string NewNamaTipeBahan
{
get { return _newnamatipebahan; }
set { _newnamatipebahan = value; }
}
private string _newlebar;
public string NewLebar
{
get { return _newlebar; }
set { _newlebar = value; }
}
private string _newpanjang;
public string NewPanjang
{
get { return _newpanjang; }
set { _newpanjang = value; }
}
private string _newsisa;
public string NewSisa
{
get { return _newsisa; }
set { _newsisa = value; }
}
private DateTime _newtanggalmasuk;
public DateTime NewTanggalMasuk
{
get { return _newtanggalmasuk; }
set { _newtanggalmasuk = value; }
}
private Supplier _selsupplier;
public Supplier SelSupplier
{
get { return _selsupplier; }
set { _selsupplier = value; }
}
private TipeBahan _seltipebahan;
public TipeBahan SelTipeBahan
{
get { return _seltipebahan; }
set
{
_seltipebahan = value;
FirePropertyChanged("SelTipeBahan");
}
}
private ObservableCollection<TipeBahan> _tipebahans;
public ObservableCollection<TipeBahan> TipeBahans
{
get { return _tipebahans; }
set { _tipebahans = value; }
}
private SolidColorBrush _tbbackground;
public SolidColorBrush TBBackground
{
get { return _tbbackground; }
set { _tbbackground = value; FirePropertyChanged("TBBackground"); }//NotifyPropertyChanged("TBBackground"); }
}
private bool _isreadonly;
public bool IsReadOnly
{
get { return _isreadonly; }
set { _isreadonly = value; FirePropertyChanged("IsReadOnly"); }
//NotifyPropertyChanged("IsReadOnly"); }
}
private ICommand _addnewbahancommand;
public ICommand AddNewBahanCommand
{
get
{
if (_addnewbahancommand == null)
{
_addnewbahancommand = new RelayCommand(AddNewBahanExecute, CanAddNewBahanExecute, false);
}
return _addnewbahancommand;
}
}
private void AddNewBahanExecute(object parameter)
{
}
private bool CanAddNewBahanExecute(object parameter)
{
if (string.IsNullOrEmpty(NewLebar)|| string.IsNullOrEmpty(NewPanjang)|| string.IsNullOrEmpty(HurufKodeBahan)|| string.IsNullOrEmpty(NoAwal) || string.IsNullOrEmpty(NoAkhir) || string.IsNullOrEmpty(NewSuratJalan))
return false;
else
return true;
}
private ICommand _deletebahancommand;
public ICommand DeleteBahanCommand
{
get
{
if (_deletebahancommand == null)
{
_deletebahancommand = new RelayCommand(DeleteBahanExecute, CanDeleteBahanExecute, false);
}
return _deletebahancommand;
}
}
private void DeleteBahanExecute(object parameter)
{
OkCancelButtonVis = "Visible"; //this is where the user interface failed to show the Ok and Cancel Button
LabelContent = "Delete " + HurufKodeBahan + "-" + NoAwal + " to " + NoAkhir +" ?";//This is where the Label Content failed to show up
}
private bool CanDeleteBahanExecute(object parameter)
{
if (string.IsNullOrEmpty(HurufKodeBahan) || string.IsNullOrEmpty(NoAwal) || string.IsNullOrEmpty(NoAkhir))
return false;
else
return true;
}
private ICommand _addnewtipebahancommand;
public ICommand AddNewTipeBahanCommand
{
get
{
if (_addnewtipebahancommand == null)
{
_addnewtipebahancommand = new RelayCommand(AddNewTipeBahanExecute, CanAddNewTipeBahanExecute, false);
}
return _addnewtipebahancommand;
}
}
private void AddNewTipeBahanExecute(object parameter)
{
}
private bool CanAddNewTipeBahanExecute(object parameter)
{
if (string.IsNullOrEmpty(NewNamaTipeBahan) || string.IsNullOrEmpty(NewGsm) )
return false;
else
return true;
}
private ICommand _okcommand;
public ICommand OkCommand
{
get
{
if (_okcommand == null)
{
_okcommand = new RelayCommand(OkExecute, CanOkExecute, false);
}
return _okcommand;
}
}
private void OkExecute(object parameter)
{
SqlDbConnect sdc = new SqlDbConnect();
DataSet ds = new DataSet();
DataTable dt = new DataTable();
DataRow dr;
string tmpKodeBahan;
int tmpNo, tmpCount;
tmpNo = int.Parse(NoAwal);
while (tmpNo <= int.Parse(NoAkhir))
{
tmpKodeBahan = HurufKodeBahan + "-" + tmpNo.ToString();
sdc.SqlQuery("select TmpCount=count(*) from TBahan where Replace(Replace(KodeBahan,'-',''),' ','')= Replace(Replace('" + tmpKodeBahan + "','-',''),' ','')");
ds = sdc.QueryEx("TBahan");
dt = ds.Tables["TBahan"];
dr = dt.Rows[0];
tmpCount = (int)dr["TmpCount"];
if (tmpCount > 0)
{
sdc.SqlQuery("delete from TBahan where Replace(Replace(KodeBahan,'-',''),' ','')=Replace(Replace('" + tmpKodeBahan + "','-',''),' ','')");
sdc.NonQueryEx();
}
tmpNo++;
}
LabelContent = "Delete is done";
sdc.SqlDbClose();
OkCancelButtonVis = "Hidden";
}
private bool CanOkExecute(object parameter)
{
return true;
}
private ICommand _cancelcommand;
public ICommand CancelCommand
{
get
{
if (_cancelcommand == null)
{
_cancelcommand = new RelayCommand(CancelExecute, CanCancelExecute, false);
}
return _cancelcommand;
}
}
private void CancelExecute(object parameter)
{
//hide ok cancel button
OkCancelButtonVis = "Hidden";
LabelContent = "";
}
private bool CanCancelExecute(object parameter)
{
return true;
}
public event PropertyChangedEventHandler PropertyChanged;
private void FirePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
这是 ViewModels Class
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
namespace XLMPIS.ViewModel
{
public class ViewModels
{
public ViewModels()
{
}
private static object inventoryViewModel = new NSViewModelExtensions.ResettableViewModel(new InventoryViewModel());
public static object InventoryViewModel
{
get
{
return ViewModels.inventoryViewModel;
}
}
private static object bahanViewModel = new NSViewModelExtensions.ResettableViewModel(new BahanViewModel());
public static object BahanViewModel
{
get
{
return ViewModels.bahanViewModel;
}
}
private static object spkdetailViewModel = new NSViewModelExtensions.ResettableViewModel(new SPKDetailViewModel());
public static object SPKDetailViewModel
{
get
{
return ViewModels.spkdetailViewModel;
}
}
}
}
这是 ResettableViewModel Class
using System;
using System.ComponentModel;
using System.Windows.Input;
namespace NSViewModelExtensions
{
[TypeDescriptionProvider(typeof(ResettableViewModelTypeDescriptionProvider))]
sealed class ResettableViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void FirePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
private static string ErrorViewModelTypeHasToMatch = "The type of the new View Model has to match that of the old View Model.";
private Func<object> creatorMethod;
public ResettableViewModel(object innerViewModel, Func<object> creatorMethod = null)
{
this.InnerViewModel = innerViewModel;
this.creatorMethod = creatorMethod;
}
public ResettableViewModel(Func<object> creatorMethod)
{
this.InnerViewModel = (this.creatorMethod = creatorMethod)();
}
public ResettableViewModel(Type innerViewModelType)
{
this.InnerViewModel = Activator.CreateInstance(innerViewModelType);
}
internal object InnerViewModel { get; private set; }
private ICommand resetCommand;
public ICommand ResetCommand
{
get
{
return resetCommand ?? (resetCommand = new InternalDelegateCommand(() =>
{
if (creatorMethod == null)
{
this.InnerViewModel = Activator.CreateInstance(this.InnerViewModel.GetType());
}
else
{
var newViewModel = creatorMethod();
if (this.InnerViewModel.GetType() != newViewModel.GetType())
{
throw new InvalidOperationException(ResettableViewModel.ErrorViewModelTypeHasToMatch);
}
this.InnerViewModel = newViewModel;
}
FirePropertyChanged(String.Empty);
}));
}
}
class InternalDelegateCommand : ICommand
{
private readonly Action executeMethod;
public InternalDelegateCommand(Action executeMethod)
{
this.executeMethod = executeMethod;
}
public void Execute(object parameter)
{
if (this.executeMethod != null)
{
this.executeMethod();
}
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
}
}
}
还有 Nish 使用的其他 4 个 类,可以从上面的 link 中找到:
- ResettableViewModelCustomField.cs
- ResettableViewModelCustomFieldPropertyDescriptor.cs
- ResettableViewModelCustomTypeDescriptor.cs
- ResettableViewModelTypeDescriptionProvider.cs
我最终放弃了 ResettableViewModel 并在我的构造函数中使用了 Initialize 方法。每当我需要重置视图模型时,我就调用这个 Initialize 方法。