Frage

I've got this UserControl defined in XAML and would like to set the ItemsPanelTemplate dynamically in my code behind class (not in the XAML like in the example):

<UserControl>
    <ItemsControl x:Name="Items">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Grid> <!-- I want to add this Grid definition in code behind -->
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition />
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition />
                    </Grid.RowDefinitions>
                </Grid>
            </ItemsPanelTemplate>
       </ItemsControl.ItemsPanel>
    </ItemsControl>
</UserControl>

I tried something like

this.Items.ItemsPanel.Template = new Grid();

but failed miserably. Any help?

Background: I only know the number of grid columns and rows at runtime.

War es hilfreich?

Lösung

You need to create an ItemsPanelTemplate and set it's VisualTree to a FrameworkElementFactory (deprecated) which creates the Grid, or use the XamlReader to parse a XAML-string which specifies the template.

This question contains usage examples of both methods (albeit for a different template property).

An easier method to manipulate the panel at runtime is outlined in this question.

Andere Tipps

You can do as you want by creating MannualCode in code behind as: 1. Create a Method as following which will return a ItemsPanelTemplate

     private ItemsPanelTemplate GetItemsPanelTemplate()
    {
        string xaml = @"<ItemsPanelTemplate   xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
                            <Grid>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition />
                                </Grid.ColumnDefinitions>
                                <Grid.RowDefinitions>
                                    <RowDefinition />
                                </Grid.RowDefinitions>
                            </Grid>
                    </ItemsPanelTemplate>";
        return XamlReader.Parse(xaml) as ItemsPanelTemplate;
    }
  1. Now add this template in your Listbox ItemsPanel as:

       MyListBox.ItemsPanel = GetItemsPanelTemplate();
    

This is working fine for me. Hope this will help.

Keep Coding....:)

In case that you still have some work to do with the elements, you should take the following (extended) code:

First we need a helper in order to get the element:

// --------------------------------------------------------------------
// This function fetches the WrapPanel from oVisual.
private WrapPanel m_FetchWrapPanel (Visual oVisual)
{
  // WrapPanel to be returned
  WrapPanel oWrapPanel = null;
  // number of childs of oVisual
  int iNumberChilds = VisualTreeHelper.GetChildrenCount (oVisual);
  // and running through the childs
  int i = 0;
  while ( ( i < iNumberChilds ) && ( oWrapPanel == null ) )
  { // fetching visual
    Visual oVisualChild = 
      ( VisualTreeHelper.GetChild (oVisual, i) as Visual );
    if ( ( oVisualChild is WrapPanel ) is true )
    { // found
       oWrapPanel = ( oVisualChild as WrapPanel );
    }
    else
    { // checking the childs of oVisualChild 
      oWrapPanel = m_FetchWrapPanel (oVisualChild);
    };
    // checking next child
    i++;
  };
  // returning WrapPanel
  return (oWrapPanel);
}

Now we create the Panel (or something):

// --------------------------------------------------------------------
private void m_SettingTemplate ()
{
  // the online doc recommends to parse the template
  string xaml = 
    @"<ItemsPanelTemplate
          xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
          xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
        <WrapPanel ItemWidth=""150"" MaxWidth=""150""/>
      </ItemsPanelTemplate>";
  // assigning the template
  oMyListView.ItemsPanel = ( System.Windows.Markup.XamlReader.Parse (xaml) as ItemsPanelTemplate );
  // fetching the WrapPanel
  WrapPanel oWrapPanel = m_WrapPanelAusVisualHolen (oMyListView);
  Debug.Assert (oWrapPanel != null);
  if ( oWrapPanel != null )
  { // adjusting the size of the WrapPanel to the ListView
    Binding oBinding = new Binding ("ActualWidth");
    oBinding.Source = oMyListView;
    oWrapPanel.SetBinding (WrapPanel.MaxWidthProperty, oBinding);
  };
}

Here's a XAML based program which uses ItemsPanelTemplate with a Grid:

MainWindow.xaml:

<Window x:Class="WpfTutorialStatusBarGrid.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfTutorialStatusBarGrid"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">

    <DockPanel>

        <StatusBar DockPanel.Dock="Bottom">

            <StatusBar.ItemsPanel>

                <ItemsPanelTemplate>

                    <Grid>

                        <Grid.ColumnDefinitions>

                            <ColumnDefinition Width="100" />
                            <ColumnDefinition Width="Auto" />
                            <ColumnDefinition Width="*" />
                            <ColumnDefinition Width="Auto" />
                            <ColumnDefinition Width="100" />

                        </Grid.ColumnDefinitions>

                    </Grid>

                </ItemsPanelTemplate>

            </StatusBar.ItemsPanel>

            <StatusBarItem Grid.Column="0">
                <TextBlock Name="lblCursorPosition" />
            </StatusBarItem>

            <Separator Grid.Column="1"/>

            <StatusBarItem Grid.Column="2">
                <TextBlock Text="c:\temp\abc.txt"/>
            </StatusBarItem>

            <Separator Grid.Column="3"/>

            <StatusBarItem Grid.Column="4">
                <ProgressBar Value="50" Width="90" Height="16"/>
            </StatusBarItem>

        </StatusBar>

        <TextBox AcceptsReturn="True" Name="txtEditor" SelectionChanged="TxtEditor_SelectionChanged"/>

    </DockPanel>

</Window>

MainWindow.xaml.cs:

using System.Windows;

namespace WpfTutorialStatusBarGrid
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void TxtEditor_SelectionChanged(object sender, RoutedEventArgs e)
        {
            var row = txtEditor.GetLineIndexFromCharacterIndex(txtEditor.CaretIndex);
            var col = txtEditor.CaretIndex - txtEditor.GetCharacterIndexFromLineIndex(row);

            lblCursorPosition.Text = $"Line {row + 1}, Char {col + 1}";
        }
    }
}

It's a simple text editor with a status bar:

enter image description here

Here's the equivalent program with the code in C# instead of XAML:

MainWindow.xaml:

<Window x:Class="WpfTutorialStatusBarGridCs.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfTutorialStatusBarGridCs"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>

    </Grid>
</Window>

MainWindow.xaml.cs:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;

namespace WpfTutorialStatusBarGridCs
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            var dock_panel = new DockPanel();

            Content = dock_panel;


            var status_bar = new StatusBar();

            dock_panel.Children.Add(status_bar);

            DockPanel.SetDock(status_bar, Dock.Bottom);

            var items_panel_template = new ItemsPanelTemplate();

            {
                var grid_factory = new FrameworkElementFactory(typeof(Grid));

                {
                    {
                        var col = new FrameworkElementFactory(typeof(ColumnDefinition));

                        col.SetValue(ColumnDefinition.WidthProperty, new GridLength(100));

                        grid_factory.AppendChild(col);
                    }

                    {
                        var col = new FrameworkElementFactory(typeof(ColumnDefinition));

                        col.SetValue(ColumnDefinition.WidthProperty, new GridLength(1, GridUnitType.Auto));

                        grid_factory.AppendChild(col);
                    }

                    {
                        var col = new FrameworkElementFactory(typeof(ColumnDefinition));

                        col.SetValue(ColumnDefinition.WidthProperty, new GridLength(1, GridUnitType.Star));

                        grid_factory.AppendChild(col);
                    }

                    {
                        var col = new FrameworkElementFactory(typeof(ColumnDefinition));

                        col.SetValue(ColumnDefinition.WidthProperty, new GridLength(1, GridUnitType.Auto));

                        grid_factory.AppendChild(col);
                    }

                    {
                        var col = new FrameworkElementFactory(typeof(ColumnDefinition));

                        col.SetValue(ColumnDefinition.WidthProperty, new GridLength(100));

                        grid_factory.AppendChild(col);
                    }
                }

                items_panel_template.VisualTree = grid_factory;
            }

            status_bar.ItemsPanel = items_panel_template;



            var text_block = new TextBlock();


            {
                var status_bar_item = new StatusBarItem();

                Grid.SetColumn(status_bar_item, 0);

                status_bar_item.Content = text_block;

                status_bar.Items.Add(status_bar_item);
            }

            {
                var separator = new Separator();

                Grid.SetColumn(separator, 1);

                status_bar.Items.Add(separator);
            }

            {
                var status_bar_item = new StatusBarItem();

                Grid.SetColumn(status_bar_item, 2);

                status_bar_item.Content = new TextBlock() { Text = "abc" };

                status_bar.Items.Add(status_bar_item);
            }

            {
                var separator = new Separator();

                Grid.SetColumn(separator, 3);

                status_bar.Items.Add(separator);
            }

            {
                var status_bar_item = new StatusBarItem();

                Grid.SetColumn(status_bar_item, 4);

                status_bar_item.Content = new ProgressBar() { Value = 50, Width = 90, Height = 16 };

                status_bar.Items.Add(status_bar_item);
            }

            {
                var text_box = new TextBox() { AcceptsReturn = true };

                text_box.SelectionChanged += (sender, e) => 
                {
                    var row = text_box.GetLineIndexFromCharacterIndex(text_box.CaretIndex);
                    var col = text_box.CaretIndex - text_box.GetCharacterIndexFromLineIndex(row);

                    text_block.Text = $"Line {row + 1}, Char {col + 1}";
                };

                dock_panel.Children.Add(text_box);
            }
        }
    }
}

The C# version is much more verbose. However, with the help of some extension methods, it can be written in a fluent style, eliminating the intermediate variables:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        var text_block = new TextBlock();

        Content = new DockPanel()

            .AddChildren(

                new StatusBar()
                    .SetDock(Dock.Bottom)
                    .SetItemsPanel(
                        new ItemsPanelTemplate()
                            .SetVisualTree(
                                new FrameworkElementFactory(typeof(Grid))
                                    .AppendChildren(
                                        new FrameworkElementFactory(typeof(ColumnDefinition))
                                            .SetValue_(ColumnDefinition.WidthProperty, new GridLength(100)),
                                        new FrameworkElementFactory(typeof(ColumnDefinition))
                                            .SetValue_(ColumnDefinition.WidthProperty, new GridLength(1, GridUnitType.Auto)),
                                        new FrameworkElementFactory(typeof(ColumnDefinition))
                                            .SetValue_(ColumnDefinition.WidthProperty, new GridLength(1, GridUnitType.Star)),
                                        new FrameworkElementFactory(typeof(ColumnDefinition))
                                            .SetValue_(ColumnDefinition.WidthProperty, new GridLength(1, GridUnitType.Auto)),
                                        new FrameworkElementFactory(typeof(ColumnDefinition))
                                            .SetValue_(ColumnDefinition.WidthProperty, new GridLength(100)))))
                    .AddItems(
                        new StatusBarItem() { Content = text_block }.SetColumn(0),
                        new Separator().SetColumn(1),
                        new StatusBarItem() { Content = new TextBlock() { Text = "abc" } }.SetColumn(2),
                        new Separator().SetColumn(3),
                        new StatusBarItem() { Content = new ProgressBar() { Value = 50, Width = 90, Height = 16 } }.SetColumn(4)),

                new TextBox() { AcceptsReturn = true }
                    .AddSelectionChanged(
                        (sender, e) =>
                        {
                            var box = sender as TextBox;

                            var row = box.GetLineIndexFromCharacterIndex(box.CaretIndex);
                            var col = box.CaretIndex - box.GetCharacterIndexFromLineIndex(row);

                            text_block.Text = $"Line {row + 1}, Char {col + 1}";
                        }));
    }
}

Here are the extension methods used:

public static class Extensions
{
    public static T SetDock<T>(this T element, Dock dock) where T : UIElement
    {
        DockPanel.SetDock(element, dock);

        return element;
    }

    public static T SetColumn<T>(this T element, int value) where T : UIElement
    {
        Grid.SetColumn(element, value);

        return element;
    }

    public static T SetValue_<T>(this T factory, DependencyProperty dp, object value) where T : FrameworkElementFactory
    {
        factory.SetValue(dp, value);

        return factory;
    }

    public static T AppendChildren<T>(this T factory, params FrameworkElementFactory[] children) where T : FrameworkElementFactory
    {
        foreach (var child in children)
            factory.AppendChild(child);

        return factory;
    }

    public static T SetVisualTree<T>(this T template, FrameworkElementFactory factory) where T : FrameworkTemplate
    {
        template.VisualTree = factory;

        return template;
    }

    public static T1 SetItemsPanel<T1,T2>(this T1 control, T2 template) where T1 : ItemsControl where T2 : ItemsPanelTemplate
    {
        control.ItemsPanel = template;

        return control;
    }

    public static T AddItems<T>(this T control, params object[] items) where T : ItemsControl
    {
        foreach (var item in items)
            control.Items.Add(item);

        return control;
    }

    public static T AddSelectionChanged<T>(this T obj, RoutedEventHandler handler) where T : TextBoxBase
    {
        obj.SelectionChanged += handler;

        return obj;
    }

    public static T1 AddChildren<T1>(this T1 panel, params UIElement[] elements) where T1 : Panel
    {
        foreach (var elt in elements)
            panel.Children.Add(elt);

        return panel;
    }

}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top