WPF_ObservableCollectionの基本的な使い方と注意点


I.はじめに

項目コントロールは、内部に複数の項目が含まれることが多い ComboBox (ドロップダウン リスト ボックス) などの GUI プログラミングでよく使用されます。

オリジナルの開発に一部のグラフィック フレームワーク (Qt、WinForm) を使用する場合、多くの場合、インターフェイスの初期化フェーズ中に UI コントロールに直接アクセスし、リスト項目が追加されます。後で変更があった場合は、イベント トリガー ハンドラーを使用して追加または追加されます。項目を削除します。

この方法には一定の制限があり、リスト内のデータを更新して UI に表示したい場合は、コントロールに直接アクセスする必要があります。ComboBox のような単純な項目コントロールの場合は、 comboBox.AddItem を直接使用しますが、より複雑なコントロール (グリッドなど) の場合、新しい項目を追加するコードを記述するのはそれほど簡単ではありません。

WPF の ObservableCollection はコントロールをデータ ソースにバインドできるため、コントロールとデータ ソースの同期が維持されます。簡単に言うと、コントロールをデータ ソースにバインドした後は、UI 変更ロジックを意識することなく、データ ソースにデータを追加または削除するだけで済み、フロント エンドとバック エンドの分離が実現します。

二、観察可能なコレクション

Microsoft の公式ドキュメントで説明されているように、ObservableCollection は、コレクション内の項目が追加、削除、更新されたとき、またはリスト全体が更新されたときに通知 (バインドされたコントロールに変更を行うよう通知) を提供する動的データ コレクションです。

以下は私が書いたデモです。左側は、ItemSource がコレクション オブジェクトにバインドされている ComboBox です。右側にはいくつかのボタンがあり、ボタンをクリックしたコマンドによってコレクション オブジェクトが変更されます。次はメインの XAML コードです。インターフェースの
ここに画像の説明を挿入
:

   <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <ComboBox ItemsSource="{Binding Items}" Height="30" x:Name="comboBox"/>
        <StackPanel Grid.Column="1">
            <StackPanel.Resources>
                <Style TargetType="Button">
                    <Setter Property="Margin" Value="8"/>
                </Style>
            </StackPanel.Resources>
            <Button Content="添加一项" Command="{Binding AddItemCommand}"/>
            <Button Content="移除一项" Command="{Binding RemoveItemCommand}"/>
            <Button Content="更新项" Command="{Binding UpdateItemCommand}"/>
            <Button Content="替换整个列表" Command="{Binding ReplaceListCommand}"/>
            <Button Content="查看ItemSource绑定对象" Command="{Binding InspectObjectCommand}" CommandParameter="{Binding ElementName=comboBox}"/>
        </StackPanel>
    </Grid>

以下はバックグラウンド ViewModel のコードであり、主にコマンドです。

		public MainWindowViewModel()
        {
    
    
            _items = new ObservableCollection<string>()
            {
    
    
                "初始项1",
                "初始项2",
                "初始项3"
            };
        }

        private ObservableCollection<string> _items;
        public ObservableCollection<string> Items
        {
    
    
            get => _items;

        }

        private RelayCommand _addItemCommand;
        public RelayCommand AddItemCommand
        {
    
    
            get => _addItemCommand?? (_addItemCommand = new RelayCommand(() =>
            {
    
    
                _items.Add("新加项");
            }));
        }

        private RelayCommand _removeItemCommand = null;
        public RelayCommand RemoveItemCommand
        {
    
    
            get => _removeItemCommand ?? (_removeItemCommand = new RelayCommand(() =>
            {
    
    
                _items.Remove(_items.Last());
            }));
        }

        private RelayCommand _updateItemCommand;
        public RelayCommand UpdateItemCommand
        {
    
    
            get => _updateItemCommand ?? (_updateItemCommand = new RelayCommand(() =>
            {
    
    
                _items[0] = "更新项";
            }));
        }

        private RelayCommand _replaceListCommand;
        public RelayCommand ReplaceListCommand
        {
    
    
            get => _replaceListCommand ?? (_replaceListCommand = new RelayCommand(() =>
            {
    
    
                _items = new ObservableCollection<string>()
                {
    
    
                    "替换后的表"
                };
                MessageBox.Show("替换完毕");
            }));
        }

        private RelayCommand<object> _inspectObjectCommand;
        public RelayCommand<object> InspectObjectCommand
        {
    
    
            get => _inspectObjectCommand ?? (_inspectObjectCommand = new RelayCommand<object>((o) => 
            {
    
    
                ComboBox cb = o as ComboBox;
                MessageBox.Show(string.Format("HashCode:\nItemSource={0}\n_items={1}\n是否相等?{2}", 
                cb.ItemsSource.GetHashCode(), _items.GetHashCode(),cb.ItemsSource.Equals(_items)));
            }));
        }

このうち、追加、削除、変更の3つのコマンドはすぐにUIに反映できます。また、replace list コマンドは機能しません。[バインディング オブジェクトの表示] ボタンをクリックすると、ItemSource と背景​​の _items の hashCode が等しいことがわかり、また、equals と比較した場合も等しいことがわかります。

ここに画像の説明を挿入
次にボタンをクリックしてリスト全体を置き換え、バインディング オブジェクトを確認します。今回は、ItemSource の hashCode は変更されておらず、equals は背景の _items と等しくないことも示しています。
ここに画像の説明を挿入
バインドされたデータ ソースを置き換えても、ItemSource は変更されないことがわかります (これはナンセンスですが、アクセス コントロールの ItemsSource を変更することは可能です)。

次の記述は間違っています!
参照型とプリミティブ型


ここでの本質的な問題は、実際には参照型と基本型の割り当てです。

int a = 1;
int b = a;

基本的な型 (数値、文字列、ブール値など、通常はより単純) の割り当ては、過去の値をコピーすることです。
基本型を宣言すると、変数の値を格納するためにスタック メモリにスペースが開かれます。a の値を b に代入すると、a の値が b の空間にコピーされます。以下の図に示すように、それらの 2 つの値の保存は、異なる空間に独立して行われます。
ここに画像の説明を挿入
参照型データの変数名はスタックメモリに、値はヒープメモリに格納され、スタックメモリはヒープメモリ上の値を指す参照アドレスを提供します。これは C 言語のポインターに非常に似ている
ここに画像の説明を挿入
ため、実際には
ここに画像の説明を挿入
、ヒープ領域に別のメモリを開き、スタック メモリ内のアドレスと _items 変数の領域を指すことになります。それ。_items = new ObservableCollection(){ "替换后的表"}したがって、ItemSource は変更されません。

したがって、リスト全体を置き換える場合は、ItemSource の値を直接置き換える必要があります。

3. 結論

ObservableCollection の基本的な使用法は、上記のコード例に示されています。ViewModel 内のバインディング オブジェクトを置き換えても、実際には ItemsSource が置き換えられるわけではないことに注意してください。また、ObservableCollection はスレッドセーフではなく、ItemSource がバインドされた後、スレッドをまたいで (UI スレッドの外で) ObservableCollection を変更することはできません。この点については、別の記事で紹介します。

おすすめ

転載: blog.csdn.net/BadAyase/article/details/129119074