I have an ItemsControl with a DataTemplate that has been defined. My ItemsControl definition looks like the following:
<ItemsControl x:Name="myItemsControl" ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<CheckBox x:Name="myCheckBox" Content="{Binding Name}" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
This is a simplified version of my DataTemplate. Regardless, when a user clicks a button on the page, I want to loop through the items in myItemsControl and determine if the CheckBox element associated with the item is checked.
How do I determine if a CheckBox is checked for a specific item within an ItemsControl?
Thank you!
Add a property to your data class and databind it, then iterate over the collection itself.
public class myDataClass
{
public string Name { get; set;}
public bool IsSomething { get; set; }
}
<CheckBox x:Name="myCheckBox" Content="{Binding Name}" IsChecked="{Binding IsChecked, Mode=TwoWay}" />
You can try something like traditional iteration:
public bool? TestMyCheckbox(string bindingName)
{
foreach (var item in myItemsControl.Items)
{
if (item.GetType() == typeof(CheckBox))
{
var checkbox = (CheckBox)item;
if (checkbox.Content.Equals(bindingName))
{
return (checkbox.IsChecked);
}
}
}
return null;
}
Additionaly (this may better fit your needs) you can look for a list of checkboxes bindings that are checked:
public IEnumerable<object> TestMyCheckboxes(ItemsControl control)
{
return from Control x in control.Items
where x.GetType().Equals(typeof(CheckBox)) && ((CheckBox)x).IsChecked == true
select ((CheckBox)x).Content;
}
Related
I have a DataGrid with 2 columns. Based on the first column which is bound to ParameterDataType I want to load the appropriate template in the second column.
Problem with this code is before the DataGrid has been loaded, the template selector is executing as a result the item is null. Is there a way to execute the Template Selector after the ControlTemplate's DataContext is set. Please help.
Here is my xaml:
<uwpControls:DataGrid Grid.Row="4"
ItemsSource="{x:Bind ViewModel.ServiceMethodsData,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
AutoGenerateColumns="False">
<uwpControls:DataGrid.Resources>
<DataTemplate x:Key="StringTemplate">
<TextBox Width="150" Height="30" VerticalAlignment="Center"></TextBox>
</DataTemplate>
<DataTemplate x:Key="IntegerTemplate">
<controls:TextBoxNumeric Width="150" Height="30" VerticalAlignment="Center"></controls:TextBoxNumeric>
</DataTemplate>
<DataTemplate x:Key="BooleanTemplate">
<CheckBox IsChecked="False"></CheckBox>
</DataTemplate>
<local:MethodValueDataTemplateSelector x:Key="MethodValueTemplateSelector"
StringTemplate="{StaticResource StringTemplate}"
IntegerTemplate="{StaticResource IntegerTemplate}"
BooleanTemplate="{StaticResource BooleanTemplate}"/>
</uwpControls:DataGrid.Resources>
<uwpControls:DataGrid.Columns>
<uwpControls:DataGridTextColumn Header="First Column"
Binding="{Binding ParameterDataType, Mode=OneWay}"/>
<uwpControls:DataGridTemplateColumn Header="Second Column">
<uwpControls:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ContentControl x:Name="MethodValueContentControl"
Content="{Binding Path=.}"
ContentTemplateSelector="{StaticResource MethodValueTemplateSelector}"></ContentControl>
</DataTemplate>
</uwpControls:DataGridTemplateColumn.CellTemplate>
</uwpControls:DataGridTemplateColumn>
</uwpControls:DataGrid.Columns>
</uwpControls:DataGrid>
Here is my DataTemplate selector
public class MethodValueDataTemplateSelector : DataTemplateSelector
{
public DataTemplate StringTemplate { get; set; }
public DataTemplate IntegerTemplate { get; set; }
public DataTemplate BooleanTemplate { get; set; }
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
//I want to do something like if(DataContext.ParameterDataType=="Int") return IntegerTemplate etc
return StringTemplate;
}
}
Here is my ViewModel
public class ServiceUtilityMethodsViewModel
{
private ObservableCollection<VmServiceMethodsViewDataGridModel> _serviceMethodsData;
public ObservableCollection<VmServiceMethodsViewDataGridModel> ServiceMethodsData
{
get => _serviceMethodsData;
set => Set(ref _serviceMethodsData, value);
}
public ServiceUtilityMethodsViewModel(INavigationService navigationService) : base(navigationService)
{
PopulateServiceData();
}
private void PopulateServiceData()
{
ServiceMethodsData = new ObservableCollection<VmServiceMethodsViewDataGridModel>();
ServiceMethodsData.Add(new VmServiceMethodsViewDataGridModel()
{
ParameterName = "Param1",
ParameterDataType = "String"
});
ServiceMethodsData.Add(new VmServiceMethodsViewDataGridModel()
{
ParameterName = "Param2",
ParameterDataType = "Int"
});
ServiceMethodsData.Add(new VmServiceMethodsViewDataGridModel()
{
ParameterName = "Param3",
ParameterDataType = "bool"
});
}
}
}
Here is my Model class
public class VmServiceMethodsViewDataGridModel : BindableBaseThreadSafe
{
private string _parameterName;
private string _parameterDataType;
public string ParameterName
{
get => _parameterName;
set => Set(ref _parameterName, value);
}
public string ParameterDataType //I want the template selector to work based on this column.
{
get => _parameterDataType;
set => Set(ref _parameterDataType, value);
}
}
You should assign the DataTemplateSelector to DataGridTemplateColumn.CellTemplateSelector and DataGridTemplateColumn.CellEditingTemplateSelector directly.
I didn't check the UWP version. I think the UWP DataGridTemplateColumn doesn't have this template selector properties. In this case you can stick to your current XAML, but don't forget to define a CellEditingTemplate too (e.g., replace the TextBlock with a TextBox for the CellEditingTemplate version - better use a TextBlock in your default CellTemplate as it looks better). The properties CellTemplate and CellEditingTemplate exist for sure in the UWP version.
XAML DataGrid definition
<uwpControls:DataGrid ItemsSource="{x:Bind ViewModel.ServiceMethodsData,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
AutoGenerateColumns="False">
<uwpControls:DataGrid.Resources>
<DataTemplate x:Key="StringTemplate">
<TextBox Width="150" Height="30" VerticalAlignment="Center" />
</DataTemplate>
<DataTemplate x:Key="IntegerTemplate">
<controls:TextBoxNumeric Width="150" Height="30" VerticalAlignment="Center" />
</DataTemplate>
<DataTemplate x:Key="BooleanTemplate">
<CheckBox IsChecked="False" />
</DataTemplate>
<local:MethodValueDataTemplateSelector x:Key="MethodValueTemplateSelector"
StringTemplate="{StaticResource StringTemplate}"
IntegerTemplate="{StaticResource IntegerTemplate}"
BooleanTemplate="{StaticResource BooleanTemplate}" />
</uwpControls:DataGrid.Resources>
<uwpControls:DataGrid.Columns>
<uwpControls:DataGridTextColumn Header="First Column"
Binding="{Binding ParameterDataType, Mode=OneWay}" />
<uwpControls:DataGridTemplateColumn Header="Second Column"
CellTemplateSelector="{StaticResource MethodValueTemplateSelector}"
CellEditingTemplateSelector="{StaticResource MethodValueTemplateSelector}">
</uwpControls:DataGridTemplateColumn>
</uwpControls:DataGrid.Columns>
</uwpControls:DataGrid>
The DataTemplateSelector is also quite simple.
The parameters of the SelectTemplateCore override are the item and the item's container (which is a FrameWorkElement and a ContentControl most of the time).
The item is always the data model and the DataContext of the current row. In your case the item is of type VmServiceMethodsViewDataGridModel.
The container is the FrameWorkElement that wraps the model for rendering e.g. ListBoxItem. In your case the container should be of type DataGridRow.
Simply cast the item parameter to the appropriate type and evaluate it.
MethodValueDataTemplateSelector.cs
public class MethodValueDataTemplateSelector : DataTemplateSelector
{
public DataTemplate StringTemplate { get; set; }
public DataTemplate IntegerTemplate { get; set; }
public DataTemplate BooleanTemplate { get; set; }
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
// Return if the control is not loaded yet and the item is therefore null
// or the item is of the wrong type
if (!(item is VmServiceMethodsViewDataGridModel dataModel))
{
return null;
}
// I want to do something like:
// if(DataContext.ParameterDataType=="Int") return IntegerTemplate etc
switch (dataModel.ParameterDataType)
{
case string value when value.Equals("Int", StringComparison.OrdinalIgnoreCase):
return IntegerTemplate;
case string value when value.Equals("String", StringComparison.OrdinalIgnoreCase):
return StringTemplate;
case string value when value.Equals("Bool", StringComparison.OrdinalIgnoreCase):
return BooleanTemplate;
default:
return null;
}
}
}
I have difficulties binding the selectedindex of a combobox to an object.
This is my code:
(Part of) CustomerClass
public class Customer : INotifyPropertyChanged
{
public int CountryCode
{
get { return _CountryCode; }
set { _CountryCode = value; NotifyPropertyChanged(); }
}
}
2a. (Part of) CustomListItem
<ComboBox x:Name="cboCountryCode" Grid.Column="5" ItemsSource="{Binding}" DisplayMemberPath="LongName" SelectedIndex="{Binding CountryCode, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
2b. (Part of) CustomListItem
public partial class CustomerListItem : UserControl
{
public CustomerListItem()
{
InitializeComponent();
ObservableCollection<CountryCode> Liste = CountryCodes.Instance.List;
cboCountryCode.DataContext = Liste;
}
(Part of) MainPage
<ItemsControl Name="itcCustomers" Style="{StaticResource ItemsControlVirtualizedStyle}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:CustomerListItem/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The Combobox List Items are shown correctly.
But the selected index is not working at all
See this screenshot
I found the problem. I tried to bind the Combobox to two different Datasources. One for the collection and one for the selectedindex. Now I combinde these two Datasources into one class and bound to it, now it works fine
I have an ItemsControl:
<ItemsControl x:Name="myList" ItemTemplateSelector="{DynamicResource mySelectorTemplate}" ItemsPanel="{StaticResource myPanelTemplate}"/>
I want to take one of the items and make a button with his logic:
<Button Style="{StaticResource myButtonStyle}"/>
One of the items of myList has a boolean with true value. This is the item with which the button will be made:
Private myCollection As ObservableCollection(Of Items.Subitems)
myList.ItemsSource = myCollection
When myCollection.myBooolean = true, is the field that gives to the item this special treatment.
What would be an efficient way to do this?
There are many possibilitys for this - but this looks like the best way.
You want to use a DataTemplateSelector. You can choose n DataTemplates with this by your own overwritten logic.
C#
Sample Class:
public class MySampleClass
{
public string Name;
public bool MyFlag;
}
Sample Template Selector
public class MyTemplateSelector : DataTemplateSelector
{
public DataTemplate IsTrueTemplate
{
get;
set;
}
public DataTemplate IsFalseTemplate
{
get;
set;
}
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
MySampleClass temp = item as MySampleClass;
if(temp != null)
{
if (temp.MyFlag)
{
return IsTrueTemplate;
}
else
{
return IsFalseTemplate;
}
// And so on
}
else
{
return base.SelectTemplate(item, container);
}
}
}
Usage in XAML
<ListBox>
<ListBox.ItemTemplateSelector>
<example:MyTemplateSelector>
<example:MyTemplateSelector.IsTrueTemplate>
<DataTemplate>
<StackPanel>
<Button Style="{StaticResource myButtonStyle}" />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</example:MyTemplateSelector.IsTrueTemplate>
<example:MyTemplateSelector.IsFalseTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</example:MyTemplateSelector.IsFalseTemplate>
</example:MyTemplateSelector>
</ListBox.ItemTemplateSelector>
</ListBox>
example is your DataTemplateSelector-Namespace.
I would recommend you - to store the DataTemplates in the Resources and set it via StaticResource.
I've got the following ItemsControl that gives me a check box for every database within the available collection. These checkboxes allow the user to select which ones to filter on. The databases to filter on are in a separate collection (FilteredDatabases). How exactly do I do this? I could add an InFilter property to the database item class. But, I don't want to start changing this code yet. The problem I can't get around in my head is the fact that I need to bind to a property that is not on the database item itself. Any ideas?
<ItemsControl ItemsSource="{Binding AvailableDatabases}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Name}" IsChecked="{Binding ???}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
// In view model
public IBindingList FilteredDatabases
{
get;
private set;
}
public IBindingList AvailableDatabases
{
get;
private set;
}
Bind CheckBox.Command to routed command instance
Bind routed command to method
Use IBindingList.Add and IBindingList.Remove methods
The following code illustrates what you are trying to do, in order to do this you are better off using ObservableCollection instead of as your collection object, if an ItemsControl is bound to it it will automatically update the UI when viewmodels are added and removed.
XAML:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ItemsControl Grid.Column="0" ItemsSource="{Binding AvailableDatabases}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Name}" IsChecked="{Binding IsChecked}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl Grid.Column="1" ItemsSource="{Binding FilteredDatabases}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
View Models:
public class MainViewModel
{
private ObservableCollection<DBViewModel> _availableDatabases;
private ObservableCollection<DBViewModel> _filteredDatabases;
public ObservableCollection<DBViewModel> AvailableDatabases
{
get
{
if (_availableDatabases == null)
{
_availableDatabases = new ObservableCollection<DBViewModel>(new List<DBViewModel>()
{
new DBViewModel(this) { Name = "DB1" , IsChecked = true},
new DBViewModel(this) { Name = "DB2" },
new DBViewModel(this) { Name = "DB3" },
new DBViewModel(this) { Name = "DB4" },
new DBViewModel(this) { Name = "DB5" },
new DBViewModel(this) { Name = "DB6" },
new DBViewModel(this) { Name = "DB7" , IsChecked = true },
});
}
return this._availableDatabases;
}
}
public ObservableCollection<DBViewModel> FilteredDatabases
{
get
{
if (_filteredDatabases == null)
_filteredDatabases = new ObservableCollection<DBViewModel>(new List<DBViewModel>());
return this._filteredDatabases;
}
}
}
public class DBViewModel
{
private MainViewModel _parentVM;
private bool _isChecked;
public string Name { get; set; }
public DBViewModel(MainViewModel _parentVM)
{
this._parentVM = _parentVM;
}
public bool IsChecked
{
get
{
return this._isChecked;
}
set
{
//This is called when checkbox state is changed
this._isChecked = value;
//Add or remove from collection on parent VM, perform sorting here
if (this.IsChecked)
_parentVM.FilteredDatabases.Add(this);
else
_parentVM.FilteredDatabases.Remove(this);
}
}
}
View models should also implement INotifyPropertyChanged, I omitted it since it was not necessary in this particular case.
I have a combo box that I have bound to a list that exists in my viewmodel. Now when a users makes a selection in that combo box I want a second combo box to update its content.
So, for example, combobox1 is States and combobox2 should contain only the Zipcodes of that state.
But in my case I don't have a predefined lists before hand for combobox2, I need to go fetch from a db.
Also, if needed, I could get all the potential values for combobox2 (for each combobox1 value) before hand, but I'd like to avoiding that if I can.
How do I implement in WPF and using MVVM? I'm fairly new to this whole wpf\databinding\mvvm world.
Something like the following. Note that the code is drastically simplified for the sake of example. In reality, your ViewModel would implement INotifyPropertyChanged and raise PropertyChanged events when the properties were modified.
The key though is the setter of SelectedState. Your ComboBox would bind its SelectedValue property to the ViewModel's SelectedState property. When the property changed, the ZipCodes collection gets re-loaded which another combobox would be bound to.
class MyViewModel {
public ObservableCollection<string> States {
get;
private set;
}
public ObservableCollection<string> ZipCodes {
get;
private set;
}
public string SelectedState {
get { return _selectedState; }
set {
_selectedState = value;
LoadZipCodes(_selectedState);
}
}
public string SelectedZipCode {
get;
set;
}
void LoadZipCodes(string state) {
// repopulate the ZipCodes property
}
}
Another solution. The approximate model:
class StateViewModel
{
public string StateName
{
get {...}
set {...}
}
public ObservableCollection<ZipCodeViewModel> ZipCodes
{
get {...}
set {...}
}
}
class ZipCodeViewModel
{
public string ZipCodeName
{
get {...}
set {...}
}
}
class MainViewModel
{
public ObservableCollection<StateViewModel> States
{
get {...}
set {...}
}
}
And XAML:
<ComboBox ItemsSource="{Binding Path=States}" IsSynchronizedWithCurrentItem="True">
<ComboBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Path=StateName}"></Label>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<ContentControl Content="{Binding Path=States}">
<ContentControl.ContentTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Path=ZipCodes}">
<ComboBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Path=ZipCodeName}"></Label>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>