Cómo establecer elementospaneltemplate en una cuadrícula creada dinámicamente en código detrás
-
28-10-2019 - |
Pregunta
tengo esto UserControl
definido en XAML
y me gustaría establecer el ItemsPanelTemplate
dinámicamente en mi código detrás de clase (no en el XAML
como en el ejemplo):
<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>
Intenté algo como
this.Items.ItemsPanel.Template = new Grid();
pero falló miserablemente. ¿Alguna ayuda?
Fondo:Solo sé el número de columnas y filas de cuadrícula en tiempo de ejecución.
Solución
Necesitas crear un ItemsPanelTemplate
Y establece que es VisualTree
a un FrameworkElementFactory
(desaprobado) que crea el Grid
, o usar el XamlReader
a analizar gramaticalmente Una cuerda XAML que especifica la plantilla.
Esta pregunta Contiene ejemplos de uso de ambos métodos (aunque para una propiedad de plantilla diferente).
Un método más fácil para manipular el panel en tiempo de ejecución se describe en esta pregunta.
Otros consejos
Puede hacer lo que desee creando MannualCode en código detrás como: 1. Cree un método que siga el que devolverá un elemento PanelTemplate
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;
}
Ahora agregue esta plantilla en su ListBox ElememsPanel como:
MyListBox.ItemsPanel = GetItemsPanelTemplate();
Esto funciona bien para mí. Espero que esto ayude.
Sigue codificando .... :)
En caso de que aún tenga algo de trabajo que ver con los elementos, debe tomar el siguiente código (extendido):
Primero necesitamos un ayudante para obtener el elemento:
// --------------------------------------------------------------------
// 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);
}
Ahora creamos el panel (o algo):
// --------------------------------------------------------------------
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);
};
}
Aquí hay un programa basado en XAML que utiliza ItemsPanelTemplate
con un 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}";
}
}
}
Es un editor de texto simple con una barra de estado:
Aquí está el programa equivalente con el código en C# en lugar de 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);
}
}
}
}
La versión C# es mucho más detallada. Sin embargo, con la ayuda de algunos métodos de extensión, se puede escribir en un estilo fluido, eliminando las variables intermedias:
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}";
}));
}
}
Aquí están los métodos de extensión utilizados:
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;
}
}