WPF Dynamic change of GridViewColumn CellTemplate c# - c#

I'm trying to learn something about the ListView and now I'm solving this problem:
I have a listview defined in the behindcode. I would like to change gridviewcolumn celltemplate dynamically. For example by the use of checkbox or button, or other. Is it even possible?
Definition of my ListView is here:
lvUsers.ItemsSource = LoadListViewData();
GridView gridview = new GridView();
lvUsers.View = gridview;
DataTemplate templateCheck = new DataTemplate();
FrameworkElementFactory factoryContentControlCheck = new FrameworkElementFactory(typeof(VsCheckBox));
factoryContentControlCheck.SetValue(VsCheckBox.MarginProperty, new Thickness(0, 0, 0, 0));
DataTemplate templateBorder = new DataTemplate();
FrameworkElementFactory factoryContentControlBorder = new FrameworkElementFactory(typeof(Border));
factoryContentControlBorder.SetValue(Border.MarginProperty, new Thickness(0, 0, 10, 0));
factoryContentControlBorder.SetValue(Border.WidthProperty, Width = 10);
factoryContentControlBorder.SetValue(Border.HeightProperty, Height = 10);
factoryContentControlBorder.SetValue(Border.BackgroundProperty, Brushes.Red);
DataTemplate templateAge = new DataTemplate();
FrameworkElementFactory factoryContentControlAge = new FrameworkElementFactory(typeof(ContentControl));
factoryContentControlName.SetValue(ContentControl.MarginProperty, new Thickness(0, 0, 10, 0));
factoryContentControlAge.SetValue(ContentControl.VerticalAlignmentProperty, VerticalAlignment.Center);
factoryContentControlAge.SetValue(ContentControl.HorizontalAlignmentProperty, HorizontalAlignment.Right);
factoryContentControlAge.SetBinding(ContentControl.ContentProperty, new Binding("Age"));
DataTemplate templateStack = new DataTemplate();
FrameworkElementFactory factoryContentControlStack = new FrameworkElementFactory(typeof(StackPanel));
factoryContentControlStack.SetValue(StackPanel.MarginProperty, new Thickness(10, 0, 0, 0));
factoryContentControlStack.SetValue(StackPanel.OrientationProperty, Orientation.Horizontal);
factoryContentControlStack.SetValue(StackPanel.VerticalAlignmentProperty, VerticalAlignment.Center);
factoryContentControlStack.AppendChild(factoryContentControlCheck);
factoryContentControlStack.AppendChild(factoryContentControlBorder);
templateStack.VisualTree = factoryContentControlStack;
DataTemplate templateStack1 = new DataTemplate();
FrameworkElementFactory factoryContentControlStack1 = new FrameworkElementFactory(typeof(StackPanel));
factoryContentControlStack1.SetValue(StackPanel.MarginProperty, new Thickness(10, 0, 0, 0));
factoryContentControlStack1.SetValue(StackPanel.OrientationProperty, Orientation.Horizontal);
factoryContentControlStack1.SetValue(StackPanel.HorizontalAlignmentProperty, HorizontalAlignment.Right);
factoryContentControlStack1.SetValue(StackPanel.VerticalAlignmentProperty, VerticalAlignment.Center);
factoryContentControlStack1.AppendChild(factoryContentControlAge);
templateStack1.VisualTree = factoryContentControlStack1;
GridViewColumn colStack = new GridViewColumn();
colStack.Header = "Stack";
colStack.CellTemplate = templateStack;
gridview.Columns.Add(colStack);
I would like to change CellTemplate of colStack to templateStack1 in runtime by the checking a checkbox or button click.
Thank you for any of your ideas.

You can make use of DataTrigger in order to change dynamically the ContentTemplate of your columns. Here is an example using XAML:
<Window x:Class="WpfApp.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"
mc:Ignorable="d"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<CheckBox x:Name="TemplateChanger" Content="Change template"
IsChecked="{Binding IsChecked}"/>
<DataGrid x:Name="DataGrid" Grid.Row="1" AutoGenerateColumns="False"
ItemsSource="{Binding Items}">
<DataGrid.Columns>
<DataGridTemplateColumn Header="My column" IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Foo}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellStyle>
<Style TargetType="DataGridCell">
<Style.Triggers>
<DataTrigger Binding="{Binding DataContext.IsChecked, RelativeSource={RelativeSource AncestorType=DataGrid}}" Value="True">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="fsdfsdf"/>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
And the codebehind file:
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using WpfApp.Annotations;
namespace WpfApp
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : INotifyPropertyChanged
{
private bool _isChecked;
public MainWindow()
{
InitializeComponent();
Items.Add(new Item {Foo = "Foo1"});
Items.Add(new Item {Foo = "Foo2"});
Items.Add(new Item {Foo = "Foo3"});
Items.Add(new Item {Foo = "Foo4"});
}
public bool IsChecked
{
get => _isChecked;
set
{
_isChecked = value;
OnPropertyChanged();
}
}
public ObservableCollection<Item> Items { get; } = new ObservableCollection<Item>();
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Item
{
public string Foo { get; set; }
}
}
The result is that whenever you toggle the checkbox's IsChecked property the content template of the cell for the given column changes too.
EDIT How to do it with code behind only
For completness sake here is how you can achieve this with code behind only:
var datagrid = new DataGrid {AutoGenerateColumns = false};
Grid.SetRow(datagrid,1);
RootGrid.Children.Add(datagrid);
var templateColumn = new DataGridTemplateColumn
{
Header = "My column",
IsReadOnly = true
};
var cellTemplate = new DataTemplate();
var factory = new FrameworkElementFactory(typeof(TextBlock));
factory.SetBinding(TextBlock.TextProperty, new Binding("Foo"));
var style = new Style(typeof(DataGridCell));
var trigger = new DataTrigger();
var triggerBinding = new Binding("DataContext.IsChecked")
{
RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(DataGrid), 1)
};
trigger.Binding = triggerBinding;
trigger.Value = true;
var triggerSetter = new Setter {Property = ContentTemplateProperty};
var triggerTemplate = new DataTemplate();
var anotherFactory = new FrameworkElementFactory(typeof(TextBlock));
anotherFactory.SetValue(TextBlock.TextProperty,"lol");
triggerTemplate.VisualTree = anotherFactory;
triggerSetter.Value = triggerTemplate;
trigger.Setters.Add(triggerSetter);
style.Triggers.Add(trigger);
templateColumn.CellStyle = style;
cellTemplate.VisualTree = factory;
templateColumn.CellTemplate = cellTemplate;
datagrid.Columns.Add(templateColumn);
datagrid.ItemsSource = Items;
IMHO this way looks a little bit messy, but the decision is yours =)

Related

I need to Delete a Control inside a WrapPanel created dynamically inside another WrapPanel while in Run-Time

So, I have a main WrapPanel called "valoresPanel". When I start running I need to click the Button labeled "2" (below) and a TextBox needs to appear inside the WrapPanel labeled "1" thats was created in runtime.
This is my code for the "+ button" right now:
void novoValor_Click(object sender, RoutedEventArgs e)
{
WrapPanel wpValores = new WrapPanel();
Button deleteValor = new Button();
TextBox txtValor = new TextBox();
deleteValor.Height = 25;
deleteValor.Width = 25;
deleteValor.Content = "X";
txtValor.Height = 25;
txtValor.Width = 70;
txtValor.Margin = new Thickness(0, 0, 8, 0);
wpValores.Height = 25;
wpValores.Width = 105;
wpValores.Children.Add(deleteValor);
wpValores.Children.Add(txtValor);
valoresPanel.Children.Add(wpValores);
deleteValor.Click += deleteValor2_Click;
}
So, i updated my code using only one WrapPanel per item, now i can add a item, add values and delete the item with its respective values but i cant delete a specific value, this is my code by now:
this image will help to understand
void novoValor_Click(object sender, RoutedEventArgs e)
{
Button btn = sender as Button;
WrapPanel wpFather = btn.Parent as WrapPanel;
WrapPanel wpValue = new WrapPanel();
Button deleteValue = new Button();
TextBox txtValue = new TextBox();
wpValue.Height = 25;
wpValue.Width = 105;
deleteValue.Height = 25;
deleteValue.Width = 25;
deleteValue.Content = "-";
txtValue.Height = 25;
txtValue.Width = 70;
txtValue.Margin = new Thickness(0, 0, 8, 0);
wpValue.Children.Add(deleteValue);
wpValue.Children.Add(txtValue);
wpFather.Children.Add(wpValue);
deleteValue.Click += deleteValor_Click;
}
void deleteValor_Click(object sender, RoutedEventArgs e)
{
Button btn = sender as Button;
WrapPanel panel = btn.Parent as WrapPanel;
entradasPanel.Children.Remove(panel);
}
If someone need any other information im willing to send it as fast as I can!
You should create a ListView which has a WrapPanel as ItemsPanel. Then to the ListView.ItemsSource you bind an ObservableCollection of data models. By defining a DataTemplate for the ListView.ItemTemplate you can make the data items be displayed as a TextBox with a Button where the TextBox binds to this item's data model. By pressing the delete button you simply remove this data model from the ObservaleColection ( the ItemsSource of the ListView).
DataModel.cs
class DataModel
{
public string UserInput { get; set; }
}
ViewModel.cs
class ViewModel
{
public ObservableCollection<DataModel> Items { get; set; }
public ICommand AddItemCommand => new AsyncRelayCommand(() => this.Items.Add(new DataModel()));
public ICommand RemoveItemCommand => new AsyncRelayCommand((item) => this.Items.Remove(item));
public ViewModel()
{
this.Items = new ObservableCollection<DataModel>();
}
}
MainWindow.xaml
<Window>
<Window.DataContext>
<ViewModel />
</Window.DataContext>
<StackPanel>
<Button Content="Add Item"
Command="{Binding AddItemCommand}" />
<ListView ItemsSource="{Binding Items}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Width="600" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate DataType="{x:Type DataModel}">
<StackPanel Orientation="Horizontal">
<Button Content="Remove Item"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=ListView}, Path=DataContext.RemoveItemCommand}"
CommandParameter="{Binding}" />
<TextBox Text="{Binding UserInput}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</Window>
I could solve my last question with the following code:
void deleteValue_Click(object sender, RoutedEventArgs e)
{
Button btn = sender as Button;
WrapPanel panel = btn.Parent as WrapPanel;
WrapPanel panelPai = panel.Parent as WrapPanel;
panelPai.Children.Remove(panel);
}

Grid is getting smaller after pressing "AddLexemesFromFolder" button

I develop one-page app. This is XAML of MainPage
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel Orientation="Vertical" Grid.Row="0">
<AppBar x:Name="MenuAppBar" IsOpen="True">
<StackPanel Orientation="Horizontal">
<AppBarButton Icon="Add" Label="Добавить лексемы" Name="AddLexemesFromFolder" Click="OpenFolderAndGetLexemes_Click" HorizontalAlignment="Left"/>
<AppBarButton Icon="Save" Label="Сохранить лексемы" Name="SaveLexemes" Click="SaveLexemes_Click" HorizontalAlignment="Left"/>
</StackPanel>
</AppBar>
</StackPanel>
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto" VerticalScrollMode="Enabled">
<Grid x:Name="GridLexemesViewer" HorizontalAlignment="Stretch"/>
</ScrollViewer>
</Grid>
When I pressed "AddLexemesFromFolder" button more than two times, GridLexemesViewer is getting smaller over and over.
This is OpenFolderAndGetLexemes code
private async void OpenFolderAndGetLexemes_Click(object sender, RoutedEventArgs routedEventArgs)
{
await StartSaveLexemes();
var folderPicker = new Windows.Storage.Pickers.FolderPicker();
folderPicker.FileTypeFilter.Add("*");
Windows.Storage.StorageFolder folder = await folderPicker.PickSingleFolderAsync();
if (folder != null)
{
StorageApplicationPermissions.FutureAccessList.AddOrReplace("PickedFolderToken", folder);
await Task.Run(() => StartNewSessionForGetLexemes(folder.Path));
InitializeGrid();
}
}
I use "InitializeGrid" method for clear Children in GridLexemesViewer, use CreateRowsAndColumns and put TextBox with content to GridLexemesViewer.
This is code of InitializeGrid and CreateRowsAndColumns()
private void InitializeGrid()
{
GridLexemesViewer.Children.Clear();
CreateRowsAndColumns();
int index = 1;
foreach (var lexem in CurrentSession.Lexemes)
{
foreach (var item in lexem.Value)
{
Binding binding = new Binding
{
Source = item,
Path = new PropertyPath("Value"),
Mode = BindingMode.TwoWay,
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
};
TextBox textBox = new TextBox { TextWrapping = TextWrapping.Wrap };
BindingOperations.SetBinding(textBox, TextBox.TextProperty, binding);
GridLexemesViewer.Children.Add(textBox);
Grid.SetColumn(textBox, CurrentSession.Languages.IndexOf(item.Language) + 1);
Grid.SetRow(textBox, index);
}
index++;
}
}
private void CreateRowsAndColumns()
{
int indexRow = 1;
int indexColumn = 1;
RowDefinition firstRowDefinition = new RowDefinition();
ColumnDefinition firstColumnDefinition = new ColumnDefinition { Width = GridLength.Auto };
GridLexemesViewer.ColumnDefinitions.Add(firstColumnDefinition);
GridLexemesViewer.RowDefinitions.Add(firstRowDefinition);
foreach (var key in CurrentSession.Lexemes.Keys)
{
RowDefinition rowDefinition = new RowDefinition();
GridLexemesViewer.RowDefinitions.Add(rowDefinition);
TextBlock textBlock = new TextBlock{Text = key};
GridLexemesViewer.Children.Add(textBlock);
Grid.SetRow(textBlock, indexRow);
indexRow++;
}
foreach (var language in CurrentSession.Languages)
{
ColumnDefinition columnDefinition = new ColumnDefinition { Width = new GridLength(1.0, GridUnitType.Star)};
GridLexemesViewer.ColumnDefinitions.Add(columnDefinition);
TextBlock textBlock = new TextBlock {Text = language};
GridLexemesViewer.Children.Add(textBlock);
Grid.SetRow(textBlock, 0);
Grid.SetColumn(textBlock, indexColumn);
indexColumn++;
}
}
This GIF shows how to reproduce bug
The problem is that you are calling CreateRowsAndColumns() each time but not removing the Rows and Columns from previous run. Using Grid.Clear() only deletes the children controls in the Grid, but the Grid.RowDefinitions and Grid.ColumnDefinitions stay intact.
To fix this, clear both definitions at the start of CreateRowsAndColumns():
GridLexemesViewer.RowDefinitions.Clear();
GridLexemesViewer.ColumnDefinitions.Clear();
However, definitely consider using the DataGrid control from the Windows Community Toolkit as it should have all the features you need and has better maintainability and performance then a custom Grid, especially for bigger data.

how to bind popup value to dynamically created textbox?

i am creating a Button and a textbox dynamically one by one in grid. My requirement is, if i click the button, the popup will show. and if i select the values from popup, i need to bind the corresponding row's textbox. Fr example, if i click the 5th row's button, i need to bind the popup item value to the 5th rows textbox. i struck on binding values to corresponding row's textbox. this may be simple one but i am unable to done this. this is my code.,
Xaml:
<Button x:Name="btn_addnewrow" Content="Add" HorizontalAlignment="Left" Margin="50,40,0,0" VerticalAlignment="Top" Width="89" Height="31" Click="btn_addnewrow_Click"/>
<Popup Name="popup" IsOpen="False" Placement="Mouse" VerticalOffset="15" HorizontalOffset="0" Margin="124,122,107,65">
<Border BorderBrush="Black" BorderThickness="1" Background="Coral">
<StackPanel Orientation="Horizontal" Height="143">
<ListView Margin="10,10,0,0" Name="ListView1" HorizontalAlignment="Left"
VerticalAlignment="Top" Width="194" Height="133" MouseDoubleClick="ListView1_MouseDoubleClick">
<ListViewItem Content="Coffie"></ListViewItem>
<ListViewItem Content="Tea"></ListViewItem>
<ListViewItem Content="Orange Juice"></ListViewItem>
<ListViewItem Content="Milk"></ListViewItem>
<ListViewItem Content="Iced Tea"></ListViewItem>
<ListViewItem Content="Mango Shake"></ListViewItem>
</ListView>
</StackPanel>
</Border>
</Popup>
cs:
public int count = 0;
public Button btn1;
public Button btn2;
public TextBox txt1;
private void btn_addnewrow_Click(object sender, RoutedEventArgs e)
{
//Creating Rows..
RowDefinition row0 = new RowDefinition();
row0.Height = new GridLength(40);
grid1.RowDefinitions.Add(row0);
//Creating columns..
ColumnDefinition col0 = new ColumnDefinition();
ColumnDefinition col1 = new ColumnDefinition();
ColumnDefinition col2 = new ColumnDefinition();
col0.Width = new GridLength(50);
col1.Width = new GridLength(100);
col2.Width = new GridLength(70);
grid1.ColumnDefinitions.Add(col0);
grid1.ColumnDefinitions.Add(col1);
grid1.ColumnDefinitions.Add(col2);
int i = count;
////1st Column button
btn1 = new Button();
btn1.Margin = new Thickness(10, 10, 0, 0);
btn1.BorderThickness = new Thickness(0);
Grid.SetRow(btn1, i);
Grid.SetColumn(btn1, 0);
btn1.Tag = btn1;
btn1.Click += btnBindList_Click;
grid1.Children.Add(btn1);
//2nd column Textbox
txt1 = new TextBox();
txt1.Margin = new Thickness(10, 10, 0, 0);
txt1.Name = "txt" + i;
Grid.SetRow(txt1, i);
Grid.SetColumn(txt1, 1);
txt1.Tag = txt1;
grid1.Children.Add(txt1);
count++;
}
private void btnBindList_Click(object sender, RoutedEventArgs e)
{
?
?
popup.IsOpen = true;
}
private void ListView1_MouseDoubleClick(object sender, RoutedEventArgs e)
{
txt1.Text = (ListView1.SelectedItem as ListViewItem).Content.ToString();
popup.IsOpen = false;
}
Create a usercontrol with button, textbox and popup. This usercontrol should set to the cell template or item template of gridview or listview. The textbox is bound to selected value of popup listview. So when user open popup and choose the value, the selected value will update in corresponding textbox.
<Grid HorizontalAlignment="Left">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBox Text="{Binding ElementName=ListView1, Path=SelectedValue.Content}" Width="200" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="2"/>
<ToggleButton Content="Select" Grid.Column="1" HorizontalAlignment="Left" Margin="2" x:Name="btn"/>
<Popup PlacementTarget="{Binding ElementName=btn}" Placement="Bottom"
StaysOpen="False"
IsOpen="{Binding ElementName=btn, Path=IsChecked}">
<ListView Name="ListView1"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Width="194"
Height="133">
<ListViewItem Content="Coffie"></ListViewItem>
<ListViewItem Content="Tea"></ListViewItem>
<ListViewItem Content="Orange Juice"></ListViewItem>
<ListViewItem Content="Milk"></ListViewItem>
<ListViewItem Content="Iced Tea"></ListViewItem>
<ListViewItem Content="Mango Shake"></ListViewItem>
</ListView>
</Popup>
</Grid>
If you want to use Binding, you need to creat a viewmodel,a class. And binding it to Button and TextBox. When you click a button,get the viewmodel, and set it's value by selecting in listview.
The code is very confused, but you can debug and rewrite it by yourself.
public int count = 0;
public Button btn1;
public Button btn2;
public TextBox txt1;
private void btn_addnewrow_Click(object sender, RoutedEventArgs e)
{
//Creating Rows..
RowDefinition row0 = new RowDefinition();
row0.Height = new GridLength(40);
grid1.RowDefinitions.Add(row0);
//Creating columns..
ColumnDefinition col0 = new ColumnDefinition();
ColumnDefinition col1 = new ColumnDefinition();
ColumnDefinition col2 = new ColumnDefinition();
col0.Width = new GridLength(50);
col1.Width = new GridLength(100);
col2.Width = new GridLength(70);
grid1.ColumnDefinitions.Add(col0);
grid1.ColumnDefinitions.Add(col1);
grid1.ColumnDefinitions.Add(col2);
int i = count;
Test t = new Test();
////1st Column button
btn1 = new Button();
btn1.Margin = new Thickness(10, 10, 0, 0);
btn1.BorderThickness = new Thickness(0);
Grid.SetRow(btn1, i);
Grid.SetColumn(btn1, 0);
Binding binding = new Binding();
binding.Source = t;
btn1.SetBinding(Button.TagProperty, binding);
btn1.Click += btnBindList_Click;
grid1.Children.Add(btn1);
Binding binding1 = new Binding("Content");
binding1.Source = t;
//2nd column Textbox
txt1 = new TextBox();
txt1.Margin = new Thickness(10, 10, 0, 0);
txt1.Name = "txt" + i;
txt1.SetBinding(TextBox.TextProperty, binding1);
Grid.SetRow(txt1, i);
Grid.SetColumn(txt1, 1);
txt1.Tag = txt1;
grid1.Children.Add(txt1);
count++;
}
private void btnBindList_Click(object sender, RoutedEventArgs e)
{
popup.IsOpen = true;
t = ((Button)sender).Tag as Test;
}
Test t;
private void ListView1_MouseDoubleClick(object sender, RoutedEventArgs e)
{
t.Content = (ListView1.SelectedItem as ListViewItem).Content.ToString();
popup.IsOpen = false;
}
}
class Test : INotifyPropertyChanged
{
private string content;
public string Content
{
get { return content; }
set
{
content = value;
OnPropertyChanged("Content");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}

Dynamically add textbox in WPF

I am creating a textbox dynamically. I have 2 columns in my grid. I want to add new textbox to the row if the other textbox value="tea". I want to create new textbox to corresponding row textbox value change. I am unable to use Tag to get selected row here. because I have already used Tag for some purpose. I don't have much idea about Tag. Anyhow, how can I add new textbox to the column1 to the corresponding row?
This is my code..
public int count = 1;
public TextBox txt1;
private void btn_addnew_Click(object sender, RoutedEventArgs e)
{
//Creating Rows..
RowDefinition row0 = new RowDefinition();
row0.Height = new GridLength(40);
grid1.RowDefinitions.Add(row0);
//Creating columns..
ColumnDefinition col0 = new ColumnDefinition();
ColumnDefinition col1 = new ColumnDefinition();
col0.Width = new GridLength(150);
col1.Width = new GridLength(250);
grid1.ColumnDefinitions.Add(col0);
grid1.ColumnDefinitions.Add(col1);
int i = count;
//1st Column TextBox
txt1 = new TextBox();
txt1.Margin = new Thickness(10, 10, 0, 0);
Grid.SetRow(txt1, i);
Grid.SetColumn(txt1, 0);
txt1.Tag = txt1;
txt1.MouseEnter+=txt1_MouseEnter;
txt1.TextChanged += txt1_TextChanged;
grid1.Children.Add(txt1);
count++;
}
private void txt1_MouseEnter(object sender, MouseEventArgs e)
{
txt1 = ((TextBox)sender).Tag as TextBox;
popup.IsOpen = true;
}
public TextBox txt2;
private void txt1_TextChanged(object sender, TextChangedEventArgs e)
{
if (txt1.Text.ToString() == "Tea")
{
txt2 = new TextBox();
//How to set row here?
Grid.SetRow(txt2, ??);
Grid.SetColumn(txt2, 1);
txt2.Margin = new Thickness(10, 10, 0, 0);
grid1.Children.Add(txt2);
}
else
{
grid1.Children.Remove(txt2);
}
}
If you just want to achieve this, then more elegant way will be to add the user control to your application like below:
<UserControl x:Class="WpfApplication4.TestUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBox Name="TextBox1"/>
<TextBox Grid.Column="1">
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Text, ElementName=TextBox1, UpdateSourceTrigger=PropertyChanged}" Value="tea">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</Grid>
</UserControl>
then in your btn_addnew_Click() method, you just need to add this usercontrol to user Grid and assign row and column to it. Showing/Hiding of textbox will be taken care of by teh user control itself.
var userControl = new MyUserControl();
userControl .Margin = new Thickness(10, 10, 0, 0);
Grid.SetRow(userControl , i);
grid1.Children.Add(userControl );
OR
if you want to have value of Grid.Row for textbox1 you can get it directly as:
if (textbox1 != null)
{
int row = (int)textbox1.GetValue(Grid.RowProperty);
Grid.SetRow(txt2, row);
}
Attached properties can be used to store such information so define an attached property in the class
public static int GetGridRow(DependencyObject obj)
{
return (int)obj.GetValue(GridRowProperty);
}
public static void SetGridRow(DependencyObject obj, int value)
{
obj.SetValue(GridRowProperty, value);
}
// Using a DependencyProperty as the backing store for GridRow. This enables animation, styling, binding, etc...
public static readonly DependencyProperty GridRowProperty =
DependencyProperty.RegisterAttached("GridRow", typeof(int), typeof(ViewModel), new PropertyMetadata(0));
then use it to store the value of the row
private void btn_addnew_Click(object sender, RoutedEventArgs e)
{
//1st Column TextBox
txt1 = new TextBox();
Grid.SetRow(txt1, i);
SetGridRow(text1, i);
...
grid1.Children.Add(txt1);
count++;
}
then use it when u need it
//How to set row here?
Grid.SetRow(txt2, GetGridRow(txt1));
Grid.SetColumn(txt2, 1);

How to set ItemsPanelTemplate to a dynamically created Grid in code behind

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.
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;
}
Now add this template in your Listbox ItemsPanel as:
MyListBox.ItemsPanel = GetItemsPanelTemplate();
This is working fine for me. Hope this will help.
Keep Coding....:)
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.
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:
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;
}
}
For anyone else...
... A control based on the ItemsControl which has an Orientation property.
It uses the FrameworkElementFactory as in the previous answers:
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);
}
}

Categories