16

Model-View-ViewModel パターンを使用した WPF アプリを作成しました。
私のViewModelには、アイテムのリストを保持するためのListCollectionViewがあります。
この ListCollectionView は、ビューの ListBox にバインドされています。

<ListBox Grid.Row="1" ItemsSource="{Binding Useragents}" SelectionMode="Multiple"/>

ListBox には SelectionMode=Multiple があるため、一度に複数の項目を選択できます。ここで、ViewModel はどの項目が選択されているかを知る必要があります。

問題は、View-Model-ViewModel パターンでは ViewModel が View にアクセスできないため、どの項目が選択されているかを ListBox に問い合わせることができないことです。私が持っているのは ListCollectionView だけですが、そこで選択されているアイテムを見つける方法が見つかりません。

では、ListBox で選択されている項目を見つけるにはどうすればよいでしょうか。または、これを達成するためのトリック (おそらく、何かをブール値の 'IsSelected' アイテムにバインドしますか?しかし、何を?どのように?)

たぶん、このパターンを使用している人もここで私を助けることができますか?

4

9 に答える 9

12

IsSelectedの概念があり、標準のWPFバインディングアーキテクチャを使用してビューでそれを表す実際のListBoxItemのIsSelectedプロパティにバインドされているViewModelを作成する必要があります。

次に、ViewModelを認識しているが、特定のビューで表されているという事実を認識していないコードで、そのプロパティを使用して、デザイナーがモデルでどのように表現するかを選択したかどうかに関係なく、モデルから実際に選択されたアイテムを見つけることができます。意見。

于 2009-01-16T21:46:20.583 に答える
9

PRISM MVVM 参照実装には、Prism4\MVVM RI\MVVM.Client\Views\MultipleSelectionView.xaml で使用される SynchronizeSelectedItems と呼ばれる動作があり、チェックされたアイテムを ViewModel プロパティと同期しますSelections

        <ListBox Grid.Column="0" Grid.Row="1" IsTabStop="False" SelectionMode="Multiple"
                 ItemsSource="{Binding Question.Range}" Margin="5">

            <ListBox.ItemContainerStyle>
                <!-- Custom style to show the multi-selection list box as a collection of check boxes -->
                <Style TargetType="ListBoxItem">
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="ListBoxItem">
                                <Grid Background="Transparent">
                                    <CheckBox IsChecked="{Binding IsSelected, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}" 
                                              IsHitTestVisible="False" IsTabStop="True"
                                              AutomationProperties.AutomationId="CheckBoxAutomationId">
                                        <ContentPresenter/>
                                    </CheckBox>
                                </Grid>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </ListBox.ItemContainerStyle>
            <i:Interaction.Behaviors>
                <!-- Custom behavior that synchronizes the selected items with the view models collection -->
                <Behaviors:SynchronizeSelectedItems Selections="{Binding Selections}"/>
            </i:Interaction.Behaviors>
        </ListBox>

http://compositewpf.codeplex.com/にアクセスして、すべてを取得するか、これを使用します。

//===================================================================================
// Microsoft patterns & practices
// Composite Application Guidance for Windows Presentation Foundation and Silverlight
//===================================================================================
// Copyright (c) Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===================================================================================
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious.  No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
//===================================================================================
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Diagnostics.CodeAnalysis;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interactivity;

namespace MVVM.Client.Infrastructure.Behaviors
{
    /// <summary>
    /// Custom behavior that synchronizes the list in <see cref="ListBox.SelectedItems"/> with a collection.
    /// </summary>
    /// <remarks>
    /// This behavior uses a weak event handler to listen for changes on the synchronized collection.
    /// </remarks>
    public class SynchronizeSelectedItems : Behavior<ListBox>
    {
        public static readonly DependencyProperty SelectionsProperty =
            DependencyProperty.Register(
                "Selections",
                typeof(IList),
                typeof(SynchronizeSelectedItems),
                new PropertyMetadata(null, OnSelectionsPropertyChanged));

        private bool updating;
        private WeakEventHandler<SynchronizeSelectedItems, object, NotifyCollectionChangedEventArgs> currentWeakHandler;

        [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly",
            Justification = "Dependency property")]
        public IList Selections
        {
            get { return (IList)this.GetValue(SelectionsProperty); }
            set { this.SetValue(SelectionsProperty, value); }
        }

        protected override void OnAttached()
        {
            base.OnAttached();

            this.AssociatedObject.SelectionChanged += this.OnSelectedItemsChanged;
            this.UpdateSelectedItems();
        }

        protected override void OnDetaching()
        {
            this.AssociatedObject.SelectionChanged += this.OnSelectedItemsChanged;

            base.OnDetaching();
        }

        private static void OnSelectionsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var behavior = d as SynchronizeSelectedItems;

            if (behavior != null)
            {
                if (behavior.currentWeakHandler != null)
                {
                    behavior.currentWeakHandler.Detach();
                    behavior.currentWeakHandler = null;
                }

                if (e.NewValue != null)
                {
                    var notifyCollectionChanged = e.NewValue as INotifyCollectionChanged;
                    if (notifyCollectionChanged != null)
                    {
                        behavior.currentWeakHandler =
                            new WeakEventHandler<SynchronizeSelectedItems, object, NotifyCollectionChangedEventArgs>(
                                behavior,
                                (instance, sender, args) => instance.OnSelectionsCollectionChanged(sender, args),
                                (listener) => notifyCollectionChanged.CollectionChanged -= listener.OnEvent);
                        notifyCollectionChanged.CollectionChanged += behavior.currentWeakHandler.OnEvent;
                    }

                    behavior.UpdateSelectedItems();
                }
            }
        }

        private void OnSelectedItemsChanged(object sender, SelectionChangedEventArgs e)
        {
            this.UpdateSelections(e);
        }

        private void UpdateSelections(SelectionChangedEventArgs e)
        {
            this.ExecuteIfNotUpdating(
                () =>
                {
                    if (this.Selections != null)
                    {
                        foreach (var item in e.AddedItems)
                        {
                            this.Selections.Add(item);
                        }

                        foreach (var item in e.RemovedItems)
                        {
                            this.Selections.Remove(item);
                        }
                    }
                });
        }

        private void OnSelectionsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            this.UpdateSelectedItems();
        }

        private void UpdateSelectedItems()
        {
            this.ExecuteIfNotUpdating(
                () =>
                {
                    if (this.AssociatedObject != null)
                    {
                        this.AssociatedObject.SelectedItems.Clear();
                        foreach (var item in this.Selections ?? new object[0])
                        {
                            this.AssociatedObject.SelectedItems.Add(item);
                        }
                    }
                });
        }

        private void ExecuteIfNotUpdating(Action execute)
        {
            if (!this.updating)
            {
                try
                {
                    this.updating = true;
                    execute();
                }
                finally
                {
                    this.updating = false;
                }
            }
        }
    }
}
于 2011-12-03T17:32:23.200 に答える
1

私にとって最良の答えは、MVVMの原則を少し破ることです。

コードビハインドで1.viewModelをインスタンス化します2.イベントハンドラーSelectionChangedを追加します3.選択したアイテムを反復処理し、各アイテムをviewModelのリストに追加します

ViewModel viewModel = new ViewModel();

viewModel.SelectedModules = new ObservableCollection<string>();

foreach (var selectedModule in listBox1.SelectedItems)
{
    viewModel.SelectedModules.Add(selectedModule.ToString());
}
于 2012-09-10T09:57:03.033 に答える
1

Drew Marsh のソリューションは非常にうまく機能するので、お勧めします。そして、私には別の解決策があります!

Model View ViewModel はPassive Viewです。プレゼンテーション モデルを使用して、WPF と結合せずにプレゼンテーションの一部のデータにアクセスすることもできます (このパターンはPRISMのStocktraderの例で使用されています)。

于 2009-03-26T20:11:08.397 に答える
1

Josh Smith によるこのブログ投稿を参照してくださいグループ化された ICollectionView にバインドするときに最初に選択された項目

于 2009-03-26T19:56:01.317 に答える
1

ドリュー・マーシュの答えは、リストが小さい場合は問題ありません。リストが大きい場合、選択したすべてのアイテムを見つけるためのパフォーマンスヒットは厄介になる可能性があります! 私のお気に入りの解決策は、選択した項目を含む ObservableCollection にバインドする ListBox に添付プロパティを作成することです。次に、添付プロパティを使用して、アイテムの SelectionChanged イベントをサブスクライブし、コレクションからアイテムを追加/削除します。

于 2009-07-02T20:05:42.903 に答える
0

これは、ViewModelがIViewインターフェイスを介してビューにアクセスできるView-Model-ViewModelパターンの別のバリアントです。

WPFバインディングを使用できず、コード内でViewとViewModelの間で状態を同期する方法が必要なシナリオが非常に多く発生しました。

これを行う方法を次に示します。

WPFアプリケーションフレームワーク(WAF)

于 2009-07-02T19:09:03.253 に答える
0

ここを見てください http://blog.functionalfun.net/2009/02/how-to-databind-to-selecteditems.html

于 2009-10-06T13:33:48.210 に答える
0

David Rogers のソリューションは素晴らしく、以下の関連する質問で詳しく説明されています。

複数選択リストボックスの SelectedItems を ViewModel のコレクションと同期する

于 2010-11-18T03:10:02.397 に答える