3
votes

I'm working on a dynamicaly created grid containing in each new column a new item added to an ItemsControl.

I'm using Rachel Lim's GridHelper

For now, i have a main window like following,

Xaml code:

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfApplication1"
    Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <local:StarsConverter x:Key="conv"/>
    </Window.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="9*"/>
        </Grid.ColumnDefinitions>
        <Button Grid.Column="0" Command="{Binding Add}"/>
        <ItemsControl x:Name="list" Grid.Column="1" ItemsSource="{Binding Oc}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Grid x:Name="grid" Background="Black"
                      local:GridHelpers.StarColumns="{Binding ColumnCount, Converter={StaticResource conv}, Mode=OneWay}" local:GridHelpers.ColumnCount="{Binding ColumnCount}"/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Canvas Loaded="canvas_Loaded" x:Name="canvas" Background="White">
                        <Border  Canvas.Left="25" Canvas.Top="25" Height="25" Width="50" Background="Red">
                            <TextBlock Text="{Binding}"/>
                        </Border>
                    </Canvas>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </Grid>
</Window>


and Xaml.cs

public partial class MainWindow : Window
{
    private Canvas canvas;
    public MainWindow()
    {
        InitializeComponent();

        this.DataContext = new ViewModel();
    }

    private void canvas_Loaded(object sender, RoutedEventArgs e)
    {
        ViewModel vm = this.DataContext as ViewModel;
        canvas = sender as Canvas;
        Binding b = new Binding();
        b.Source = vm;
        b.Path = new PropertyPath(ViewModel.ColumnCountProperty);
        b.Mode = BindingMode.OneWay;
        canvas.SetBinding(Grid.ColumnProperty, b);
    }
}

This MainWindow has a ViewModel as DataContext:

public class ViewModel : DependencyObject
{
    private RelayCommand add;

    public ViewModel()
    {
    }

    public ObservableCollection<String> Oc
    {
        get { return (ObservableCollection<String>)GetValue(OcProperty); }
        set { SetValue(OcProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Oc.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty OcProperty =
        DependencyProperty.Register("Oc", typeof(ObservableCollection<String>), typeof(ViewModel), new UIPropertyMetadata(new ObservableCollection<string>()));

    public int ColumnCount
    {
        get { return (int)GetValue(ColumnCountProperty); }
        set { SetValue(ColumnCountProperty, value); }
    }

    // Using a DependencyProperty as the backing store for ColumnCount.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty ColumnCountProperty =
        DependencyProperty.Register("ColumnCount", typeof(int), typeof(ViewModel), new UIPropertyMetadata(0));

    public RelayCommand Add
    {
        get
        {
            if (this.add == null)
                this.add = new RelayCommand(param => this.AddString(), param => this.CanAddString());
            return this.add;
        }
    }

    private bool CanAddString()
    {
        return true;
    }

    private void AddString()
    {
        this.Oc.Add("test" + ColumnCount);
        ColumnCount++;
    }
}

When i click the button, the command is doing fine, so i have a new itemscontrol item and my grid as ItemsPanelTemplate is updating with new ColumnDefinition but the item is not in the right column.

2

2 Answers

3
votes

I know its a late answer :)
A more elegant solution would be adding two Dependency Properties Row and Column to your Items ViewModel

then in your ItemsControl you define an ItemContainerStyle:

<ItemsControl>
     <ItemsControl.ItemContainerStyle>
       <Style>                            
          <Setter Property="Grid.Row" Value="{Binding Path=Row}" />
          <Setter Property="Grid.Column" Value="{Binding Path=Column}" />
       </Style>                  
    </ItemsControl.ItemContainerStyle>
</ItemsControl>

when you populate your ItemsSource collection you have set The Row and Column properties:

for (uint i = 0; i < yourCollection.Count; ++i )
{
    var item = yourCollection[i];
    item.Row = (int)(i / ColumnCount);
    item.Column = (int)(i % ColumnCount);
}
1
votes

In fact the canvas_loaded thing is useless...

The problem was coming from the GridHelpers class in the ColumnCountChanged.

I added this at the end of the ColumnCountChanged event after SetStarColumns(grid) in order to add the items in the corresponding cells:

for (int i = 0; i < childCount; i++)
{
    var child = grid.Children[i] as FrameworkElement;
    Grid.SetColumn(child, i);
}

and it worked!


Edit: I also used a converter (called "conv" in the Xaml sample above) for the OneWay binding of the GridHelper.StarColumns Property on a ColumnCount ViewModel Property to have all the newly created columns to be equally sized:

public class StarsConverter : IValueConverter
{
    public object Convert(object value, Type TargetType, object parameter, CultureInfo culture)
    {
        int i = (int)value;
        string s = "0";
        for (int j = 1; j < i; j++)
        {
            s = s + ',' + j;

        }
        return s;
    }
    public object ConvertBack(object value, Type TargetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}