1

WPF ListViewでアラームのリストを表示しようとしています。これを実現するために、Listbox をアラームのリストを含むプロパティにデータバインドしました。私は MVC プログラミング パラダイムを使用しているため、プロパティはコントローラーに配置され、ビューのデータ コンテキストはそのコントローラーに設定されます。

リストにアラームを追加したときに、ビューに新しいアラームが表示されないことに気付きました。いくつかの調査の結果、これを正しく行うには ObservableCollection クラスを使用する必要があることがわかりました。

ただし、アラームのリストを表示することだけが必要なわけではないため、リストの変数タイプを ObservableCollection に変更することはできません/したくありません。

ObservableCollection 型のプロパティを作成しようとしましたが、これも機能しません。プロパティにアラームを追加せず、List 型のままである変数に追加するので、これはごく普通のことです。

リストが更新されたときにプロパティに通知する方法、またはアラームを表示してプログラムの他の部分で使いやすくする他の/より良い方法はありますか?

編集:

私の回避策:PropertyChangedイベントの eventhandler にあるプロパティ FutureEvents をPropertyChangedアラーム変数からクリアすることで、イベントをトリガーします。

私のコード: class cMain { private static volatile cMain instance; プライベート静的オブジェクト syncRoot = new Object();

    ObservableCollection<Alarm> alarms;

    #region properties
    /// <summary>
    /// Returns the list of alarms in the model. Can't be used to add alarms, use the AddAlarm method
    /// </summary>
    public ObservableCollection<Alarm> Alarms
    {
        get
        {
            return alarms;
        }
    }

    /// <summary>
    /// Returns the ObservableCollection of future alarms in the model to be displayed by the vieuw.
    /// </summary>
    public ObservableCollection<Alarm> FutureAlarms
    {
        get
        {
            //Only show alarms in the future and alarm that recure in the future
            var fAlarms = new ObservableCollection<Alarm>(alarms.Where(a => a.DateTime > DateTime.Now || (a.EndRecurrency != null && a.EndRecurrency > DateTime.Now)));
            return fAlarms;
        }
    }

    /// <summary>
    /// Returns a desctription of the date and time of the next alarm
    /// </summary>
    public String NextAlarmDescription
    {
        get
        {
            if (alarms != null)
            {
                return alarms.Last().DateTimeDescription;
            }
            else
            {
                return null;
            }
        }
    }
    #endregion //properties


    #region public

    /// <summary>
    /// Returns the instance of the singleton
    /// </summary>
    public static cMain Instance
    {
        get
        {
            if (instance == null) //Check if an instance has been made before
            {
                lock (syncRoot) //Lock the ability to create instances, so this thread is the only thread that can excecute a constructor
                {
                    if (instance == null) //Check if another thread initialized while we locked the object class
                        instance = new cMain();
                }
            }
            return instance;
        }
    }

    /// <summary>
    /// Shows a new intance of the new alarm window
    /// </summary>
    public void NewAlarmWindow()
    {
        vNewAlarm newAlarm = new vNewAlarm();
        newAlarm.Show();
    }

    public void AddAlarm(Alarm alarm)
    {
        alarms.Add(alarm);            
    }

    public void RemoveAlarm(Alarm alarm)
    {
        alarms.Remove(alarm);
    }

    public void StoreAlarms()
    {
        mXML.StoreAlarms(new List<Alarm>(alarms));
    }

    #endregion //public

    #region private

    //Constructor is private because cMain is a singleton
    private cMain()
    {
        alarms = new ObservableCollection<Alarm>(mXML.GetAlarms());
        alarms.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(alarms_CollectionChanged);
    }

    private void alarms_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        FutureAlarms.Clear(); //Needed to trigger the CollectionChanged event of FutureAlarms
        StoreAlarms();
    }


    #endregion //private
}
4

5 に答える 5

0

すべての get で将来のアラームを使用して ObservableCollection を再作成する代わりに、リストが変更されたときにコレクションを直接更新してみてください。

public ObservableCollection<Alarm> FutureAlarms { get; private set;} // initialize in constructor

private void UpdateFutureAlarms() {
    fAlarms.Clear();
    fAlarms.AddRange(
        alarms.Where(
            a => a.DateTime > DateTime.Now 
                || (a.EndRecurrency != null && a.EndRecurrency > DateTime.Now)
        )
    )
}

//... somewhere else in the code... 

public void Foo () {
    // change the list
    alarms.Add(someAlarm);
    UpdateFutureAlarms();
}

変更時に発生するイベントがある場合は、UpdateFutureAlarms をイベント ハンドラーとして登録することもできListます。

于 2012-04-21T20:14:24.413 に答える
0

WPF では、コレクションに正しくバインドするには、アイテムがコレクションに追加またはコレクションから削除されるたびに発生する必要がある CollectionChanged イベントを持つ INotifyCollectionChanged を実装するようにバインドされたコレクションが必要です。

<T>そのため、すでにそのインターフェイスを実装している ObservableCollection クラスを使用することをお勧めします。また、使用する List<T>変数については、ObservableCollection によっても実装されるインターフェイス タイプ IList に切り替える方がよいと思います。<T>追加の利点として、ObservableCollection 通知を必要としないアプリケーションの部分は必要ありません。追加の参照を追加するか、Observable コレクションについて知ってください。

于 2012-04-22T22:04:39.117 に答える
0

プロパティをアラームに追加する

public bool Future 
{   get return (DateTime > DateTime.Now 
            || (EndRecurrency != null && EndRecurrency > DateTime.Now));  
}

アラームが更新されると、Future の NotifyPropertyChanged がすべて (または適切なサブセット) に対して呼び出されます。

次に、DataTrigger または CollectionViewSource フィルターを使用して非表示にします

<DataTrigger Binding="{Binding Path=Future, Mode=OneWay}" Value="False">
                                <Setter Property="Visibility" Value="Collapsed"/>
                            </DataTrigger> 

フィルタリングまたは非表示はプレゼンテーション レベルのようなものであるため、アラーム クラスとアラーム コレクション全体をビジネスおよびデータ レイヤー用に残す必要があります。

ObservableCollection は iList を実装しているため、互換性があるはずです。

現在のモデルでは、FurtureAlarms はリストである可能性があります。構文を短縮できます

 (alarms.Where(a => a.DateTime > DateTime.Now || (a.EndRecurrency != null && a.EndRecurrency > DateTime.Now))).toList(); 
于 2012-04-22T13:14:43.673 に答える
0

ObservableCollection<T>両方の既存のクラスを組み合わせてカプセル化しようとするのではなく、独自のクラスを派生させてそれを使用する方がよいでしょう。理由について:

  • ObservableCollection<T>まず、サポートするすべてのインターフェイスが既に実装されてList<T>いるため、実際に必要なメソッドを直接実装するだけでList<T>、WPF データバインディングが機能するため、痛みが大幅に軽減されます。
  • 2番目に、唯一の現実的な他の代替手段です。このINotifyPropertyChangedアプローチは実装が面倒です(効果的に書き換えますObservableCollection<T>)。または、バインディングを更新するためだけに変更のたびにコレクションを新しいコレクションに置き換えると、大きなコレクションでパフォーマンスが低下します。
于 2012-04-21T20:17:50.717 に答える
0

WPFPropertyChangedはインターフェイスのイベントに反応するINotifyPropertyChangedため、このインターフェイスを実装し、モデルのプロパティを変更するときにイベントを発生させる必要があります。これを行うと、ObservableCollection<T>まったく使用する必要はありません。ただし、プロパティが でListあり、項目の追加または削除のみを行った場合、WPF は同じリストであると認識し、何もしないことに注意してください。したがって、PropertyChangedイベントを発生させる前に、プロパティをリストの新しいインスタンスに設定する必要があります。これは次のように簡単に実行できます。

MyList.add(newItem);
MyList = new List<something>(MyList);
#raise the event
于 2012-04-21T19:59:34.983 に答える