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

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.

Related

DataTrigger Binding to a ToggleButton within another namespace

I try to access a ToggleButton that is within a separate UserControl to Trigger a DockPanel.Style DataTrigger.
Here is how I made it work when both, the ToggleButton and the DockPanel, are in the same namespace:
<ToggleButton x:Name="OneToggleButton"
Content="Click me..." />
<DockPanel>
<DockPanel.Style>
<Style>
<Setter Property="UIElement.Visibility"
Value="Visible"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked,
ElementName=DetailsBookToggleButton}"
Value="False">
<Setter Property="UIElement.Visibility"
Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DockPanel.Style>
<TextBlock DockPanel.Dock="Top" Text="..." />
</DockPanel>
But now when I move the ToggleButton into an other file (other namespace) it doesn't work anymore. ElementName (as I understand it) only works for elements within the same file.
So how can I manage a Binding to the IsChecked of my ToggleButton in another file?
Anybody have a suggestion? Would be great :)
FYI, the term you are looking for is "name scope", and there is no way to reference an element defined in another name scope. Arguably, you should not be allowed to do this.
Rather than binding one UI element to another, consider binding them both to a common property, either in your view model, on some common ancestor element, or via dependency property inheritance.

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.

Databinding and Triggers compatability in WPF

I have a problem. I made a DataTemplate for a TreeView and I need to set the initial value of the ToggleButton's IsChecked property depending on my model. But it turns out that setting this property using triggers/setters disables the databinding.
Is it so? If yes, give me a suggestion how it can be fixed?
<DataTemplate x:Key="CellTemplate_Name">
<DockPanel x:Name="dock">
<ToggleButton x:Name="Expander"
IsChecked="{Binding Path=IsExpanded, RelativeSource={RelativeSource AncestorType={x:Type TreeViewItem}}}"> <--- Binding
...
<ToggleButton/>
...
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=ObjIsOpened, Converter={StaticResource DebugConverter}}" Value="true"> <--- Trigger
<Setter TargetName="Expander" Property="IsChecked" Value="true"></Setter>
</DataTrigger>
...
</DataTemplate.Triggers>
</DataTemplate>
Regards, Lerax.
First of all I suggest you read the excellent article by Josh Smith
Simplifying the WPF TreeView by Using the ViewModel Pattern
Based on that article, I would suggest defining a style for the TreeViewItem (using the ItemContainerStyle property of the TreeView) which binds its IsExpanded property to your model object's ObjIsOpened property. Then get rid of your trigger.
Example:
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded"
Value="{Binding ObjIsOpened, Converter={StaticResource DebugConverter}}"/>
</Style>
<DataTemplate x:Key="CellTemplate_Name">
<DockPanel x:Name="dock">
<ToggleButton x:Name="Expander"
IsChecked="{Binding Path=IsExpanded,
RelativeSource={RelativeSource
AncestorType={x:Type TreeViewItem}}}"> <--- Binding
...
<ToggleButton/>
...
</DataTemplate>
I suspect they do not disable data binding, they just have higher priority. Instead of using binding and trigger at the same time, why don't you use one of them (either binding or trigger)? E.g. you could bind to model directly, and don't use trigger at all...

Is it possible to highlight a ComboBoxItem, but not set it as the selected item?

I have a ComboBox that I am populating with a list of parts for a Return Authorization receiving app. In our RA system, a customer can specify a return for a Kit, but only actually return part of the kit. So because of this, my ComboBox shows a list of parts that belong to the kit, and asks the receiver to choose which part was actually received.
I have found the defaulting the selected item in my received part list to the part specified in the return makes to lazy receivers, and incorrect part information being received. So I have left the ComboxBox unselected.
What I would like to do is to highlight the specified part in the ComboBox, without actually selecting it. This way the actual part can be quickly found, while still requiring the user to actually choose it.
Although, this doesn't work, I think it will illustrate what I would LIKE to do:
<ComboBox Grid.Column="1" ItemsSource="{Binding Path=Part.MasterPart.FamilyParts}"
SelectedItem="{Binding Path=ReceivedPart, ValidatesOnDataErrors=True}" >
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Part.MaxId}"
Value="{Binding Path=Part.MaxId}">
<Setter Property="Background" Value="LightSalmon" />
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
You've got the right idea. The only thing I see wrong with your code is the DataTrigger attributes.
If Value was, well, just a value, it would work:
<DataTrigger Binding="{Binding Path=Part.MaxId}" Value="999" >
I would wrap this logic up into a new property on the viewmodel for simplicity:
<DataTrigger Binding="{Binding Path=Part.ShouldHighlight}" Value="true">
There are ways to highlight other than setting the background color, and I'd recommend you explore them because it can be confusing to users to have different background colors for different reasons (selection versus highlighting). For example, you could put a little star next to relevant items or make them bold.
That said, You can just do this to set the background color of the ComboBoxItem:
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Setter Property="Background" Value="{Binding Part.MaxId, Converter={StaticResource BackgroundConverter}}"/>
</Style>
</ComboBox.ItemContainerStyle>
Even better, use a view model and just bind directly to a BackgroundColor property:
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Setter Property="Background" Value="{Binding BackgroundColor}"/>
</Style>
</ComboBox.ItemContainerStyle>
Instead of adding a property to the view model, you could use a Style Selector (http://msdn.microsoft.com/en-us/library/system.windows.controls.styleselector.aspx) to determine which style to use for an item.
You're on the right track, but what I would do is put a bool read-only property in your Part class that told the combobox whether it should be highlighted in this instance. You could try something like this:
<ComboBox Grid.Column="1" ItemsSource="{Binding Path=Part.MasterPart.FamilyParts}"
SelectedItem="{Binding Path=ReceivedPart, ValidatesOnDataErrors=True}" >
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid>
<Border Background="LightSalmon" Visibility="{Binding Part.Highlighted, Converter={StaticResource BoolToVizConverter}}"/>
<TextBlock Text="{Binding Part.Name}"/>
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
This way, the background of the border wouldn't display at all if Highlighted were false.

Select All Checkbox in XAML using trigger?

I'd like to implement a select all checkbox in xaml.
I have several (templated) checkboxes in a listview. Then I have a checkbox outside of the listview, which I want to have a "select all"-behaviour.
I could easily solve the problem in my ViewModel, however, I think it would be more elegant to do this in xaml, since the select all checkbox doesn't (directly) have anything to do with my ViewModel.
The code looks something like this:
<ListView>
<ListView.ItemTemplate>
<DataTemplate>
<CheckBox Content="Globale Eingabe"
Name="SelectSingle"
IsChecked="{Binding IsChecked}">
</CheckBox>
</DataTemplate>
<ListView.ItemTemplate>
</ListView>
<CheckBox Name="SelectAll" />
As you see the IsChecked Property of the SelectSingle is already bound to my ViewModel. So I reckon I need a trigger to manipulate the state of the checkbox.
Now I already tried sth like this:
<CheckBox Content="Globale Eingabe"
Name="SelectSingle"
IsChecked="{Binding IsChecked}">
<CheckBox.Triggers>
<Trigger SourceName="SelectAll" Property="IsChecked" Value="True">
<Setter TargetName="SelectSingle" Property="IsChecked" Value="True"/>
</Trigger>
</CheckBox.Triggers>
</CheckBox>
or sth like this:
<CheckBox Content="Globale Eingabe"
Name="SelectSingle"
IsChecked="{Binding IsChecked}">
<CheckBox.Triggers>
<DataTrigger Binding="{Binding ElementName=SelectAll, Path=IsChecked}"
Value="True">
<Setter TargetName="Check"
Property="IsChecked"
Value="True"/>
</DataTrigger>
</CheckBox.Triggers>
</CheckBox>
I also tried the same thing within a style, but to no avail. I always obtain an error, sth along the lines of "static member "IsCheckedProperty couldn't be found in type "ContentPresenter"".
Now that sounds as if the Target/SourceName binding wouldn't work, but why?
Is there something that I am missing?
I think that you should put the Check All logic in the ViewModel after all. In this Code Project article, WPF Guro Josh Smith solves similar problem (in his case it's TreeView and not ListView) in the ViewModel with the following title: "Putting the Smarts in a ViewModel".
I think it'd be easier to implement and debug this logic in the ViewModel, than to do some complicated MultiBinding that you wouldn't know where it'll bite you.
Last note - I'd always follow Josh's advice :-)
Torsten, I am sorry if I didn't understand what you've tried already, but you need to bind the IsChecked property of the CheckBoxes inside the ListView to the IsChecked property of the CheckBox outside it using:
IsChecked="{Binding Path=IsChecked, Mode=OneWay,ElementName=OutsideCheckBox}"

Resources