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).
Related
I have a ListBox, where the list element has a ComboBox, a TextBox and a slider. Depending on the selction of the ComboBox either the TextBox or the slider should be visible.
<ListBox Name="lstPWM" >
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80"/>
<!-- more definitions -->
</Grid.ColumnDefinitions>
<ComboBox ItemsSource="{Binding Path=Gebertyp, Converter={local1:EnumToCollectionConverter}, Mode=OneTime}"
SelectedValuePath="Value"
DisplayMemberPath="Description"
SelectionChanged="PWMTyp_SelectionChanged"
SelectedValue="{Binding Path=Gebertyp}" />
<TextBox Visibility="{Binding GeberVisible}" Text="{Binding GeberNmr, Mode=TwoWay}"/>
<Slider Visibility="{Binding WertVisible}" Value="{Binding Wert, Mode=TwoWay}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The code behind is:
public partial class MainWindow : Window
{
public ObservableCollection<PWMKanal> PWM_col { get; set; } = new();
private void Window_Loaded(object sender, RoutedEventArgs e)
{
lstPWM.ItemsSource = PWM_col;
foreach (var item in Board.PWM) PWM_col.Add(item); //Board.PWM is the data source.
}
private void PWMTyp_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ComboBox box = sender as ComboBox; // Finding the line in the ListBox.
PWMKanal PWM = box.DataContext as PWMKanal;
int z = PWM_col.IndexOf(PWM);
Board.PWM[z].Gebertyp = (QuellePWM)box.SelectedValue;
if (Board.PWM[z].Gebertyp == QuellePWM.Sender)
{
PWM_col[z].GeberVisible = Visibility.Visible; // I thought that i may change the
PWM_col[z].WertVisible = Visibility.Hidden; // ObservableColelction directly
} // but the display is not updated.
else // In Debug mode i see, that PWM_coll
{ // is changed as expected, but no effect
PWM_col[z].GeberVisible = Visibility.Hidden; // on the GUI.
PWM_col[z].WertVisible = Visibility.Visible;
}
if (PWM_col.Count != 0) // this code is intended to update the GUI, but every time
{ // a new item is added the Selection Change fires again
PWM_col.Clear(); // and i get a stack overflow in an endless loop.
foreach (var item in Board.PWM) PWM_col.Add(item);
}
}
}
The comments describe my approaches and problems:
I change the selected element of the ObservableCollection directly, but this has no effect on GUI. At least tho code doesn't crash.
I clear the list ObservableCollection PWM_col, but then i get an infinite loop: every time an element is added to the list the SelectionChange event fires, calling the routin again. Result is stack overflow.
Now my questions to my approaches:
Is it possible to change an element of an ObservableCollection directly by code, and the display is automatically refreshed?
Is it possible to somehow catch the SelectionChanged event before the handler is executed? Or is it possible to temporary dissable the event?
Any other idear?
Thank you for your help!
CollectionChanged does notify, that collection itself, not the
single items, is changed. Therefore to see the changes item's
property need to implement INotifyPropertyChanged. Also remove Mode=OneTime
You can of course set the flag, that PWMTyp_SelectionChanged is
running:
private bool selChangedIsRunning = false;
private void PWMTyp_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if(selChangedIsRunning) return;
selChangedIsRunning = true;
// do stuff ....
selChangedIsRunning = false;
}
Other idea is - don't use the SelectionChange event, but do bind
Slider.Visibility and TextBox.Visibility to the
ComboBox.SelectedValue and use value converter to define the
Visibilty, also you can use the ConverterParameter.
<ComboBox x:Name="CmbPWMTyp" ItemsSource="{Binding Path=Gebertyp, Converter={local1:EnumToCollectionConverter}, Mode=OneTime}"
SelectedValuePath="Value"
DisplayMemberPath="Description"
SelectionChanged="PWMTyp_SelectionChanged"
SelectedValue="{Binding Path=Gebertyp}" />
<TextBox Visibility="{Binding ElementName=CmbPWMTyp, Path=SelectedValue, Converter={StaticResource YourConverter}, ConverterParameter=TBX}" Text="{Binding GeberNmr, Mode=TwoWay}"/>
<Slider Visibility="{Binding ElementName=CmbPWMTyp, Path=SelectedValue, Converter={StaticResource YourConverter}, ConverterParameter=SLDR}" Value="{Binding Wert, Mode=TwoWay}"/>
This link can be also very helpful for you: Difference between SelectedItem SelectedValue and SelectedValuePath
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>
Thank you for reading my question.
The situation:
I have an ObservableCollection<CheckableListItem<T>> CheckableItems
The class CheckableListItem<T> has 2 elements: bool IsChecked and T Item.
The class acts as a wrapper class that adds a checkbox to each Item.
In this project the Item passed has a string element called Name.
How it is displayed in XAML code:
<ListBox ItemsSource="{Binding CheckableItems}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay}" Content="{Binding Path=Item.Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This gives me a Listbox with every entry containing a checkbox and the content of the checkbox is the Item.Name string.
The problem:
I have added a textbox in XAML <TextBox></TextBox> And Now I would like the listbox to only display the objects from the observable collection which match the text from the TextBox.
How I think it could be done:
Create a view of some kind to bind to the listbox and update the view with only the objects that match the search criteria. If no text is entered in the searchbox then all object must be displayed, If only the letter E for example is entered, only the objects containing a letter E in the Item.Name property should be displayed.
I think best would be to bind the text to a string variable in my datacontext and fire an event each time the string changes, something like this:
string SearchString
<TextBox Text="{Binding Path=SearchString, UpdateSourceTrigger=PropertyChanged}" TextChanged="TextBox_TextChanged" />
The function:
private void TextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
{
// Called each time the text changes, perform search here?
}
I just lack the knowledge of WPF syntax for how to create this or how to google the right terms.
Edit:
I now have an ICollectionView checkableItemsView of my ObservableCollection<CheckableListItem<T>> CheckableItems But how to filter it on the Item.Name property?
The binding works, just the filtering I need help with:
<ListBox Grid.Column="1" ItemsSource="{Binding CheckableItemView}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay}" Content="{Binding Path=Item.Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Any input is welcome. Thanks in advance.
Rather than binding directly to your ObservableCollection, you could bind to an ICollectionView.
This would allow you to set a Filter property (using a predicate) that would filter out entries at the UI level, without changing the underlying collection.
Take a look at the filtering section of this page:
http://wpftutorial.net/DataViews.html
edited to add example of filtering:
ICollectionView _customerView = CollectionViewSource.GetDefaultView(customers);
_customerView.Filter = CustomerFilter
private bool CustomerFilter(object item)
{
Customer customer = item as Customer;
return customer.Name.Contains( _searchString );
}
.After some more research I came with the following solution:
Credits go to Paul for pointing me in the right direction.
Source: http://jacobmsaylor.com/?p=1270
private bool CustomFilter(object item)
{
CheckableListItem<Item> checkableItem = item as CheckableListItem<Item>;
if (checkableItem != null && checkableItem.Item.Name.Contains(SearchString))
{
return true;
}
return false;
}
private void TextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
{
checkableItemsView.Filter = CustomFilter;
}
let me start by introducing my current setup:
I have a ListView that binds its SelectedItem property to the ViewModel, like this:
<ListView Name="FileListView" ItemsSource="{Binding ImageList}"
SelectionChanged="ImageSelectionChanged"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
SelectionMode="Single">
<ListView.ItemTemplate>
<DataTemplate>
<view:FileListItem />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
It's item template (view:FileListItem) is the following:
<Grid MouseDown="FileListItemMouseDown" KeyDown="FileListItemKeyDown">
....
<TextBlock Name="NewNameTextBlock"
Text="{Binding NewName}"
Grid.Column="2"
Visibility="{Binding TextBlockVisibility}" />
<TextBox Name="NewNameTextBox"
Text="{Binding NewName, UpdateSourceTrigger=PropertyChanged}"
Grid.Column="2"
Visibility="{Binding TextBoxVisibility}" />
</Grid>
The idea here is to switch on the TextBox and switch off the TextBlock when the corresponding ListView item is being edited. This works ok, but when I hit a particular key, I want the ListView to select the next item and put that item into editing mode. I catch the KeyDown event as seen above in the ItemTemplate and broadcast a message, which is caught in the DataContext of my ListView like this:
public ImageFile SelectedItem {
get { return _selectedItem; }
set { _selectedItem = value; NotifyPropertyChanged("SelectedItem"); }
}
public void SelectAndEditThisHandler (object x)
{
ImageFile file = x as ImageFile;
SelectedItem = file;
}
The result is that the selection actually changes for a split second, but then it changes back to the previous selection. I suspect some other UI elements might be handling my key-presses and doing something to change the selection back, but I can't figure out which elements and how to pinpoint them.
Any help would be greatly appreciated! Thanks!
EDIT:
As requested, the SelectionChanged handler:
private void ImageSelectionChanged(object sender, SelectionChangedEventArgs e)
{
System.Collections.IList filelist = FileListView.SelectedItems;
if (filelist.Count == 1)
{
ImageFile selectedFile = FileListView.SelectedItem as ImageFile;
Mediator.Instance.NotifyColleagues(Mediator.Operations.ImagePathSelected, selectedFile.OriginalPath);
}
}
The mediator message broadcast doesn't do anything related to these controls/this problem at all.
I'm using .Net 3.5 with WPF and XAML.
I have a datagrid with the first column being a DataGridCheckBoxColumn.
This is inside of a window popup.
In the constructor, I sometimes want to initialize all rows to be selected, and other cases no rows to be selected.
Using data binding, I can initialize the checkbox on a row to either checked or not checked.
But I can't get the header checkbox checked along with all the checks on the rows in the case when all rows are to be checked. How can I get to the checkbox?
<toolkit:DataGridCheckBoxColumn CellStyle="{StaticResource SingleClickEditing}" Visibility="{Binding exists}" Binding="{Binding Path=toTransfer, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsReadOnly="False">
<toolkit:DataGridCheckBoxColumn.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Checked="CheckBox_Checked" Unchecked="CheckBox_Checked"/>
</StackPanel>
</DataTemplate>
</toolkit:DataGridCheckBoxColumn.HeaderTemplate>
</toolkit:DataGridCheckBoxColumn>
I'm doing something like:
public MyPopupWindow()
{
InitializeComponent();
if(checkMode.Equals("all"))
{
// Check all the items
foreach (var item in bindList)
{
item.toTransfer = true;
}
// How to check the header checkbox?
}
I'm not sure if this is the best way to do this but you can do
<CheckBox Checked="CheckBox_Checked" Unchecked="CheckBox_Checked" Loaded="CheckBox_Loaded"/>
and
void CheckBox_Loaded(object sender, RoutedEventArgs e)
{
CheckBox checkBox = sender as CheckBox;
if(checkMode.Equals("all"))
{
checkBox.IsChecked = true;
}
}