WPF checkbox affecting multiple items in collection - wpf

I am binding a nested object to a set of controls:
Parameter.ParameterOptionGroup.ParameterOptions
binded to:
<ItemsControl ItemsSource="{Binding Parameters}">
<ItemsControl ItemSource="{Binding ParameterOptionGroup.ParameterOption}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding State}"></CheckBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ItemsControl>
Oddly, whenever I check some of the checkboxes, it triggers other checkboxes. I noticed that this happens when the checkbox's source object has the same Id. Truth to be told, a ParameterOption can be assigned as a member of any ParameterOptionGroup.
Is there a way to avoid this without changing my schema?

Related

Bound String List displays containing class name not the string

My Problem is the following:
I have a class which has a ObservableCollection of strings, which gets populated by the selected children.
Those are supposed to be listed in a ListBox.
But the strings aren't listed, rather the object type in FilterList is shown.
<ListBox Grid.Row="2" Grid.Column="0"
ItemsSource="{Binding FilterList}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel IsItemsHost="True"
Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding ListOfActiveFilters}">
<!--<ContentPresenter Content="{Binding ListOfActiveFilters.}"/>-->
<TextBlock Text="{Binding}" />
</HierarchicalDataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Filter List contains the classes which have ListOfActiveFilters as a Property.
I thought that it would work the way I showed it, but it doesn't.
Since that is also the way I saw others do it.
If I were to use a Class with a single string Property as the type of the Collection instead of the Collection of strings I have right now, I think it would work.
I just don't really see the point in creating a class which holds a string property just so that I can bind the ContentPresenter or TextBox to that property.
What am I doing wrong? I am rather new to the topic.
One single ListBox will only be able to display the filters in one single ListOfActiveFilters collection. You could use nested ItemsControls to display them all:
<ItemsControl ItemsSource="{Binding FilterList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ListBox ItemsSource="{Binding ListOfActiveFilters}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The other option is to modify your data model and put all strings in a single collection. You could then bind the ItemsSource property of the ListBox directly to this one. But a ListBox itself has no concept of hierarhical data.

Trouble binding selected item in nested Listview

I currently use a listview nested inside a listview as a way to show a Knockout style tournament graphically, backed up in ViewModel by SectionTreeOne, which contains a List of Lists of objects "TournamentNode". I cannot however get my selected "Tournament Node" to bind when I click on it.
<Grid Grid.Row="2">
<ListView ItemsSource="{Binding SectionTreeOne}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate >
<DataTemplate>
<ListView ItemsSource="{Binding}" SelectionMode="Single"
SelectedItem="{Binding SelectedTournamentNode}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
C# binding:
Collection
public List<List<TournamentNodeModel>> SectionTreeOne
{
get { return _sectionTreeOne; }
set
{
_sectionTreeOne = value;
base.OnPropertyChanged("SectionTreeOne");
}
}
Selected Item:
public TournamentNodeModel SelectedTournamentNode
{
get { return _selectedTournamentNode; }
set
{
if (value == _selectedTournamentNode)
return;
_selectedTournamentNode = value;
base.OnPropertyChanged("SelectedTournamentNode");
}
}
Try with the folowing binding:
SelectedItem="{Binding SelectedTournamentNode, Mode=TwoWay}"
Keep in mind that WinRT always use the OneWay binding mode as default unlike in WPF where it automatically selects a binding mode depending on the property nature or accessibility.
A good principle I used with WinRT to avoid this kind of mistake is to always explicitely specify the binding mode.
So I finally figured out what were the mistakes in your binding. Firstly, the SelectedItem binding mode has to be set to TwoWay explicitely as I stated above.
Secondly, the nested list was binding to an inner list in the SectionTreeOne list, therefore if you want to bind SelectedItem to a property on your view model, you have to rebind this property to the DataContext of the parent list using named elements. You were actually trying to bind to a non-existant property on the inner list instead of binding to the view model where the property is located.
<ListView x:Name="listView" ItemsSource="{Binding SectionTreeOne}">
...
<ListView.ItemTemplate >
<DataTemplate>
<ListView ItemsSource="{Binding}" SelectionMode="Single"
SelectedItem="{Binding Path=DataContext.SelectedTournamentNode, ElementName=listView, Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Do read the Visual Studio debugger output, it has really useful information about binding errors that could occur in the binding chain, especially if you bind a list nested in another list, it will save you lot of headaches!

WPF DataTemplate for Collection

I must now hang my head, for I have scoured the Google for hours and still have no clue what I'm doing wrong.
<DataTemplate DataType="{x:Type local:Controllers}">
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="{Binding Path=Port}" />
</WrapPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
What I'm trying to do is to display an arbitrary number of controller objects in this list. "Controllers" is just an alias for "List<Controller>". "Port" is a property of each "Controller" object, but of course that's not showing up on the list. Items are being correctly added to the collection the list is based on (the collection is actually stored as the Content property of the ContentControl that is displaying this collection of objects), but no item in the collection itself is being displayed.
I thought at first that it might be an update issue--that the collection was being correctly displayed in its initial, empty state, but that isn't the case; if I start with the collection already populated, I still get no items.
Help me, Obi Wan. :(
You need to bind something to the ListBox. That something is just {Binding}, which refers to the instance of the DataTemplates DataType that is passed in at run-time.
<DataTemplate DataType="{x:Type local:Controllers}">
<ListBox ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="{Binding Path=Port}" />
</WrapPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>

Binding ListBox Items using Templates

I am going crazy trying to figure this out without success.
I have a DependencyObject, ("UserObject"). It has a "DataItems" DependecyProperty that is an ObservableCollection. "UserDefiniton" is a DependencyObject with a DependencyProperty of "Data". Data has two properties: DataType (an enumeration) and Value (a string).
I am trying to define a ListBox in XAML that uses the "DataItems" property as its ItemsSource. In the ItemTemplate, I have several different controls. For simplicity of this issue, I am using a CheckBox and a TextBox. I want CheckBox to be available and visible when DataType is 0, while I want the TextBox to be available and visible when the DataType is 1. Only one control can be available and visible at a time.
This works:
<ListBox
ItemsSource={Binding DataItems, Mode=OneWay}>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<CheckBox
Visibility="{Binding Path=Data.DataType, Mode=OneWay, Converter={StaticResource VisibilityConverter}, ConverterParameter=0}"
IsChecked="{Binding Path=Data.Value, Mode=TwoWay, Converter={StaticResource StringToBoolean}}" />
<TextBox
Visibility="{Binding Path=Data.DataType, Mode=OneWay, Converter={StaticResource VisibilityConverter}, ConverterParameter=1}"
Text="{Binding Path=Data.Value, Mode=TwoWay}" />
</StackPanel>
</DataTemplate>
<Listbox.ItemTemplate>
</ListBox>
The problem is that even though only one is visible, both are fighting over the Data.Value property (the boolean of the checkbox will show in the textbox, even though the checkbox is hidden).
Basically, though, the binding in this case is working--but the implementation is incorrect.
So, I switched to using Templates. The problem I am having is that I can't get the binding to work.
This is the code that I have for the Template. The Template selector is working correctly, but the Text property of the TextBox and the IsChecked property of the checkbox are not binding to Data.Value:
<DataTemplate x:Key="TextBoxItem">
<TextBox
Text="{Binding Path=Data.Value, Mode=TwoWay}" />
</DataTemplate>
<DataTemplate x:Key="CheckBoxItem">
<CheckBox
IsChecked="{Binding Path=Data.Value, Mode=TwoWay, Converter={StaticResource StringToBoolean}}" />
</DataTemplate>
...
<ListBox
ItemsSource={Binding DataItems, Mode=OneWay}>
<ListBox.ItemTemplate>
<DataTemplate>
<ContentControl
Content="{Binding Path=Data.DataType, Mode=OneWay}"
ContentTemplateSelector="{DynamicResource UserDefinitionTemplateSelector}"/>
</DataTemplate>
<ListBox.ItemTemplate>
</ListBox>
So how do I fix the binding?
Content should be set to {Binding}, since the Content will be the DataContext of the data-templates, hence you should just pass on the current DataContext. If you need to pass specific data to the template selector you can just drill down in the whole object.
There also is a template selector on the level of the ListBox, so you do not really need the internal ContentControl.
(You might also be interested in generic methods of debugging data bindings.)

Binding acting strange with ItemPanel and ok without

The following code on displays the {Binding text} and the dependency property for Sprites does not run the propertyvaluechanged for text runs but not for sprites.
<ItemsControl x:Name="AnswerListBox" ItemsSource="{Binding Answers}" ScrollViewer.VerticalScrollBarVisibility="Disabled" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:spriteRadioButton Text="{Binding text}" Sprites="{Binding Path=DataContext.UISprites, ElementName=questionField}" GroupName="{Binding Path=DataContext.QuestionTitle, ElementName=questionField}" IsChecked="{Binding selected}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<toolkit:WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
if i don't use an itemspaneltemplate then the properties work as expected.
At the moment you're using the default "OneWay" binding mechanism. This means that your object can update the UI but the UI cannot update the object.
Your binding should use "TwoWay" binding in order to allow the UI to notify the object of changes:
<DataTemplate>
<local:spriteRadioButton Text="{Binding text,Mode=TwoWay}" Sprites="{Binding Path=DataContext.UISprites, ElementName=questionField,Mode=TwoWay}" GroupName="{Binding Path=DataContext.QuestionTitle, ElementName=questionField,Mode=TwoWay}" IsChecked="{Binding selected,Mode=TwoWay}" />
</DataTemplate>
Keep in mind, these changes will update your Answers object. If you want to change the Answers object itself this too will need to be marked as TwoWay binding as well.

Resources