バージョン

xamGrid コントロールで CRUD 操作の元に戻す/やり直しを実装

トピックの概要

目的

このトピックでは、xamGrid™ コントロールで Infragistics Undo/Redo Framework™ を使用する方法を紹介します。

前提条件

以下の表に、このトピックを理解するための前提条件として求められるトピックをリストします。

トピック 目的

このトピックは、UndoManager クラスの重要なプロパティおよびメソッドをいくつかリストします。

このトピックは、UndoHistoryItem クラスのプロパティおよびメソッドをリストします。

このトピックは、ObservableCollectionExtendedWithUndo クラスの重要なプロパティおよびメソッドをいくつかリストします。

xamGrid コントロールでエンドユーザーによる元に戻す/やり直し操作を実装

概要

Infragistics Undo/Redo Framework はエンドユーザーによる元に戻す/やり直し操作のサポートを提供します。以下の例は、xamGrid コントロールと共にこの機能を実装できる方法を示します。新しい行を追加、セルの編集、行の削除などの一般的な操作を元に戻すことができます。

プレビュー

以下のスクリーンショットは最終結果のプレビューです。

UndoRedo xamGrid.png

要件

この手順を実行するには、以下が必要です。

  • 以下の NuGet パッケージ参照を追加します。

    • Infragistics.WPF.Undo

    • Infragistics.WPF.Controls.Grids.XamGrid

  • 追加された Data.xml ファイル (アプリケーションで提供される)

NuGet フィードのセットアップと NuGet パッケージの追加の詳細については、NuGet フィード ドキュメントを参照してください。

手順

以下の手順は、xamGrid コントロールでエンドユーザーによる元に戻す/やり直し操作を実装する方法を提供します。

  1. Product という名前の元に戻す/やり直し操作をサポートするデータ モデルを作成します。

Product データ モデルは、INotifyPropertyChanged を実装する ObservableModel クラスを継承します。

プロパティの変更ごとに、UndoUnit インスタンスが作成されます。

詳細については、コード例: Product クラスを参照してください。

  1. ProductViewModel という名前の元に戻す/やり直し操作をサポートする viewmodel クラスを作成します。

このクラスで、XML データは読み込まれ、ObservableCollectionExtendedWithUndo クラスから派生する ProductCollection に保存されます。

詳細については、コード例: ProductViewModel クラスを参照してください。

  1. ProductCollection という名前の ObservableCollectionExtendedWithUndo クラスから派生するコレクション クラスを作成します。

このクラスでは、InsertItem および RemoveItem メソッドがオーバーライドされ、コレクションは UndoManager インスタンスに関連付けられます。

詳細については、コード例: ProductCollection クラスを参照してください。

  1. DataContext プロパティを設定します。

ProductViewModel をインスタンス化して、DataContext プロパティに設定します。

C# の場合:

this.DataContext = new ProductViewModel();

Visual Basic の場合:

Me.DataContext = New ProductViewModel()
  1. 元に戻す/やり直し履歴項目を表示する xamMenu コントロールを追加して、元に戻す/やり直しコマンドを実行します。

履歴項目を表示する xamMenu コントロールを追加して、元に戻す/やり直しコマンドを使用します。

詳細については、コード例: xamMenu コントロールで元に戻す/やり直し履歴を表示を参照してください。

  1. 行の追加と削除およびセルの編集を可能にする xamGrid コントロールを追加します。

新しい行の追加、編集および削除機能を有効にして xamGrid コントロールを追加します。

詳細については、コード例: ページに xamGrid コントロールを追加を参照してください。

  1. xamGrid コントロールの RowAdding イベントを処理します。

RowAdding イベント ハンドラーで、複数の操作がトランザクションを使用する 1 つの操作に結合されます。このように、ユーザーは新しい行の追加を 1 つの操作として元に戻す/やり直すことができます。

詳細については、コード例: xamGrid RowAdding イベントを処理を参照してください。

コード例

概要

以下の表には、このトピックのコード例が示されています。

説明

プロパティの変更の元に戻す/やり直しをサポートするデータ モデル クラス。

コレクションでの変更の記録をサポートする Viewmodel クラス。

ObservableCollectionExtendedWithUndo クラスからの派生クラス。

元に戻す/やり直し機能が実装された xamMenu コントロールを追加するための XAML コード。

xamGrid コントロールを追加するための XAML コード。

RowAdding イベントを処理します。

コード例: Product クラス

説明

プロパティの変更の元に戻す/やり直しをサポートするデータ モデル クラス。

コード

C# の場合:

public class Product : ObservableModel
{
    private object _owner;
    internal object Owner
    {
        get { return _owner; }
        set { _owner = value; }
    }
    private int _productID;
    public int ProductID
    {
        get { return _productID; }
        set { this.SetField(ref _productID, value, "ProductID"); }
    }
    private string _productName;
    public string ProductName
    {
        get { return _productName; }
        set { this.SetField(ref _productName, value, "ProductName"); }
    }
    private decimal _unitPrice;
    public decimal UnitPrice
    {
        get { return _unitPrice; }
        set { this.SetField(ref _unitPrice, value, "UnitPrice"); }
    }
    private int _unitsInStock;
    public int UnitsInStock
    {
        get { return _unitsInStock; }
        set { this.SetField(ref _unitsInStock, value, "UnitsInStock"); }
    }
    private int _unitsOnOrder;
    public int UnitsOnOrder
    {
        get { return _unitsOnOrder; }
        set { this.SetField(ref _unitsOnOrder, value, "UnitsOnOrder"); }
    }
    protected bool SetField<T>(ref T member, T newValue, string propertyName)
    {
        if (EqualityComparer<T>.Default.Equals(member, newValue))
            return false;
        if (_owner != null)
            UndoManager.FromReference(_owner).AddPropertyChange(this, propertyName, member, newValue);
        member = newValue;
        this.NotifyPropertyChanged(propertyName);
        return true;
    }
}
public class ObservableModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected void NotifyPropertyChanged(String info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }
}

Visual Basic の場合:

Public Class Product
    Inherits ObservableModel
    Private _owner As Object
    Friend Property Owner() As Object
        Get
            Return _owner
        End Get
        Set(value As Object)
            _owner = value
        End Set
    End Property
    Private _productID As Integer
    Public Property ProductID() As Integer
        Get
            Return _productID
        End Get
        Set(value As Integer)
            Me.SetField(_productID, value, "ProductID")
        End Set
    End Property
    Private _productName As String
    Public Property ProductName() As String
        Get
            Return _productName
        End Get
        Set(value As String)
            Me.SetField(_productName, value, "ProductName")
        End Set
    End Property
    Private _unitPrice As Decimal
    Public Property UnitPrice() As Decimal
        Get
            Return _unitPrice
        End Get
        Set(value As Decimal)
            Me.SetField(_unitPrice, value, "UnitPrice")
        End Set
    End Property
    Private _unitsInStock As Integer
    Public Property UnitsInStock() As Integer
        Get
            Return _unitsInStock
        End Get
        Set(value As Integer)
            Me.SetField(_unitsInStock, value, "UnitsInStock")
        End Set
    End Property
    Private _unitsOnOrder As Integer
    Public Property UnitsOnOrder() As Integer
        Get
            Return _unitsOnOrder
        End Get
        Set(value As Integer)
            Me.SetField(_unitsOnOrder, value, "UnitsOnOrder")
        End Set
    End Property
    Protected Function SetField(Of T)(ByRef member As T, newValue As T, propertyName As String) As Boolean
        If EqualityComparer(Of T).[Default].Equals(member, newValue) Then
            Return False
        End If
        If _owner IsNot Nothing Then
            UndoManager.FromReference(_owner).AddPropertyChange(Me, propertyName, member, newValue)
        End If
        member = newValue
        Me.NotifyPropertyChanged(propertyName)
        Return True
    End Function
End Class
Public Class ObservableModel
    Implements INotifyPropertyChanged
    Public Event PropertyChanged(ByVal sender As Object, ByVal e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
    Protected Overridable Sub NotifyPropertyChanged(ByVal propertyName As String)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
    End Sub
End Class

コード例: ProductViewModel クラス

説明

コレクションでの変更の記録をサポートする Viewmodel クラス。

コード

C# の場合:

public class ProductViewModel : ObservableModel
{
    private ProductCollection _products;
    private UndoManager _undoManager;
    public UndoManager UndoManager
    {
        get { return _undoManager; }
    }
    private CollectionViewSource _viewSource;
    public ICollectionView Products
    {
        get { return _viewSource.View; }
    }
    public ProductViewModel()
    {
        _undoManager = new UndoManager();
        _undoManager.RegisterReference(this);
        _products = new ProductCollection(_undoManager);
        // データを読み込み中に履歴で UndoUnits の記録を中断します
        UndoManager.Suspend();
        try
        {
            this.LoadXMLData();
        }
        finally
        {
            // 履歴で記録を再開します
            UndoManager.Resume();
        }
        _viewSource = new CollectionViewSource();
        _viewSource.Source = _products;
        this.Products.MoveCurrentToFirst();
    }
    private void LoadXMLData()
    {
        XDocument doc = XDocument.Load("Data.xml");
        var data = (from d in doc.Descendants("Product")
                    select new Product
                    {
                        ProductID = this.GetInt(d.Element("ProductID").Value),
                        ProductName = d.Element("ProductName").Value,
                        UnitPrice = this.GetDecimal(d.Element("UnitPrice").Value),
                        UnitsInStock = this.GetInt(d.Element("UnitsInStock").Value),
                        UnitsOnOrder = this.GetInt(d.Element("UnitsOnOrder").Value)
                    });
        foreach (var productItem in data)
        {
            _products.Add(productItem);
        }
    }
    private int GetInt(string element)
    {
        int value = 0;
        if (element != null)
            int.TryParse(element, out value);
        return value;
    }
    private decimal GetDecimal(string element)
    {
        decimal value = 0m;
        if (element != null)
            decimal.TryParse(element, out value);
        return value;
    }
}

Visual Basic の場合:

Public Class ProductViewModel
    Inherits ObservableModel
    Private _products As ProductCollection
    Private _undoManager As UndoManager
    Public ReadOnly Property UndoManager() As UndoManager
        Get
            Return _undoManager
        End Get
    End Property
    Private _viewSource As CollectionViewSource
    Public ReadOnly Property Products() As ICollectionView
        Get
            Return _viewSource.View
        End Get
    End Property
    Public Sub New()
        _undoManager = New UndoManager()
        _undoManager.RegisterReference(Me)
        _products = New ProductCollection(_undoManager)
        UndoManager.Suspend()
        Try
            Me.LoadXMLData()
        Finally
            UndoManager.[Resume]()
        End Try
        _viewSource = New CollectionViewSource()
        _viewSource.Source = _products
        Me.Products.MoveCurrentToFirst()
    End Sub
    Private Sub LoadXMLData()
        Dim doc As XDocument = XDocument.Load("Data.xml")
        Dim data = (From d In doc.Descendants("Product")
                    Select New Product With
                    {
                        .ProductID = Me.GetInt(d.Element("ProductID").Value),
                        .ProductName = d.Element("ProductName").Value,
                        .UnitPrice = Me.GetDecimal(d.Element("UnitPrice").Value),
                        .UnitsInStock = Me.GetInt(d.Element("UnitsInStock").Value),
                        .UnitsOnOrder = Me.GetInt(d.Element("UnitsOnOrder").Value)
                    })
        For Each productItem In data
            _products.Add(productItem)
        Next
    End Sub
    Private Function GetInt(element As String) As Integer
        Dim value As Integer = 0
        If element IsNot Nothing Then
            Integer.TryParse(element, value)
        End If
        Return value
    End Function
    Private Function GetDecimal(element As String) As Decimal
        Dim value As Decimal = 0D
        If element IsNot Nothing Then
            Decimal.TryParse(element, value)
        End If
        Return value
    End Function
End Class

例: ProductCollection クラス

説明

Undo/Redo Framework ObservableCollectionExtendedWithUndo クラスからの派生クラス

コード

C# の場合:

public class ProductCollection : ObservableCollectionExtendedWithUndo<Product>
{
    public ProductCollection(UndoManager undoManager)
        : base(undoManager)
    {
        undoManager.RegisterReference(this);
    }
    protected override void InsertItem(int index, Product item)
    {
        item.Owner = this;
        base.InsertItem(index, item);
    }
    protected override void RemoveItem(int index)
    {
        Product item = this[index];
        item.Owner = null;
        base.RemoveItem(index);
    }
}

Visual Basic の場合:

Public Class ProductCollection
    Inherits ObservableCollectionExtendedWithUndo(Of Product)
    Public Sub New(undoManager As UndoManager)
        MyBase.New(undoManager)
        undoManager.RegisterReference(Me)
    End Sub
    Protected Overrides Sub InsertItem(index As Integer, item As Product)
        item.Owner = Me
        MyBase.InsertItem(index, item)
    End Sub
    Protected Overrides Sub RemoveItem(index As Integer)
        Dim item As Product = Me(index)
        item.Owner = Nothing
        MyBase.RemoveItem(index)
    End Sub
End Class

コード例: xamMenu コントロールで元に戻す/やり直し履歴を表示

説明

元に戻す/やり直し機能が実装された xamMenu コントロールを追加するための XAML コード。

xamMenu 項目は元に戻すことができる履歴項目を表します。

コード

XAML の場合:

Code
<ig:XamMenu Grid.Row="0">
  <ig:XamMenu.Resources>
    <DataTemplate x:Key="historyItemTemplate">
      <TextBlock Text="{Binding LongDescription}" />
    </DataTemplate>
    <DataTemplate x:Key="undoRedoMenuItem">
      <ig:XamMenuItem>
        <ig:Commanding.Command>
          <ig:UndoManagerCommandSource CommandType="UndoRedoHistoryItem"
                                       ParameterBinding="{Binding}"
                                       EventName="Click" />
        </ig:Commanding.Command>
      </ig:XamMenuItem>
    </DataTemplate>
  </ig:XamMenu.Resources>
  <ig:XamMenuItem Header="Undo"
                  IsEnabled="{Binding UndoManager.CanUndo}"
                  ItemsSource="{Binding UndoManager.UndoHistory}"
                  DefaultItemsContainer="{StaticResource undoRedoMenuItem}"
                  ItemTemplate="{StaticResource historyItemTemplate}">
    <ig:Commanding.Command>
      <ig:UndoManagerCommandSource EventName="SubmenuOpened"
                                   CommandType="PreventMerge"
                                   ParameterBinding="{Binding UndoManager}" />
    </ig:Commanding.Command>
  </ig:XamMenuItem>
  <ig:XamMenuItem Header="Redo"
                  IsEnabled="{Binding UndoManager.CanRedo}"
                  ItemsSource="{Binding UndoManager.RedoHistory}"
                  DefaultItemsContainer="{StaticResource undoRedoMenuItem}"
                  ItemTemplate="{StaticResource historyItemTemplate}" />
</ig:XamMenu>

コード例: ページに xamGrid コントロールを追加

説明

以下のコードは、新しい行の追加、セルの編集、行の削除など、CRUD 操作を可能にする xamGrid コントロールの追加について説明します。

xamGrid コントロール ItemsSource プロパティは、元に戻す/やり直しに対して有効に設定された ICollectionView データ コレクションを含む、ProductViewModel Products メンバーにバインドされます。

Delete キーを押したときにすべての選択行を削除するために、xamGrid DeleteKeyAction プロパティを DeleteSelectedRows に設定します。

新しい行の追加、セルの編集、複数行の選択、行の削除およびページングなどの、データを操作するために必要とされるすべての主要な機能は有効になっています。

データでの操作 (作成、編集および削除) は、Undo/Redo Framework で元に戻すことができます。

コード

XAML の場合:

<ig:XamGrid x:Name="dataGrid" Grid.Row="1"
            AutoGenerateColumns="True"
            ItemsSource="{Binding Products}"
            DeleteKeyAction="DeleteSelectedRows"
            RowAdding="dataGrid_RowAdding"
            ColumnWidth="*">
  <!-- 新しい行の追加を有効にします -->
  <ig:XamGrid.AddNewRowSettings>
    <ig:AddNewRowSettings AllowAddNewRow="Top" />
  </ig:XamGrid.AddNewRowSettings>
  <!-- セルの編集をダブルクリックで有効にします -->
  <ig:XamGrid.EditingSettings>
    <ig:EditingSettings AllowEditing="Cell"
                        IsMouseActionEditingEnabled="DoubleClick" />
  </ig:XamGrid.EditingSettings>
  <!-- 行セレクターを追加します -->
  <ig:XamGrid.RowSelectorSettings>
    <ig:RowSelectorSettings Visibility="Visible" />
  </ig:XamGrid.RowSelectorSettings>
  <!-- 複数行選択を有効にします -->
  <ig:XamGrid.SelectionSettings>
    <ig:SelectionSettings RowSelection="Multiple" />
  </ig:XamGrid.SelectionSettings>
  <!-- ページャーを追加します -->
  <ig:XamGrid.PagerSettings>
    <ig:PagerSettings AllowPaging="Bottom" PageSize="10" />
  </ig:XamGrid.PagerSettings>
</ig:XamGrid>

コード例: xamGrid RowAdding イベントを処理

説明

行を xamGrid に追加することは、いくつかのエンドユーザーのアクションを伴います (たとえば - Add new 行のセルに値を入力)。元に戻すことができる 1 つのアクションにするために、トランザクションとこれらのアクションを合体する必要があります。

コード

C# の場合:

private void dataGrid_RowAdding(object sender, Infragistics.Controls.Grids.CancellableRowAddingEventArgs e)
{
    string description = "";
    string detailedDescription = "Add New Row";
    // いくつかの undo 単位を 1 つの項目にグループ化します
    UndoTransaction transaction = this._undoManager.StartTransaction(description, detailedDescription);
    new DispatcherSynchronizationContext().Post(new SendOrPostCallback(CommitTransaction), transaction);
}
private void CommitTransaction(object obj)
{
    if (obj != null)
    {
        UndoTransaction transaction = obj as UndoTransaction;
        if (!transaction.IsClosed)
        {
            transaction.Commit();
        }
    }
}
private UndoManager _undoManager
{
    get { return ((ProductViewModel)this.DataContext).UndoManager; }
}

Visual Basic の場合:

Private Sub dataGrid_RowAdding(sender As Object, e As Infragistics.Controls.Grids.CancellableRowAddingEventArgs)
    Dim description As String = ""
    Dim detailedDescription As String = "Add New Row"
    ' いくつかの undo 単位を 1 つの項目にグループ化します
    Dim transaction As UndoTransaction = Me._undoManager.StartTransaction(description, detailedDescription)
    Dim sendOrPostCallback As New SendOrPostCallback(AddressOf CommitTransaction)
    Dim dispatcher As New DispatcherSynchronizationContext()
    dispatcher.Post(sendOrPostCallback, transaction)
End Sub
Private Sub CommitTransaction(obj As Object)
    If obj IsNot Nothing Then
        Dim transaction As UndoTransaction = TryCast(obj, UndoTransaction)
        If Not transaction.IsClosed Then
            transaction.Commit()
        End If
    End If
End Sub
Private ReadOnly Property _undoManager() As UndoManager
    Get
        Return DirectCast(Me.DataContext, ProductViewModel).UndoManager
    End Get
End Property

以下のトピックでは、このトピックに関連する情報を提供しています。

トピック 目的

このトピックは、Infragistics Undo/Redo Framework™ で使用可能な主要なクラス、プロパティおよびメソッドの概要を提供します。