here's what i want: if i bind ICollectionview to a DataGrid, I don't want to lose SortDescription in my Viewmodel.
I am creating a small sample project to see what I mean. In my projects, I just use Usercontrol to display my data in a DataGrid. If I do this, SortDescritpion will disappear when UserControl is turned off, because the ItemsSource parameter is set to null. If I use TemplateSelector to display my UserControl, SortDescription will not go away , and ItemSource is not not null in Unload. the question is why are these different behaviors? Is one of the 2 behaviors a mistake?
by the way. I am using .Net 4.5.1, but 4.6.1 is installed and system.Windows.Interactivity 4.0.0.0 is installed
MainWindow.xaml
<Window x:Class="DataGridICollectionView.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:DataGridICollectionView"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate DataType="{x:Type local:ViewmodelListe}">
<local:MyViewUc/>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ToolBar Grid.Row="0">
<Button Content="SetWorkspace MyView" Click="Button_Click"/>
<Button Content="SetWorkspace Other" Click="Button_Click_1"/>
</ToolBar>
<ContentPresenter Grid.Row="1" Content="{Binding Workspace}"/>
</Grid>
</Window>
MainWindow.xaml.cs
namespace DataGridICollectionView
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
private object _workspace;
public MainWindow()
{
InitializeComponent();
MyViewVm = new ViewmodelListe();
DataContext = this;
}
public ViewmodelListe MyViewVm { get; set; }
public object Workspace
{
get { return _workspace; }
set
{
_workspace = value;
OnPropertyChanged();
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Workspace = MyViewVm;
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
Workspace = "Other";
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class ViewmodelListe : INotifyPropertyChanged
{
public ViewmodelListe()
{
Persons = new ObservableCollection<Person>();
MyView = CollectionViewSource.GetDefaultView(Persons);
Persons.Add(new Person() {FirstName = "P1", LastName = "L1"});
Persons.Add(new Person() {FirstName = "P2", LastName = "L2"});
Persons.Add(new Person() {FirstName = "P3", LastName = "L3"});
}
public ObservableCollection<Person> Persons { get; private set; }
public ICollectionView MyView { get; private set; }
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Person : INotifyPropertyChanged
{
private string _firstName;
private string _lastName;
public string FirstName
{
get { return _firstName; }
set
{
_firstName = value;
OnPropertyChanged();
}
}
public string LastName
{
get { return _lastName; }
set
{
_lastName = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class TestBehavior : Behavior<DataGrid>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Unloaded += AssociatedObjectUnloaded;
}
private void AssociatedObjectUnloaded(object sender, RoutedEventArgs e)
{
var itemssource = AssociatedObject.ItemsSource;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.Unloaded -= AssociatedObjectUnloaded;
}
}
}
MyGridControl.xaml
<UserControl x:Class="DataGridICollectionView.MyGridControl"
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:local="clr-namespace:DataGridICollectionView"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<DataGrid ItemsSource="{Binding MyView}" AutoGenerateColumns="True">
<i:Interaction.Behaviors>
<local:TestBehavior/>
</i:Interaction.Behaviors>
</DataGrid>
</Grid>
</UserControl>
Myviewuc.xaml
<UserControl x:Class="DataGridICollectionView.MyViewUc"
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:local="clr-namespace:DataGridICollectionView"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<DataTemplate x:Key="MyViewCrap">
<local:MyGridControl/>
</DataTemplate>
<local:MyTemplateSelector x:Key="Selector" GridView="{StaticResource MyViewCrap}" />
</UserControl.Resources>
<Grid>
<ContentControl Content="{Binding .}" ContentTemplateSelector="{StaticResource Selector}"/>
</Grid>
</UserControl>
MyViewUc.xaml.cs
namespace DataGridICollectionView
{
public partial class MyViewUc : UserControl
{
public MyViewUc()
{
InitializeComponent();
}
}
public class MyTemplateSelector : DataTemplateSelector
{
public DataTemplate GridView { get; set; }
public override DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container)
{
var chooser = item as ViewmodelListe;
if (chooser == null)
{
return base.SelectTemplate(item, container);
}
return GridView;
}
}
}
EDIT: End up using
public class MyDataGrid : DataGrid
{
static MyDataGrid ()
{
ItemsSourceProperty.OverrideMetadata(typeof(MyDataGrid ),new FrameworkPropertyMetadata(null, OnPropertyChangedCallBack, OnCoerceItemsSourceProperty));
}
private ICollectionView _defaultView;
protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
{
if(_defaultView != null)
_defaultView.CollectionChanged -= LiveSortingPropertiesOnCollectionChanged;
base.OnItemsSourceChanged(oldValue, newValue);
_defaultView = newValue as ICollectionView;
if(_defaultView != null)
_defaultView.CollectionChanged += LiveSortingPropertiesOnCollectionChanged;
}
private void LiveSortingPropertiesOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Reset)
{
foreach (var dataGridColumn in this.Columns)
{
var isSortDirectionSetFromCollectionView = false;
foreach (var sortDescription in _defaultView.SortDescriptions)
{
if (dataGridColumn.SortMemberPath == sortDescription.PropertyName)
{
dataGridColumn.SortDirection = sortDescription.Direction;
isSortDirectionSetFromCollectionView = true;
break;
}
}
if (!isSortDirectionSetFromCollectionView)
{
dataGridColumn.SortDirection = null;
}
}
}
}
private static void OnPropertyChangedCallBack(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var grd = d as MyDataGrid ;
var view = e.NewValue as ICollectionView;
if (grd == null || view == null)
return;
foreach (var dataGridColumn in grd.Columns)
{
var isSortDirectionSetFromCollectionView = false;
foreach (var sortDescription in view.SortDescriptions)
{
if (dataGridColumn.SortMemberPath == sortDescription.PropertyName)
{
dataGridColumn.SortDirection = sortDescription.Direction;
isSortDirectionSetFromCollectionView = true;
break;
}
}
if (!isSortDirectionSetFromCollectionView)
{
dataGridColumn.SortDirection = null;
}
}
}
private static object OnCoerceItemsSourceProperty(DependencyObject d, object baseValue)
{
return baseValue;
}
}