C# Master Detail View - 还原未保存的更改
C# Master Detail View - Reverting unsaved changes
我正在使用 Caliburn.Micro 在 C# 中创建 CRUD 应用程序,我有一些看起来像这样的主细节视图:
我想要实现的是,当我进行一些更改时(例如我将容量从 47 更改为 50),然后 select 另一个地方,比如说 地方 4 和 select 再次放置 5 我的容量将是 47,而不是现在的 50。
我在考虑 OneTime 绑定,然后手动触发绑定到 viewmodel,但是 viewModel 不应该知道视图,所以这似乎是个坏主意。我的代码如下。
XAML:
<DataGrid x:Name="Places"
Grid.Column="0" Grid.Row="0" Grid.RowSpan="2"
Width="290"
HorizontalAlignment="Left"
CanUserReorderColumns="False" CanUserResizeColumns="False" IsReadOnly="True"
AutoGenerateColumns="False" IsSynchronizedWithCurrentItem="False" SelectionMode="Single">
<DataGrid.Columns>
<DataGridTextColumn Header="Place" Binding="{Binding Name, Mode=OneTime}" MinWidth="150"
Width="SizeToCells" />
</DataGrid.Columns>
</DataGrid>
<Grid Grid.Column="0" Grid.Row="2"
Width="290" Height="210"
HorizontalAlignment="Left" VerticalAlignment="Bottom">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="110" />
<ColumnDefinition Width="180" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="30" />
<RowDefinition Height="60" />
<RowDefinition Height="30" />
<RowDefinition Height="60" />
</Grid.RowDefinitions>
<Label Content="ID"
Grid.Column="0" Grid.Row="0" VerticalAlignment="Top" />
<Label Content="Place*"
Grid.Column="0" Grid.Row="1" VerticalAlignment="Top" />
<Label Content="Address*"
Grid.Column="0" Grid.Row="2" VerticalAlignment="Top" />
<Label Content="Capacity*"
Grid.Column="0" Grid.Row="3" VerticalAlignment="Top" />
<Label Content="Comments"
Grid.Column="0" Grid.Row="4" VerticalAlignment="Top" />
<TextBox x:Name="SelectedPlace_Id"
Grid.Column="1" Grid.Row="0" VerticalAlignment="Top"
IsEnabled="False" />
<TextBox x:Name="SelectedPlace_Name"
Grid.Column="1" Grid.Row="1" VerticalAlignment="Top" />
<TextBox x:Name="SelectedPlace_Address"
Grid.Column="1" Grid.Row="2"
Height="55" VerticalAlignment="Top"
TextWrapping="Wrap" />
<TextBox x:Name="SelectedPlace_Capacity"
Grid.Column="1" Grid.Row="3" VerticalAlignment="Top" />
<TextBox x:Name="SelectedPlace_Comments"
Grid.Column="1" Grid.Row="4" Height="55"
VerticalAlignment="Top"
TextWrapping="Wrap" />
</Grid>
<Button x:Name="DeletePlace" Content="Delete"
Grid.Column="0" Grid.Row="3"
Width="100" Height="30" Margin="0 0 110 0"
HorizontalAlignment="Right" VerticalAlignment="Top" />
<Button x:Name="SavePlace" Content="Save"
Grid.Column="0" Grid.Row="3"
Width="100" Height="30" Margin="0 0 5 0"
HorizontalAlignment="Right" VerticalAlignment="Top" />
视图模型:
class PlacesTabViewModel : TabViewModel
{
#region Fields
private BindableCollection<Place> _places;
private Place _selectedPlace;
#endregion
#region Methods
public PlacesTabViewModel()
{
using (var ctx = new DbCtx(App.DatabasePath))
{
_places = new BindableCollection<Place>(ctx.Places.OrderBy(p => p.Name));
}
}
public override string ToString()
{
return "Places";
}
#endregion
#region Events
public bool CanDeletePlace => _selectedPlace != null;
public bool CanSavePlace => (_selectedPlace != null) && _selectedPlace.IsValid();
public void DeletePlace()
{
using (var ctx = new DbCtx(App.DatabasePath))
{
try
{
ctx.Entry(SelectedPlace).State = EntityState.Deleted;
ctx.SaveChanges();
}
catch (DbUpdateException)
{
//TODO: Error
return;
}
Places.Remove(SelectedPlace);
NotifyOfPropertyChange(nameof(Places));
NotifyOfPropertyChange(() => CanDeletePlace);
NotifyOfPropertyChange(() => CanSavePlace);
}
}
public void SavePlace()
{
using (var ctx = new DbCtx(App.DatabasePath))
{
try
{
ctx.Places.Attach(_selectedPlace);
ctx.Entry(_selectedPlace).State = EntityState.Modified;
ctx.SaveChanges();
}
catch
{
//TODO: Error
}
NotifyOfPropertyChange(nameof(Places));
}
}
#endregion
#region Properties
public BindableCollection<Place> Places
{
get { return _places; }
set
{
if (value != _places)
{
_places = value;
NotifyOfPropertyChange(nameof(Places));
}
}
}
public Place SelectedPlace
{
get { return _selectedPlace; }
set
{
if (value != _selectedPlace)
{
_selectedPlace = value;
if (_selectedPlace != null)
{
_selectedPlace.PropertyChanged += (sender, args) =>
{
NotifyOfPropertyChange(() => CanSavePlace);
};
}
NotifyOfPropertyChange(nameof(SelectedPlace));
NotifyOfPropertyChange(() => CanDeletePlace);
NotifyOfPropertyChange(() => CanSavePlace);
}
}
}
#endregion
}
主窗口的 ViewModel
class MainWindowViewModel : PropertyChangedBase
{
#region Fields
private BindableCollection<TabViewModel> _tabs = new BindableCollection<TabViewModel>();
#endregion
public MainWindowViewModel()
{
_tabs.Add(new PlacesTabViewModel());
}
#region Properties
public BindableCollection<TabViewModel> Tabs
{
get { return _tabs; }
set
{
if (value != _tabs)
{
_tabs = value;
NotifyOfPropertyChange(nameof(Tabs));
}
}
}
#endregion
}
WPF 不知道法线属性。您要么必须使其成为依赖项 属性,或者在您的情况下,您的 VM 必须实现 INotifyPropertyChanged。当值改变时它会自动更新。
可能是您没有为 SelectedPlace_Capacity.
应用 INotifyPropertyChanged 属性
我认为这是问题所在。
我正在使用 Caliburn.Micro 在 C# 中创建 CRUD 应用程序,我有一些看起来像这样的主细节视图:
我想要实现的是,当我进行一些更改时(例如我将容量从 47 更改为 50),然后 select 另一个地方,比如说 地方 4 和 select 再次放置 5 我的容量将是 47,而不是现在的 50。
我在考虑 OneTime 绑定,然后手动触发绑定到 viewmodel,但是 viewModel 不应该知道视图,所以这似乎是个坏主意。我的代码如下。
XAML:
<DataGrid x:Name="Places"
Grid.Column="0" Grid.Row="0" Grid.RowSpan="2"
Width="290"
HorizontalAlignment="Left"
CanUserReorderColumns="False" CanUserResizeColumns="False" IsReadOnly="True"
AutoGenerateColumns="False" IsSynchronizedWithCurrentItem="False" SelectionMode="Single">
<DataGrid.Columns>
<DataGridTextColumn Header="Place" Binding="{Binding Name, Mode=OneTime}" MinWidth="150"
Width="SizeToCells" />
</DataGrid.Columns>
</DataGrid>
<Grid Grid.Column="0" Grid.Row="2"
Width="290" Height="210"
HorizontalAlignment="Left" VerticalAlignment="Bottom">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="110" />
<ColumnDefinition Width="180" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="30" />
<RowDefinition Height="60" />
<RowDefinition Height="30" />
<RowDefinition Height="60" />
</Grid.RowDefinitions>
<Label Content="ID"
Grid.Column="0" Grid.Row="0" VerticalAlignment="Top" />
<Label Content="Place*"
Grid.Column="0" Grid.Row="1" VerticalAlignment="Top" />
<Label Content="Address*"
Grid.Column="0" Grid.Row="2" VerticalAlignment="Top" />
<Label Content="Capacity*"
Grid.Column="0" Grid.Row="3" VerticalAlignment="Top" />
<Label Content="Comments"
Grid.Column="0" Grid.Row="4" VerticalAlignment="Top" />
<TextBox x:Name="SelectedPlace_Id"
Grid.Column="1" Grid.Row="0" VerticalAlignment="Top"
IsEnabled="False" />
<TextBox x:Name="SelectedPlace_Name"
Grid.Column="1" Grid.Row="1" VerticalAlignment="Top" />
<TextBox x:Name="SelectedPlace_Address"
Grid.Column="1" Grid.Row="2"
Height="55" VerticalAlignment="Top"
TextWrapping="Wrap" />
<TextBox x:Name="SelectedPlace_Capacity"
Grid.Column="1" Grid.Row="3" VerticalAlignment="Top" />
<TextBox x:Name="SelectedPlace_Comments"
Grid.Column="1" Grid.Row="4" Height="55"
VerticalAlignment="Top"
TextWrapping="Wrap" />
</Grid>
<Button x:Name="DeletePlace" Content="Delete"
Grid.Column="0" Grid.Row="3"
Width="100" Height="30" Margin="0 0 110 0"
HorizontalAlignment="Right" VerticalAlignment="Top" />
<Button x:Name="SavePlace" Content="Save"
Grid.Column="0" Grid.Row="3"
Width="100" Height="30" Margin="0 0 5 0"
HorizontalAlignment="Right" VerticalAlignment="Top" />
视图模型:
class PlacesTabViewModel : TabViewModel
{
#region Fields
private BindableCollection<Place> _places;
private Place _selectedPlace;
#endregion
#region Methods
public PlacesTabViewModel()
{
using (var ctx = new DbCtx(App.DatabasePath))
{
_places = new BindableCollection<Place>(ctx.Places.OrderBy(p => p.Name));
}
}
public override string ToString()
{
return "Places";
}
#endregion
#region Events
public bool CanDeletePlace => _selectedPlace != null;
public bool CanSavePlace => (_selectedPlace != null) && _selectedPlace.IsValid();
public void DeletePlace()
{
using (var ctx = new DbCtx(App.DatabasePath))
{
try
{
ctx.Entry(SelectedPlace).State = EntityState.Deleted;
ctx.SaveChanges();
}
catch (DbUpdateException)
{
//TODO: Error
return;
}
Places.Remove(SelectedPlace);
NotifyOfPropertyChange(nameof(Places));
NotifyOfPropertyChange(() => CanDeletePlace);
NotifyOfPropertyChange(() => CanSavePlace);
}
}
public void SavePlace()
{
using (var ctx = new DbCtx(App.DatabasePath))
{
try
{
ctx.Places.Attach(_selectedPlace);
ctx.Entry(_selectedPlace).State = EntityState.Modified;
ctx.SaveChanges();
}
catch
{
//TODO: Error
}
NotifyOfPropertyChange(nameof(Places));
}
}
#endregion
#region Properties
public BindableCollection<Place> Places
{
get { return _places; }
set
{
if (value != _places)
{
_places = value;
NotifyOfPropertyChange(nameof(Places));
}
}
}
public Place SelectedPlace
{
get { return _selectedPlace; }
set
{
if (value != _selectedPlace)
{
_selectedPlace = value;
if (_selectedPlace != null)
{
_selectedPlace.PropertyChanged += (sender, args) =>
{
NotifyOfPropertyChange(() => CanSavePlace);
};
}
NotifyOfPropertyChange(nameof(SelectedPlace));
NotifyOfPropertyChange(() => CanDeletePlace);
NotifyOfPropertyChange(() => CanSavePlace);
}
}
}
#endregion
}
主窗口的 ViewModel
class MainWindowViewModel : PropertyChangedBase
{
#region Fields
private BindableCollection<TabViewModel> _tabs = new BindableCollection<TabViewModel>();
#endregion
public MainWindowViewModel()
{
_tabs.Add(new PlacesTabViewModel());
}
#region Properties
public BindableCollection<TabViewModel> Tabs
{
get { return _tabs; }
set
{
if (value != _tabs)
{
_tabs = value;
NotifyOfPropertyChange(nameof(Tabs));
}
}
}
#endregion
}
WPF 不知道法线属性。您要么必须使其成为依赖项 属性,或者在您的情况下,您的 VM 必须实现 INotifyPropertyChanged。当值改变时它会自动更新。
可能是您没有为 SelectedPlace_Capacity.
应用 INotifyPropertyChanged 属性我认为这是问题所在。