wpfxamlmvvmdata-bindingcompositecollection

Merging two ObservableCollection and Bind Datagrid with this MergedCollection as source using CompositeCollection


.Xaml

DataContext="{DynamicResource ViewModelCombine}">

<Window.Resources>
    <vm:ViewModelCombine x:Key="ViewModelCombine"/>
</Window.Resources>

<Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

    <DataGrid x:Name="grd">

            <DataGrid.ItemsSource>
            <CompositeCollection>
                <CollectionContainer Collection="{Binding  Source={StaticResource ViewModelCombine}, Path=MergedSource}"/>
            </CompositeCollection>
            </DataGrid.ItemsSource>

            <DataGrid.Columns>
                <DataGridTextColumn Header="AMP" Binding="{Binding AMP}" Width="100"/>
                <DataGridTextColumn Header="PW" Binding="{Binding PW}" Width="100" />
                <DataGridTextColumn Header="DZ0" Binding="{Binding DZ0}" Width="100" />
                <DataGridTextColumn Header="DELTA" Binding="{Binding DELTA}" Width="100" />
                <DataGridTextColumn Header="DZ1" Binding="{Binding DZ1}" Width="100"/>
                <DataGridTextColumn Header="M" Binding="{Binding M_View}" Width="100" />
                <DataGridTextColumn Header="DZ2" Binding="{Binding DZ2}" Width="100" />
                <DataGridTextColumn Header="N" Binding="{Binding N}" Width="100" />
            </DataGrid.Columns>

    </DataGrid>

</Grid>

.Xaml.cs

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        ViewModelCombine VMC = new ViewModelCombine();
        grd.DataContext = VMC;
    }
}

ViewModelCombine.cs

public class ViewModelCombine
{
    private ObservableCollection<TherapyTiming> secondsource;
    public ObservableCollection<TherapyTiming> SecondSource
    {
        get { return secondsource; }
        set { secondsource = value; }
    }

    private ObservableCollection<PulseTiming> firstsource;
    public ObservableCollection<PulseTiming> FirstSource
    {
        get { return firstsource; }
        set { firstsource = value; }
    }

    private CompositeCollection mergedSource;
    public CompositeCollection MergedSource
    {
        get { return mergedSource; }
        set { mergedSource = value; OnPropertyChanged("MergedSource"); }
    }

    public ViewModelCombine()
    {
        var secondinfo = new ViewModelTherapy();
        SecondSource = secondinfo;

        var firstinfo = new ViewModelPulse();
        FirstSource = firstinfo;

        mergedSource = new CompositeCollection();
        CollectionContainer collection1 = new CollectionContainer() { Collection = SecondSource };
        CollectionContainer collection2 = new CollectionContainer() { Collection = FirstSource };

        mergedSource.Add(collection1);
        mergedSource.Add(collection2);
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }
}

Issue while binding Datagrid to MergedCollection as itemsource using CompositeCollection..displays the data in two rows instead of one row and check output here.

Requirement is to display all columns in one row...! Thanks.


Solution

  • Ok. The problem is in the code below

     mergedSource.Add(collection1);
        mergedSource.Add(collection2);
    

    This code creates one list with unique items of both collections without merging their properties. So that's why you have the output as on your link.

    Instead of doing that i would suggest the following solution

    1. Create a collection of ObservableCollection<Newobject>
    2. Newobject should contain all the properties from PulseTiming and TherapyTiming
    3. Insted of adding the items from collection1 and collection2 on the same list, i would merge (copy all properties) each item from collection1 and collection2 based on their ids(add them if you don't have id properties cause it will help you identify the connection between the colelctions) to the Newobject.

    4. Make sure that you bind your datagrid to the new collection(itemsource)

    That approach will show your items in one row per item. But you cannot do that without merging the properties into a new object.

    UPDATED: Working sample

    Code:

        public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
    
    
            ViewModelCombine VMC = new ViewModelCombine();
            VMC.VP.Add(new ViewModelPulse() { ID = 1, Name = "test1", xaxa = "xaxa" });
            VMC.VT.Add(new ViewModelTherapy() { ID = 1, Name = "test1", Description = "desc" });
            VMC.VP.Add(new ViewModelPulse() { ID = 2, Name = "test2", xaxa = "xaxa2" });
            VMC.VT.Add(new ViewModelTherapy() { ID =2, Name = "test2", Description = "desc2" });
            VMC.BuildSource();
            this.DataContext = VMC;
        }
    
    }
    
    public class ViewModelCombine
    {
        public ObservableCollection<ViewCommon> Source { get; set; }
    
        public void BuildSource()
        {
            Source.Clear();
            foreach(var itm in VT)
            {
                var itmF = VP.FirstOrDefault(x => x.ID == itm.ID);
                if(itmF != null)
                {
                    Source.Add(new ViewCommon() {
                        ID = itm.ID,
                        Description = itm.Description,
                        Name = itm.Name,
                        xaxa = itmF.xaxa
                    });
                }
            }
        }
    
        public ObservableCollection<ViewModelTherapy> VT { get; set; }
        public ObservableCollection<ViewModelPulse>  VP { get; set; }
    
        public ViewModelCombine()
        {
            Source = new ObservableCollection<ViewCommon>();
            VT = new ObservableCollection<ViewModelTherapy>();
            VP = new ObservableCollection<ViewModelPulse>();
        }
    
    }
    
    public class ViewCommon
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public string xaxa { get; set; }
    
    
    }
    public class ViewModelTherapy
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
    
    }
    
    public class ViewModelPulse
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public string xaxa { get; set; }
    
    }
    

    Xaml:

       <Window.Resources>
        <CollectionViewSource x:Key="ViewSource1" Source="{Binding Source}"/>
    
        <CompositeCollection x:Key="CombinedCollection">
            <CollectionContainer Collection="{Binding Source={StaticResource ViewSource1}}" />
         </CompositeCollection>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
    
        <DataGrid ItemsSource="{StaticResource CombinedCollection}">
    
    
            <DataGrid.Columns>
                <DataGridTextColumn Header="ID" Binding="{Binding ID}" Width="100"/>
                <DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="100" />
                <DataGridTextColumn Header="xaxa" Binding="{Binding xaxa}" Width="100" />
                <DataGridTextColumn Header="Description" Binding="{Binding Description}" Width="100" />
            </DataGrid.Columns>
    
        </DataGrid>
    
    </Grid>
    

    Screenshot:

    enter image description here