Wpf ItemsControl.ItemsPanelTemplate como una cuadrícula y agregue nuevos elementos en diferentes columnas

Estoy trabajando en una cuadrícula creada dinámicamente que contiene en cada nueva columna un nuevo elemento agregado a un ItemsControl.

Estoy usando el de Rachel Lim GridHelper

Por ahora, tengo una ventana principal como la siguiente,

Código Xaml:

<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>


y 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);
    }
}

Esta MainWindow tiene un ViewModel como 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++;
    }
}

Cuando hago clic en el botón, el comando funciona bien, por lo que tengo un nuevo elemento de control de elementos y mi cuadrícula como ItemsPanelTemplate se actualiza con una nueva definición de columna, pero el elemento no está en la columna de la derecha.

preguntado el 03 de julio de 12 a las 23:07

2 Respuestas

Sé que es una respuesta tardía :)
Una solución más elegante sería agregar dos filas y columnas de propiedades de dependencia a su modelo de vista de elementos

luego en su ItemsControl define un 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>

cuando completa su colección ItemsSource, ha establecido las propiedades de Fila y Columna:

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

Respondido 21 ago 13, 13:08

De hecho, la cosa canvas_loaded es inútil ...

El problema provenía de la clase GridHelpers en ColumnCountChanged.

Agregué esto al final del evento ColumnCountChanged después de SetStarColumns(grid) para agregar los elementos en las celdas correspondientes:

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

¡Y funcionó!


Edit: También utilicé un convertidor (llamado "conv" en el ejemplo de Xaml anterior) para el enlace OneWay de la propiedad GridHelper.StarColumns en una propiedad ColumnCount ViewModel para que todas las columnas recién creadas tengan el mismo tamaño:

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();
    }
}

Respondido 04 Jul 12, 22:07

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.