wpf

WPF - How to implement ICollectionViewLiveShaping between two TabItems?


I am using a TabControl with two TabItems. In the first TabItem, I have a list, and in the second TabItem, I have the details. I want that when I save the details in the second TabItem, the new item is added to the list in the first TabItem and ordered in real time.

The new item is added correctly, but it is not sorted by date as it should be, even though I have implemented ICollectionViewLiveShaping. How can I ensure that the new item is automatically sorted by date in the list?

        ObservableCollection<UscitaModelView> _ocUscite=new();

        // Configura ICollectionView e ICollectionViewLiveShaping
        var collectionView = CollectionViewSource.GetDefaultView(_ocUscite) as ICollectionView;
        if (collectionView != null)
        {
            collectionView.SortDescriptions.Add(new SortDescription("Date", ListSortDirection.Descending));
            Uscite = collectionView as ICollectionViewLiveShaping;

            if (Uscite != null)
            {
                Uscite.IsLiveSorting = true;
                Uscite.IsLiveFiltering = true;
            }
        }

        LoadUscite();

"Uscite" is a ICollectionViewLiveShaping type.

UPDATE

MainViewMOdel

    public partial class MainViewModel:ObservableObject
{
    public MainViewModel(NavigationService<BaseTabViewModel> ns)
    {
        Tabbing= ns;
        
    }

    [ObservableProperty] NavigationService<BaseTabViewModel> _tabbing;
    [ObservableProperty] FatturaFornitoreManager? _fattureFornitori=new();
    [ObservableProperty] UsciteManagerViewModel _usciteManager = new();


}

The NavigationService for add or remove tabitem

    public partial class NavigationService<T>:ObservableObject
{
    [ObservableProperty] ObservableCollection<T> _tabs = new();
    [ObservableProperty] T? _selectedTab;

    public void AddTab(T tab)
    {
        if(tab is not null)
        {
            Tabs.Add(tab);
            SelectedTab = tab;
        }
    }

    [RelayCommand]
    void RemoveTab(T tab)
    {
        if (tab is not null)
        {
            Tabs.Remove(tab);
            if (Tabs.Count > 1)
            {
                SelectedTab = Tabs[Tabs.Count - 1];
            }
        }
    }

}

BaseTabViewModel

    public partial class BaseTabViewModel:ObservableObject
{
    [ObservableProperty] string? _header;
    [ObservableProperty] ViewModelBase? _contentTab;
}

UsciteManagerViewModel

    public partial class UsciteManagerViewModel: ObservableObject
{
    readonly NavigationService<BaseTabViewModel> _ns;
    public UsciteManagerViewModel()
    {
        _ns = App.Me.ServiceProvider!.GetRequiredService<NavigationService<BaseTabViewModel>>();
    }

    [RelayCommand(CanExecute = nameof(CanOpenUscite))]
    void OpenUscite()
    {
        UsciteViewModel usc = new();
        BaseTabViewModel tab = new();
        tab.Header = "Lista uscite";
        tab.ContentTab = usc;
        _ns.AddTab(tab);

    }

    private bool CanOpenUscite()
    {
        var tab = _ns.Tabs.Where(c => c.ContentTab is UsciteViewModel).FirstOrDefault();
        if (tab is null) return true;
        return false;
    }
}

Snippet in MainWindow

    <Ribbon>
<RibbonTab>
<RibbonGroup Header="Uscita"
                                 DataContext="{Binding UsciteManager}">
                        <RibbonButton Label="Movimenti"
                                      
                                      ToolTipTitle="Lista dei movimenti"
                                      ToolTipDescription="Apre la lista dei movimenti"
                                      Command="{Binding OpenUsciteCommand}"
                                      Height="18"
                                      VerticalAlignment="Top" />
    </RibbonGroup>
</RibbonTab>
</Ribbon>
    <TabControl Grid.Row="1"
                        DataContext="{Binding Tabbing}"
                        SelectedItem="{Binding SelectedTab,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
                        ItemsSource="{Binding Tabs}"
                        ItemContainerStyle="{StaticResource MainTab}"
                        IsSynchronizedWithCurrentItem="True">
                    <DataTemplate DataType="{x:Type vm:FattureFornitoriViewModel}">
                        <views:ucFattureFornitori />
                    </DataTemplate>
                    <DataTemplate DataType="{x:Type vm:UsciteViewModel}">
                        <views:ucUscite />
                    </DataTemplate>
                </TabControl.Resources>
            </TabControl>

UsciteViewModel

    public partial class UsciteViewModel:ViewModelBase
{
    GenericRepository<UscitaModel> _repoUscite;
    ObservableCollection<UscitaModelView> _ocUscite;

    public UsciteViewModel()
    {
        _repoUscite = new(Tabella.uscita);
        _ocUscite = new();

        // Configura ICollectionView e ICollectionViewLiveShaping
        var collectionView = CollectionViewSource.GetDefaultView(_ocUscite) as ICollectionView;
        if (collectionView != null)
        {
            collectionView.SortDescriptions.Add(new SortDescription("Data", ListSortDirection.Descending));
            Uscite = collectionView as ICollectionViewLiveShaping;

            if (Uscite != null)
            {
                Uscite.LiveSortingProperties.Add("Data");
                Uscite.IsLiveSorting = true;
                Uscite.IsLiveFiltering = true;

            }
        }

        LoadUscite();
    }

    [ObservableProperty] ICollectionViewLiveShaping _uscite;
    [ObservableProperty] string? _searchText;

    [RelayCommand]
    async Task OpenUscita(UscitaModelView uscita)
    {
        if (uscita is null) return;

    }

    partial void OnSearchTextChanged(string? value)
    {
        if (_ocUscite is null || _ocUscite.Count == 0) return;

    }

    async void LoadUscite()
    {
        var uscitemodel = await _repoUscite.GetAllAsync();
        if (uscitemodel is not null)
        {
            foreach (var uscita in uscitemodel)
            {
                _ocUscite.Add(new UscitaModelView(uscita));
            }
        }
    }

    public void AddUscita(UscitaModel uscita)
    {
        if (uscita is not null)
        {
            _ocUscite.Add(new UscitaModelView(uscita));
        }
    }


}

ViewModel that add new uscitamodelview

    public partial class FatturaFornitoreViewModel: FatturaFornitoreModelView
{
    public event EventHandler<EventArgs> OnFatturaMovedToUscite;

    public FatturaFornitoreViewModel(FatturaFornitore fatturaFornitore) : base(fatturaFornitore)
    {

    }

    [ObservableProperty] FatturaFornitoreToMovement? _fatturaFornitoreToMovement;
    [ObservableProperty] bool _isAddDettagliFatturaFornitore;
    [ObservableProperty] bool _isSaveAllegati;
    [ObservableProperty] bool _isSaveAllegatiEnabled;

    partial void OnIsAddDettagliFatturaFornitoreChanged(bool value)
    {
        if (value)
        {
            FatturaFornitoreToMovement = new();
            FatturaFornitoreToMovement.OnIsSavable += (s, e) =>
            {
                SaveToUsciteCommand.NotifyCanExecuteChanged();
            };
        }
        else
        {
            FatturaFornitoreToMovement.OnIsSavable -= (s, e) =>
            {
                SaveToUsciteCommand.NotifyCanExecuteChanged();
            };
            FatturaFornitoreToMovement = null;
        }
    }

    [RelayCommand]
    async Task VisualizzaFattura()
    {
        await ((string)Fattura!.Data!).ViewHtmlFromStringXmlAsync(Fattura.NomeFile!);
    }

    [RelayCommand(CanExecute =nameof(CanOpenAllegato))]
    async Task VisualizzaAllegato()
    {
        var primoFile=Allegati!.First();
        await primoFile.Attachment!.ViewFileFromBytesAsync($"{primoFile.NomeAttachment!}.{primoFile.FormatoAttachment}");
    }

    bool CanOpenAllegato()
    {
        bool isValid = Allegati is not null && Allegati.Any();
        IsSaveAllegatiEnabled = isValid;
        return isValid;
    }

    [RelayCommand(CanExecute =nameof(CanSave))]
    async Task SaveToUscite()
    {
        // Implement logic to save the invoice to the Uscite
        UscitaModel model = new();

        model.IdUscita = IdFattura;
        model.Numero = Numero;
        model.Data = Data;
        model.Categoria = FatturaFornitoreToMovement?.Categoria!;
        model.SubCategoria = FatturaFornitoreToMovement?.SubCategoria;// ?? string.Empty;
        model.Fornitore = Fornitore;
        model.TipoDocumento = TipoDocumento;
        model.DettaglioFornitura =DettaglioFornitura;
        model.ImportoTotaleDocumento = ImportoTotaleDocumento;
        model.DataScadenza = DataScadenza;
        model.DataPagamento = FatturaFornitoreToMovement?.DataPagamento;
        model.ModalitaPagamento = FatturaFornitoreToMovement?.ModoPagamento;
        model.Destinatario = Destinatario;
        model.DataOperazione=FatturaFornitoreToMovement?.ModoPagamento is null ? null : DateTime.Now;

        if (FatturaFornitoreToMovement is not null && FatturaFornitoreToMovement.DocumentiPagamento is not null && FatturaFornitoreToMovement.DocumentiPagamento.Count>0)
        {
            model.Documenti = FatturaFornitoreToMovement.DocumentiPagamento;
        }
        if (IsSaveAllegati)
        {
            model.Allegati = Allegati;
        }

        model.Fattura=Fattura;

        GenericRepository<UscitaModel> _repoUscite = new(Tabella.uscita);
        if(await _repoUscite.CreateAsync(model))
        {
            var _repoFattureFornitori = new GenericRepository<FatturaFornitore>(Tabella.fatturafornitore);
            await _repoFattureFornitori.DeleteAsync<string>(IdFattura!);

            OnFatturaMovedToUscite?.Invoke(this, EventArgs.Empty);
        }

        FatturaFornitoreToMovement?.CheckExistsAndValidation();

        var tabs=App.Me.ServiceProvider!.GetRequiredService<NavigationService<BaseTabViewModel>>().Tabs;
        var tab = tabs.FirstOrDefault(t => t.ContentTab is UsciteViewModel);
        if (tab is not null)
        {
            var uscite=tab.ContentTab as UsciteViewModel;
            uscite?.AddUscita(model);
        }

    }

    bool CanSave()=>FatturaFornitoreToMovement?.Categoria.CheckString() != null;



}

I hope it is clear enough.

Thanks


Solution

  • I solved it by adding the following to the resources of ucUscite and setting the type of the Uscite property to ObservableCollection<UscitaModelView>:

         <UserControl.Resources>
            <CollectionViewSource x:Key="UsciteList" Source="{Binding Uscite}">
                <CollectionViewSource.SortDescriptions>
                    <scm:SortDescription PropertyName="Data"
                                         Direction="Descending" />
                    <scm:SortDescription PropertyName="Fornitore" />
                </CollectionViewSource.SortDescriptions>
            </CollectionViewSource>
         </UserControl.Resources>
    

    and

    <DataGrid ItemSource={Binding Source={StaticResource UsciteList}}>
    

    Thanks