i´m a newbie in WPF and have a Problem with data-binding. I hope you can help me:
Here is my XAML- Code:
<Window x:Class="WpfApplication4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TabControl Name="MainTabControl" ItemsSource="{Binding}" Margin="2,0,-2,0">
<TabControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Header}" Tag="{Binding Rid}" />
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<DataGrid ItemsSource="{Binding Dg}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
</Window>
So i have a TabControl with a binding to a ObservableCollection of Tabs. Each Tab has a TextBlock as Header and a DataGrid.
Binding:
public MainWindow()
{
InitializeComponent();
DataGrid dg = new DataGrid();
DataGridTemplateColumn xx = new DataGridTemplateColumn()
{
Header = new TextBlock() { Text = "Edit", Tag = "Edit" }
};
xx.Width = new DataGridLength(1, DataGridLengthUnitType.Auto);
dg.Columns.Add(xx);
OCTabs.Add(new OCTab() { Header = "test1", Rid = 1 , Dg = dg});
OCTabs.Add(new OCTab() { Header = "test2", Rid = 2 , Dg = dg});
OCTabs.Add(new OCTab() { Header = "test3", Rid = 3, Dg = dg });
MainTabControl.DataContext = OCTabs;
}
and here the Tab- Class:
public class OCTab : INotifyPropertyChanged
{
private string _header;
private int _rid;
private DataGrid dg;
public DataGrid Dg { get { return dg; } set { dg = value; NotifyPropertyChanged("Dg"); } }
public string Header { get { return _header; } set { _header = value; NotifyPropertyChanged("Header"); } }
public int Rid { get { return _rid; } set { _rid = value; NotifyPropertyChanged("Rid"); } }
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Now my problem:
everything works fine, except for the DataGrids from each Tab. The DataGrid is empty (no Columns,...)
The Binding doesn´t work and i don´t know why...
EDIT:
so is it possible, that each Tabitem has it´s own datagrid?
Related
I have an ItemsControl that is displaying all the items on top of each other. The default ItemsPanelTemplate is a StackPanel with a vertical orientation so I don't see why the items are not spread out vertically.
This example has a window which contains a ContentControl. This control is bound to a property called ElementColl which is found in the Resources class. The Resources class is set as the DataContext of the window.
The ElementColl property is of the type Elements. The Elements class contains a property of the type ObservableCollection. The Element object has a Number property and a SomeText property.
The constructor of the Window creates three Element instances and puts them into the collection.
The image at the end shows all three Elements being displayed on top of each other.
<Window x:Class="POC_ObservableCollectionInListbox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:POC_ObservableCollectionInListbox"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<DataTemplate DataType="{x:Type local:Elements}">
<ItemsControl ItemsSource="{Binding Collection}">
<!--<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>-->
<ItemsControl.ItemTemplate>
<DataTemplate>
<Canvas>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Path=Number}"/>
<Label Content="{Binding Path=SomeText}"/>
</StackPanel>
</Canvas>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</Window.Resources>
<Grid>
<ContentControl Content="{Binding ElementColl}"/>
</Grid>
</Window>
public partial class MainWindow : Window
{
private Resource _resource = null;
public MainWindow()
{
InitializeComponent();
_resource = new Resource();
this.DataContext = _resource;
Element e1 = new Element();
e1.Number = 123;
e1.SomeText = "Apple";
_resource.ElementColl.Collection.Add(e1);
Element e2 = new Element();
e2.Number = 345;
e2.SomeText = "Bannana";
_resource.ElementColl.Collection.Add(e2);
Element e3 = new Element();
e3.Number = 567;
e3.SomeText = "Clementine";
_resource.ElementColl.Collection.Add(e3);
}
}
public class Element : INotifyPropertyChanged
{
private Int32 _number = 0;
public Int32 Number
{
get { return _number; }
set
{
_number = value;
OnPropertyChanged("Number");
}
}
private string _someText = "";
public string SomeText
{
get { return _someText; }
set
{
_someText = value;
OnPropertyChanged("SomeText");
}
}
#region PropertyChanged
#endregion PropertyChanged
}
public class Elements : INotifyPropertyChanged
{
public Elements()
{
Collection = new ObservableCollection<Element>();
}
private ObservableCollection<Element> _col;
public ObservableCollection<Element> Collection
{
get { return _col; }
set
{
_col = value;
OnPropertyChanged("Collection");
}
}
#region PropertyChanged
#endregion PropertyChanged
}
public class Resource : INotifyPropertyChanged
{
public Resource()
{
ElementColl = new Elements();
}
private Elements _elements = null;
public Elements ElementColl
{
get { return _elements; }
set
{
_elements = value;
OnPropertyChanged("ElementColl");
}
}
#region PropertyChanged
#endregion PropertyChanged
}
It's because of your Canvas. Comment out those lines and you'll see your items.
<DataTemplate>
<!--<Canvas>-->
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Path=Number}"/>
<Label Content="{Binding Path=SomeText}"/>
</StackPanel>
<!--</Canvas>-->
</DataTemplate>
Output:
Do you know why looking at the code in the XAML and ViewModel why a Data Grid wont update when the an item is added? I know the DataGrid binding is correct as will add an item if I add it by code, however I am adding a item via a view.
Also the getter of the ObservableCollection is getting hit every time I do add a item.
Thanks
------ Code ------
XAML
<Window x:Class="Importer.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Product Group" Height="350" Width="721"
xmlns:VM="clr-namespace:Importer.ViewModels">
<Window.DataContext>
<VM:ProductsViewModel />
</Window.DataContext>
<Grid Margin="0,0,0.4,0.4">
<DataGrid x:Name="dgGrid" ColumnWidth="Auto" Margin="10,10,0,0" VerticalAlignment="Top"
ItemsSource="{Binding ProductCollection}" HorizontalAlignment="Left" AutoGenerateColumns="False" >
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name}" Header="Name" Width="150"/>
<DataGridTextColumn Binding="{Binding Description}" Header="Description" Width="400" />
</DataGrid.Columns>
</DataGrid>
<Button x:Name="btnAddProjectGroup" Content="Add Project Group" HorizontalAlignment="Left" Margin="10,277,0,0" VerticalAlignment="Top" Width="135" Click="btnAddProjectGroup_Click"/>
</Grid>
</Window>
VIEW 1 (that uses the XAML above)
public partial class MainWindow : Window
{
ProductsViewModel m_VM = new ProductsViewModel();
public MainWindow()
{
InitializeComponent();
}
private void btnAddProjectGroup_Click(object sender, RoutedEventArgs e)
{
// Open new window here to add a new project group
AddProductGroup dlg = new AddProductGroup(m_VM);
dlg.ShowDialog();
}
}
VIEW 2 - The view that passes the values to add to the collection
public partial class AddProductGroup : Window
{
ProductGroupBindable newProduct = new ProductGroupBindable();
ProductsViewModel viewModel = null;
public AddProductGroup(ProductsViewModel vm)
{
viewModel = vm;
InitializeComponent();
}
private void btnOK_Click(object sender, RoutedEventArgs e)
{
newProduct.Id = System.Guid.NewGuid();
newProduct.Name = txtName.Text;
newProduct.Description = txtDescription.Text;
viewModel.AddProduct(newProduct);
this.Close();
}
}
VIEWMODEL
private ObservableCollection<ProductGroupBindable> m_ProductCollection;
public ProductsViewModel()
{
if (m_ProductCollection == null)
m_ProductCollection = new ObservableCollection<ProductGroupBindable>();
}
public ObservableCollection<ProductGroupBindable> ProductCollection
{
get
{
return m_ProductCollection;
}
set
{
m_ProductCollection = value;
}
}
private ObservableCollection<ProductGroupBindable> Test()
{
m_ProductCollection.Add(new ProductGroupBindable { Description = "etc", Name = "test12" });
m_ProductCollection.Add(new ProductGroupBindable { Description = "etc", Name = "test123" });
return ProductCollection;
}
public void AddProduct(ProductGroupBindable newProduct)
{
m_ProductCollection.Add(newProduct);
//NotifyPropertyChanged("ProductCollection");
}
You set DataContext in XAML:
<Window.DataContext>
<VM:ProductsViewModel />
</Window.DataContext>
and your dialog works on another instance of ProductsViewModel which you create in code:
ProductsViewModel m_VM = new ProductsViewModel();
Easiest workaround would be to pass DataContext to your AddProductGroup dialog:
AddProductGroup dlg = new AddProductGroup(this.DataContext as ProductsViewModel);
EDIT
or instead of setting DataContext in XAML do set it in code:
public MainWindow()
{
InitializeComponent();
this.DataContext = m_VM;
}
I have an ItemsControl with ItemsSource bound to an IEnumerable<MyDataItem>.
The ItemTemplate consists of two textboxes. (Friendly name & name). This is how it looks like: http://i.stack.imgur.com/Rg1dC.png
As soon as the "Friendly name" field is filled in, I add an empty row. I use the LostKeyboardFocus event to check whether to add an empty "MyDataItem" and refresh the IEnumerable<MyDataItem> property.
The problem: I loose the focus when adding an item. So if I tab from friendly name to name and a new row is added, the focus is lost from name. How can I solve this?
EDIT: Underneath some code to show my problem. I want to be able to TAB from cell to cell. When both cells of a row are left empty, I want to remove that line. At the end I want to have an empty row (both cells empty). At this point the code works, but loses focus if you use tab. And working with a listbox makes that TAB doesn't work to go to the next item in list.
XAML:
<Window x:Class="Focus.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Focus"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance Type=local:MainViewModel, IsDesignTimeCreatable=True}"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate x:Key="DataTemplate1">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="5"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBox LostKeyboardFocus="TextBox_LostKeyboardFocus">
<TextBox.Text>
<Binding Path="FriendlyName" UpdateSourceTrigger="PropertyChanged"/>
</TextBox.Text>
</TextBox>
<TextBox Grid.Column="2" LostKeyboardFocus="TextBox_LostKeyboardFocus">
<TextBox.Text>
<Binding Path="Name" UpdateSourceTrigger="PropertyChanged"/>
</TextBox.Text>
</TextBox>
</Grid>
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox Margin="10" ItemsSource="{Binding OrderedItems}" ItemTemplate="{DynamicResource DataTemplate1}" HorizontalContentAlignment="Stretch">
</ListBox>
</Grid>
Code behind:
using System.Windows;
using System.Windows.Input;
namespace Focus
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainViewModel();
}
private void TextBox_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
MainViewModel vm = this.DataContext as MainViewModel;
vm.CheckToAddEmptyItem();
}
}
}
The ViewModel
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
namespace Focus
{
public class MainViewModel : INotifyPropertyChanged
{
private List<MyItem> _myItems = new List<MyItem>();
public IEnumerable<MyItem> OrderedItems
{
get { return _myItems.OrderBy(i => i.IsEmpty); }
}
internal void CheckToAddEmptyItem()
{
int count = _myItems.Count(i => i.IsEmpty);
if (count == 0)
{
_myItems.Add(new MyItem());
if (null != PropertyChanged)
PropertyChanged(this, new PropertyChangedEventArgs("OrderedItems"));
}
else if (count > 1)
{
var items = _myItems.Where(i => i.IsEmpty).Skip(1).ToArray();
foreach (MyItem item in items)
_myItems.Remove(item);
if (null != PropertyChanged)
PropertyChanged(this, new PropertyChangedEventArgs("OrderedItems"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public MainViewModel()
{
for(int i=1; i <= 5; ++i)
{
_myItems.Add(new MyItem() { FriendlyName = "Item #" + i, Name = "ITEM" + i });
}
if (null != PropertyChanged)
PropertyChanged(this, new PropertyChangedEventArgs("OrderedItems"));
CheckToAddEmptyItem();
}
}
}
The MyItem class:
using System.ComponentModel;
namespace Focus
{
public class MyItem : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _name = string.Empty;
public string Name
{
get { return _name; }
set
{
if (value != _name)
{
_name = value;
if (null != PropertyChanged)
{
PropertyChanged(this, new PropertyChangedEventArgs("Name"));
PropertyChanged(this, new PropertyChangedEventArgs("IsEmpty"));
}
}
}
}
private string _friendlyName = string.Empty;
public string FriendlyName
{
get { return _friendlyName; }
set
{
if (value != _friendlyName)
{
_friendlyName = value;
if (null != PropertyChanged)
{
PropertyChanged(this, new PropertyChangedEventArgs("FriendlyName"));
PropertyChanged(this, new PropertyChangedEventArgs("IsEmpty"));
}
}
}
}
public bool IsEmpty
{
get { return string.IsNullOrEmpty(Name) && string.IsNullOrEmpty(FriendlyName); }
}
}
}
ItemsControl is not that friendly according to your needs as there are no properties to get a perticular selected item. Try using a Listbox Instead, as it exposes the properties like SelectedItem, SelectedIndex etc, Using these properties you can get the child control at any index value.
PS: I could elaborate my answer if you are looking to use alistbox and get the child elements.
I can bind a combobox in the codebehind like this:
private void comboBox1_Loaded(object sender, RoutedEventArgs e)
{
var combo = sender as ComboBox;
App.SchedulerVM = new ScheduleViewModel();
combo.DataContext = App.SchedulerVM;
combo.ItemsSource = App.SchedulerVM.Frequency;
}
This works - my combobox has the items from the Frequency List in the SchedulerVM object.
However, I don't want to do any of this in the codebehind. But the ways I've done this in WP7 before aren't working here. If I comment out the last line in the Loaded method above and try to set the ItemsSource in XAML, it doesn't work - nothing shows up:
<ComboBox Name="comboBox1" Loaded ="comboBox1_Loaded" ItemsSource="{Binding
Frequency}" />
This doesn't work either:
<ComboBox Name="comboBox1" Loaded ="comboBox1_Loaded" ItemsSource="{Binding
App.SchedulerVM.Frequency}" />
Nor this:
<ComboBox Name="comboBox1" Loaded ="comboBox1_Loaded" ItemsSource="{Binding
SchedulerVM.Frequency}" />
Ideally, the DataContext wouldn't have to be explicitly set in the codebehind for this control either, it would be inherited from the LayoutRoot, where I've set it in the codebehind. But that's step 2 of my troubleshooting here.
What am I doing wrong? '
Thanks!
Edit
The ScheduleViewModel looks like this:
namespace SchedulerUI.ViewModels
{
public class ScheduleViewModel : INotifyPropertyChanged
{
//private properties
private Schedule _thisSchedule;
//public properties
public Schedule ThisSchedule
{
get { return _thisSchedule; }
set
{
if (value != _thisSchedule)
{
NotifyPropertyChanged("ThisSchedule");
}
_thisSchedule = value;
}
}
public List<string> Frequency = new List<string>();
public string Test;
//constructors
public ScheduleViewModel()
{
Frequency.AddRange(new string[] { "Daily", "Weekly", "Monthly" });
Test = "This is only a test.";
}
//INotifyPropertyChanged Implementation
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
Here's the entire XAML:
<UserControl x:Class="SchedulerUI.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White" Loaded="LayoutRoot_Loaded">
<ComboBox Height="23" HorizontalAlignment="Left" Margin="34,41,0,0" Name="comboBox1" Loaded ="comboBox1_Loaded" VerticalAlignment="Top" Width="120" ItemsSource="{Binding Frequency}" />
<TextBox BorderBrush="Black" HorizontalAlignment="Left" Margin="34,41,0,0" Width="100" Height="100" DataContext="LayoutRoot.DataContext" Text="{Binding Test}" />
</Grid>
</UserControl>
Here's the entire codebehind:
namespace SchedulerUI
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
App.SchedulerVM = new ScheduleViewModel();
comboBox1.DataContext = App.SchedulerVM;
List<string> testlist = App.SchedulerVM.Frequency;
string teststring = App.SchedulerVM.Test;
}
private void LayoutRoot_Loaded(object sender, RoutedEventArgs e)
{
//App.SchedulerVM = new ScheduleViewModel();
//var root = sender as Grid;
//if (root != null)
//{
// root.DataContext = App.SchedulerVM;
//}
}
private void comboBox1_Loaded(object sender, RoutedEventArgs e)
{
//var combo = sender as ComboBox;
//App.SchedulerVM = new ScheduleViewModel();
//combo.DataContext = App.SchedulerVM;
//combo.ItemsSource = App.SchedulerVM.Frequency;
}
}
}
You binding is not working, because:
when you set ItemsSource in XAML its get executed first and it tries to bind the wrong/empty DataContext
then the Loaded event is raised which will set the correct DataContext but your already existing binding won't be refreshed automatically.
If you have to set the DataContext in the codebehind do it in your views constructor:
public YourView()
{
InitializeComponent();
combo.DataContext = App.SchedulerVM;
}
Then the following binding should work:
<ComboBox Name="comboBox1" ItemsSource="{Binding Frequency}" />
The databinding in WPF/Silverlight needs public properties. Currently Frequency is a public field on your viewmodel change it to a property and everthing should work:
private List<string> frequency = new List<string>();
public List<string> Frequency { get { return frequency; } set { frequency = value; }
And that is why it worked your initial loaded event because you didn't used databind there but you just set the combo.ItemsSource.
I'm using the following (simplified) code to display an element in all the items in an ItemsControl except the first:
<TheElement Visibility="{Binding RelativeSource={RelativeSource PreviousData},
Converter={StaticResource NullToVisibility}}/>
NullToVisibility is a simple converter that returns Visibility.Hidden if the source is null, Visibility.Visible otherwise.
Now, this works fine when binding the view initially, or adding elements to the list (an ObservableCollection), but the element is not made invisible on the second element when removing the first.
Any ideas on how to fix this?
Had some wasted code leftover from a previous answer... might as well use it here:
The key is to refresh the viewsource e.g. :
CollectionViewSource.GetDefaultView(this.Categories).Refresh();
Full example source below. Remove First Item removes first element and refreshes the view:
RelativeSourceTest.xaml
<UserControl x:Class="WpfApplication1.RelativeSourceTest"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:PersonTests="clr-namespace:WpfApplication1" mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<PersonTests:NullToVisibilityConvertor x:Key="NullToVisibility"/>
</UserControl.Resources>
<Grid>
<StackPanel Background="White">
<Button Content="Remove First Item" Click="Button_Click"/>
<ListBox ItemsSource="{Binding Categories}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Checked, Mode=TwoWay}" >
<StackPanel>
<TextBlock Text="{Binding CategoryName}"/>
<TextBlock Text="Not The First"
Visibility="{Binding RelativeSource={RelativeSource PreviousData},
Converter={StaticResource NullToVisibility}}"/>
</StackPanel>
</CheckBox>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Grid>
</UserControl>
RelativeSourceTest.xaml.cs
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace WpfApplication1
{
public partial class RelativeSourceTest : UserControl
{
public ObservableCollection<Category> Categories { get; set; }
public RelativeSourceTest()
{
InitializeComponent();
this.Categories = new ObservableCollection<Category>()
{
new Category() {CategoryName = "Category 1"},
new Category() {CategoryName = "Category 2"},
new Category() {CategoryName = "Category 3"},
new Category() {CategoryName = "Category 4"}
};
this.DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
this.Categories.RemoveAt(0);
CollectionViewSource.GetDefaultView(this.Categories).Refresh();
}
}
}
Category.cs
using System.ComponentModel;
namespace WpfApplication1
{
public class Category : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _checked;
public bool Checked
{
get { return _checked; }
set
{
if (_checked != value)
{
_checked = value;
SendPropertyChanged("Checked");
}
}
}
private string _categoryName;
public string CategoryName
{
get { return _categoryName; }
set
{
if (_categoryName != value)
{
_categoryName = value;
SendPropertyChanged("CategoryName");
}
}
}
public virtual void SendPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
It's '17 now, but the problem is here. MVVM approach (as I see it):
public class PreviousDataRefreshBehavior : Behavior<ItemsControl> {
protected override void OnAttached() {
var col = (INotifyCollectionChanged)AssociatedObject.Items;
col.CollectionChanged += OnCollectionChanged;
}
protected override void OnDetaching() {
var col = (INotifyCollectionChanged)AssociatedObject.Items;
col.CollectionChanged -= OnCollectionChanged;
}
private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
if(e.Action != NotifyCollectionChangedAction.Remove) {
return;
}
CollectionViewSource.GetDefaultView(AssociatedObject.ItemsSource).Refresh();
}
}
and usage:
<ItemsControl>
<int:Interaction.Behaviors>
<behaviors:PreviousDataRefreshBehavior/>
</int:Interaction.Behaviors>
</ItemsControl>
Underlying CollectionViewSource has to be refreshed after remove.
CollectionViewSource.GetDefaultView(this.Items).Refresh();