Can the Setter of a DataTrigger modify a property within the DataContext? - c#

I have a check box which I plan to implement a 'Select All' feature on.
I figured the easiest implementation was to modify my DataTemplate with a DataTrigger to change the IsChecked property to true/false.
<UserControl.Resources>
<DataTemplate x:Key="MyTemplate">
<Grid>
<CheckBox x:Name="Selection" IsChecked="{Binding Selected}" />
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding ElementName=SelectAll, Path=IsChecked}" Value="true">
<Setter TargetName="Selection" Property="DataContext.Selected" Value="true" />
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=SelectAll, Path=IsChecked}" Value="false">
<Setter TargetName="Selection" Property="IsChecked" Value="false" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</UserControl.Resources>
<Grid>
<CheckBox Name="SelectAll />
<ListView Name="lvSteps" ItemTemplate="{StaticResource MyTemplate}" ItemsSource="{Binding MyList}" />
</Grid>
However, this overwrites the binding of the CheckBox which is set to the DataContext property 'Selected' to just the bool values set in the DataTrigger.
Which thinking about it makes perfect sense.
So my question is, can I change the value of DataContext property within a DataTrigger via Setter?
So I can keep my CheckBox bound to 'Selected', and change the 'Selected' value within the Setter of the DataTrigger?

you maybe think about it in the wrong way.
SelectAll is surly part of your container Element (lets say ListVM) which intern holds your objects (ListItemVM) which contains Selected so if you check SelectAll you can and should iterate over your Elements and set Selected to true and this items use there INotifyPropertyChanged to inform your CheckBox
Edit for Comment
there is no need to hook up the event of SelectAll just bind to it and do your thing in the Set-Part
public bool SelectAll
{
get { return _selectAll;}
set
{
_selectAll = value;
RaisPropertyChanged();
foreach( var item in MyList)
{
item.Select = _selectAll;
}
}
}
Also why would you like to do this in xaml?
Because you could use interfaces and factor it out in a way that you can us it at many places without code redundancy also you will be able to do it in code from where ever you are able to access the "ListVM"

Related

C# Wpf Set Costum Style to ComboBoxItem

I've got a ComboBox bound to a List. The ComboBox has 3 ComboBoxItems from that list. I also got a CheckBox. If the CheckBox is unchecked and the first ComboBoxItem selected, the property "Property1" in my viewmodel is set to false. Now if "Property1" is false I'd like to change the Background and Foreground of this ComboBoxItem. How can I achieve this?
I was trying to get that done with Style and MultiDataTrigger but wasn't succesful - I only managed to change the Style for all of the ComboBoxItems and not specific one.
<Style TargetType="ComboBoxItem">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Path=SelectedComboBoxItem}" Value="Item1" />
<Condition Binding="{Binding Path=CheckStatus}" Value="False" />
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="GhostWhite" />
<Setter Property="Foreground" Value="Gainsboro" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
How can I achieve to change the style of a specific ComboBoxItem when its bound via List?
Thanks for your hints / help.
This is probably not the best solution, but a working one:
Instead of using a style, i created a combobox with an ItemTemplate
<ComboBox Width="200" Height="30" ItemsSource="{Binding SimpleList, UpdateSourceTrigger=PropertyChanged}" >
<ComboBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Name}" Background="{Binding BackGround}" Foreground="{Binding ForeGround}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
My SimpleList contains 3 "SimpleObjects" with Properties for Name, BackGround and ForeGround. BackGround is set to white and ForeGround to black by default.
Once my CheckBox is checked, I get the first Item of my List and change its properties.
private void CheckBox_Checked(object sender, RoutedEventArgs e)
{
SimpleObject obj = SimpleList.FirstOrDefault();
obj.BackGround = new SolidColorBrush(Colors.Black);
obj.ForeGround = new SolidColorBrush(Colors.White);
}
Looking like this.

Is possible bind with IF ELSE statement

<TextBlock Text="{Binding Name}" />
if Name is empty or null, bind with NameOpt
Something like this:
<TextBlock Text="{Binding if Name ? Name : NameOpt}" />
You can use DataTriger to achieve the behavior
<Style TargetType="TextBlock">
<Setter Property="Text" Value="{Binding Name}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Name}" Value="{x:Null}">
<Setter Property="Text" Value="{Binding NameOpt}"/>
</DataTrigger>
</Style.Triggers>
</Style>
Well you could with a DataTrigger and there is nothing stopping you, but that's arguably a bad way to do binding, particularly from a MVVM point of view. Generally triggers are for changing a property based on conditions either in the XAML itself or a particular property in the VM. It's kind of weird to do dynamic binding to a property depending upon the state of whether a VM property is null or not.
To do so purely from the VM try, your view should bind to a single property like Name:
<TextBlock Text="{Binding Name}" />
...and in the processing in your VM do something like:
public string Name { get ; set; } // TODO: add usual property changed stuff
void UpdateStuff()
{
// perhaps update Name and NameOpt here
// ...
// Now update the exposed property
Name ? Name : NameOpt
}

What is the standard way to configure an ItemTemplate without violating the Item separation?

In using WPF for a while, this is actually the first time that I've come across a situation where I have an ItemTemplate for a ListBox that I want to be configurable based on properties outside the item itself. The problem came up when I wanted to have a Font selector dialog where the user could click a chechbox to enable the font previews (I actually changed my mind about this implementation, but I'd still like to know the answer).
It seems, because the DataTemplate may be used in any situation where that type is provided, it's considered good practice, not to bind to any parameters outside the item's configuration (seems like the code to bind to properties of a containing DataTemplate is particularly obtuse.)
I was wondering how I was supposed to implement this kind of situation. The code below works, but the binding is to a visual element, whereas I would rather bind to the property in the ViewModel.
<Window x:Class="ScreenWriter.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" DataContext="{Binding Mode=OneWay, RelativeSource={RelativeSource Self}}" >
<Grid>
<StackPanel>
<CheckBox Name="ShowPreview" IsChecked="{Binding IsShowPreviewChecked}">
Show Preview
</CheckBox>
<ListBox ItemsSource="{Binding Source={x:Static Fonts.SystemFontFamilies}}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}">
<TextBlock.Style>
<Style>
<Setter Property="TextBlock.FontFamily" Value="Arial" />
<Style.Triggers>
<DataTrigger Value="True" Binding="{Binding IsChecked, ElementName=ShowPreview}">
<Setter Property="TextBlock.FontFamily" Value="{Binding}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Grid>
</Window>
This doesn't seem like an unusual situation, but I can't find any solution that isn't prefixed with "this is a clever way to get around what you're not supposed to do".
Thanks for any help...
There are no 'best practice' solution for this problem.
As you wrote, "the DataTemplate may be used in any situation where that type is provided ... not to bind to any parameters outside the item's configuration".
Binding/DataContext are based on LogicalTree. So your Trigger binding trys to find the closest datacontext on the tree (in this case your ListItem DataContext = FontFamily )
You have to make same 'jump' from this 'LogicalTree Island' to the main Tree - like your control/element did a jump.
So the real question is, how could you change the binding source smoothly.
You could bind Target element DataContext directly (CheckBox has no DataContext, but its grandparent - Windows has):
<DataTrigger Binding="{Binding DataContext.IsShowPreviewChecked, ElementName=ShowPreview}" Value="True" >
or just find ListItem ancestor directly:
<DataTrigger Binding="{Binding DataContext.IsShowPreviewChecked, RelativeSource={RelativeSource AncestorType=ListBox, Mode=FindAncestor}}" Value="True" >
If you will use this jump many times, you will try to use BindingProxy
which will provide a shortcut for your bindings:
public class BindingProxy : Freezable
{
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
public object Context
{
get { return (object)GetValue(ContextProperty); }
set { SetValue(ContextProperty, value); }
}
public static readonly DependencyProperty ContextProperty =
DependencyProperty.Register("Context", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
}
Add this proxy as resouce:
<Window.Resources>
<this:BindingProxy x:Key="Proxy" Context="{Binding}" />
</Window.Resources>
and your binding is:
<DataTrigger Binding="{Binding Context.IsShowPreviewChecked, Source={StaticResource Proxy}}" Value="True" >
Do not forget add INotifyPropertyChanged Interface to your ViewModel

How to label content set using triggers/( etc) when bind value null or empty in wpf

I want to bind label content dynamically at run time. When the binding property null or empty I want to show bind value(name) as content. I have try as below but it not works:
when binding property has a value it works fine.
XAML code as follows.
<Label Content="{DynamicResource name}">
<Label.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding name}" Value="{x:NULL}">
<Setter Property="Label.Content" Value="name" />
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
Help me. Thanks!
Instead of using DataTrigger you can use TargetNullValue property (msdn).
Gets or sets the value that is used in the target when the value of
the source is null.
Example:
<Label Content="{Binding LabelContent, TargetNullValue=LabelContent}" />
Solution in code-behind:
You don't have to write trigger. You can check value before adding it to Resources:
...
foreach (var item in resourceList)
{
if(!string.IsNullOrEmpty(item.Value))
window.Resources.Add(item.Key, item.Value);
else
window.Resources.Add(item.Key, item.Key);
}
...

WPF Listview & Checkboxes - Binding issue when using relative source

I have a listview with two columns, one contains a textbox and the other a checkbox. These are bound to an ObservableCollection of a custom object containing a string for the textbox and a boolean for the checkbox.
All was working well until I tried having the check event of the checkbox highlight it's the row in the listview as in this article.
My problem is that checkbox no longer binds to the ObservableCollection. The textbox binds okay, but changing the checbox declaration from:
<CheckBox IsChecked="{Binding RestrictedEdit}"/>
to this:
<CheckBox IsChecked="{Binding RestrictedEdit, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListViewItem}}}"/>
stops the checkbox binding and the listview is displayed with the checkboxes all unchecked irrespectivate of status of the boolean. What am I doing wrong?
You are trying to bind to RestrictedEdit property, which ListViewItem doesn't have. This property is declared in view model, which is stored in DataContext, so this should work:
<CheckBox IsChecked="{Binding DataContext.RestrictedEdit,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListViewItem}}}"/>
However, I don't see any reason to use this code instead of simple IsChecked="{Binding RestrictedEdit}". CheckBox inherits DataContext from ListViewItem, so there is no reason to use relative source.
Let the binding as it is (no RelativeSource) and use rather a style or a DataTemplate having your custom object class as TargetType, and with a DataTrigger set on RestrictedEdit.
example with style :
<Style x:Key="MyStyle" TargetType="MyClass">
<Setter Property="BackGround" Value="White" />
<Style.Trigger>
<DataTrigger Binding="{Binding RestrictedEdit}" Value="False">
<Setter Property="BackGround" Value="Gray" />
</DataTrigger>
</Style.Trigger>
</Style>
Define this style, say, in your application resources (in App.xaml).
Then in your listview, use :
ItemContainerStyle="{StaticResource MyStyle}"

Categories