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 属性

我认为这是问题所在。