【问题标题】:DataGrid: Allow edit for second row only after saving changes for the first rowDataGrid:仅在保存第一行的更改后才允许编辑第二行
【发布时间】:2014-02-18 04:33:32
【问题描述】:

我有一个DataGrid,其ItemsSource 绑定到ViewModel 的ObservableCollection(of MY_TYPE) 类型的属性。另外,在 ViewModel 中,我还有一个名为 SelectedTarget 的属性,它绑定到 DataGridSelectedItem (类型当然是 MY_TYPE )。

现在我想要实现的是:用户可以将光标移动到另一行,前提是前一行(SelectedTarget)没有任何更改,否则他必须首先保存这些更改。但我不知道该怎么做。

感谢您的帮助!

更新

这是我的 xaml 代码和 ViewModel 属性,就像 Sheridan 建议的那样。

xaml

<DataGrid Grid.Column="1" x:Name="ItemsGrid" ItemsSource="{Binding MyItems}" SelectionMode="Single"  AutoGenerateColumns="False"
                       CanUserResizeRows="False" CanUserSortColumns="True" IsReadOnly="True" VirtualizingStackPanel.IsVirtualizing="True"
                       VirtualizingStackPanel.VirtualizationMode="Standard" EnableRowVirtualization="True" 
                       SelectedItem="{Binding SelectedTarget, Mode=TwoWay}"                        
                       SelectionUnit="FullRow">

以及 ViewModel 中的属性:

Public Property SelectedTarget As MY_TYPE
        Get
            Return _selectedTarget
        End Get
        Set(value As MY_TYPE)
            If _selectedTarget Is Nothing OrElse (_selectedTarget.Status <> Status.Editing AndAlso _selectedTarget.Status <> Status.New) Then
                _selectedTarget = value
                RaisePropertyChanged("SelectedTarget")
            End If
        End Set
    End Property
    Private _selectedTarget As MY_TYPE

我已经检查过,当SelectedTargetStatus 正在编辑时,它不会改变SelectedTarget 本身。但是,这并不能阻止另一行中的值被更改。无论如何,光标都会移动到另一行,但我不确定这是否意味着我的 SelectedItem 绑定无法正常工作。另一方面,如果我根本没有在DataGrid 中设置SelectedItem 的绑定,即使我不保存第一行中的更改,第二行中的值仍然会发生变化。这有点道理,因为每一行本质上都绑定到一个项目。

【问题讨论】:

  • 你可以绑定到一个标志(布尔)
  • @WiiMaxx 你能给我更多细节吗?在 MY_TYPE 类中,我确实有一个 Enum 标志来指示状态(有效、编辑和新建),但我想不出在这种情况下使用它的方式。我有点感觉这应该很简单
  • 我现在编辑了我的答案,它应该适合您的需要;)

标签: wpf vb.net mvvm datagrid


【解决方案1】:

以下是您如何执行此操作的示例。我知道它很丑,但你明白了:)

代码:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new VM();
    }
}

public class VM : INotifyPropertyChanged
{
    private ObservableCollection<MyItem> mySource;

    private MyItem myTarget;

    public ObservableCollection<MyItem> MySource
    {
        get { return mySource; }
        set { mySource = value; }
    }

    public MyItem MyTarget
    {
        get { return myTarget; }
        set
        {
            if (myTarget == null)
                myTarget = value;
            else if (myTarget.IsSaved)
                myTarget = value;

            INotifyPropertyChanged("MyTarget");
        }
    }

    public VM()
    {
        mySource = new ObservableCollection<MyItem>();

        mySource.Add(new MyItem { Text1 = "1", Text2 = "8" });
        mySource.Add(new MyItem { Text1 = "2", Text2 = "7" });
        mySource.Add(new MyItem { Text1 = "3", Text2 = "6" });
        mySource.Add(new MyItem { Text1 = "4", Text2 = "5" });
        mySource.Add(new MyItem { Text1 = "5", Text2 = "4" });
        mySource.Add(new MyItem { Text1 = "6", Text2 = "3" });
        mySource.Add(new MyItem { Text1 = "7", Text2 = "2" });
        mySource.Add(new MyItem { Text1 = "8", Text2 = "1" });
    }

    //INotifyPropertyChanged
}

public class MyItem : INotifyPropertyChanged
{
    private string text1;

    public string Text1
    {
        get { return text1; }
        set
        {
            text1 = value;
            isSaved = false;

            INotifyPropertyChanged("Text1");
            INotifyPropertyChanged("IsSaved");
        }
    }

    private string text2;

    public string Text2
    {
        get { return text2; }
        set
        {
            text2 = value;
            isSaved = true;

            INotifyPropertyChanged("Text2");
            INotifyPropertyChanged("IsSaved");
        }
    }

    private bool isSaved = true;

    public bool IsSaved
    {
        get { return isSaved; }
        set
        {
            isSaved = value;

            RaisePropertyChanged(() => Reg(() => IsSaved));
        }
    }

    //INotifyPropertyChanged
}

XAML

<DataGrid Name="MyDataGrid" ItemsSource="{Binding MySource}"
          SelectedItem="{Binding MyTarget, UpdateSourceTrigger=PropertyChanged}">
    <DataGrid.Resources>
        <Style TargetType="{x:Type DataGridCell}">
            <Setter Property="Visibility" Value="Visible"/>
            <Style.Triggers>

                <!-- for your other items-->
                <MultiDataTrigger>
                    <MultiDataTrigger.Conditions>
                        <Condition  Binding="{Binding ElementName=MyDataGrid, Path=SelectedItem.IsSaved, UpdateSourceTrigger=PropertyChanged}" Value="false"/>
                        <Condition Binding="{Binding IsSaved}" Value="true"/>
                    </MultiDataTrigger.Conditions>
                    <MultiDataTrigger.Setters>
                        <Setter Property="Visibility" Value="Hidden"/>
                    </MultiDataTrigger.Setters>
                </MultiDataTrigger>

            </Style.Triggers>
        </Style>
    </DataGrid.Resources>

</DataGrid>

编辑

我找到了一种展示 Row 的方法,希望你喜欢 :)

        <Style TargetType="{x:Type DataGridRow}">
            <Style.Triggers>

                <!-- for your other items-->
                <MultiDataTrigger>
                    <MultiDataTrigger.Conditions>
                        <Condition  Binding="{Binding ElementName=MyDataGrid, Path=SelectedItem.IsSaved, UpdateSourceTrigger=PropertyChanged}" Value="false"/>
                        <Condition Binding="{Binding IsSaved}" Value="true"/>
                    </MultiDataTrigger.Conditions>
                    <MultiDataTrigger.Setters>
                        <Setter Property="IsEnabled" Value="False"/>
                    </MultiDataTrigger.Setters>
                </MultiDataTrigger>

            </Style.Triggers>
        </Style>

【讨论】:

  • 感谢您的回复。它有点工作。但是有没有办法不隐藏其他行(当 SelectedItem 发生变化时)?我想这就是我的问题所在:我认为我的 SelectedItem 绑定工作正常。但是,它不会阻止其他行被修改。在您的解决方案中,当 SelectedItem 发生更改时,所有其他行都被隐藏,因此无法再更改。但是您的解决方案绝对是有效的,并且具有有趣的效果
  • @tete 实际上我试图绑定到来自DataGridCellIsReadyOnly 值,我对DataGridRow 也不起作用,它不像我预期的那样起作用,但也许你能够绑定到其中一个,所以它会像你想要的那样工作:)
  • 是的,我明白你的意思,这也是困扰我的地方。但目前我对你的解决方案很满意:)
  • @WiiMaxx 您只能在整个列上应用 IsReadOnly。我能想出解决这个限制的唯一方法是我们根据绑定来设置单元格的背景颜色,以确定它是否应该是可编辑的单元格。然后我们使用BeginningEdit 事件来检查单元格的背景颜色,如果它应该是不可编辑的则取消它。类似的解决方案也可以在这里工作。
  • @WiiMaxx 您编辑的版本绝对是一个可行的解决方案。非常感谢!
【解决方案2】:

第一步是为您创建一些基本框架,该框架将告诉您项目是否有更改。这可以通过使用您的 MY_TYPE 类的另一个实例并通过简单地比较每个属性的值来轻松实现。

如果用户对当前对象进行了更改,则下一个阶段是阻止用户更改所选项目。如果您的SelectedTarget 属性已正确绑定数据,那么您应该能够阻止用户更改当前行,如下所示:

public MY_TYPE SelectedTarget
{
    get { return selectedTarget; }
    set 
    {
        if (!SelectedTarget.HasChanges)
        {
            selectedTarget = value; 
            NotifyPropertyChanged("SelectedTarget"); 
        }
    }
}

最后,当用户保存当前项目时,你需要同步你的两个对象以表示不再有任何更改,然后用户将能够再次更改记录。

【讨论】:

  • 感谢您的回复。这基本上就是我所做的,但到目前为止它不起作用。我在原始帖子中添加了一些代码,请查看我还需要做什么。我的感觉是 SelectedItem 没有正确绑定,或者不需要更改 SelectedItem 来进行这些更改(因为每一行都绑定到集合中的一个项目)
  • 它不像你正在做的那样看起来。 “也许SelectedItem 没有正确绑定数据”是什么意思?如果 your Binding 不起作用,那么只有 you 可以告诉我们。
  • 你能看到我在更新代码中做错了什么吗?我做了什么:1)将DataGrid中的SelectedItem绑定设置为SelectedTarget和2)在xaml中,仅当当前项目的状态为有效时才设置SelectedTarget。我认为第二部分与您的代码相同。当我说“可能 SelectedItem 的绑定”无法正常工作时,我的意思是即使 SelectedTarget 没有更改,因为 IF 条件不成立,但 DataGrid 中的光标确实移到了另一行。而且我不知道这是否是SelectedItem 的正确行为。
  • 就像我提到的,如果我根本不绑定SelectedItem,我可以修改多行中的值,因为从本质上讲,每一行都绑定到一个项目在 ItemsSource 集合中。没有什么(也不应该)停止对第二行的修改。这有点道理。那么添加 SelectedItem 绑定会改变这种行为吗?
猜你喜欢
  • 1970-01-01
  • 2017-10-04
  • 2021-10-22
  • 2015-02-11
  • 2021-07-29
  • 1970-01-01
  • 2020-07-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多