Textblock won't recieve trigger in HierachicalDataTemplate - c#

I'm trying to get a TreeView to display items as a TextBlock, and then based on a boolean inside the data-bound object to either make the FontWeight Normal or Bold, pretty much the following:
<TreeView x:Name="TreeView" ItemsSource="{Binding Layers}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type viewModels:Layer}" ItemsSource="{Binding Path=Layers}">
<TextBlock Text="{Binding Path=Name}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="FontWeight" Value="Normal" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=ShowInPreview}">
<Setter Property="FontWeight" Value="Bold" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
The Setter outside the trigger actually works, when I set that one to "Bold", everything goes Bold right away. It's just the DataTrigger that never, well... triggers :P
The ItemSource implements INotifyPropertyChanged, and so does the Layer object on all properties (including the ShowInPreview).
I've tried all kinds of different setups I could find on the web (using Window.Resources, putting it in TreeView.ItemContainerStyle, etc. etc), so I'm completely at a loss right now!

Set the Value on your data trigger.

I dont know exactly where is your property, try something like this. I think, issue in binding:
<DataTrigger Binding="{Binding Path=DataContext.ShowInPreview, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TreeViewItem}}}">

Related

Access a previous sibling element using RelativeSource binding (not ElementName) in WPF Application?

I am trying to access a property of a sibling element that comes directly BEFORE the target element. This is something I will have to duplicate several times in my app so I'd rather use something reproduceable (not elementName) if possible.
Any Ideas?
<Groupbox x:Name="GB1">
<Checkbox x:Name="CB1" IsChecked="True"/>
<TextBlock>
<Style TargetType="TextBlock">
<Style.Triggers>
<Data.Trigger Binding RelativeSource={??? (I want this to access the Checkbox CB1 above), Path=IsChecked}" Value="True>
<Setter Property="*Do a Thing if IsChecked=True*" Value="..."/>
</Style.Triggers>
</Style>
</TextBlock>
</Groupbox>
Maybe this example will give you an idea.
<StackPanel>
<TextBlock x:Name="textBlock1" Text="Text" />
<TextBlock x:Name="textBlock2" Text="Text">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type StackPanel}}, Path=Children[0].Text}"/>
</Style>
</TextBlock.Style>
</TextBlock>
</StackPanel>
Path=Children[0].Text it will look for the first child element of this ancestor and access its Text property.

WPF DataGrid Custom Row Header

I am working on a DB FrontEnd with WPF / EntityFramework / MVVM
Now i got stuck when i allow the user to add data to a datagrid (which is bound to an Observable Collection).
What i want to achieve, is to get a row header like in MS Access:
So my WPF DataGrid should look like this basically:
Is there any way to bind the RowHeaderStyle to the RowState?
For Example:
RowState.Editing: Show Edit Icon
RowState.NewRow: Show Star
RowState.Default: Show Default Row Header
I found no solution so far, but i think WPF should pe powerfull enough to get this job done.
Thank you!
Simple. Give the DataGrid a RowHeaderStyle that swaps in different ContentTemplates depending on the state of the DataGridRow. Fortunately the DataGridRow is a visual ancestor of the DataGridRowHeader, so it's relatively simple to reach up there with a RelativeSource binding and get the values of the relevant properties: DataGridRow.IsEditing and DataGridRow.IsNewItem.
I used <Label>New</Label> etc. as an arbitrary stand-in for whatever content you want to use.
<DataGrid
ItemsSource="{Binding Rows}"
>
<DataGrid.RowHeaderStyle>
<Style
TargetType="{x:Type DataGridRowHeader}"
BasedOn="{StaticResource {x:Type DataGridRowHeader}}"
>
<!--
Empty content template for default state.
Triggers below replace this for IsNewItem or IsEditing.
-->
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Label></Label>
</DataTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger
Binding="{Binding
IsEditing,
RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}}"
Value="True"
>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Label>Edit</Label>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger
Binding="{Binding
IsNewItem,
RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}}"
Value="True"
>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Label>New</Label>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowHeaderStyle>
</DataGrid>

StackPanel visibility depending on Combobox selection

I want to have a StackPanel who's visibility should be depending on a Combobox selection. Unfortunatly the XAML below does not work.
I found a solution with a new property which will be set on the PropertyChanged event of the Combobox selection, though I would prefer a strict XAML solution for this.
Any hints on how to solve this?
<StackPanel>
<Label>Picture in Picture function</Label>
<ComboBox Name="cbPictureInPicture" ItemsSource="{Binding Path=PictureInPictureCodeList, Mode=OneWay}" DisplayMemberPath="CodeText"
SelectedValuePath="CodeID" SelectedValue="{Binding Path=PictureInPicture, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
<StackPanel>
<StackPanel.Style>
<Style TargetType="StackPanel">
<Setter Property="Visibility" Value="Visible" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=cbPictureInPicture, Path=IsSelected.CodeText}" Value="Yes">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<Label>Picture in Picture is used</Label>
(...)
</StackPanel>
you may perhaps rewrite the same as
<DataTrigger Binding="{Binding ElementName=cbPictureInPicture, Path=SelectedItem.CodeText}" Value="Yes">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
assuming the combobox is bound to a collection whose item has CodeText property. so SelectedItem.CodeText will point to the same.
additionally it may not be required to set <Setter Property="Visibility" Value="Visible" /> as it is the default value. it does not have any effect in this case just some extra line of code which can be removed.
You can also use a converter and bind directly to the PictureInPicture property:
<StackPanel Visibility="{Binding PictureInPicture, Converter={StaticResource myVisibilityConverter}}"/>
<Label>Picture in Picture is used</Label>
(...)
</StackPanel>
Create flags and pass this flag in stackpanel visibility converter.
On the basis of flag in converter make decision stackpanel visible/hide whatever
Set this flat in comboBox selection change event if selected value as per your requirement.

Datagrid and Listview with same itemssource

I have a WPF application with a DataGrid and ListView that share the same ObservableCollection ItemsSource. When the DataGrid's CanUserAddRows property is True it causes the ListView to display the extra item that the DataGrid uses to add new rows.
How can I get the extra row from the DataGrid to not show in the ListView?
I tried using a trigger on the ListView's DataTemplate and checking if the items Id was empty or 0
`<ListView.ItemTemplate>
<DataTemplate>
<Label Margin="-2,0,0,0" Name="CategoryLabel" >
<TextBlock TextWrapping="Wrap" Text="{Binding categoryName}" Height="46"></TextBlock>
</Label>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding categoryId}" Value="0" > <!-- also tried Value="" -->
<Setter TargetName="CategoryLabel" Property="Visibility" Value="Hidden" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListView.ItemTemplate>`
I just posted an answer to a problem of changing the template using a data template selector
Change View with its ViewModel based on a ViewModel Property
Possibly just because I have recently looked at this but I wonder if it might be possible to use the same technique here.
Have one template for where the category has a value,then another blank template for values without a category. The important part is you do the test in code rather than XAML so easier to inspect.
You can solve your problem without any modification of your ViewModel or code behind. You can do well without explicitly defining CollectionView's of any kind. Just add to your view's XAML one more (or only) DataTrigger that triggers on the NewItemPlaceholder item of the default view of ListView ItemsSource's collection. Have this trigger to set the UIElement.Visibility attached property to "Hidden". Place it within ItemContainerStyle style triggers. Like this:
<ListView
ItemsSource="{Binding ...}"
>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
...
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding}"
Value="{x:Static CollectionView.NewItemPlaceholder}">
<Setter Property="UIElement.Visibility" Value="Hidden"/>
</DataTrigger>
</Style.Triggers>
<Setter Property="..." Value="{Binding ...}" />
...
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Label Margin="..." Name="...">
<TextBlock TextWrapping="Wrap"
Text="{Binding ...}" />
</Label>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
</ListView>

DataTemplate in ControlTemplate not updating Binding

I've created a control with 3 PART_s, one PART_ changes depending on the type bound to it, however values changed within the Control do not update the Binding, it seems to work as OneWay Binding.
Here's part of the code I beleive is relevant:
<DataTemplate x:Key="BooleanDAView" DataType="{x:Type sys:Boolean}">
<CheckBox IsChecked="{Binding ., Mode=TwoWay}"/>
</DataTemplate>
<DataTemplate x:Key="DateTimeDAView" DataType="{x:Type sys:DateTime}">
<extToolkit:DateTimePicker Value="{Binding ., Mode=TwoWay}"/>
</DataTemplate>
<DataTemplate x:Key="Int32DAView" DataType="{x:Type sys:Int32}">
<extToolkit:IntegerUpDown Value="{Binding ., Mode=TwoWay}"/>
</DataTemplate>
<DataTemplate x:Key="StringDAView" DataType="{x:Type sys:String}">
<TextBox Text="{Binding ., Mode=TwoWay}"/>
</DataTemplate>
....
<ContentControl x:Name="PART_Content"
Grid.Row="0" Grid.Column="1"
Margin="{TemplateBinding Padding}"
VerticalAlignment="Center"
VerticalContentAlignment="Center"
Content="{Binding Path=Content, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
>
<ContentControl.ContentTemplateSelector>
<controls:TypeBasedDataTemplateSelector>
<controls:TypeBasedDataTemplateSelector.Templates>
<controls:TypedDictionary>
<sys:String x:Key="{x:Type sys:Boolean}">BooleanDAView</sys:String>
<sys:String x:Key="{x:Type sys:DateTime}">DateTimeDAView</sys:String>
<sys:String x:Key="{x:Type sys:Int32}">Int32DAView</sys:String>
<sys:String x:Key="{x:Type sys:String}">StringDAView</sys:String>
</controls:TypedDictionary>
</controls:TypeBasedDataTemplateSelector.Templates>
</controls:TypeBasedDataTemplateSelector>
</ContentControl.ContentTemplateSelector>
</ContentControl>
For Content I've also tried ... RelativeSource={RelativeSource AncestorType=local:DABaseControl} but no change.
If the DataTemplate Binding use "{Binding Path=Content, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}" the template doesn't change once set.
Or is there a better way to do this?
Thanks
I just encountered the same problem, I wanted to create a DataTemplate with DataType="{x:Type sys:Boolean} that just had a checkbox. But there were many warning signs along the way telling me this isn't the way it should be done.
At first, the simple binding of {Binding} would throw an exception "Two-way binding requires path or xpath", which was the first warning sign. I changed the binding to {Binding .} which worked (even though this MSDN article clearly states that they're equivalent). That fact that voodoo was helping was the second warning sign. It then displayed correctly and the checked state was according to the boolean value, but when clicking the checkbox (even with UpdateSourceTrigger=PropertyChanged), it refused to update the binding source, no matter what I tried. Using diagnostics:PresentationTraceSources.TraceLevel=High showed that it didn't even try to bind back (third warning sign).
I went ahead and created a simple "box" for the bool value - a class with a single bool property named Value with anINotifyPropertyChanged implementation. I changed the binding to {Binding Value} and now everything worked, including two way binding.
Conclusion: It seems a binding can't update the bound object itself, but only properties of that object (which is why {Binding} throws an exception, but the more explicit {Binding .} suppresses that exception, according to H.B.'s answer). In any case, the approach of creating a ViewModel and creating templates that target it appears to be more than a mere design guideline, but an actual technical requirement.
I've actually never worked with a ContentTemplateSelector, but if I had to hazard a guess I would say either it's not responding to PropertyChanged events on your ContentControl.Content property, or your Content binding is incorrect.
You can easily check if your binding is correct or not by removing the ContentTemplateSelector and seeing if data shows up at all. If it does, your binding is correct. If it doesn't, it's incorrect and you need to fix it.
If the problem is the ContentTemplateSelector, then I would suggest switching to a DataTrigger which determines which ContentTemplate to use based on the Content. This is what I usually do, and it uses a Converter which simply returns typeof(value)
<Style TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate" Value="{StaticResource StringDAView}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Converter={StaticResource ObjectToTypeConverter}"
Value="{x:Type sys:Boolean">
<Setter Property="ContentTemplate" Value="{StaticResource BooleanDAView}" />
</DataTrigger>
<DataTrigger Binding="{Binding Converter={StaticResource ObjectToTypeConverter}"
Value="{x:Type DateTime">
<Setter Property="ContentTemplate" Value="{StaticResource DateTimeDAView}" />
</DataTrigger>
<DataTrigger Binding="{Binding Converter={StaticResource ObjectToTypeConverter}"
Value="{x:Type sys:Int32">
<Setter Property="ContentTemplate" Value="{StaticResource Int32DAView}" />
</DataTrigger>
</Style.Triggers>
</Style>

Categories