Kohnarik
Kohnarik

Reputation: 377

Write selected items into ObservableCollection

I have a simple ListView in my View:

<ListView x:Name="ObjectListView" HorizontalAlignment="Left" Height="105" Margin="253,268,0,0" VerticalAlignment="Top" Width="163" SelectionChanged="ObjectListView_SelectionChanged">
     <TextBox Width="100"/>
     <Button Width="100" Content="Test"/>
     <Label Width="100" Content="Label"/>
</ListView>

In my ViewModel, I have an ObservableCollection, which does a few (to this question irrelevant) things:

public ObservableCollection<Object> ObjectCollection
    {
        get { return _conversionCollection; }
        set
        {
            if (_conversionCollection != value)
            {
                _conversionCollection = value;
                RaisePropertyChanged("ObjectList");
            }
        }
    }

Ultimatively, those Objects naturally land in the Model(edit: through the help of the RaisePropertyChanged and a few functions), but my problem here is the connection between View and ViewModel.

Currently, I have solved it like this (In the View's code-behind):

public MainWindow()
{
    InitializeComponent();
    _viewModel = (RibbonViewModel)base.DataContext;
}

private void ObjectListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        _viewModel.ObjectCollection.Clear();
        foreach(Object item in ObjectListView.SelectedItems)
        {
            _viewModel.ObjectCollection.Add(item);
        }
    }

This isn't all too beautiful, so I'd like to do it properly.

Upvotes: 0

Views: 1669

Answers (4)

AnjumSKhan
AnjumSKhan

Reputation: 9827

<ListView x:Name="Lst" SelectedItem="{Binding ChosenItem}"> ... </ListView>

This will bring chosen item in the ChosenItem DP, in its setter you can simply add the item to the collection.

private static void ChosenItemPropertyCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {            
            controls.Add(e.NewValue.ToString());
        }

Upvotes: -1

Liero
Liero

Reputation: 27360

Basicaly, you need to databind ViewModel.ObjectCollection to ListView.SelectedItems.

WPF controls does not support this by default, however you could extend the control to support this feaure. One way of extending controls is behaviors. There are two types of behaviors:

  1. System.Windows.Interactivity Behaviors.
  2. Behaviors implemented as attached property.

    <ListView my:ListViewExtension.SelectedItems="{Binding ObjectCollection}" />
    

I've decided to use second. Basically you create custom attached property and in DependencyPropertyChanged callback you can "inject" any code to the framework element by attaching to the element' eventhandlers.

public static class ListViewExtentions
{
    public static readonly DependencyProperty SelectedItemsProperty = DependencyProperty.RegisterAttached(
        "SelectedItems", typeof (IList), typeof (ListViewExtentions), new PropertyMetadata(SelectedItems_PropertyChanged));
    public static void SetSelectedItems(DependencyObject element, IList value)
    {
        element.SetValue(SelectedItemsProperty, value);
    }
    public static IList GetSelectedItems(DependencyObject element)
    {
        return (IList)element.GetValue(SelectedItemsProperty);
    }


    private static void SelectedItems_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var listView = (ListView) d;
        SynchronizeCollections(listView.SelectedItems, (IList)e.NewValue);

        listView.SelectionChanged += (sender, args) =>
        {
            var listviewSelectedItems = ((ListView) sender).SelectedItems;
            var viewmodelCollection = GetSelectedItems((ListView) sender);
            SynchronizeCollections(listviewSelectedItems, viewmodelCollection);
        };
    }

    private static void SynchronizeCollections(IList source, IList target)
    {
        var oldItems = target.OfType<object>().Except(source.OfType<object>()).ToArray();
        var newItems = source.OfType<object>().Except(target.OfType<object>()).ToArray();

        foreach (var oldItem in oldItems) target.Remove(oldItem);
        foreach (var newItem in newItems) target.Add(newItem);
    }
}

you can use propa code snippet to generate attached property

TIP: I recommend you to rename ViewModel.ObjectCollection to ViewModel.SelectedItems, because now it is misleading.

Upvotes: 1

MRebai
MRebai

Reputation: 5474

You just need to bind your ListView SelectedItems to your ObservableCollection, and thus your collection will be updated automatically using the binding. Actually you don't need to add he event to your code behind.

<ListView x:Name="ObjectListView" HorizontalAlignment="Left" Height="105" Margin="253,268,0,0" VerticalAlignment="Top" Width="163" SelectedItems="{Binding Path=ObjectCollection}">
     <TextBox Width="100"/>
     <Button Width="100" Content="Test"/>
     <Label Width="100" Content="Label"/>
</ListView>

EDIT

To achieve what you want try to use Interaction triggers as below

Add below xmlns to your xaml

xmlns:i="http://schemas.microsoft.com/expression//2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"

Don't forget to add that reference :

Microsoft.Expression.Interactions System.Windows.Interactivity

<ListView x:Name="ObjectListView" HorizontalAlignment="Left" Height="105" Margin="253,268,0,0" VerticalAlignment="Top" Width="163">
         <TextBox Width="100"/>
         <Button Width="100" Content="Test"/>
         <Label Width="100" Content="Label"/>
         <i:Interaction.Triggers>
            <i:EventTrigger EventName="SelectionChanged">
            <ei:ChangePropertyAction TargetObject="{Binding Mode=OneWay}" PropertyName="SelectedItems" Value="{Binding Path=SelectedItems,     ElementName=ObjectListView}"/>
         </i:EventTrigger>
        </i:Interaction.Triggers>
    </ListView>

ViewModel

public System.Collections.IList SelectedItems {
    get {
      return ObjectCollection;
    }
    set {
      ObjectCollection.Clear();
      foreach (var model in value) {
        ObjectCollection.Add(model);
      }
    }
  }

Upvotes: 3

A191919
A191919

Reputation: 3462

Maybe this example will help you.

<Grid>
    <ListView  HorizontalAlignment="Left" VerticalAlignment="Top" ItemsSource="{Binding ObjectCollection}" SelectedItem="{Binding SelectedItem}">
        <ListView.ItemTemplate>
            <DataTemplate>
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition/>
                        <ColumnDefinition/>
                        <ColumnDefinition/>
                    </Grid.ColumnDefinitions>
                    <TextBox Width="100" Grid.Column="0" Text="{Binding Text}"/>
                    <Button Width="100" Content="Test" Grid.Column="1"/>
                    <Label Width="100" Grid.Column="2" Content="{Binding Label}"/>
                </Grid>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</Grid>

Code:

namespace WpfApplication
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new ViewModel();
    }
}

public class Obj : INotifyPropertyChanged
{
    public string Text { get; set; }

    private string label;
    public string Label
    {
        get
        {
            return this.label;
        }

        set
        {
            this.label = value;
            this.RaisePropertyChaged("Label");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void RaisePropertyChaged(string info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }
}

public class ViewModel
{
    private Obj selectedItem;
    public Obj SelectedItem
    {
        get
        {
            return this.selectedItem;
        }

        set
        {
            this.selectedItem = value;
            this.selectedItem.Label = value.Text;
        }
    }
    public ObservableCollection<Obj> ObjectCollection { get; set; }
    public ViewModel()
    {
        ObjectCollection = new ObservableCollection<Obj>();
        ObjectCollection.Add(new Obj { Text = "First" });
        ObjectCollection.Add(new Obj { Text = "Second" });
        ObjectCollection.Add(new Obj { Text = "Third" });
    }
}
}

Upvotes: 0

Related Questions