Add checkboxes to a list of dynamically generated result - c#

I have a list of result listed after extracting from a .txt file. I would like to add a checkbox behind every results listed. The following is my code:
private void LoadFile() {
List<string> lines = new List<string>();
try
{
StreamReader sr = new StreamReader("test.txt");
while (!sr.EndOfStream)
{
lines.Add(sr.ReadLine());
}
sr.Close();
for (int i = 3; i < lines.Count; i++)
{
resultsTreeView.Items.Add(lines[i].ToString().Substring(67,17));
resultsTreeView.Items.Add(CheckBox[i]);
}
How can I add checkboxes as the results extracted will change every time? I would like to track which boxes has checked as well so that I can print the result to users. Thank you!

for (int i = 3; i < lines.Count; i++)
{
resultsTreeView.Items.Add(lines[i].ToString().Substring(67,17));
resultsTreeView.Items.Add(new CheckBox());
// resultsTreeView.Items.Add(BuildCheckBox())
}
OR
CheckBox BuildCheckbox()
{
CheckBox C = new CheckBox();
return C;
}
That's all you need to create a checkbox, or you can create a function that returns a checkbox, inside it you create a new instance of checkbox and set the attributes/subscribe to events the way you want and return it.
As for the tracking which checkboxes are checked, I only need you to provide me with the type of your "resultsTreeView"
EDIT :
To loop through checkboxes in the TreeView and do something on the checked ones:
resultsTreeView.Items.OfType<CheckBox>().ToList()
.ForEach(C =>
{
if (C.IsChecked.HasValue && C.IsChecked.Value == true)
{
//DoSomething
}
});

I am not sure exactly what you are looking for. I assume resultsTreeView is a TreeViewItem. and I also assume that you are working in wpf. You can do the following through wpf.
for (int i = 0; i < lines.Count(); i++)
{
resultsTreeView.Items.Add(lines[i].ToString().Substring(67,17));
}
<TreeView x:Name="resultsTreeView" HorizontalAlignment="Left" Height="100" Margin="37,344,0,0" VerticalAlignment="Top" Width="257" >
<TreeView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding}"/>
<CheckBox/>
</StackPanel>
</DataTemplate>
</TreeView.ItemTemplate>
</TreeView>
Something similar could be done through code behind
for (int i = 0; i < mylist.Count(); i++)
{
resultsTreeView.Items.Add(mylist[i]);
}
resultsTreeView.ItemTemplate = TreeViewDataTemp;
And then create TreeViewDataTemp the following way
private static DataTemplate TreeViewDataTemp
{
get
{
DataTemplate TVDT = new DataTemplate();
FrameworkElementFactory Stack = new FrameworkElementFactory(typeof(StackPanel));
Stack.SetValue(StackPanel.OrientationProperty, Orientation.Horizontal);
FrameworkElementFactory TextB = new FrameworkElementFactory(typeof(TextBlock));
TextB.SetValue(TextBlock.TextProperty, new Binding());
FrameworkElementFactory ChkBox = new FrameworkElementFactory(typeof(CheckBox));
Stack.AppendChild(TextB);
Stack.AppendChild(ChkBox);
TVDT.VisualTree = Stack;
return TVDT;
}
}
The above gives you 1 item which is text together with a checkbox.
Alternatively your method will add a checkbox as a new item after every string item that you add.. which is
for (int I=0; I<lines.Count(); I++)
{
resultsTreeView.Items.Add(mylist[i]);
resultsTreeView.Items.Add(new CheckBox());
}

Related

At Runtime Add X number of ComboBoxes with SelectedItem to DataGrid (WPF)

I would like to create an entire row of ComboBoxes in a DataGrid. I have made some progress with the following:
// Declare it
private DataGridComboBoxColumn CreateCustomComboBoxDataSouce(string ColumnName)
{
string[] data = { "Date", "LEInt", "String" };
DataGridComboBoxColumn dgCmbCol = new DataGridComboBoxColumn();
dgCmbCol.Header = ColumnName;
dgCmbCol.ItemsSource = data;
return dgCmbCol;
}
// Later somewhere you can add this to create 20 columns:
for (int i = 0; i < 20; i++)
{
DataGridComboBoxColumn newColumn = CreateCustomComboBoxDataSouce("Column-" +i);
}
// Sadly nothing is shown unless you manually specify a new row and an
// extra column as done here below, but then you get an extra column.
DataTable table = new DataTable();
table.Columns.Add("|", typeof(string));
table.Rows.Add("");
DataGridCombo.DataContext = table;
The XAML is kept to a minimum:
<DataGrid x:Name="DataGridCombo" ItemsSource="{Binding}" Margin="0,0,0,0" />
Is there a way to set the default SelectedValue of each ComboBox? Within my for loop I do have access to the desired setting. Also, is there a way to git it to show without adding the extra column? This DataGrid is being lined up with another DataGrid that won't have that extra column.
Is there a way to set the default SelectedValue of each ComboBox?
Add a DataColumn to the DataTable per DataGridComboBoxColumn, and then set the value of the column for each row to the desired selected value:
const int n = 20;
DataTable table = new DataTable();
for (int i = 0; i<n; i++)
{
string columnName = "Column-" + i;
DataGridCombo.Columns.Add(CreateCustomComboBoxDataSouce(columnName));
table.Columns.Add(columnName, typeof(string));
}
table.Rows.Add(new object[n]);
//select some values...
table.Rows[0][1] = "LEInt";
table.Rows[0][5] = "String";
DataGridCombo.DataContext = table;
Also, is there a way to git it to show without adding the extra column?
Set the HorizontalAlignment property to Left to prevent the DataGrid from stretching horizontally:
<DataGrid x:Name="DataGridCombo" ItemsSource="{Binding}" HorizontalAlignment="Left" />
After a lot of working around the issue, I eventually came to the conclusion that what I was trying to do was either not possible, or unreasonable. I changed to using a StackPanel with horizontal orientation. Here are the relevant snippets:
//XAML Layout
<StackPanel Orientation="Horizontal" VerticalAlignment="Top" Name="ComboBoxPanel" Grid.Row="1" Margin="0,0,0,0" HorizontalAlignment="Left" />
//Function to create new box and set options
private ComboBox CreateComboBox(string ColumnName, string selectedItem, int width)
{
string[] data = { "Date", "LEInt", "String" };
ComboBox dgCmbCol = new ComboBox();
dgCmbCol.ItemsSource = data;
dgCmbCol.Name = ColumnName;
dgCmbCol.Width = width;
dgCmbCol.SelectedItem = selectedItem;
dgCmbCol.SelectionChanged += DataTypeSelectionChanged;
return dgCmbCol;
}
// This reads a JSON file and uses it's options to set the width of all the columns
// for the DataGrid and matches the ComboBoxes to it so they all line up
dynamic TableData = JsonConvert.DeserializeObject(Config.DataLayoutDefinition);
ComboBoxPanel.Children.Clear(); //Since this is run on user interaction, clear old contents.
for (int i = 0; i < DataGridView.Columns.Count; i++)
{
int width = TableData.data[i].ColumnWidth;
if (TableData.data[i].DataType == "String")
{
width = 125;
}
else if (TableData.data[i].DataType == "Date")
{
width = 150;
}
else if (TableData.data[i].DataType == "LEInt")
{
width = 80;
}
else
{
width = 100;
}
ComboBoxPanel.Children.Add(CreateComboBox(TableData.data[i].Column.ToString(), TableData.data[i].DataType.ToString(), width));
DataGridView.Columns[i].Width = width;
}

WPF Add listview item in listview created dynamically

This is my first question here :)
I create a listview columns form a textfile in Window_Loaded()
After this I want to add items to this listview...
foreach (KeyValuePair<string, string> key in App.sysIni.monitorProperties)
{
GridViewColumn column = new GridViewColumn();
column.Header = key.Value;
column.Width = 70;
GridViewControlMonitor.Columns.Add(column);
}
After this I create listview items:
string[] rowItems = new string[list.Count];
for (int i = 1; i < list.Count; i++)
{
rowItems[i] = list[i];
}
var item = new ListViewItem { Content = rowItems};
itemsList.Add(item);
And add this items in listview:
RequestMonitorListV.Items.Add(item);
And my listview is populated with "String[] Array" not values...
Is possible to create a listview item with content and some kind of binding for that content?
var item = new ListViewItem { Content = rowItems, !!soomeBindingOptinos!!};
Can someone help me with this issue?
Thank you in advance
You should bind the DisplayMemberBinding property of each GridViewColumn to a property of an object in the ListView's ItemsSource collection:
for (int i = 0; i<App.sysIni.monitorProperties.Count; ++i)
{
KeyValuePair<string, string> key = App.sysIni.monitorProperties[i];
GridViewColumn column = new GridViewColumn();
column.Header = key.Value;
column.Width = 70;
column.DisplayMemberBinding = new Binding("[" + i + "]");
GridViewControlMonitor.Columns.Add(column);
}
...and set the latter to an IEnumerable like for example a string[]:
string[] rowItems = new string[list.Count];
for (int i = 0; i<list.Count; i++)
{
rowItems[i] = list[i];
}
RequestMonitorListV.ItemsSource = rowItems;
I'm going to be focusing on the second section of your code, as that seems to be the portion that is actually causing trouble. Unfortunately there are a few details missing, so I'll be making some assumptions on intent, type, etc.
Here is the .xaml I'll be working against:
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ListView x:Name="myListView" HorizontalAlignment="Left" Height="301" Margin="10,10,0,0" VerticalAlignment="Top" Width="498">
<ListView.View>
<GridView>
<GridViewColumn/>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
And here is my interpretation of that chunk of C#:
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
namespace WpfApp1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var myListViewVar = this.FindName("myListView") as ListView;
string[] rowItems = new string[10];
for (int i = 0; i < 10; i++)
{
rowItems[i] = i.ToString();
}
var item = new ListViewItem { Content = rowItems };//You are adding your string array to a singular element. It does not want it. It wants a nice string.
var itemsList = new List<ListViewItem>();//You then wrap this singular item in a list. I'm inferring the type here, since the rest of your code does not reference it.
itemsList.Add(item);
myListViewVar.Items.Add(item);//You then add the ARRAY to the list of items. Again, this is not desired. See https://msdn.microsoft.com/en-us/library/system.windows.forms.listview.items(v=vs.110).aspx for usage examples.
}
}
}
As explained above, WPF doesn't know how to stringify your array. Interpreting from what the rest of your code implies, what I think you want is:
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
namespace WpfApp1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var myListViewVar = this.FindName("myListView") as ListView;
string[] rowItems = new string[10];
for (int i = 0; i < 10; i++)
{
rowItems[i] = i.ToString();
}
for (int i = 0; i < 10; i++)
{
var item = new ListViewItem { Content = rowItems[i] };//Each ListViewItem holds an individual string rather than an array
myListViewVar.Items.Add(item);//Then add that ListViewItem.
}
}
}
}
Obviously this has a redundancy in the form of the second loop, but I wanted to keep with your code where you store everything outside of the loop.
As for data binding, I'd recommend looking at WPF Binding to local variable
I don't use WPF too often, but it looks like what you want.
Hope this helps. Let me know if I've missed the mark on what you're intending.

Find child element from ListBox wp8

I am trying to find child element from Listbox. Listbox contains Itemtemplate. Here is my design.
I have created UserControl. In that I have added ListBox.
I am showing this control as pop up. Here is code for pop up
GlobalSettings.popup = new Popup();
//GlobalSettings.popup.VerticalOffset = 50;
FilesListControl popupcontrol = new FilesListControl();
popupcontrol.Height = 480;
popupcontrol.Width = 480;
GlobalSettings.popup.Child = popupcontrol;
popupcontrol.fileListbox.ItemsSource = filesList;
LayoutRoot.IsHitTestVisible = false;
GlobalSettings.popup.IsOpen = true;
//Here I need to create checkbox. so thats why I need to find the child elemnt of listbox
popupcontrol.btnDone.Click += (s, args) =>
{
};
Here code from FilesListControl
<ScrollViewer Grid.Row="0" HorizontalScrollBarVisibility="Auto">
<ListBox Name="fileListbox">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<CheckBox Name="chkFile" CommandParameter="{Binding value}" Content="{Binding Key}" Click="chkFile_Click" FontFamily="Segoe WP SemiLight"></CheckBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</ScrollViewer>
I want to find the CheckBox i.e. chkFile. Here is my code
ListBoxItem item = popupcontrol.fileListbox.ItemContainerGenerator.ContainerFromIndex(1) as ListBoxItem;
CheckBox chk = FindFirstElementInVisualTree<CheckBox>(item);
private T FindFirstElementInVisualTree<T>(DependencyObject parentElement) where T : DependencyObject
{
var count = VisualTreeHelper.GetChildrenCount(parentElement);
if (count == 0)
return null;
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(parentElement, i);
if (child != null && child is T)
{
return (T)child;
}
else
{
var result = FindFirstElementInVisualTree<T>(child);
if (result != null)
return result;
}
}
return null;
}
But nothing is getting. What I did wrong? How can I access CheckBox Click event?

Add items into DataContext via loop

I have following code in C#, WPF:
base.DataContext = new DataTemplate[]
{
new DataTemplate
{
lblText = "First",
txtBoxContent = ""
},
new DataTemplate
{
lblText = "Second",
txtBoxContent = "Something"
}
};
but i need to fill DataContext dynamically from database. My idea looks like this:
base.DataContext = new DataTemplate[]
{
for(int i = 0; i< dsTmp.Tables[0].Rows.Count; i++)
{
new DataTemplate
{
lblText = "Count: ",
txtBoxContent = dsTmp.Tables[0].Rows[i][0].ToString();
}
}
};
When i type this, it yells some syntax errors on me;
Could anybody tell me, how to write it correctly?
You can't have code inside object initializer syntax. Why not simply do this:
var list = new DataTemplate[dsTmp.Tables[0].Rows.Count];
for(int i = 0; i< dsTmp.Tables[0].Rows.Count; i++)
{
var item = new DataTemplate
{
lblText = "Count: ",
txtBoxContent = dsTmp.Tables[0].Rows[i][0].ToString();
};
list[i] = item;
}
this.DataContext = list;
MBen and Habib have already answered why the for is failing, because you can't do a loop in an object initializer and have provided loop alternatives.
Alternatively you can use linq to perform an initialization.
this.DataContext=dsTmp.Tables[0].Rows.Select(
x=>new DataTemplate {
lblText = "Count: ",
txtBoxContent=x[0].ToString()
}).ToArray();
The error that ; is missing is bit misleading. The actual problem is that you are trying to create an array of DataTemplate with the loop, You can't use loop in array/object initialization. Try the following.
DataTemplate[] tempDataTemplate = new DataTemplate[ds.Temp.Tables[0].Rows.Count]();
for(int i = 0; i< dsTmp.Tables[0].Rows.Count; i++)
{
tempDataTemplate[i] = new DataTemplate
{
lblText = "Count: ",
txtBoxContent = dsTmp.Tables[0].Rows[i][0].ToString();
};
}
base.DataContext = tempDataTemplate;
i dont know what you wanna achieve, but did you ever try mvvm with viewmodel first approach?
create a viewmodel class, e.g. MyData with 2 public properties MyText, MyContent. create a collection of these objects and fill this from your database.
at least you need an itemscontrol with itemssource binding set to your collection and a datatemplate for your MyData object.
<DataTemplate DataType="{x:Type local:MyData}">
<view:MyDataViewControl />
</DataTemplate>
now you see all your dynamic objects in your itemscontrol.

How do I access my ComboBoxes inside of a GridViewColumn HeaderTemplate?

I found a way to template the embedding of controls within the column headings of a GridView. However I do not know how to find the controls using my code behind to fill them up with items. I have figured out how to respond to an event raised from the embedded control and determine which column it was in. Otherwise I don't know how to get a reference to the embedded ComboBoxes.
Some code to give you an idea:
<Page.Resources>
<DataTemplate x:Key="ComboHeaderTemplate">
<DockPanel>
<ComboBox Name="columnHeading" />
</DockPanel>
</DataTemplate>
</Page.Resources>
And in the code-behind:
GridView grdView = new GridView();
for (int column = 1; column <= context.data.GetLength(1); column++)
{
GridViewColumn gvc = new GridViewColumn();
gvc.DisplayMemberBinding = new Binding(column.ToString());
gvc.Header = column.ToString();
gvc.Width = 120;
gvc.HeaderTemplate = (DataTemplate)this.Resources["ComboHeaderTemplate"];
grdView.Columns.Add(gvc);
}
ListView1.View = grdView;
ListView1.ItemsSource = dt.DefaultView;
If every ComboBox had the same list of items to choose from using data binding that would be fine as long as I could select unique values for each column.
You could use the VisualTreeHelper to retrieve the combo-box:
Create a helper method to find the combo box on the GridViewColumn:
public T FindVisualChild<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
return (T)child;
}
T childItem = FindVisualChild<T>(child);
if (childItem != null) return childItem;
}
}
return null;
}
To get a reference to the combo-box then do something like the following:
ComboBox cb = FindVisualChild<ComboBox>(gvc);
Hopefully this is what you are looking for?
Here's what I ended up with.
XAML:
<DataTemplate x:Key="ComboHeaderTemplate">
<DockPanel>
<ComboBox Name="columnHeading" Loaded="columnHeadingLoaded" SelectionChanged="columnHeadingSelectedChanged" Width="Auto" />
</DockPanel>
</DataTemplate>
Code behind:
private void columnHeadingLoaded(object sender, RoutedEventArgs e)
{
((ComboBox)sender).ItemsSource = myList;
((ComboBox)sender).SelectedIndex = 0;
}
// My columns are named "1", "2" etc
private void columnHeadingSelectedChanged(object sender, SelectionChangedEventArgs e)
{
int columnIndex = int.Parse(((ComboBox)sender).DataContext.ToString()) - 1;
if (((ComboBox)sender).SelectedIndex == 0)
{
this.Headings[columnIndex] = null;
}
else
{
this.Headings[columnIndex] = ((ComboBox)sender).SelectedValue.ToString();
}
}
Thought I should use Data Binding in the XAML but this was easier.
You can try this:
var cbx= gvc.HeaderTemplate.LoadContent() as ComboBox;
gvc is GridViewColumn

Categories

Resources