Address a property of parent ListView from a listview item - wpf

Desired: set border properties of a listview depending on conditions in displayed items. For example:
Set the border of an entire ListView red if any one of its (string) items begins with "S". So the problem is how to address a parent listview from its items (within xaml preferably)
Something like
<ListView Name="lv" Grid.Row="2" ItemsSource="{Binding TheItemList}" Height="100" BorderBrush="Black">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding }">
<TextBlock.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Converter={StaticResource StringToBoolean}}
Value="True">
<Setter ???? Value="Red"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
(where the converter is the obvious thing) does not work: I can' use
TargetName="lv" in the ???? part of the setter, that's outside the scope. Neither can I use <Style TargetType="ListView"> in the style declaration. I could of course go up and back through the view model, but how does one do this in .xaml?
Must be having a bad day, this seems like an obvious thing. My googling hasn't yielded much unfortunately.

A setter in a Style can only set a property of the element to which the Style is applied, i.e. a Style for a TextBlock cannot set the property of the parent ListView.
You could "move" the Style to the ListView and implement the converter to return true if ListBox.Items.OfType<string>().Any(x => x?.StartsWith("s") == true) or something similar.
But a Style for a child element cannot set a property of the parent element.

Related

WPF: Binding not working inside ContentControl

I've been on this for hours now and I am not getting what is not working or how it is working. A bit of explanation would be appreciate how all this behave.
I'm trying to add a trigger based on another cell content and it is working fine if I forget the binding part.
My problem is actually the Binding itself. It simply doesn't work if it's inside the ContentControl.
My Code:
<GridViewColumn.CellTemplate>
<DataTemplate>
<ContentControl>
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding CBW_Type}" Value="Text">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding Path=CBW_Content, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>
</GridViewColumn.CellTemplate>
I tried the below and it work as expected (Without the trigger). The data get into place just fine.
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=CBW_Content, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
I searched a lot of quite a few have solved this with the UpdateSourceTrigger=PropertyChanged but it didn't work for me.
Anyone can tell what is not working once I embed the binding inside the ContentControl?
Regards,
Just found the answer. I needed to add so the ContentControl is not empty I guess. If anyone can put a light on that one, I'd appreciate it.
The DataContext of the ContentTemplate of a ContentControl is the Content of the ContentControl. So for your binding to the CBW_Content property to work, you need to set or bind the Content property of the ContentControl to an instance of an object where the CBW_Content property is defined.
In this case this is the corresponding object in the ItemsSource collection of the DataGrid. That's why <ContentControl Content="{Binding}"> works.
If you don't set or bind the Content property so something, there is nothing to bind to and that's why your DataTrigger didn't work.
Hopes that makes sense.
ContentTemplate is to provide a different template for the Content, no Content => ContentTemplate clueless. So, you can also replace ContentTemplate with Content and remove DataTemplate like this in your original code :
<Setter Property="Content">
<Setter.Value>
<TextBlock Text="{Binding Path=CBW_Content, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
</Setter.Value>
</Setter>

Binding in Style Setter in DataTemplate WPF

Okay, I have a relatively involved problem. I'm trying to create a Window in WPF. The main element on this window is a DataGrid. Each one of the rows in the DataGrid has a DetailsPane which I set using DataGrid.RowDetailsTemplate. Depending on certain row-specific values, I need the DetailsPane to display different elements. To accomplish this I placed a ContentControl at the root of the DataTemplate and used a Style with DataTriggers to set its Content property. Now, inside one of these Setters is a ComboBox. This ComboBox needs to have its ItemsSource bound to a list, which is stored in a dependency property on the Window level (because its the same list regardless of the row). Below is a simplified version of what I'm looking at:
<Window>
...
<DataGrid>
...
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<ContentControl>
<Style TargetType="ContentControl">
<Style.Triggers>
<DataTrigger Binding="{Binding RowSpecificBooleanProperty}" Value="False">
<Setter Property="Content">
<Setter.Value>
...
<ComboBox ItemsSource={HowDoIBindThisToTheWindowProperty}/>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
</Window>
So what I'm trying to figure out is how to bind the ItemsSource of that ComboBox to a dependency property of the top-level window. Andy idea how to accomplish that?
EDIT:
I should have mentioned this before but I've already tried using {RelativeSource AncestorType=Window} and ElementName in the binding. In both cases the list in the ComboBox is blank at runtime.
ItemsSource="{Binding WhateverList, RelativeSource={RelativeSource AncestorType=Window}}"

Toggle two different elements in DataTemplate using Style Triggers

I have an object in ViewModel whose properties are displayed by a datatemplate. The screen also has a button toggling the IsEditing flag in ViewModel, which should make the object properties editable, like the following:
Name should change from TextBlock to TextBox;
Color should change from colored rectangle to ComboBox with color options;
Category should change from TextBlock to ComboBox;
I know how to implement this with two completely independent DataTemplates, using a Style and a DataTrigger to toggle between them:
<ContentControl Content="{Binding FancyObject}">
<ContentControl.Style>
<Style TargetType="ContentControl">
<Setter Property="ContentTemplate" Value="{StaticResource DisplayTemplate}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding DataContext.IsEditing, ElementName=UserControl}" Value="True">
<Setter Property="ContentTemplate" Value="{StaticResource EditTemplate}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
And currently the DisplayTemplate is like this:
<DataTemplate x:Key="DisplayTemplate" DataType="my:FancyObject">
<Border>
<DockPanel DataContext="{Binding Metadata}">
<Border>
<TextBlock Text="{Binding Name}"/>
</Border>
<DataGrid
AutoGenerateColumns="False"
ItemsSource="{Binding FancyObjectCollection}">
<DataGrid.Columns>
<!-- Text and Template columns -->
</DataGrid.Columns>
</DataGrid>
</DockPanel>
</Border>
</DataTemplate>
The problem is: using two independent but similar templates would mean a duplication of layout, since only some fields would change, but the overall structure is the same.
Another option I imagine is to use a single template defined inside the Style, and use the Trigger to change the fields individually, but I don't know how to do it, or even if it is possible at all.
You can use one template.
In the template add both TextBlock and TextBox, same for all your other controls on the original template.
Bind the controls visibility to bool to visibility converter. (Or use triggers) Only one set of your control will be seen each time (based on IsEditing flag)
The ControlTemplate is only used upon generating the UI Elements. If you change the template AFTER generating the items, the generated items will not change.
You can also not use a trigger to change a TextBox to a TextBlock and vice versa.
Your only option is indeed to mirror the layout twice and hide/display it via the data bound property.

Conditionally changing the Foreground of a WPF ComboBox item depending upon a value in the databound ItemsSource item

I have a WPF ComboBox bound to a Collection List<Users>. I have applied a DataTemplate to show the FirstName using a TextBlock and this works as expected:
<ComboBox Margin="5" ItemsSource="{Binding Path=TheUsers}" Name="cboUsers">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Margin="10" Text="{Binding Path=FirstName}">
</TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>`
I have an item in my User class called IsActive which is a Boolean value. If true then I want to set the Foreground of the TextBlock to Navy.
I have spent so much time on what should be so easy and looked all over the web but most articles talk about changing the overall colour or binding to another element in the xaml.
I tried implementing a DataTrigger and after an hour removed the code because it was not working. It would not recognise my field name. Does anyone have a very simple guide to how to do this or what would be the best approach?
As you apparently are not dealing with fields after all, this style should do what you want:
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsActive}" Value="True">
<Setter Property="Foreground" Value="Navy"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
It would not recognise my field name.
You cannot bind to fields, end of story.

How can I change styles of XAML file using Data Binding?

I'm binding a collection of objects to a listbox in WPF, for simplicity we'll say the object i'm binding has 3 propertys: Name, URL, IsBold. What I want to do, is have it displayed different if the IsBold is set to true, again as an example I want to set the TextBlock that Name appears in, to be bold. Is something like this even possible? Can I use a different style or something if one of my properties is a certain value? (can I do anything like an if/else in XAML)? I really have no idea where to start with this.
Say I have this in my DataTemplate
<TextBlock Style="{StaticResource notBold}" Text="{Binding Path=Name}"></TextBlock>
And if the IsBold is set to true for that particular Item I would like it to be (notice the style changes from 'notBold' to 'isBold')
<TextBlock Style="{StaticResource isBold}" Text="{Binding Path=Name}"></TextBlock>
Or something similar. I guess the more general question. Is it possible to change the apperance of something based on an item that's databound? And if it's not possible, how would something like this be done? Thru the code-behind somehow?
Thanks
What you'd normally do is write a DataTemplate for the objects in the list and then have a DataTrigger set the Fontweight of the TextBlock/TextBox based on the IsBold Property.
<DataTemplate DataType="DataItem">
<TextBlock x:Name="tb" Text="{Binding Name}"/>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsBold}" Value="true">
<Setter TargetName="tb" Property="FontWeight" Value="Bold" />
</DataTrigger>
<DataTrigger Binding="{Binding IsBold}" Value="false">
<Setter TargetName="tb" Property="FontWeight" Value="Normal" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
You'd then set a list of DataItems to the ItemsSource property of your ComboBox (either by Databinding or directly in the codebehind myComboBox.ItemsSource=myDataItems). The rest is done by WPF for you.

Resources