WPF Databinding to Window - c#

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

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>

WPF Context Menu pops up after Binding changes

I have a Border control inside a UserControl which is used as a switch for graphical changes. The Border has a context menu with two MenuItems: "Open" and "Close".
When the switch is closed only the "Open" MenuItem is visible, and when the switch is open only "Close" is visible. For some switches I need to completely disable Open or Close, so I don't want them to be visible at any time. Here is the code:
<Border.ContextMenu>
<ContextMenu Name="switchContextMenu">
<ContextMenu.Visibility>
<MultiBinding Converter="{StaticResource ContextMenuBoolToVisibility}">
<Binding Path="OpenAvailable" />
<Binding Path="OpenVisible" />
<Binding Path="CloseAvailable" />
<Binding Path="CloseVisible" />
</MultiBinding>
</ContextMenu.Visibility>
<MenuItem Name="miOpen" Header="{Binding Path=Resources.PowerControlSystem_OPEN, Source={StaticResource LocalizedStrings} }"
Click="miOpen_Click">
<MenuItem.Visibility>
<MultiBinding Converter="{StaticResource BooleanToVisibilityMultiValueAND}">
<Binding Path="OpenAvailable" />
<Binding Path="OpenVisible" />
</MultiBinding>
</MenuItem.Visibility>
</MenuItem>
<MenuItem Name="miClose" Header="{Binding Path=Resources.PowerControlSystem_CLOSE, Source={StaticResource LocalizedStrings} }"
Click="miClose_Click">
<MenuItem.Visibility>
<MultiBinding Converter="{StaticResource BooleanToVisibilityMultiValueAND}">
<Binding Path="CloseAvailable" />
<Binding Path="CloseVisible" />
</MultiBinding>
</MenuItem.Visibility>
</MenuItem>
</ContextMenu>
</Border.ContextMenu>
I use this switch as a generic component, so inside another .xaml code I set the "Open Available" and "Close Available" properties. Here is my problem:
I have a switch with the Open Available property set to False. By default, this switch is open. I can closed it, and that's OK. Then when I right click on this switch again the context menu is not visible. This is also OK.
The problem is when I change some values this switch opens again, and at that point of time, the Context Menu pops up. I think that the problem is in the Converter for the ContextMenu visibility. It is triggered at this point and the ContextMenu becomes visible.
Does anyone have an idea how to stop this behavior?
Instead of setting the ContextMenu visibility, you can style the Border
<Border>
<Border.Style>
<Style TargetType="Border">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu Name="switchContextMenu">
<MenuItem Name="miOpen" Header="{Binding Path=Resources.PowerControlSystem_OPEN, Source={StaticResource LocalizedStrings} }"
Click="miOpen_Click">
<MenuItem.Visibility>
<MultiBinding Converter="{StaticResource BooleanToVisibilityMultiValueAND}">
<Binding Path="OpenAvailable" />
<Binding Path="OpenVisible" />
</MultiBinding>
</MenuItem.Visibility>
</MenuItem>
<MenuItem Name="miClose" Header="{Binding Path=Resources.PowerControlSystem_CLOSE, Source={StaticResource LocalizedStrings} }"
Click="miClose_Click">
<MenuItem.Visibility>
<MultiBinding Converter="{StaticResource BooleanToVisibilityMultiValueAND}">
<Binding Path="CloseAvailable" />
<Binding Path="CloseVisible" />
</MultiBinding>
</MenuItem.Visibility>
</MenuItem>
</ContextMenu>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Value="False">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource ContextMenuBoolAggregate}">
<Binding Path="OpenAvailable" />
<Binding Path="OpenVisible" />
<Binding Path="CloseAvailable" />
<Binding Path="CloseVisible" />
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="ContextMenu" Value="{x:Null}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
So the trigger is conditionally removing the ContextMenu instead of changing its visibility. I used the ContextMenuBoolAggregate converter name to indicate you need to create a new converter that is determining the correct value to remove the context menu - I think it's clear how to implement that part.
If your condition is simple enough, you can use MultiDataTrigger instead of DataTrigger with MultiBinding and Converter:
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding OpenAvailable}" Value="False"/>
<Condition Binding="{Binding OpenVisible}" Value="False"/>
<Condition Binding="{Binding CloseAvailable}" Value="False"/>
<Condition Binding="{Binding CloseVisible}" Value="False"/>
</MultiDataTrigger.Conditions>
<Setter Property="ContextMenu" Value="{x:Null}"/>
</MultiDataTrigger>
I found solution in another way. I used ContextMenuService.IsEnabled property. When is set to false, Context Menu can't be shown. So, I added this in my Border.xaml code:
<ContextMenuService.IsEnabled>
<MultiBinding Converter="{StaticResource ContextMenuBoolToVisibility}">
<Binding Path="OpenAvailable" ElementName="mySwitch" />
<Binding Path="OpenVisible" ElementName="mySwitch" />
<Binding Path="CloseAvailable" ElementName="mySwitch" />
<Binding Path="CloseVisible" ElementName="mySwitch" />
</MultiBinding>
</ContextMenuService.IsEnabled>
I also changed my ContextMenuBoolToVisibility converter. Instead of returning Visibility.Visible or Visibility.Collapsed, now returns true or false for ContextMenuService.IsEnabled property.

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.

'MultiBinding' cannot be used within a 'SetterBaseCollection'

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.

How to get a parent value in multibinding

I'm using dataTemplate. This is the template:
<ItemsControl ItemsSource="{Binding RAM.Partitions}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Position, StringFormat={}{0}k}"/>
<Grid Grid.Column="1">
<Border>
<Border.Height>
<MultiBinding Converter="{StaticResource MultiplyConverter}">
<Binding ElementName="LayoutRoot" Path="ActualHeight"/>
<Binding Path="Size" />
<Binding Path="RAM.Size" />
</MultiBinding>
</Border.Height>
</Border>
</Grid>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Can you see this line?
<Binding Path="RAM.Size" />
That line throws me an exception, it should be because RAM.Size is from a parent element. How might I get that value?
Thanks in advance!
So you're trying to get to the RAM.Size value on the same object that your ItemsControl is getting its ItemsSource from?
See if this works:
<MultiBinding Converter="{StaticResource MultiplyConverter}">
<Binding ElementName="LayoutRoot" Path="ActualHeight"/>
<Binding Path="Size" />
<Binding Path="DataContext.RAM.Size"
RelativeSource="{RelativeSource Mode=FindAncestor,AncestorType=ItemsControl}" />
</MultiBinding>
So the binding is going up in through the visual tree to the ItemsControl, then binding to the Ram.Size property of its DataContext.

Categories