【问题标题】:How to set ItemsPanelTemplate to a dynamically created Grid in code behind如何在后面的代码中将 ItemsPanelTemplate 设置为动态创建的 Grid
【发布时间】:2012-01-13 22:36:48
【问题描述】:

我在XAML 中定义了这个UserControl,并希望在我的代码后面的类中动态设置ItemsPanelTemplate(而不是像示例中的XAML):

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

我尝试了类似的东西

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

但惨遭失败。有什么帮助吗?

背景: 我只知道运行时的网格列数和行数。

【问题讨论】:

    标签: c# wpf xaml itemscontrol itemspaneltemplate


    【解决方案1】:

    您需要创建一个ItemsPanelTemplate 并将它的VisualTree 设置为创建GridFrameworkElementFactory(已弃用),或使用XamlReaderparse 一个指定模板的XAML 字符串.

    This question 包含两种方法的使用示例(尽管针对不同的模板属性)。

    this question 中概述了在运行时操作面板的更简单方法。

    【讨论】:

    • 好吧,我不得不承认我是一个 WPF 初学者,我完全不明白你的答案(我的错)。我希望我可以创建网格(new Grid)并将模板设置为动态创建的网格(就像我的问题中暗示的那样)。你能举个例子吗?
    • @nabulke:你看过链接的问题了吗?
    • 现在检查示例。感谢您的补充。我希望有一种更简单的方法可以在运行时设置网格行/列的数量。
    • 好吧,如果你想从头开始定义它,这就是这样做的方法,如果你只是想编辑它(你可以添加一个 Grid 没有定义)this question here 可能是什么您正在寻找。
    • 您提供的链接中发布的解决方案效果很好:我添加了一个加载的事件处理程序,它将列/行添加到 XAML 中定义的网格。感谢您的帮助。
    【解决方案2】:

    您可以通过在后面的代码中创建 MannualCode 来做您想做的事: 1. 创建一个方法如下,它将返回一个 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. 现在将此模板添加到您的 Listbox ItemsPanel 中:

         MyListBox.ItemsPanel = GetItemsPanelTemplate();
      

    这对我来说很好用。希望这会有所帮助。

    继续编码....:)

    【讨论】:

      【解决方案3】:

      如果您仍然需要处理元素,您应该使用以下(扩展)代码:

      首先我们需要一个助手来获取元素:

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

      现在我们创建面板(或其他东西):

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

      【讨论】:

        【解决方案4】:

        这是一个基于 XAML 的程序,它使用 ItemsPanelTemplateGrid

        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}";
                }
            }
        }
        

        这是一个带有状态栏的简单文本编辑器:

        这是使用 C# 代替 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);
                    }
                }
            }
        }
        

        C# 版本更加冗长。不过,借助一些扩展方法,可以写出流畅的风格,省去中间变量:

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

        这里是使用的扩展方法:

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

        【讨论】:

          【解决方案5】:

          对于其他人...

          ...基于 ItemsControl 的控件,具有 Orientation 属性。

          它像之前的答案一样使用 FrameworkElementFactory:

          public class OrientationItemsControl : ItemsControl
          {
          
              public static readonly DependencyProperty OrientationProperty = WrapPanel.OrientationProperty.AddOwner(typeof(OrientationItemsControl), new PropertyMetadata(Changed));
          
              private static void Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
              {
                  if (d is ItemsControl itemsControl && e.NewValue is Orientation orientation)
                  {
                      var factory = new FrameworkElementFactory(typeof(StackPanel));
                      factory.SetValue(OrientationProperty, orientation);
                      itemsControl.ItemsPanel = TemplateGenerator.CreateItemsPanelTemplate(factory);
                  }
              }
              
              public Orientation Orientation
              {
                  get => (Orientation)GetValue(OrientationProperty);
                  set => SetValue(OrientationProperty, value);
              }
          }
          

          【讨论】:

            猜你喜欢
            • 2017-11-17
            • 2023-01-02
            • 2023-03-09
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-05-19
            • 1970-01-01
            相关资源
            最近更新 更多