3

この問題はしばらく頭を悩ませており、プロジェクトの進行を妨げています。コントロールが ViewModel にバインドされた WPF XAML フォームを考えてみましょう。(データには Caliburn.Micro MVVM フレームワークと Entity Framework を使用しています)。Initialize()データベースからフォームのデータをロードし、PropertyChanged イベント ハンドラーを設定するために、メソッドがシェルによって呼び出されます。フォームに変更されたデータがあるかどうかを追跡するIsDirtyフラグがあります。IsDirtyデータが変更されたときに有効になるように、プロパティにバインドされた [保存] ボタンがあります。

// Sample code; forms have many controls....

// this is the property that the controls are bound to
public Entity BoundData { get; set; }

public void Initialize()
{
    // this is an example line where I query the database from the Entity Framework ObjectContext...
    BoundData = objectContext.DataTable.Where(entity => entity.ID == 1).SingleOrDefault();

    // this is to cause the form bindings to retrieve data from the BoundData entity
    NotifyOfPropertyChange("BoundData");

    // wire up the PropertyChanged event handler
    BoundData.PropertyChanged += BoundData_PropertyChanged;

    IsDirty = false;
}

void BoundData_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    IsDirty = true;
}

// implementation of the IsDirty flag
public bool IsDirty
{
    get
    {
        return _isDirty;
    }
    set
    {
        _isDirty = value;
        NotifyOfPropertyChange("IsDirty");
    }
}

問題は、メソッドが終了BoundData_PropertyChangedした後にフォームがデータベースから初期化されるために、イベント ハンドラーがヒットすることです。Initialize()そのためIsDirty、フォームがロードされたばかりで、ユーザーが何も変更していない場合でも、フラグは true に設定され、[保存] ボタンが有効になります。私は何が欠けていますか?確かにこれはよくある問題ですが、良い解決策を見つけることができませんでした。これは私の最初の MVVM プロジェクトなので、基本的な概念が欠けている可能性は十分にあります。

更新:明確にするために、問題は、すべてのバインディングの更新が完了したときに発生するイベントまたはコールバックにフックできる必要があるため、PropertyChanged イベント ハンドラーを接続できることだと思います。

4

4 に答える 4

0

このソリューションは確実ではないかもしれませんが、私が利用した限られたテストケースでは機能しています。

最初のエントリでは、EntityKey 値は null になります。これを確認してください。

/// <summary>
/// Log the invoice status change
/// </summary>
/// <param name="value">The value that the invoice status is changing to</param>
partial void OnInvoiceStatusValueChanging(string value)
{
    var newStatus = ConvertInvoiceStatus(value);
    if (this.EntityKey != null && InvoiceStatus != newStatus)
    {
        AddNewNote(string.Format("Invoice status changing from [{0}] to [{1}]", InvoiceStatus.GetDescription(), newStatus.GetDescription()));
    }
}

/// <summary>
/// Log the invoice status change
/// </summary>
partial void OnInvoiceStatusValueChanged()
{
    if (this.EntityKey != null)
        AddNewNote(string.Format("Invoice status changed to [{0}]", InvoiceStatus.GetDescription()));
}
于 2015-05-16T08:05:52.110 に答える
0

そのためにできることの 1 つは、変更をトリガーするプロパティを次のように設定することです。

    public virtual bool Prop1
    {
       get
       {
            return _prop1;
       }
       set
       {
            if (_prop1 != value)
            {
                _prop1 = value;
                NotifyOfPropertyChange("IsDirty");
            }
       }

そうすれば、値が実際に変更された場合にのみイベントがトリガーされ、冗長に設定されるだけではありません。もちろん、これは、値が実際に変更されていないことを前提としています。

于 2013-05-23T20:39:49.630 に答える
0

この質問は古くからあることは知っていますが、まったく同じ問題があり、それを理解するのに非常に苦労しました. 私はWPF/MVVMにかなり慣れていないので、おそらくGoogleや質問に正しいことを知らなかったでしょう. これが私の解決策です。うまくいけば、それは誰かを助けます。

元の投稿のものとほぼ同じ IsDirty フラグがあります。唯一の違いは、ビューのレンダリングが終了したときに false にリセットするコマンドを追加したことです。基本的なアイデアは、ビューがレンダリング/インスタンス化されたときにビューモデルに通知することから来ました

ビューで Loaded イベントを発生させます。

<UserControl x:Class="MyUserControl"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity">
<i:Interaction.Triggers>
    <i:EventTrigger EventName="Loaded">
        <i:InvokeCommandAction Command="{Binding Path=OnLoadedCommand}"/>
    </i:EventTrigger>
</i:Interaction.Triggers>

次に、ビュー モデルで、イベントが発生したときに IsDirty フラグを false に設定します。

public ICommand OnLoadedCommand { get; private set; }

// Constructor
public MyUserControlViewModel()
{
    OnLoadedCommand = new DelegateCommand(OnLoaded);
}

public void OnLoaded()
{
  // Ignore any PropertyChanged events that fire 
  // before the UserControl is rendered.  
   IsDirty = false; 
}
于 2016-04-05T14:41:15.503 に答える