Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Itemscontrol that arrange Items with Gridsplitter-Functionality

I need a layout that stacks Items horizontal or vertical. Each Item should be separated by a Splitter, so the User can change the Size (Like the Toolbox Container in VisualStudio) The Items come from a Model, so I need a ItemsControl where I can set the ItemsSource.

My first idea was to build a CustomControl derived from Grid. Their I used the "OnVisualChildrenChanged" Method to react when a new Item is added. Then I wantet to add new Column-/Rowdefinitions (Depending wether it schould be horizontal or vertical). But here was the first Proplem. When the Grid is set to IsItemsHost="true" I get a Error that the DefinitionsCollection is not accessable...

Heres a CodeSnipped from the custom control, where I tried to add a row.

 private void SetRows()
    {
       this.RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto };             
    }

    protected override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved)
    {
        base.OnVisualChildrenChanged(visualAdded, visualRemoved);
        SetRows();            
    }

EDIT:

I think I found a solution right now.

First I created a CustomControl that derives from ItemsControl. In his template I add a Grid without inserting a Itempresenter. Now I override OnItemsChange. When a Item is added to the Collection I create a FrameworkElement and set DataContext to the added Item, than I add two Column definition to the Grid and add my Item plus an Gridsplitter to the Grid. This works well, now I only need to get the ItemTemplate from my Itemscontrol to use it in the grid.

When I solved this last issue I will add a small example.

like image 935
waits83 Avatar asked Apr 02 '14 13:04

waits83


1 Answers

Heres my Solution so far. If someone has a better idea, you're welcome.

Heres the CustomControl Code behind:

public class ResizableItemControl : ItemsControl
{
    public ObservableCollection<FrameworkElement> _gridItems = new ObservableCollection<FrameworkElement>();
    private Grid _grid;

    static ResizableItemControl()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(ResizableItemControl), new FrameworkPropertyMetadata(typeof(ResizableItemControl)));
    }

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();

        if (this.Template != null)
        {
            _grid = this.Template.FindName("PART_Grid", this) as Grid;
        }

        // Add all existing items to grid
        foreach (var item in Items)
        {
            AddItemToGrid(item);
        }
    }

    /// <summary>
    /// Called when Items in ItemsCollection changing
    /// </summary>
    /// <param name="e"></param>
    protected override void OnItemsChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        base.OnItemsChanged(e);
        switch (e.Action)
        {
            case NotifyCollectionChangedAction.Add:
                if (Items.Count > 0)
                {
                    //Add Items in Grid when new Items where add
                    var myItem = this.Items[Items.Count - 1];
                    AddItemToGrid(myItem);
                }
                break;
        }


    }

    /// <summary>
    ///  Adds Items to grid plus GridSplitter
    /// </summary>
    /// <param name="myItem"></param>
    private void AddItemToGrid(object myItem)
    {
        var visualItem = this.ItemTemplate.LoadContent() as FrameworkElement;
        if (visualItem != null)
        {
            visualItem.DataContext = myItem;

            if (_grid != null)
            {
                if (_grid.ColumnDefinitions.Count != 0)
                {
                    _grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Auto) });
                    _grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Auto) });
                    var gridSplitter = CreateSplitter();
                    Grid.SetColumn(gridSplitter, _grid.ColumnDefinitions.Count - 2);
                    Grid.SetColumn(visualItem, _grid.ColumnDefinitions.Count - 1);
                    _grid.Children.Add(gridSplitter);
                    _grid.Children.Add(visualItem);

                    //_grid.Children.Add(myTest);
                }
                else
                {
                    _grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Auto) });
                    Grid.SetColumn(visualItem, _grid.ColumnDefinitions.Count - 1);
                    _grid.Children.Add(visualItem);
                }
            }
        }

    }

    private static GridSplitter CreateSplitter()
    {
        var gridSplitter = new GridSplitter() {ResizeDirection = GridResizeDirection.Columns};
        gridSplitter.Width = 5;
        gridSplitter.HorizontalAlignment = HorizontalAlignment.Stretch;
        gridSplitter.Background = new SolidColorBrush(Colors.Black);
        return gridSplitter;
    }
}

The generic Xaml:

<Style TargetType="{x:Type controls:ResizableItemControl}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type controls:ResizableItemControl}">
                <Border Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                    <ScrollViewer HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Hidden">
                        <Grid x:Name="PART_Grid"></Grid>
                    </ScrollViewer>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

And how to use it:

            <controls:ResizableItemControl
                ItemsSource="{Binding ElementName=this,Path=Items}">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Label
                            Content="{Binding Name}"
                            ToolTip="{Binding Description}"
                            Background="Black"/>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </controls:ResizableItemControl>
like image 114
waits83 Avatar answered Oct 01 '22 13:10

waits83