How to perform DataBinding on a specific property from the ObservableCollection list?

Elad770

Member
Joined
Oct 4, 2021
Messages
20
Programming Experience
1-3
Hello everyone I am trying to perform Data Binding to ListView with class properties.
I do this by keeping in a two-dimensional array of object the names of the properties from which I want to make the binding. Most properties are themselves a bit complex because they are actually properties that are themselves classes within a class.
What I'm trying to do without success is do the same for the last limb of
ObservableCollection
C#:
public partial class MainWindow : Window
{
    public MainWindow()
    {
         InitializeComponent();
         CreateGridView();
    }
    private void CreateGridView()
    {
        Grid[] grids = GridSecond.Children.OfType<Grid>().ToArray();

        if (grids.Count() != 0)
        {
            GridSecond.Children.Remove(grids[0]);
        }

        Grid GView = new Grid() { Margin = new Thickness(0, -60, 0, 0) };
        GView.RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });
        GView.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
        ListView lv = new ListView()
        {
            BorderThickness = new Thickness(1, 1, 1, 1),
            BorderBrush = Brushes.Ivory,
            HorizontalAlignment = HorizontalAlignment.Left,
            FontSize = 18,
            Foreground = (SolidColorBrush)new BrushConverter().ConvertFrom("#3071a9"),
            Background = (SolidColorBrush)new BrushConverter().ConvertFrom("#FFE3E3E3"),
            Visibility = ModeVis[IndexVisbale++ % 2],
            MaxHeight = 510,

            };
                //ScrollViewer.SetVerticalScrollBarVisibility(lv, ScrollBarVisibility.Disabled);
            lv.SetBinding(ListView.ItemsSourceProperty, new Binding());
            GridView myGridView = new GridView();
            object[,] bidingsCol = new object[,]
            {
                { "PackageID","Package ID",120 },
                { "SenderAddress.FullPostal","Sender" ,115},
                { "DestinationAddress.FullPostal","Destination",120 },
                { "Priority","Priority",115 },
                { "Status","Status",180 },
                //-------------------------יHERE-----------------------------
                //Not working
                {"Tracking.Last().Time","Time",150 },
                //this is working
                //{"Tracking.[0].Time","Time",150 }
                //not working
                //{"Tracking.[Tracking.Count-1].Time","Time",150 }
                };

            for (int i = 0; i < bidingsCol.GetLength(0); i++)
            {
                GridViewColumn gvc1 = new GridViewColumn();
                gvc1.DisplayMemberBinding = new Binding(bidingsCol[i, 0].ToString());
                gvc1.Header = bidingsCol[i, 1];
                gvc1.Width = int.Parse(bidingsCol[i, 2].ToString());

                myGridView.Columns.Add(gvc1);
            }
            lv.View = myGridView;
            GView.Children.Add(lv);
            Grid.SetRow(GView, 1);
            GridSecond.Children.Add(GView);
    }
}
//class on make DataBinding on property

using System;

public abstract class Package: INotifyPropertyChanged
{
        public int PackageID { get; }
        static int count = 1000;
        public Address SenderAddress { set; get; }
        public Address DestinationAddress { set; get; }
        public Priority Priority { set; get; }
        public ObservableCollection<Tracking> Tracking { get; }
        public Ellipse[] EPackage { set; get; }
        public List<Line> LPackage { set; get; }

        Status _status;
        public Status Status
        { set
            {
                _status = value;
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("Status"));
                }
            }
            get { return _status; } }
        public event PropertyChangedEventHandler PropertyChanged;

        public Package(Priority priority, Address senderAddress, Address destinationAdress)
        {
            PackageID = count++;
            Priority = priority;
            Tracking = new ObservableCollection<Tracking>();
           
            SenderAddress = senderAddress;
            DestinationAddress = destinationAdress;
           // Status = Status.CREATION;
            AddTracking(null);
            Console.Write("Creating " + GetType().Name+" ");

        }
     

        public static void ResetCount()
        {
            count = 1000;
        }


        public override string ToString()
        {
            return "[packageID=" + PackageID + ", priority=" + Priority + ", status=" + Status + ", startTime=, senderAddress=" + SenderAddress + ", destinationAddress=" + DestinationAddress + ", ";
        }
        public void AddTracking(INode node, Status status = Status.CREATION)
        {
            Tracking.Add(new Tracking(MainOffice.Clock, node, status));
        }

    }
}
The two-dimensional array basically contains 3 elements
One is the name of the property from which I make the Data Binding, the second is the title that will appear on the ListView as a column and the third is the size of that column.
The Package Class Tracking property is ObservableCollection and I just want to take its last element, but only if I actually put a particular index into it does it work, but does not work if I try to run a Last function function string or call the ObservableCollection Count field.
How is there a simple way to request the last element?
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
5,711
Location
Chesapeake, VA
Programming Experience
10+
Not directly related to your problem, you shouldn't use a 2 dimensional-array of different types. It makes things even more confusing when you have 2 dimensions but 3 columns. That leads to your magic numbers to index the columns. Furthermore, the compiler can't help you as much with type safety if you just use object.

What you should have is a class (or struct):
C#:
class ColumnInfo
{
    public string Name { get; set; }
    public int Width { get; set; }
    public Binding Binding { get; set; }
}
or if you are using newer version of C#:
C#:
record ColumnInfo(string Name, int Width, Binding Binding);

and then a 1-dimensional array of those:
C#:
var columnInfo = ColumnInfo []
{
    new ColumnInfo() { Name = "Package ID", Width = 120, new Binding("PackageId") },
    :
};
OR
C#:
var columnInfo = ColumnInfo []
{
    ColumnInfo("Package ID", 120, new Binding("PackageId")),
    :
};
 
Last edited:

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
5,711
Location
Chesapeake, VA
Programming Experience
10+
Anyway with regards to your problem, you can read start reading the docmentation:



which leads to the kinds of expressions you can use within the indexer [ ... ]. Within the indexer, you can bind to other properties which will give you the index value of the last item. Unfortunately, you cannot invoke the LINQ extension method Last().

Personally, since I would end up having to expose the index of the last item, I might as well just expose the last item instead. Just add another property to your Package class. Something like:
C#:
class Package : ...
{
    :
    public Tracking LastTracking => Tracking.Last();
    :
}
so that in the binding, I would just use "LastTracking.Time".

As I'm writing this, I'm also seeing a lot of instances of breaking The Law of Demeter which will make the code pretty fragile.
 

Elad770

Member
Joined
Oct 4, 2021
Messages
20
Programming Experience
1-3
Anyway with regards to your problem, you can read start reading the docmentation:



which leads to the kinds of expressions you can use within the indexer [ ... ]. Within the indexer, you can bind to other properties which will give you the index value of the last item. Unfortunately, you cannot invoke the LINQ extension method Last().

Personally, since I would end up having to expose the index of the last item, I might as well just expose the last item instead. Just add another property to your Package class. Something like:
C#:
class Package : ...
{
    :
    public Tracking LastTracking => Tracking.Last();
    :
}
so that in the binding, I would just use "LastTracking.Time".

As I'm writing this, I'm also seeing a lot of instances of breaking The Law of Demeter which will make the code pretty fragile.
Thank you so much I used with the lambda you suggested
I usually do not use the technique in general, I tried to do in the usual form of the get of the property
 
Top Bottom