I have a combobox which has checkbox as its combobox.itemtemplate.
<ComboBox Name="comboBoxTest"
SelectedValuePath="Test"
SelectedItem="{Binding SelectedTest, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedValue="{Binding SelectedTest, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding Test, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
TextBoxBase.TextChanged ="comboBoxTest_TextChanged" Grid.ColumnSpan="2"
TextSearch.TextPath="Model" >
<ComboBox.ItemTemplate>
<DataTemplate>
<CheckBox Name="checkBoxTest"
Content="{Binding Test}"
Click="checkBoxTest_Click"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
An “--Select All--” item has been add into the result list when the result list is produced.
When user checks on the “All” item, the other checkboxes should be checked as well.
I am using the codes below but it doesn’t work.
if (checkBoxTest.Content.ToString().Equals("--Select All--"))
{
foreach (object item in comboBoxTest.Items)
{
ComboBoxItem comboBoxItem = comboBoxTest.ItemContainerGenerator.ContainerFromItem(item) as ComboBoxItem;
FrameworkElement element = comboBoxItem.ContentTemplate.LoadContent() as FrameworkElement;
CheckBox checkBox = element.FindName("checkBoxTest") as CheckBox;
checkBox.IsChecked = true;
}
}
There are few issues in your code let me first tell you about those issue.
your if condition for identifying the "Select All" checkbox is incorrect. Your need to use Contains() instead of equals()
The checkbox you are fetching is not the correct one within the comboBox Item. If you try to see the checkBox.Content property you will see null as result.
See below code to select all the checkboxes within the comboBox when "Select All" checkbox is checked.
Your Checkbox Click event should be as below.
private void checkBoxTest_Click(object sender, RoutedEventArgs e)
{
CheckBox checkBoxTest = e.Source as CheckBox;
if (checkBoxTest == null)
return;
if (checkBoxTest.Content.ToString().Contains("Select All") && checkBoxTest.IsChecked == true)
{
foreach (object item in comboBoxTest.Items)
{
ComboBoxItem comboBoxItem = comboBoxTest.ItemContainerGenerator.ContainerFromItem(item) as ComboBoxItem;
if (comboBoxItem == null)
{
return;
}
CheckBox checkBox = FindVisualChildByName<CheckBox>(comboBoxItem, "checkBoxTest");
checkBox.IsChecked = true;
}
}
}
I have added a new method to fetch the visual child within any element from the name of the child and its type.
private static T FindVisualChildByName<T>(DependencyObject parent, string name) where T : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
string controlName = child.GetValue(NameProperty) as string;
if (controlName == name)
{
return child as T;
}
T result = FindVisualChildByName<T>(child, name);
if (result != null)
return result;
}
return null;
}
use CompositeCollection inside your ComboBox, check below answers for more information
Combobox and checkbox with "Select All" Checkbox with binding in wpf
How can I insert a "Select All" item at the top of a ComboBox with its ItemsSource set?
Binding/Triggering "Select all"-CheckBox ComboBoxItem in WPF
Related
I have a datagrid as below. I want to reference CheckBox which is named ckbSelectedAll in code behind. WPF does not allow to reference with the name in code behind. Which way should i follow ?
<DataGrid x:Name="deckGrid" ItemsSource="{Binding DeckList}" AutoGenerateColumns="False"
SelectionMode="Single" Margin="10,10,0,0" SelectionUnit="FullRow" CanUserAddRows="False" IsReadOnly="True">
<DataGrid.Columns >
<DataGridTemplateColumn Width="70">
<DataGridTemplateColumn.HeaderTemplate >
<DataTemplate>
<CheckBox x:Name="ckbSelectedAll" IsThreeState="True" Margin="10,0,0,0" Checked="CheckBox_Checked" Unchecked="CheckBox_Checked">
</CheckBox>
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
I answered a similar question regarding accessing buttons from with a DataTemplate of a ListView. Fortunately the process is very much the same. Here, I enumerate each of the DataGrid items from the ItemSource. I randomly uncheck a couple of items that I have stored in a List. I process the items from a Button_Click but doing it from the Window constructor after a call to InitializeComponent() should yield the same results.
List<CheckBox> checkboxes =
new List<CheckBox>();
private void Button_Click(object sender, RoutedEventArgs e)
{
deckGrid.Items
.Cast<dynamic>()
.ToList()
.ForEach(item => {
var checkitem =
(deckGrid
.ItemContainerGenerator
.ContainerFromItem(item));
CheckBox checkbox =
FindVisualChild<CheckBox>
(checkitem);
checkboxes
.Add(checkbox);
});
checkboxes[1]
.IsChecked = false;
checkboxes[3]
.IsChecked = false;
}
private childItem FindVisualChild<childItem>(DependencyObject obj)
where childItem : DependencyObject
{
for (int i = 0;
i < VisualTreeHelper
.GetChildrenCount(obj);
i++)
{
DependencyObject child =
VisualTreeHelper
.GetChild(obj, i);
if (child != null
&& child is childItem)
{
return
(childItem)child;
}
else
{
childItem childOfChild =
FindVisualChild<childItem>
(child);
if (childOfChild != null)
return
childOfChild;
}
}
return null;
}
I have a ListView that displays a table with various columns. Each cell of a row in the table contains a different type of control; I am trying to allow the user to edit the data in each row by selecting a row and double-clicking it to make the cells editable. So I have been able to get all of them to work with the exception of the column that contains ComboBoxes.
XAML code:
This is the XAML code for the ListView. It has about 7 columns but I am focusing on the column with ComboBoxes as depicted here.
<ListView x:Name="MyListView" IsSynchronizedWithCurrentItem="True" Grid.Row="0" Grid.Column="0" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,12,0,0" Height="315" Width="560" ItemsSource="{Binding People}">
<ListView.View>
<GridView>
<!-- More Grid column code here -->
<GridViewColumn Header="Fleet" Width="70">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ComboBox Name="locationCmboBx" ItemsSource="{Binding DataContext.SchoolLocations, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Mode=TwoWay}" Loaded="OnCmboBxLoad" IsEnabled="False" Width="55" HorizontalAlignment="Center"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<!-- More Grid column code here -->
</GridView>
</ListView.View>
</ListView>
C# code:
So here in the code-behind I am trying to use the VisualTreeHelper as recommended by others to get access to the locationsCmboBx (ComboBox) nested inside of the DataTemplate, CellTemplate and other XAML headers in the ListView.
// More code before here
ListView listViewItem = (ListView)(MyListView.ItemContainerGenerator.ContainerFromItem(MyListView));
ContentPresenter myContentPresenter = FindVisualChild<ContentPresenter>(listViewItem);
DataTemplate myDataTemplate = myContentPresenter.ContentTemplate;
ComboBox comboBox = (ComboBox)myDataTemplate.FindName("locationsCmboBx", myContentPresenter);
// More code before here
private childItem FindVisualChild<childItem>(DependencyObject obj) where childItem : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is childItem)
{
return (childItem)child;
}
else
{
childItem childOfChild = FindVisualChild<childItem>(child);
if (childOfChild != null)
{
return childOfChild;
}
}
}
return null;
}
So everything I have works but when I debug through the code and get to the FindName function ComboBox is null. Ultimately, I want to set the IsEnabled property on it and get the SelectedValue from the locationsCmboBx. I believe I am missing something but not sure of what. Any help would be appreciated?
The problem in your code is a typo by combo box's name in XAML locationCmboBx and in code behind is locationsCmboBx.
The code:
ListView listViewItem = (ListView)(MyListView.ItemContainerGenerator.ContainerFromItem(MyListView));
is also wrong. Argument of ContainerFromItem() must be a data item. Returned type is also wrong. It must be ListViewItem
I would recommend you to use a ViewModel + bindings and not a code behind to access the data. So you can avoid such a tipo errors. See also: Detect in XAML broken bindings already at compile time
I found this information:
public static class ListViewHelper
{
public static FrameworkElement GetElementFromCellTemplate(ListView listView, Int32 column, Int32 row, String name)
{
if (row >= listView.Items.Count || row < 0)
{
throw new ArgumentOutOfRangeException('row');
}
GridView gridView = listView.View as GridView;
if (gridView == null) { return null; }
if (column >= gridView.Columns.Count || column < 0)
{
throw new ArgumentOutOfRangeException('column');
}
ListViewItem item = listView.ItemContainerGenerator.ContainerFromItem(listView.Items[row]) as ListViewItem;
if (item != null)
{
GridViewRowPresenter rowPresenter = GetFrameworkElementByName(item);
if (rowPresenter != null)
{
ContentPresenter templatedParent = VisualTreeHelper.GetChild(rowPresenter, column) as ContentPresenter;
DataTemplate dataTemplate = gridView.Columns[column].CellTemplate;
if (dataTemplate != null && templatedParent != null)
{
return dataTemplate.FindName(name, templatedParent) as FrameworkElement;
}
}
}
return null;
}
private static T GetFrameworkElementByName(FrameworkElement referenceElement) where T : FrameworkElement
{
FrameworkElement child = null;
for (Int32 i = 0; i < VisualTreeHelper.GetChildrenCount(referenceElement); i++)
{
child = VisualTreeHelper.GetChild(referenceElement, i) as FrameworkElement;
System.Diagnostics.Debug.WriteLine(child);
if (child != null && child.GetType() == typeof(T))
{
break;
}
else if (child != null)
{
child = GetFrameworkElementByName(child);
if (child != null && child.GetType() == typeof(T))
{
break;
}
}
}
return child as T;
}
}
Source: How do I access the ui element at a row/cell in my GridView?
I have added checkbox in datagridview and I want check whether an item is checked or not, and then read the content value, but I am little bit confuse how to accomplish it.
This is Xmal code
<DataGrid.Columns>
<DataGridTemplateColumn Header="#">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox x:Name="checkboxinstance" Checked="checked_it" Unchecked="unchecked_it" content ="{Binding apiName }" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
This is behind the code:
private void checked_it(object sender, RoutedEventArgs e)
{
List<CheckBox> checkBoxlist = new List<CheckBox>();
foreach (CheckBox c in checkBoxlist)
{
//what I add here
}
}
You can use the IsChecked property to check if the checkbox has been ticked.
To read the value of the Content you have to cast the type to a TextBlock
foreach (CheckBox c in checkBoxlist)
{
If (c.IsChecked == true)
{
//Code when checkbox is checked
var _tempTBL = (TextBlock) c.Content; //Get handle to TextBlock
var foo = _tempTBL.Text; //Read TextBlock's text
//foo is now a string of the checkbox's content
}
}
MSDN link to the IsChecked property
MSDN link to the Content property
Ran into this issue on several occasions.
When finding SelectedItem or selected column on say right click menu or selecting combo box in a cell. The SelectedItem will be null or the previously selected row.
private void ComboBox_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e) {
// Correct
m_BeginEditString = ((ComboBox)sender).SelectedValue.ToString();
// Wrong. selected item is last selected row, example clicking directly on combobox will not select row, and be null.
m_BeginEditRow = (RowItem)MyDataGrid.SelectedItem;
}
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox SelectedItem="{Binding myItem, Mode=TwoWay,
NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding Source={StaticResource enum}}"
SelectionChanged="ComboBox_Changed"
LostKeyboardFocus="ComboBox_LostKeyboardFocus"
GotKeyboardFocus="ComboBox_GotKeyboardFocus" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Solved by doing it a completely different way, thanks #Ramesh Muthiah for direction:
private void ComboBox_Changed(object sender, SelectionChangedEventArgs e) {
if (((ComboBox)sender).IsLoaded) { // disregard SelectionChangedEvent fired on population from binding
if (e.RemovedItems.Count != 0) {
for (Visual visual = (Visual)sender; visual != null; visual = (Visual)VisualTreeHelper.GetParent(visual)) { // Traverse tree to find corred selected item
if (visual is DataGridRow) {
DataGridRow row = (DataGridRow)visual;
m_BeginEditRow = new MyRowItem((MyRowItem)row.Item); // Copy constructor, otherwise passed by reference
break;
}
}
MyEnum newItem = (MyEnum)e.AddedItems[0];
MyEnum oldItem = (MyEnum)e.RemovedItems[0];
if (m_BeginEditRow.Combo1 == newItem) {
m_BeginEditRow.Combo1 = oldItem;
} else {
m_BeginEditRow.Combo2 = oldItem;
}
DoStuff(m_BeginEditRow, false);
}
}
}
Instead of accessing the selected Item directly, you can access through the parent object and try to access whatever you want. This is alternative approach. I hopes this helps you
Combobox objMyButton = null;
if (sender is Combobox)
{
objMyButton = (sender as Combobox );
}
//You can access the parent object which means corresponding DataGridRow and do whatever you want
for (var vis = sender as Visual; vis != null; vis = VisualTreeHelper.GetParent(vis) as Visual)
if (vis is DataGridRow)
{
var row = (DataGridRow)vis;
break;
}
I have a DataGrid with 2 columns defined in XAML as
<DataGrid x:Name="marketInfodg" Grid.Row="1"
ItemsSource = "{Binding ElementName=This, Path=dataTableTest}"
CanUserAddRows="False">
<DataGrid.Columns>
<DataGridComboBoxColumn Header="Department Id" x:Name="comboboxColumn1"
SelectedValueBinding="{Binding Department Id}" />
<DataGridTemplateColumn x:Name="DataGridTempCol" Header="Selection">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox
x: Name = "combo"
SelectedValue = "{Binding Selection, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ItemsSource = "{Binding comboBoxSelections, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"
DropDownOpened = "combo_DropDownOpened"
DisplayMemberPath = "Key"
IsEditable="True">
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
The constructor code is
_dataTableTest = new DataTable();
DataColumn dc = new DataColumn();
dc.ReadOnly = false;
dc.DataType = typeof(String);
dc.ColumnName = "Department Id";
_dataTableTest.Columns.Add(dc);
DataColumn dc1 = new DataColumn();
dc1.ReadOnly = false;
dc1.DataType = typeof(KeyValuePair<string, double>);
dc1.ColumnName = "Selection";
_dataTableTest.Columns.Add(dc1);
marketInfodg.ItemsSource = _dataTableTest.DefaultView;
var row = _dataTableTest.NewRow();
row = _dataTableTest.NewRow();
_dataTableTest.Rows.Add(row);
row["Department Id"] = "X567";
row["Selection"] = (KeyValuePair<string, double>)comboBoxSelections[0];
which effectively sets a single row as column "Department Id" = "X567" and a second column is a combobox set to the first item in comboBoxSelections[0]
The combo_DropDownOpened event fires when any Combobox drop down is opened (obviously) and I can set the variable cb based on the sender, using
private void combo_DropDownOpened(object sender, EventArgs e)
{
var cb = ((System.Windows.Controls.ComboBox)sender);
}
How do I also get the associated Row (all columns in the Row) and RowIndex/number of the firing ComboBox in the combo_DropDownOpened event?
ComboBox lies in the visual tree of the DataGrid. If you travel up Visual Tree you will find the DataGridRow on the way to the top to the DataGrid.
You can use the VisualTreeHelper class to walk up to the Visual tree. Generally, you can use this method to find any parent in the Visual tree of your control. Put this method in some Utility class and use whenever you feel like walking up to Visual tree for your control to find any parent -
public static Parent FindParent<Parent>(DependencyObject child)
where Parent : DependencyObject
{
DependencyObject parentObject = child;
//We are not dealing with Visual, so either we need to fnd parent or
//get Visual to get parent from Parent Heirarchy.
while (!((parentObject is System.Windows.Media.Visual)
|| (parentObject is System.Windows.Media.Media3D.Visual3D)))
{
if (parentObject is Parent || parentObject == null)
{
return parentObject as Parent;
}
else
{
parentObject = (parentObject as FrameworkContentElement).Parent;
}
}
//We have not found parent yet , and we have now visual to work with.
parentObject = VisualTreeHelper.GetParent(parentObject);
//check if the parent matches the type we're looking for
if (parentObject is Parent || parentObject == null)
{
return parentObject as Parent;
}
else
{
//use recursion to proceed with next level
return FindParent<Parent>(parentObject);
}
}
Now, in your dropDown event handler you can use the above function to find the DataGridRow like this -
private void combo_DropDownOpened(object sender, EventArgs e)
{
var cb = ((System.Windows.Controls.ComboBox)sender);
DataGridRow dataGridRow = FindParent<DataGridRow>(cb);
int index = dataGridRow.GetIndex();
}