'MultiBinding' cannot be used within a 'SetterBaseCollection' - c#

I just started to programm in C# and got the following error message "A 'MultiBinding' cannot be used within a 'SetterBaseCollection' collection. A 'MultiBinding' can only be set on a DependencyProperty of a DependencyObject". My dea was to disable some of the combobox depedant of the input values given to the converter. use the same converter later in the xaml file and have no error..
<DataTemplate x:Key="ComboBoxDirectionCellDataTemplate">
<Canvas>
<ComboBox Name="DirectionBi" ItemsSource="{Binding Source={StaticResource DirectionBiList}}" SelectedItem="{Binding Direction, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Background="#FFCDCDCD" />
<ComboBox Name="DirectionOut" ItemsSource="{Binding Source={StaticResource DirectionOutList}}" SelectedItem="{Binding PinFunctionOptions[SelectedPinFunctionIdx].SupportedDirections, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Background="#FFCDCDCD" />
<ComboBox Name="DirectionIn" ItemsSource="{Binding Source={StaticResource DirectionOutList}}" SelectedItem="{Binding PinFunctionOptions[SelectedPinFunctionIdx].SupportedDirections, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Background="#FFCDCDCD" />
</Canvas>
<DataTemplate.Triggers>
<DataTrigger Value="In">
<MultiBinding Converter="{StaticResource FunctionToGroupConverter}">
<Binding Path="PinFunctionOptions"/>
<Binding Path="SelectedPinFunctionIdx"/>
</MultiBinding>
<Setter TargetName="DirectionBi" Property="Visibility" Value="Hidden"/>
Thanks for help!!!!

I may be wrong - and i have no way of checking this at the moment...
I think what you want is the following...
<DataTrigger ...>
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource FunctionToGroupConverter}">
<Binding Path="PinFunctionOptions"/>
<Binding Path="SelectedPinFunctionIdx"/>
</MultiBinding>
</DataTrigger.Binding>
</DataTrigger>

DataTemplate.Triggers is expecting a collection of Setter why are you attempting to put a MultiBinding in there?
The exception you're getting is a result of this.
I'm not entirely certain what you're attempting with the MultiBinding but you may fare better by performing the bindings individually on the respective elements.

Related

binding to an object on a data template [duplicate]

I define a headertemplate into a wpf groupbox and the databinding doesn't work. I don't understand why.
<GroupBox>
<GroupBox.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" >
<Image Source="/PopuAssuNetApplication.UI.Control;component/Images/Members.png" Width="24" />
<TextBlock VerticalAlignment="Center">
<TextBlock.Text>
<MultiBinding StringFormat="{x:Static Member=resx:Resources.PersonsInContractGroupBox}">
<Binding Path="CurrentContract.Federation" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type GroupBox}}">
</Binding>
<Binding Path="CurrentContract.Type" Converter="{StaticResource contractTypeConverter}" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type GroupBox}}">
</Binding>
<Binding Path="CurrentContract.Number" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type GroupBox}}">
</Binding>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<WpfComponent:WaitControl Margin="7,0,0,0" VerticalAlignment="Top" Width="24" Height="24" MarginCenter="4">
<WpfComponent:WaitControl.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsMembersOfContractBusy, UpdateSourceTrigger=PropertyChanged, ElementName=PersonsInContract}" Value="true">
<Setter Property="WpfComponent:WaitControl.Visibility" Value="Visible" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=IsMembersOfContractBusy, UpdateSourceTrigger=PropertyChanged, ElementName=PersonsInContract}" Value="false">
<Setter Property="WpfComponent:WaitControl.Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</WpfComponent:WaitControl.Style>
</WpfComponent:WaitControl>
</StackPanel>
</DataTemplate>
</GroupBox.HeaderTemplate>
The problem is that the HeaderTemplate is used for templating the Header thus within the HeaderTemplate your DataContext is whatever you bind or assign to the Header property of your GroupBox.
Think of the Header property as almost like the DataContext for the header of the control. Normally the DataContext property inherits its value from its parent but since not every control has a Header the Header is blank unless you set it.
By binding your Header explicitly to the current DataContext Header="{Binding}" your example should work as you expect. To help illustrate how this works I've created a simple example below that shows how the Header and DataContext work independently from each other for providing data to either the body or header of the control.
<GroupBox Header="HEADER TEXT" DataContext="BODY TEXT">
<GroupBox.HeaderTemplate>
<DataTemplate>
<Button Content="{Binding}"
Background="LightGreen" />
</DataTemplate>
</GroupBox.HeaderTemplate>
<CheckBox HorizontalAlignment="Center"
VerticalAlignment="Center" Content="{Binding}" />
</GroupBox>
This will yield a GroupBox that looks like the following.
I think that by default in databinding, wpf always gets data from the DataContext property. Seems not in datatemplate
Your assumption is correct about DataContext and it does work in the DataTemplate as I've demonstrated it's just that in the Header's template the DataContext is the value from the Header Property and not the DataContext itself.
The GroupBox does not have a member called "CurrentContract". Most probably, you want to accesss a property called "CurrentContract" from the corresponding ViewModel?! The ViewModel is the GroupBox's DataContext, so you have to change the Binding Paths to something like...
<Binding Path="DataContext.CurrentContract.Type" Converter="{StaticResource contractTypeConverter}" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type GroupBox}}">
<GroupBox >
<GroupBox.HeaderTemplate>
<DataTemplate>
<RadioButton Content="myR"
IsChecked="{Binding rIsChecked, Mode=TwoWay}"
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type GroupBox}}}" />
</DataTemplate>
</GroupBox.HeaderTemplate>
<GroupBox.Content>
<Grid IsEnabled="{Binding rIsChecked}">
</Grid>
</GroupBox.Content>
</GroupBox>
Just propagate the GroupBox DC to the DataTemplate content...works like a charm...
The lesson learned above is useful in general for DataTemplates, but I actually found out recently there is a better way to change the header of a groupbox:
<GroupBox>
<GroupBox.Header>
<CheckBox IsChecked="{Binding Path=mSomeBoolean}"/>
</GroupBox.Header>
</GroupBox>
This way there is no need to define a relative source in the bindings.
Also please note this issue with GroupBoxes and the header.
This is what worked for me:
<HeaderedContentControl Header="{Binding}" Style="{StaticResource TallHeaderedContentStyle}">
<HeaderedContentControl.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=HeaderText"} />
</DataTemplate>
</HeaderedContentControl.HeaderTemplate>

Update property in ViewModel also if there are Validation-Errors

In my application I have the following definition of a DataGridTemplateColumn:
<DataGridTemplateColumn Header="Placeholder-Name" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox IsEditable="True" VerticalAlignment="Center" Margin="2"
ItemsSource="{Binding DataContext.AvailablePlaceholders, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}"
DisplayMemberPath="PlaceholderName"
Validation.ErrorTemplate="{StaticResource ComboBoxErrorTemplate}">
<ComboBox.Text>
<Binding Path="PlaceholderName" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:PlaceholderValidationRule>
<local:PlaceholderValidationRule.PlaceholderValidationRuleParamData>
<local:PlaceholderValidationRuleParamData
UsedPlaceholders="{Binding Source={StaticResource proxy}, Path=Data.PlaceholderItems}"
AvailablePlaceholders="{Binding Source={StaticResource proxy}, Path=Data.AvailablePlaceholders}"/>
</local:PlaceholderValidationRule.PlaceholderValidationRuleParamData>
</local:PlaceholderValidationRule>
</Binding.ValidationRules>
</Binding>
</ComboBox.Text>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Everything works fine with this, but I just realized that if PlaceholderValidationRule reports a not valid result, the property PlaceholderName in my ViewModel where the ComboBox-Text is bound to will not be updated.
Is there any way to update the property in the ViewModel from the View also if there are Validation-Errors?
Is there any way to update the property in the ViewModel from the View also if there are Validation-Errors?
Set the ValidationStep property of the ValidationRule to UpdatedValue:
<local:PlaceholderValidationRule ValidationStep="UpdatedValue">
This will cause the validation rule to be run after the source property has been updated.

Obtain ListBox SelectedItem through ContextMenu with Multibinding in Windows.Resource

I'm trying to use multibinding on a context menu stored in Window.Resources and I can't figure out how to get the SelectedItem of the ListBox I attach the menu to.
I think I need to use PlacementTarget, but can't get anything to work. I tried using FindAncestor, but that didn't work. I also tried getting the listbox by the ElementName to no avail.
<Window ...>
<Window.DataContext>
<local:IPViewModel/>
</Window.DataContext>
<Window.Resources>
<sys:Int32 x:Key="Number">0</sys:Int32>
<sys:Int32 x:Key="NumberLetter">1</sys:Int32>
<sys:Int32 x:Key="NumberLetterNumber">2</sys:Int32>
<local:CombineParams x:Key="CombineParams"/>
<ContextMenu x:Key="DetailMenu">
<MenuItem Header = "Number">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:InvokeCommandAction Command="{Binding SwitchLabelMode}">
<i:InvokeCommandAction.CommandParameter>
<MultiBinding Converter="{StaticResource CombineParams}">
<!-- The next line is where I'm not sure what to write to get to the listbox -->
<Binding Source="{RelativeSource AncestorType={x:Type ListBox}}"/>
<Binding Source="{StaticResource Number}"/>
</MultiBinding>
</i:InvokeCommandAction.CommandParameter>
</i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</MenuItem>
...
</Window.Resources>
...
<ListBox Name="IndexDetailsListBox"
...
ContextMenu="{StaticResource DetailMenu}"
...>
</ListBox>
...
</Window>
I can't get it to give back anything other than unset value.
I'm grateful for any help. None of the other questions I found have the contextmenu in Window.Resources with MultiBinding which seems to be my issue.
Your problem is, that you assign RelativeSource- object to the Source property of the Binding object. RelativeSource- object belongs to the RelativeSource-property.
So you get your List to the multi value converter:
<MultiBinding Converter="{StaticResource CombineParams}">
<Binding Path="PlacementTarget" RelativeSource="{RelativeSource AncestorType={x:Type ContextMenu}}"/>
<Binding Source="{StaticResource Number}"/>
</MultiBinding>

WPF Databinding to Window

I want to link the DataBinding of a listbox item with a new window
A "Name" button should open a new window with the DataBinding of the listbox item.
The Binding is a .xml File:
<People>
<Person image="Test.jpg" company="" position="" website="" chat="" notes="">
<Name first_name="Max" second_name="" last_name="Mustermann" salutation="" />
<Email email1="" email2="" email3="" />
<Phone_Numbers business="" private="" mobile="" />
<Business_Adress street="" place="" state="" postalcode="" country="" />
<Private_Adress street="" place="" state="" postalcode="" country="" />
</Person>
</People>
And the new Window should be linked to the Name Element of the Person.
Though there will be more then one person, but the window should be linked to the right person.
This is the XmlDataProvider:
<XmlDataProvider x:Name="XmlData" Source="People.xml" XPath="/People/Person" />
And the binding of the listbox looks like this:
<ListBox
Grid.Row="0"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding UpdateSourceTrigger=Explicit}"
x:Name="PeopleListBox"
SelectionMode="Single">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0}, {1} {2}">
<Binding XPath="Name/#last_name" />
<Binding XPath="Name/#first_name" />
<Binding XPath="Name/#second_name" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Unless DataContext is set to XmlData your ItemsSource binding won't work. You did not mention where you define XmlDataProvider because if in Resources then you need to specify x:Key instead of x:Name and then you would have to specify it as Binding.Source. You also did not say what is People.xml as it must be added to your solution as a resource
<Window ...>
<Window.Resources>
<XmlDataProvider x:Key="XmlData" XPath="/People/Person" Source="People.xml"/>
</Window.Resources>
<ListBox ItemsSource="{Binding Source={StaticResource XmlData}}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0}, {1} {2}">
<Binding XPath="Name/#last_name" />
<Binding XPath="Name/#first_name" />
<Binding XPath="Name/#second_name" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Window>
or you can load it manually by setting XmlDataProvider.Source manually in code

Loading Different DataTemplate for each item in ListBox

I am trying to create a learn application and I would like to load data template for based on Question type
as explained below.
If Question Type is TYPE1
load InstructionTemplate_Type1.xaml
load ChoiceTemplate_Type1.xaml
load QuestionTemplate_Type1.xaml
If Question Type is TYPE2
load InstructionTemplate_Type2.xaml
load ChoiceTemplate_Type2.xaml
load QuestionTemplate_Type2.xaml
If Question Type is TYPE3
load InstructionTemplate_Type3.xaml
load ChoiceTemplate_Type3.xaml
load QuestionTemplate_Type3.xaml
else
load InstructionTemplate_Type3.xaml
load ChoiceTemplate_Type3.xaml
load QuestionTemplate_Type3.xaml
and the my page should look like...
Can Someone help me how to do this.
I am using the code from my previous post
Nested ObservableCollection data binding in WPF
and the xaml is ...
<learn:SelectedItemIsCorrectToBooleanConverter x:Key="SelectedCheckedToBoolean" />
<Style x:Key="ChoiceRadioButtonStyle" TargetType="{x:Type RadioButton}" BasedOn="{StaticResource {x:Type RadioButton}}">
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource SelectedCheckedToBoolean}">
<Binding Path="IsCorrect" />
<Binding RelativeSource="{RelativeSource Self}" Path="IsChecked" />
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="Background" Value="Green"></Setter>
</DataTrigger>
<DataTrigger Value="False">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource SelectedCheckedToBoolean}">
<Binding Path="IsCorrect" />
<Binding RelativeSource="{RelativeSource Self}" Path="IsChecked" />
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="Background" Value="Red"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
<DataTemplate x:Key="InstructionTemplate" DataType="{x:Type learn:Question}">
<TextBlock Text="{Binding Path=Instruction}" />
</DataTemplate>
<DataTemplate x:Key="ChoiceTemplate" DataType="{x:Type learn:Choice}">
<RadioButton Content="{Binding Path=Name}" IsChecked="{Binding RelativeSource= {RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}}, Path=IsSelected}" Margin="10 1"
Style="{StaticResource ChoiceRadioButtonStyle}" />
</DataTemplate>
<DataTemplate x:Key="QuestionTemplate" DataType="{x:Type learn:Question}">
<StackPanel Margin="10 0">
<TextBlock Text="{Binding Path=Name}" />
<ListBox ItemsSource="{Binding Path=Choices}" SelectedItem="{Binding Path=SelectedChoice}" HorizontalAlignment="Stretch" ItemTemplate="ChoiceTemplate">
</ListBox>
</StackPanel>
</DataTemplate>
</Window.Resources>
<DockPanel>
<StackPanel Orientation="Horizontal" DockPanel.Dock="Bottom">
<Button Content="Select Question 3 choice 3" Click="ButtonBase_OnClick" />
</StackPanel>
<ItemsControl ItemsSource="{Binding Path=Questions}">
<ItemsControl.ItemTemplateSelector>
<learn:QuestionTemplateSelector QuestionTemplate="{StaticResource QuestionTemplate}" InstructionTemplate="{StaticResource InstructionTemplate}" />
</ItemsControl.ItemTemplateSelector>
</ItemsControl>
</DockPanel>
Can Some one help me in understanding how this can be archived with smarter design
(may be a common base class for Question and have Derived Question class for each Question Type and load the data template using a virtual function from the class...) but I am wondering how this can be done using Template Selector ...or do we need to use some different approach..
If you create your ViewModel-s derived from a common Quiestion ViewModel, you can create the list (ObservableCollection<Question>). Then use the following ListBox:
<ListBox ItemsSource="{Binding YourQuestionList}">
<ListBox.Resources>
<DataTemplate DataType="{x:Type VM:QuestionType1}">
( ... question1 full design ... )
</DataTemplate>
<DataTemplate DataType="{x:Type VM:QuestionType2}">
( ... question2 full design ... )
</DataTemplate>
( ... other data templates ... )
</ListBox>
The DataContext will be the specific Question ViewModel inside your custom full design, so you can use those properties for binding, too. You need to add the reference to the namespace your ViewModels are in (eg.: xmlns:VM="clr-namespace:YourApp.VMs") to the top of the xaml file (if your namespace for ViewModels is VMs.
I think this should do the work for you.

Categories