wpfbindingperformancerelativesourceelementname

Connecting width/height of cell in two different controls?


I need to create two controls that contain the same amound of items (a dynamic amount), the first control represents the keys, the second represents the values.

I need it so that when the user resizes the upper column width it should affect the same column in the lower row (of the values).

Here is an example to what I desire:

<Window 
  DataContext="{Binding RelativeSource={RelativeSource Self}}"
  x:Class="MainWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Window.Resources>
    <ItemsPanelTemplate x:Key="ItemsPanelTemplate">
      <VirtualizingStackPanel Orientation="Horizontal"/>
    </ItemsPanelTemplate>
  </Window.Resources>
  <StackPanel>
    <ItemsControl ItemsSource="{Binding Keys}" 
                  ItemsPanel="{StaticResource ItemsPanelTemplate}"/>
    <ItemsControl Grid.Row="1" ItemsSource="{Binding Values}" 
                  ItemsPanel="{StaticResource ItemsPanelTemplate}"/>
  </StackPanel>
</Window>

Imports System.Collections.Specialized
Class MainWindow
  Private Sub Window_Loaded(ByVal sender As Object,
                            ByVal e As RoutedEventArgs) Handles MyBase.Loaded
    DataContext =
      New StringDictionary From
      {
        {"key1", "value1"},
        {"key2", "value2"},
        {"key3", "value3"},
        {"key4", "value4"}
      }
  End Sub
End Class

Result:

Again, I want to be able to create a DataGrid-like control that even supports cell borders and cell widths and heights should be connected to the other controls' width + allow resize.

I prefer it to be done xamly. Note: it's a custom control, so I can declare appropriate properties if necessary. but remember the cells' heights and width has to be dynamic and individual to specific columns/rows.

In reference to this question, I created it in a slightly different way (having a third control for cells), but the question is still the same, I want the height width of the columns and cells to be dynamic, and give the user ability to resize them affecting each other.

UPDATE

decyclone's answer is something I would love to implement, but I tried the example he provided setting the ItemsControls' Grid.IsSharedSizeScope property to true, but it didn't work, here is the result (cropped):

Is it possible to apply a shared size scope between two different controls?


Solution

  • I tried something and seems to work :

    XAML :

    <Window x:Class="WpfApplication2.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:WpfApplication2"
            Title="MainWindow"
            Height="350"
            Width="525">
        <Window.Resources>
            <local:GroupNameGenerator x:Key="GroupNameGenerator1" />
            <local:GroupNameGenerator x:Key="GroupNameGenerator2" />
        </Window.Resources>
        <Grid>
            <StackPanel Grid.IsSharedSizeScope="True">
                <ItemsControl Name="ItemsControl1">
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <StackPanel Orientation="Horizontal" />
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <Grid>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition SharedSizeGroup="{Binding Converter={StaticResource GroupNameGenerator1}}" />
                                </Grid.ColumnDefinitions>
                                <Border BorderBrush="Black"
                                        BorderThickness="1"
                                        Margin="5"
                                        Padding="5">
                                    <TextBlock Text="{Binding}" />
                                </Border>
                            </Grid>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
                <ItemsControl Name="ItemsControl2">
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <StackPanel Orientation="Horizontal" />
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <Grid>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition SharedSizeGroup="{Binding Converter={StaticResource GroupNameGenerator2}}" />
                                </Grid.ColumnDefinitions>
                                <Border BorderBrush="Black"
                                        BorderThickness="1"
                                        Margin="5"
                                        Padding="5">
                                    <TextBlock Text="{Binding}" />
                                </Border>
                            </Grid>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </StackPanel>
        </Grid>
    </Window>
    

    Code :

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    using System.Collections.ObjectModel;
    
    namespace WpfApplication2
    {
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
            ObservableCollection<Int32> list1 = new ObservableCollection<Int32>();
            ObservableCollection<String> list2 = new ObservableCollection<String>();
    
            public MainWindow()
            {
                InitializeComponent();
    
                for (int i = 0; i < 25; i++)
                {
                    list1.Add(i + 1);
                    list2.Add(new String('0', ((i + 1) / 3)));
                }
    
                ItemsControl1.ItemsSource = list1;
                ItemsControl2.ItemsSource = list2;
            }
        }
    
        public class GroupNameGenerator : IValueConverter
        {
            public Int32 Index { get; set; }
    
            public GroupNameGenerator()
            {
                Index = 0;
            }
    
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                return String.Format("Group{0}", ++Index);
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                throw new NotImplementedException();
            }
        }
    }