How to make size of rectangle depend on biggest item in a list of items

I have this simple example program with 2 columns. The column on the left holds a datagrid, and the column on the right has a red rectangle in the middle. I want to make a binding, so that the width/height of the rectangle is always equal to the size of the biggest item in the list.

Picture of Main Window:

Picture of Solution explorer:

So far i have this:

MainWindow.xaml

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code><Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<!-- Left Column -->
<StackPanel Margin="0 20 0 0">
<DataGrid ItemsSource="{Binding Items}" Margin="20 0 20 0"
AutoGenerateColumns="False"
CanUserResizeColumns="False" CanUserResizeRows="False"
CanUserSortColumns="False" CanUserReorderColumns="False"
CanUserDeleteRows="False" CanUserAddRows="False"
SelectionMode="Single" SelectionUnit="Cell">
<DataGrid.Columns>
<!-- Column 1 - Name -->
<DataGridTextColumn Header="Name"
Binding="{Binding Name}"
Width="150">
</DataGridTextColumn>
<!-- Column 2 - Size -->
<DataGridTextColumn Header="Size"
Binding="{Binding Size}"
Width="*">
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
<!-- Right Column -->
<Border Grid.Column="1"
BorderBrush="Black"
BorderThickness="1">
<Rectangle Width="{Binding SizeOfRectangle}"
Height="{Binding SizeOfRectangle}"
Fill="Red">
</Rectangle>
</Border>
</Grid>
</code>
<code><Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="300"/> <ColumnDefinition/> </Grid.ColumnDefinitions> <!-- Left Column --> <StackPanel Margin="0 20 0 0"> <DataGrid ItemsSource="{Binding Items}" Margin="20 0 20 0" AutoGenerateColumns="False" CanUserResizeColumns="False" CanUserResizeRows="False" CanUserSortColumns="False" CanUserReorderColumns="False" CanUserDeleteRows="False" CanUserAddRows="False" SelectionMode="Single" SelectionUnit="Cell"> <DataGrid.Columns> <!-- Column 1 - Name --> <DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="150"> </DataGridTextColumn> <!-- Column 2 - Size --> <DataGridTextColumn Header="Size" Binding="{Binding Size}" Width="*"> </DataGridTextColumn> </DataGrid.Columns> </DataGrid> </StackPanel> <!-- Right Column --> <Border Grid.Column="1" BorderBrush="Black" BorderThickness="1"> <Rectangle Width="{Binding SizeOfRectangle}" Height="{Binding SizeOfRectangle}" Fill="Red"> </Rectangle> </Border> </Grid> </code>
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="300"/>
        <ColumnDefinition/>
    </Grid.ColumnDefinitions>

    <!-- Left Column -->
    <StackPanel Margin="0 20 0 0">
        <DataGrid ItemsSource="{Binding Items}" Margin="20 0 20 0"
                  AutoGenerateColumns="False"
                  CanUserResizeColumns="False" CanUserResizeRows="False"
                  CanUserSortColumns="False" CanUserReorderColumns="False"
                  CanUserDeleteRows="False" CanUserAddRows="False"
                  SelectionMode="Single" SelectionUnit="Cell">
            <DataGrid.Columns>
                <!-- Column 1 - Name -->
                <DataGridTextColumn Header="Name"
                                    Binding="{Binding Name}"
                                    Width="150">
                </DataGridTextColumn>
                <!-- Column 2 - Size -->
                <DataGridTextColumn Header="Size"
                                    Binding="{Binding Size}"
                                    Width="*">
                </DataGridTextColumn>
            </DataGrid.Columns>
        </DataGrid>
    </StackPanel>

    <!-- Right Column -->
    <Border Grid.Column="1"
            BorderBrush="Black"
            BorderThickness="1">
        <Rectangle Width="{Binding SizeOfRectangle}"
                   Height="{Binding SizeOfRectangle}"
                   Fill="Red">
        </Rectangle>
    </Border>
</Grid>

MainWindow.xaml.cs

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>public partial class MainWindow : Window
{
public MainWindow()
{
MainWindowViewModel viewModel = new MainWindowViewModel();
this.DataContext = viewModel;
InitializeComponent();
}
}
</code>
<code>public partial class MainWindow : Window { public MainWindow() { MainWindowViewModel viewModel = new MainWindowViewModel(); this.DataContext = viewModel; InitializeComponent(); } } </code>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        MainWindowViewModel viewModel = new MainWindowViewModel();
        this.DataContext = viewModel;
        InitializeComponent();
    }
}

Item.cs

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>public class Item
{
public string Name { get; set; }
public double Size { get; set; }
}
</code>
<code>public class Item { public string Name { get; set; } public double Size { get; set; } } </code>
public class Item
{
    public string Name { get; set; }
    public double Size { get; set; }
}

MainWindowViewModel.cs

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>public class MainWindowViewModel
{
public ObservableCollection<Item> Items { get; }
public double SizeOfRectangle { get; set; }
public MainWindowViewModel()
{
//Initialize list of items:
Items = new ObservableCollection<Item>()
{
new Item { Name = "Item 1", Size = 120 },
new Item { Name = "Item 2", Size = 150 },
new Item { Name = "Item 3", Size = 180 },
new Item { Name = "Item 4", Size = 100 },
new Item { Name = "Item 5", Size = 225 }
};
//Find the biggest size and assign to "SizeOfRectangle"
double MaxSize = Items[0].Size;
for (int i = 1; i < Items.Count; i++)
{
if (Items[i].Size > MaxSize)
MaxSize = Items[i].Size;
}
SizeOfRectangle = MaxSize; //Width and Height of rectangle bound to this
}
}
</code>
<code>public class MainWindowViewModel { public ObservableCollection<Item> Items { get; } public double SizeOfRectangle { get; set; } public MainWindowViewModel() { //Initialize list of items: Items = new ObservableCollection<Item>() { new Item { Name = "Item 1", Size = 120 }, new Item { Name = "Item 2", Size = 150 }, new Item { Name = "Item 3", Size = 180 }, new Item { Name = "Item 4", Size = 100 }, new Item { Name = "Item 5", Size = 225 } }; //Find the biggest size and assign to "SizeOfRectangle" double MaxSize = Items[0].Size; for (int i = 1; i < Items.Count; i++) { if (Items[i].Size > MaxSize) MaxSize = Items[i].Size; } SizeOfRectangle = MaxSize; //Width and Height of rectangle bound to this } } </code>
public class MainWindowViewModel
{
    public ObservableCollection<Item> Items { get; }
    public double SizeOfRectangle { get; set; }

    public MainWindowViewModel() 
    {
        //Initialize list of items:
        Items = new ObservableCollection<Item>()
        {
            new Item { Name = "Item 1", Size = 120 },
            new Item { Name = "Item 2", Size = 150 },
            new Item { Name = "Item 3", Size = 180 },
            new Item { Name = "Item 4", Size = 100 },
            new Item { Name = "Item 5", Size = 225 }
        };

        //Find the biggest size and assign to "SizeOfRectangle"
        double MaxSize = Items[0].Size;
        for (int i = 1; i < Items.Count; i++)
        {
            if (Items[i].Size > MaxSize)
                MaxSize = Items[i].Size;
        }
        SizeOfRectangle = MaxSize; //Width and Height of rectangle bound to this
    }
}

I have initialized a list of 5 items, and made an iteration in the ViewModel which finds the largest size (and sets the height/width of the rectangle to 225 in this case). I want to make it so that when i change the sizes of the items through the datagrid, the program will check if the max size has changed, and the size of the rectangle can change accordingly.

I am expecting I need to make some sort of event, and make use of INotifyPropertyChanged, etc. But currently I am at a bit of a loss as to where to start. I would greatly appreciate any help you can offer.

4

No need to download a toolkit (like a different answer is suggesting), you just need to implement the interfaces. I answer the question on how to handle the data grid edit’s to Size at the bottom of my answer.

Your View Model should be implementing INotifyPropertyChanged Then, after you set the SizeOfRectangle you need to trigger the property changed event so that the binding will refresh. All view models must implement this interface otherwise the initial get on the vm properties will never update. After reading your question a few times it’s clear that you know this.

So add the event and these 2 helper methods after you extend the class

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>// implementing the INotifyPropertyChanged interface
public event PropertyChangedEventHandler PropertyChanged = delegate { };
// add some helper methods to fire the property changed.
public virtual void TriggerPropertyChanged(string propertyName)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = "")
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
</code>
<code>// implementing the INotifyPropertyChanged interface public event PropertyChangedEventHandler PropertyChanged = delegate { }; // add some helper methods to fire the property changed. public virtual void TriggerPropertyChanged(string propertyName) { this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = "") { this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } </code>
// implementing the INotifyPropertyChanged interface
public event PropertyChangedEventHandler PropertyChanged = delegate { };

// add some helper methods to fire the property changed.
public virtual void TriggerPropertyChanged(string propertyName)
{
   this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}

protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = "")
{
        this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}

I always code my properties of a vm using real backend vars.. but your code (if left as-is) would just need to change your existing code like below (after you add the code I posted above):

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>//Find the biggest size and assign to "SizeOfRectangle"
double MaxSize = Items[0].Size;
for (int i = 1; i < Items.Count; i++)
{
if (Items[i].Size > MaxSize)
MaxSize = Items[i].Size;
}
SizeOfRectangle = MaxSize; //Width and Height of rectangle bound to this
TriggerPropertyChanged("SizeOfRectagle"); // update the view
</code>
<code>//Find the biggest size and assign to "SizeOfRectangle" double MaxSize = Items[0].Size; for (int i = 1; i < Items.Count; i++) { if (Items[i].Size > MaxSize) MaxSize = Items[i].Size; } SizeOfRectangle = MaxSize; //Width and Height of rectangle bound to this TriggerPropertyChanged("SizeOfRectagle"); // update the view </code>
//Find the biggest size and assign to "SizeOfRectangle"
double MaxSize = Items[0].Size;
for (int i = 1; i < Items.Count; i++)
{
   if (Items[i].Size > MaxSize)
      MaxSize = Items[i].Size;
}
 
SizeOfRectangle = MaxSize; //Width and Height of rectangle bound to this
TriggerPropertyChanged("SizeOfRectagle"); // update the view

This is how you should code vm properties going forward:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>private double _Size;
public double SizeOfRectangle
{ get { return _Size; }
set
{
if (_Size != value)
{
_Size = value;
OnPropertyChanged(); //call this method without
//having to specify the property.
//The methods [CallerMemberName] will handle at compile time.
}
}
}
</code>
<code>private double _Size; public double SizeOfRectangle { get { return _Size; } set { if (_Size != value) { _Size = value; OnPropertyChanged(); //call this method without //having to specify the property. //The methods [CallerMemberName] will handle at compile time. } } } </code>
private double _Size;
public double SizeOfRectangle 
{ get  { return _Size; }
  set
  {
     if (_Size != value)
     {
          _Size = value;
          OnPropertyChanged();  //call this method without 
                                //having to specify the property.  
                      //The methods [CallerMemberName] will handle at compile time.
     }
  }
}

You are correctly using the ObserrvableCollection<Item> however your Item class is not a “real view model”. Won’t be a problem until you start binding to properties to the Item. You should always dedicate view models and avoid using Domain level classes as view models.

To automatically update the SizeOfRectangle do the following:

Implement the INotifyPropertyChanged on Item class and add those 2 helper methods just like you did in the MainViewModel class.

Your Item class needs to be able to signal the SizeOfRectangle when the Size property is changed. So add a reference to the MainViewModel in the Item.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>public class Item : INotifyPropertyChanged
{
...
public MainViewModel MainViewModel { get; set; }
}
</code>
<code>public class Item : INotifyPropertyChanged { ... public MainViewModel MainViewModel { get; set; } } </code>
public class Item : INotifyPropertyChanged
{
    ...

    public MainViewModel  MainViewModel  { get; set; }  

 }

So when you create an Item instance set the property (set Size last):

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>new Item { Name = "Item 1", MainViewModel = this, Size = 120 }
</code>
<code>new Item { Name = "Item 1", MainViewModel = this, Size = 120 } </code>
new Item { Name = "Item 1",  MainViewModel = this, Size = 120 }

You could always make the MainViewModel implement some interface that updates the SizeOfRectangle if you were concerned with reuse and didn’t want to hard code a VM in the Item class, but for this example we will just use the MainViewModel itself.

Then change the Item setter and use a backend prop:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>private double _Size;
public double Size {
get {
return _Size;
}
set {
if ( _Size != value)
{
_Size = value;
if ( MainViewModel!= null && MainViewModel.SizeOfRectangle < _Size)
{
MainViewModel.SizeOfRectangle = _Size;
}
OnPropertyChanged();
}
}
}
</code>
<code>private double _Size; public double Size { get { return _Size; } set { if ( _Size != value) { _Size = value; if ( MainViewModel!= null && MainViewModel.SizeOfRectangle < _Size) { MainViewModel.SizeOfRectangle = _Size; } OnPropertyChanged(); } } } </code>
private double _Size;
public double Size {
    get {
       return _Size;
    }
    set {
       if ( _Size != value)
       {
           _Size = value;
           if ( MainViewModel!= null && MainViewModel.SizeOfRectangle < _Size)
           {
              MainViewModel.SizeOfRectangle = _Size;
           }
           OnPropertyChanged();

       }
    }
}

18

In order to get notified about changes of their properties, the Item and MainViewModel class would need to implement the INotifyPropertyChanged interface. In the following code snippet, this is done by deriving from the ObservableObject class in the CommunityToolkit.Mvvm package.

MainViewModel attaches a CollectionChanged event handler to its Items property, in which a PropertyChanged event handler is attached to added Items and detached from removed Items.

The PropertyChanged handler in turn notifies about a change of the MainViewModel’s MaxSize property whenever the Size property of an Item changes.

This implementation will update the MaxSize property whenever

  • an Item is added to the collection
  • an Item is removed from the collection
  • an Item’s Size property is modified

It does not cover the case where you call Items.Clear(), which would require another switch section for case NotifyCollectionChangedAction.Reset. It is left out here for brevity.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>public partial class Item : ObservableObject
{
[ObservableProperty]
private string name;
[ObservableProperty]
private double size;
}
public partial class MainViewModel : ObservableObject
{
public ObservableCollection<Item> Items { get; } = [];
public double MaxSize => Items.Select(item => item.Size).Max();
public MainViewModel()
{
Items.CollectionChanged += OnItemsCollectionChanged;
}
private void OnItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
foreach (Item item in e.NewItems)
{
item.PropertyChanged += OnItemPropertyChanged;
}
OnPropertyChanged(nameof(MaxSize));
break;
case NotifyCollectionChangedAction.Remove:
foreach (Item item in e.OldItems)
{
item.PropertyChanged -= OnItemPropertyChanged;
}
OnPropertyChanged(nameof(MaxSize));
break;
default:
break;
}
}
private void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(Item.Size))
{
OnPropertyChanged(nameof(MaxSize));
}
}
}
</code>
<code>public partial class Item : ObservableObject { [ObservableProperty] private string name; [ObservableProperty] private double size; } public partial class MainViewModel : ObservableObject { public ObservableCollection<Item> Items { get; } = []; public double MaxSize => Items.Select(item => item.Size).Max(); public MainViewModel() { Items.CollectionChanged += OnItemsCollectionChanged; } private void OnItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { switch (e.Action) { case NotifyCollectionChangedAction.Add: foreach (Item item in e.NewItems) { item.PropertyChanged += OnItemPropertyChanged; } OnPropertyChanged(nameof(MaxSize)); break; case NotifyCollectionChangedAction.Remove: foreach (Item item in e.OldItems) { item.PropertyChanged -= OnItemPropertyChanged; } OnPropertyChanged(nameof(MaxSize)); break; default: break; } } private void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == nameof(Item.Size)) { OnPropertyChanged(nameof(MaxSize)); } } } </code>
public partial class Item : ObservableObject
{
    [ObservableProperty]
    private string name;

    [ObservableProperty]
    private double size;
}

public partial class MainViewModel : ObservableObject
{
    public ObservableCollection<Item> Items { get; } = [];

    public double MaxSize => Items.Select(item => item.Size).Max();

    public MainViewModel()
    {
        Items.CollectionChanged += OnItemsCollectionChanged;
    }

    private void OnItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        switch (e.Action)
        {
            case NotifyCollectionChangedAction.Add:
                foreach (Item item in e.NewItems)
                {
                    item.PropertyChanged += OnItemPropertyChanged;
                }
                OnPropertyChanged(nameof(MaxSize));
                break;
            case NotifyCollectionChangedAction.Remove:
                foreach (Item item in e.OldItems)
                {
                    item.PropertyChanged -= OnItemPropertyChanged;
                }
                OnPropertyChanged(nameof(MaxSize));
                break;
            default:
                break;
        }
    }

    private void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == nameof(Item.Size))
        {
            OnPropertyChanged(nameof(MaxSize));
        }
    }
}

5

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật