Binding a Style DataTrigger to a custom ICommand's status property - wpf

I have a custom implementation of WPF's ICommand, that executes its bindings in a BackgroundWorker, so that the UI stays responsive.
I would like to display a custom effect while the bindings are being executed. For now, the effect is simplified to setting command source's IsEnabled property to False. Later, our designer will come up with some nice progress-style animation.
Here's an example of what I'm trying to do, that works (sort of):
<Button Width="80"
Command="{x:Static Member=local:AppCommands.Test}"
DataContext="{x:Static Member=local:AppCommands.Test}">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="IsEnabled" Value="True"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsExecuting, Mode=OneWay}" Value="True">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
As evident, my ICommand contains an IsExecuting property, which is set to True while the BackgroundWorker does its thing with the bindings, consequently disabling the button in the UI.
Unfortunately, having to explicitly set the button's DataContext to command instance prevents me from referring to the original DataContext in cases like this one:
<Button Width="80"
Command="{x:Static Member=local:AppCommands.ParamTest}"
CommandParameter="{Binding Path=SelectedItem}"
DataContext="{x:Static Member=local:AppCommands.ParamTest}">
Here, the CommandParameter should bind to the window's DataContext (which is set to an instance of my view-model), but instead it binds to the command which knows nothing about SelectedItem.
There are probably more ways to solve this, and I'm interested to hear all of them. I myself have been thinking along the lines of using an attached property to replace the direct binding to command's IsExecute and with it the need for setting the DataContext, but I'm not yet sure how to do that.
Any advice is most welcome. Thank you!

For now, I've come up with this simple solution:
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Command.IsExecuting, Mode=OneWay}" Value="True">
It does what I wanted so I'm happy. If anyone thinks there's a better way, I'm still interested, though.

Related

WPF trigger can't change a property in mvvm

I wanted to make it happened to change property in ViewModel using Trigger tag in xaml.
The code I made was like this.
<Grid x:Name="LogoGrid">
<Grid.Style>
<Style TargetType="Grid">
<Setter Property="Height" Value="{Binding LogoHeight, Mode=TwoWay}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Grid}, Path=ActualHeight, Converter={StaticResource ImageHeight}}" Value="False">
<Setter Property="Height" Value="150"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<Image Source="/Resources/Logo/Logo.png" Style="{StaticResource GameWindowLogoStyle}"/>
</Grid>
I bound data to LogoHeight property in ViewModel and I wanted to set this property to 150 when parents grid is big enough to show Logo Image.
It works fine in the UI. but it didn't change LogoHeight property.
It is the same things keep happening when I use animation with Storyboard tag.
Is it normal things that propery can't be changed by Trigger tag or animation in the Storyboard tag.
Please tell me the way if there is a way to change property in Trigger tag or Storyboard tag.
Thank you.
A Setter in a Style cannot set the property of a view model. It can only set a property of the element to which the Style is being applied, i.e. the Grid in this case.
You may be able to work around this by using a Storyboard that animates the source property:
Setting a ViewModel boolean property inside a storyboard

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.

wpf datagrid checkbox column header de/selects all

I'm trying to create a datagrid with a checkbox column that has a header with a checkbox that selects and deselects the rows' checkboxes. Also I'm attempting to do it with no behind code.
So far I've been partialy successful in that I used the datagrid's tag as a middle man in the binding, and could de/select the rows' checkboxes from the header checkbox using data triggers but once I check a row the binding in that row 'dies' and it is no longer effected by the header. Also, I already tried it with one way bindings instead of data triggers and got the same result...
Does anyone know of a way to keep the binding alive or pheraps another to achive the effect I'm looking for inxaml only.
header:
<CheckBox IsChecked="{binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}, Mode=FindAncestor}, Path=Tag}"/>
column template:
<CheckBox>
<CheckBox.Style>
<Style TargetType="{x:Type CheckBox}">
<Style.Triggers>
<DataTrigger Binding="{binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}, Mode=FindAncestor}, Path=Tag}" Value="True">
<Setter Property="IsChecked" Value="True"/>
</DataTrigger>
<DataTrigger Binding="{binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}, Mode=FindAncestor}, Path=Tag}" Value="False">
<Setter Property="IsChecked" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
That is actually very good idea to solve that problem. I can't see why that shouldn't work.
That said, there are a few things to try:
Place your header checkbox and the row checkbox on a window normally (not inside a DataGrid) to see what effect that has.
The other option is to try naming the Header checkbox (for example <CheckBox Name="cbxHeader") and then binding to it with {Binding ElementName=cbxHeader Path=Tag}.
Also, check the "Output" window in Visual Studio while you are testing (while it is running, Debug > Windows > Output; it might appear empty but just scroll up) for any messages about why the bindings are failing.
You can also use a bogus ValueConverter to try and see if the bindings are still firing.

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}"

WPF Debugging datatriggers?

I am trying to do something very simple. I have a ToggleButton.IsChecked property bound to a bool. I want the background to toggle between red(false) and green(true). But for some reason it seems to be toggling between red and no background. I used a converter to check if I am getting proper notifications from the source and I am, so not sure why one trigger(false/red) works and the other(true/green) doesnt. Also would like to hear how people debug these kind of issues. Thanks!
Here is the code.
<DataTemplate x:Name"Flipper">
<StackPanel>
...
<ToggleButton IsChecked="{Binding Path=BoolValue,
Converter={StaticResource converter}}"
Name="onoff" >
</ToggleButton>
...
<StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding ElementName=onoff,Path=IsChecked}"
Value="True">
<Setter TargetName="onoff" Property="Background" Value="Green"/>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=onoff,Path=IsChecked}"
Value="False">
<Setter TargetName="onoff" Property="Background" Value="Red"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
Update: I changed the togglebutton to a checkbox and it works. No idea why...
Try using WPF Inspector:
https://wpfinspector.codeplex.com/
Once you attach to your running WPF application, highlight the element in question by holding down ctrl + clicking on it. Then, select the element in the visual tree (might be a parent) that contains the trigger. Click on the triggers tab and you can see the current evaluation (e.g. True == True). If the datatrigger condition is met, the little icon will be orange (lit).
It looks ok to me, can you try altering the converter to return "red" or "green" rather than True/False (and alter the trigger accordingly). I have seen some wierd behaviour with WPF triggers when using NULL or Booleans in that it "unsets" the property if it's the opposite of your trigger value, rather than using another trigger value.
As for debugging them.. I'd love to know if there's a better way than the hack and hope methods I generally use for XAML debugging :D

Resources