So I am having an issue where I have a GridView's ItemsSource bound to a collection of object. I also have a column of Check boxes that can be used to select object the user wishes to remove and all related items. The problem I am having is that when the user selects one item, I get stuck in a loop of the items being continually selected. Does anyone have an idea on how I can stop the programmatic selection of these check boxes from firing the Checked event.
Property in use:
List<MyObject> _localCollection = new List<MyObject>();
List<MyObject> LocalCollection
{
get { return _localCollection; }
set
{
_localCollection = value;
OnPropertyChanged("LocalCollection");
}
}
Loose example of XML code:
<GridView Name="grdItems">
<GridViewColumn>
<GridViewColumn.Header>
<CheckBox/>
</GridViewColumn.Header>
<!--Column Template-->
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Tag="{Binding ObjID}"
IsChecked="{Binding ToRemove, Mode=OneWay}"
Checked="SelectRelative" />
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
The "SelectRelative" method looks as follows:
private void SelectRelative(object sender, RoutedEventArgs e)
{
Dispatcher.BeginInvoke((Action)(() =>
{
//Get the Object Id we need
int selectedId = Convert.ToInt32(((CheckBox)sender).Tag);
//Get all objects that share this ID
List<MyObjects> objLst = new List<MyObjects>(((IEnumerable<MyObjects>)grdItems.ItemsSource));
//Clear the local collection property of our items used in the items source
LocalCollection.Clear();
//Remove the items source since we are updating it
grdItems.ItemsSource = null;
//Go through each item in the list and if the object id's match select them to remove
foreach(var item in objLst)
{
if(item.ObjId == selectedId)
item.ToRemove = true;
//Add the object to our property
LocalCollection.Add(item);
}
//Re-establish the item source with our new collection
grdItems.ItemsSource = LocalCollection;
}));
}
try this:
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox x:Name="chkboxPaid" Checked="chkboxPaid_Checked" Content="check" Style="{StaticResource MaterialDesignCheckBox}" IsChecked="{Binding Path=IsSelected, Mode=TwoWay,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type DataGridRow}}}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
Related
I would like to be able to get and set the state of a checkbox in a listview. I would like to either be able to automatically update MyListItems[row].myCheckedValue when the box is clicked by somehow binding in xaml (I know very little about binding) or to be able to loop through each list item by row and access the checkboxes in C#. I don't know how to approach either. I'm just starting out with WPF.
I Could also use Checked and Unchecked events, but I don't know how to retrieve the row of the list item the checkbox is in.
<ListView Name="listView">
<ListView.ItemTemplate>
<DataTemplate>
<CheckBox x:Name="checkBox" Checked="itsChecked" Unchecked="itsUnchecked"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
public List<myListItem> MyListItems;
...
listView.ItemsSource = MyListItems;
...
public class myListItem {
public bool myCheckedValue;
}
private void getCheckedItems() {
//Loop through listview rows and get checkbox state
//???
}
private void itsChecked(object sender, RoutedEventArgs e) {
//How can I get the row this checkbox is in??
}
something like
<GridViewColumn Header="Selected">
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox x:Name="chk" IsChecked="{Binding MyListItemsBoolField}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
assuming that your listView.ItemsSource = MyListItems; will stay
It should be as simple as binding IsChecked property of the CheckBox to a property on the ViewModel (you may need to add a new property if it doesn't already exist).
In my WPF project I'm using an ItemsControl to show items and delete/move up/down them:
<ItemsControl ItemsSource="{Binding TestList, Mode=OneWay}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
...
<TextBox IsReadOnly="True" Text="{Binding Path=Value , Mode=OneWay}" />
<Button Content"Remove" Click="RemoveClick" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
private ObservableCollection<KeyValuePair<int, string>> TestList;
private void RemoveClick(object sender, RoutedEventArgs e)
{
var removebutton = sender as Button;
if (removebutton != null)
{
var test = removebutton.DataContext.ToString(); // That works
// var test removebutton.DataContext.Key;
}
}
I want to get the index (Key) of the selected ObservableCollection TestList item.
The removebutton.DataContext.ToString(); works fine, I get a string with key and value.
But I need only the Key and that doesn't work: removebutton.DataContext.Key; (Error: Cannot resolve symbol 'Key').
If I debug, I can access the Key:
You need to cast removebutton.DataContext to KeyValuePair<int, string>, since DataContext's type is object
This will work:
var test = ((System.Collections.Generic.KeyValuePair<int, string>)removebutton.DataContext).Key
I have a WPF datagrid source which has CabinetName as property. I want the selected item to be this Cabinet name and the combobox dropdown to be filled from a new List FCabinetNames. I write this combobox template into my wpf datagrid to implement this functionality.
<DataGridTemplateColumn Header="Cabinet">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding CabinetName}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding FCabinetNames}"></ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
Now the selected item is showing up correctly so TextBlock Text part is fine. But the item source is not loaded fine.
I have this snippet for the item source.
public class FCabinetNames:List<string>
{
BusinessLogic admintasks = new BusinessLogic();
public FCabinetNames()
{
try
{
List<CabinetData> cab1 = admintasks.CabinetDataforGrid();
List<string> fcabinetname = new List<string>();
foreach (var c1 in cab1)
{
this.Add(c1.CabinetName);
}
}
catch
{
}
}
}
I using a ComboBox with CheckBox as ItemTemplate and I want to iterate through all items, get their checked status and write their content to a string if checked is true. The problem is that I am using a SqlDataReader to fill and bind the ComboBox from database and I can't find a way to access the items IsChecked property.
<ComboBox>
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Click="CheckBox_Click" Content="{Binding}" IsChecked="{Binding Path=IsSelected, Mode=TwoWay}" Tag="{RelativeSource FindAncestor, AncestorType={x:Type ComboBox}}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
I have tried casting the ComboBox items as CheckBoxes on the click event of them this way:
private void CheckBox_Click(object sender, RoutedEventArgs e)
{
for (int i = 0; i < myComboBox.Items.Count; i++)
{
CheckBox cb = (myComboBox.Items[i] as CheckBox);
if (cb.IsChecked == true)
{
myString += "," + myComboBox.SelectedItem.ToString() + "";
}
}
}
but cb always returns NULL. I am guessing it is something with the IsChecked property binding.
I'd like to get this working but I don't want to create an object/class to fill the combobox because I need it to be filled with database. I'd really appreciate any help.
You could do something like this (I'm not stick to MVVM pattern) and this is written on the fly :
public ArrayList List { get; set; }
public MainWindow()
{
InitializeComponent();
SqlDataReader rdr = cmd.ExecuteReader();
List = new ArrayList();
while (rdr.Read()){
List.Add(new Class{ Id = rdr.GetString(0), Value = rdr.GetString(1), IsChecked= rdr.GetString(1) as bool}); //this class will contain the same data schema in your datareader but using properties
}
rdr.Close();
DataContext = List;
}
<ComboBox Name="ComboBox" ItemsSource="{Binding}" >
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Tag="{Binding Id}" Content="{Binding Name}" IsChecked="{Binding IsChecked}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
I am looking to implement a treeview inside a combobox. Basically I want it to show as a combobox when collapsed but a treeview inside combo box when expanded. When a user clicks on a node, I want it to show in the collapsed combo box. I have got this working so far.
The problem I am having is that how do I show a default value from c# when this combo box is loaded. Please help guys as I am running out of ideas :)
Thanks in advance.
Data Template
<DataTemplate x:Key="TreeViewExpanded" >
<StackPanel>
<TreeView x:Name="DPointTree" Margin="5" ItemsSource="{Binding Datapoint}"
Tag="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ComboBox}}}"
>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Field}">
<TextBlock Text="{Binding Name}"/>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding = "{Binding Path=SelectedFieldName, Mode=TwoWay}" Value = "">
<Setter Property = "Visibility" Value = "Collapsed"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
Here is the Xaml
<ComboBox Name="cmbFieldName" Width="150" Background="White" ItemsSource="{Binding}" SelectedItem="{Binding SelectedFieldName , Mode=TwoWay}" >
<ComboBox.ItemTemplateSelector>
<local:TreeViewSelector/>
</ComboBox.ItemTemplateSelector>
</ComboBox>
Here is the DataSet passed to this.
Datapoint _datapoint2 = new Datapoint();
_datapoint2.Name = "Alpha";
_datapoint2.FieldID.Add("Contains Elements");
_datapoint2.Field.Add("1 Year");
_datapoint2.Field.Add("2 Year");
_datapoint2.Field.Add("3 Year");
Try this:
private void TreeView_Loaded(object sender, RoutedEventArgs e)
{
TreeView areaTreeView = sender as TreeView;
// Expand the treeview
if (areaTreeView != null)
{
ExpandCollapseTreeNodes(areaTreeView, true);
}
}
public static void ExpandCollapseTreeNodes(ItemsControl parentContainer, bool isExpanded)
{
foreach (Object item in parentContainer.Items)
{
TreeViewItem currentContainer = parentContainer.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem;
if (currentContainer != null) // && currentContainer.Items.Count > 0
{
//expand the item
currentContainer.IsExpanded = isExpanded;
//if the item's children are not generated, they must be expanded
if (isExpanded && currentContainer.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
{
//store the event handler in a variable so we can remove it (in the handler itself)
EventHandler eh = null;
eh = new EventHandler(delegate
{
//once the children have been generated, expand those children's children then remove the event handler
if (currentContainer.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
{
ExpandCollapseTreeNodes(currentContainer, isExpanded);
currentContainer.ItemContainerGenerator.StatusChanged -= eh;
}
});
currentContainer.ItemContainerGenerator.StatusChanged += eh;
}
//otherwise the children have already been generated, so we can now expand those children
else
{
ExpandCollapseTreeNodes(currentContainer, isExpanded);
}
}
}
}
it is not pretty but works to select a node in the tree
// in code behind after the tree is declared, e.g. after InitializeComponent();
SynchronizationContext.Current.Post(delegate
{
// expand the tree to the selected node after loading
treeView.ExpandAll(); // or if path is known treeView.ExpandPath(...);
SynchronizationContext.Current.Post(delegate
{
treeView.GetContainerFromItem(root.Children[0]).IsSelected = true;
}, null);
}, null);