Switch ItemsSource dynamically in XAML - wpf

I'm trying to switch the itemssource of a treeview depending on a value of a property in my viewmodel. I've tried the code below and the trigger doesnt seem to be firing, can someone tell me where I went wrong?
<Window.Resources>
<Style x:Key="TreeViewItemSource" TargetType="TreeView">
<Style.Triggers>
<DataTrigger Binding="{Binding CurrentReportRequested, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Value="TollFree">
<Setter Property="ItemsSource" Value="{Binding InsertTFSQueryList, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<TreeView ItemsSource="{Binding Source={StaticResource TreeViewItemSource}, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource DebugConverter}}" />

Set the ItemsSource in Style otherwise local set value will always holds priority.
Read more about it here - Dependency Property Value Precedence Order.
<Style x:Key="TreeViewItemSource" TargetType="TreeView">
<!-- Set ItemsSource here but you need to separate Style out of it. -->
<Setter Property="ItemsSource"
Value="{Binding Source={StaticResource TreeViewItemSource},
UpdateSourceTrigger=PropertyChanged,
Converter={StaticResource DebugConverter}}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding CurrentReportRequested, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
Value="TollFree">
<Setter Property="ItemsSource" Value="{Binding InsertTFSQueryList,
Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataTrigger>
</Style.Triggers>
</Style>
Most importantly you have set ItemsSource to the Style and somewhere in converter, you are converting it to actual value. This is the strangest thing I ever looked. How style is convertible to ItemsSource. Refactor the logic and separate out both so that above XAML works for you.

To solve my issue, I used good-old oop...polymorphism.
I used a polymorphic structure in my ViewModel. Now my ItemsSource is bound to one IEnumerable list which holds the base class...which i can equate to any of the derived types.

Related

Triggers override binding

Why is it that the binding Marked stops working when the triggers are used? Is there some way to fix this?
The multi selection ListBox has ListBoxItems with CheckBoxes, and the checkbox in an item is ticked when the corresponding item is selected, using mouse or keyboard or touch or whatever. The point with this demo is to not tick the checkboxes directly, but just multiselect the items and see the checkboxes get ticked as a consequence of that. This synchronization in the XAML works well, but since the Marked binding isn't working, the model won't get updated.
If I remove the triggers, then the binding Marked starts working. I know, because then the model gets updated when the checkboxes are ticked.
If I attach event handlers for Checked and Unchecked to the CheckBox, they fire even when the triggers are there.
<ListBox ItemsSource="{Binding Lines}" SelectionMode="Extended">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<CheckBox x:Name="cb"
Content="{Binding DisplayText}"
IsChecked="{Binding Marked, Mode=TwoWay}">
</CheckBox>
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListBoxItem}}, Path=IsSelected}" Value="true">
<Setter TargetName="cb" Property="IsChecked" Value="true"/>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListBoxItem}}, Path=IsSelected}" Value="false">
<Setter TargetName="cb" Property="IsChecked" Value="false"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
EDIT:
Although the accepted solution below is good XAML, it crashes with Elmish.WPF, which I'm using. That's another issue, and will be solved through other forums.
The DataTriggers will obviously override the Binding, since the ListBoxItem's IsSelected property is always either true or false.
Replace the DataTriggers by an ItemContainerStyle:
<ListBox ItemsSource="{Binding Lines}" SelectionMode="Extended">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding DisplayText}"
IsChecked="{Binding Marked}"/>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected" Value="{Binding Marked}"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>

Runtime binding change + unexpected event firing issue WPF

I change binding at runtime with datatriggers in XAML like this:
<ToggleButton Checked="MinSyncX_OnChecked" Unchecked="MinSyncX_OnUnchecked" TargetUpdated="FrameworkElement_OnTargetUpdated"
Tag="{Binding Path=CameraViewDirection, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
ToolTip="{Binding Source={StaticResource CameraLocalization}, Path=ToolTips.SyncX, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}">
<ToggleButton.Style>
<Style TargetType="ToggleButton" BasedOn="{StaticResource CameraSyncCommonStyle}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=CameraViewDirection.Limitations.UI.SelectedLimitation.W, NotifyOnTargetUpdated=True, NotifyOnSourceUpdated=True, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" Value="0">
<Setter Property="IsEnabled" Value="{Binding Path=CameraViewDirection.Limitations.UI.IsMinTripleEnabled.X, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"></Setter>
<Setter Property="IsChecked" Value="{Binding Path=CameraViewDirection.Limitations.UI.IsMinTripleChecked.X, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding Path=CameraViewDirection.Limitations.UI.SelectedLimitation.W, NotifyOnTargetUpdated=True, NotifyOnSourceUpdated=True, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" Value="1">
<Setter Property="IsEnabled" Value="{Binding Path=CameraViewDirection.Limitations.UI.IsMaxTripleEnabled.X, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></Setter>
<Setter Property="IsChecked" Value="{Binding Path=CameraViewDirection.Limitations.UI.IsMaxTripleChecked.X, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding Path=CameraViewDirection.Limitations.UI.SelectedLimitation.W, NotifyOnTargetUpdated=True, NotifyOnSourceUpdated=True, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" Value="2">
<Setter Property="IsEnabled" Value="{Binding Path=CameraViewDirection.Limitations.UI.IsBothTripleEnabled.X, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></Setter>
<Setter Property="IsChecked" Value="{Binding Path=CameraViewDirection.Limitations.UI.IsBothTripleChecked.X, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
<ToggleButton.Content>
<Image Source="{Binding Source={StaticResource CameraIcons}, Path=XAxis, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"></Image>
</ToggleButton.Content>
</ToggleButton>
Everything seems fine - binding really changes, but I have one issue which I don`t know how to solve:
When binding changed, MinSyncX_OnUnchecked event firing even if binded value is true which means that Toggle button must stays checked.
Firing of this event during binding switching braked my code, so I wish to now how to suppress this event during binding change if IsChecked property must be set to true.
A little tricky, but I hope you will understand my problem.
Edit
One thing I noticed that unchecked event ALWAYS fired if data trigger value switched to the first in list. Even it has a value=1 or value=2.
Does this by design or it can be fixed somehow?
I realise that I have twoway binding, so, when binding changed, my binded property receive value from button first (thats the reason why unchecked event firing). The solution I found is: lock logic of unchecked event by some bool value and switch that value back when binding changed (selection changed event on combobx in my case)

Change ItemsControl ItemsTemplate based on data value

I have an ItemsControl bound to a Dictionary and am trying to apply a template based on the value of the Dictionaries key. I am trying to do:
<ItemsControl ItemsSource="{Binding Path=CommonItems, Mode=OneWay}">
<ItemsControl.Style>
<Style TargetType="ItemsControl">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Key}" Value="authentication">
<Setter Property="ItemTemplate" Value="{StaticResource AuthenticationTemplate}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ItemsControl.Style>
</ItemsControl>
That doesn't work at all and my errors say Key doesn't exist on ItemsControl. That leads me to believe that It is using my TargetType to search for the binding. So how do I do this and have it actually use the ItemsSource?
If you want to do this per-item, i would suggest using an ItemTemplateSelector.

ContentControl two way binding within DataTemplate not working?

I've setup a reusable datatemplate "DataGridCheckBoxEdit" for a datagrid column. Binding to it one way works like a charm through ContentControl. Binding directly works two way correctly. However, binding two way within that DataTemplate, from a ContentControl just won't work.
Here are the snippets:
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ContentControl Content="{Binding Path=IsMadeAvailable, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" ContentTemplate="{StaticResource DataGridCheckBoxEdit}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
and the reusable template:
<DataTemplate x:Key="DataGridCheckBoxEdit">
<CheckBox Name="CheckBoxControl" IsChecked="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContentControl}, Path=DataContext.Content, Mode=TwoWay, BindsDirectlyToSource=True, UpdateSourceTrigger=PropertyChanged}" Margin="8,4,2,2" />
<DataTemplate.Triggers>
<Trigger SourceName="CheckBoxControl" Property="IsVisible" Value="True">
<Setter TargetName="CheckBoxControl" Property="FocusManager.FocusedElement" Value="{Binding ElementName=CheckBoxControl}"/>
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
As I said, one way binding works like a charm...but getting the data back to the property doesn't.
Of course, putting it without being reusable:
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<CheckBox Name="GasIsAvailableCheckBox" IsChecked="{Binding Path=IsMadeAvailable, UpdateSourceTrigger=PropertyChanged}" Margin="8,4,2,2" />
<DataTemplate.Triggers>
<Trigger SourceName="GasIsAvailableCheckBox" Property="IsVisible" Value="True">
<Setter TargetName="GasIsAvailableCheckBox" Property="FocusManager.FocusedElement" Value="{Binding ElementName=GasIsAvailableCheckBox}"/>
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
also works great, and works two-way.
What am I doing wrong?
Thanks!
Vladan
Your binding is just broken (see the output window of Visual Studio for the respective errors), you do not want to bind to DataContext.Content but just Content, the DataContext would be the object in that row instead of the ContentControl itself.
Change that in the binding path of the reusable template and it will work. You also set a lot of properties to values they already have by default, this would be the minimal version:
{Binding Content, RelativeSource={RelativeSource AncestorType=ContentControl}}

Wpf Combobox selectedvalue trigger

I am trying to change the visibility with a trigger when a particular value in a combobox is selected, and I got the following XAML
<ItemsControl ItemsSource="{Binding AccessControl.Credentials}" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid >
<ComboBox Name="chkFieldType"
SelectedValue="{Binding Path=ValueSourceType,Converter={StaticResource enumstringConv}}"
SelectedValuePath="Tag" SelectionChanged="chkFieldType_SelectionChanged" >
<ComboBoxItem Tag="User">User</ComboBoxItem>
<ComboBoxItem Tag="SessionCredential">Field</ComboBoxItem>
<ComboBoxItem Tag="Inherit">From other Resource</ComboBoxItem>
</ComboBox>
<Border " Visibility="Hidden">
<Border.Resources>
<Style TargetType="{x:Type Border}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=SelectedValue, ElementName=chkFieldType}" Value="Inherit">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Resources>
<ComboBox/>
</Border>
In this case a border. The selected value is "Inherit" of type string but the border remainds hidden.
I ran into the same problem and found that you have to set the visibility property using the style only. So instead of having the initial visibility set with:
<Border Visibility="Hidden">
You should set the initial visibility using the style:
<Style TargetType="....">
<Setter Property="Visibility" Value="Hidden"/>
<Style.Triggers>
....
</Style.Triggers>
</Style>
(I know it's kinda overdue but I thought maybe someone else might run into the same problem).
Try SelectedItem.Tag or SelectedItem.Content instead of SelectedValue
Set your binding on SelectedValue, not SelectedItem.SelectedValue. The way you currently have it, it is looking for ComboBoxItem.SelectedValue, which doesn't exist
<DataTrigger Value="Inherit"
Binding="{Binding Path=SelectedValue,
Converter={StaticResource enumstringConv},
ElementName=chkFieldType}">
I think it's because you are putting the DataTrigger in Border.Resources.
Try putting the style in the window.resources, with a x:key in order to apply the style to the border.
I think that the border.resources can not access to a control "outside it's own resources context"
SelectedItem and SelectedValue are two seperate properties on the ComboBox.
Since your ComboBoxItems are all strings you can change
<DataTrigger Binding="{Binding Path=SelectedItem.SelectedValue, ElementName=chkFieldType}" Value="Inherit">
to
<DataTrigger Binding="{Binding Path=SelectedItem, ElementName=chkFieldType}" Value="Inherit">
I ended up setting the visibility manually via the code behind when the selectedItem event is fired..

Resources