How to bind CustomControl DataTrigger to viewmodel DataContext - c#

Using MVVM light, I have a control template for a custom control. There are multiple instances of this control. The style is set in Generic.xaml.
<Style TargetType="{x:Type tgvw:TimeSlotRect}">
<Setter Property="DataContext" Value="{Binding TimeSlotRectViewModel, Mode=OneWay, Source={StaticResource Locator}}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type tgvw:TimeSlotRect}">
<Border Background="{TemplateBinding Background}"
Name="TimeSlotBorder"
BorderBrush="#A5BFE1"
BorderThickness="0,0.5,0,0.5" Height="30">
<Rectangle Fill="Beige" Name="Rect">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<cmd:EventToCommand Command="{Binding MouseDownCommand, Mode=OneWay}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Rectangle>
</Border>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding DataContext.IsSelected}" Value="True" >
<Setter Property="Fill" Value="Black" TargetName="Rect"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The idea is that the mouse button click fires a command in the viewmodel that sets it's IsSelected property to true and then the rectangle should pick that up and change it's fill to black - but it doesn't seem to work.
If I bind the DataTrigger to the viewmodel like this:
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding TimeSlotRectViewModel.IsSelected, Source={StaticResource Locator}}" Value="True" >
<Setter Property="Fill" Value="Black" TargetName="Rect"/>
</DataTrigger>
</ControlTemplate.Triggers>
It fires and every control changes to black. Any ideas what I'm doing wrong?

Related

Listbox Item selection

I have a WPF project (C#, Visual Studio 2010, MVVM).
In this project I have a ListBox. When I select the items I am getting a blue box around my objects and I'm not sure where said box comes from. Obviously it's something to do with selection, but I'm not sure what to alter to get it to disappear.
The Listbox is here:
<ListBox ItemsSource="{Binding ChatNodeListViewModel.ChatNodeVMs, Source={StaticResource Locator}}" Background="Transparent" Name="LbNodes" SelectedItem="{Binding ChatNodeListViewModel.SelectedNode, Source={StaticResource Locator}}" >
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<Canvas HorizontalAlignment="Left" VerticalAlignment="Top" Width="2000" Height="1600"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Canvas.Left" Value="{Binding XCoord}"/>
<Setter Property="Canvas.Top" Value="{Binding YCoord}"/>
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="lb_PreviewMouseLeftButtonDown" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Thumb Name="myThumb" Template="{StaticResource NodeVisualTemplate}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="DragDelta">
<cmd:EventToCommand Command="{Binding ChatNodeListViewModel.DragDeltaCommand, Source={StaticResource Locator}}" PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Thumb>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding NodeVisualMode}" Value="0">
<Setter TargetName="myThumb" Property="Template" Value="{StaticResource NodeVisualTemplate}" />
</DataTrigger>
<DataTrigger Binding="{Binding NodeVisualMode}" Value="1">
<Setter TargetName="myThumb" Property="Template" Value="{StaticResource NodeTemplateTest}" />
</DataTrigger>
<DataTrigger Binding="{Binding NodeVisualMode}" Value="2">
<Setter TargetName="myThumb" Property="Template" Value="{StaticResource NodeTemplateTest2}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
There is a main ControlTemplate which can be switched out for others (the main one being NodeVisualTemplate). But since this issue seems to happen on all of them, I'm inclined to believe it's further up the tree.
Still, I did do some testing for it, and will show it here:
<ControlTemplate x:Key="NodeVisualTemplate">
<Grid>
<Border x:Name="_Border" Padding="1" SnapsToDevicePixels="true" BorderThickness="3" Margin="2" CornerRadius="5,5,5,5" BorderBrush="SteelBlue" Visibility="Visible">
</Border>
</Grid>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=ListBoxItem}}" Value="true">
<Setter TargetName="_Border" Property="Effect">
<Setter.Value>
<DropShadowEffect ShadowDepth="0" Color="Black" Opacity="1" BlurRadius="20" />
</Setter.Value>
</Setter>
<Setter TargetName="_Border" Property="Background" Value="Transparent"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Within the border at the top of this template are some text boxes and stuff, but I don't suspect them as being part of the affair. The point is that I've changed the background of this control template. It is 'transparent' in the XAML above, but I've changed it to red and other colours to test. It doesn't get rid of the blue square that surrounds the ListBoxItem.
Here is an image of the horrible blue background I'm getting:
Can I get rid of it? Honestly I'm really at a loss.
Thanks.
Edit: I should mention something else I tried that is rather important here. I went up a layer into the ListBox.ItemContainerStyle part above and just under this line:
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="lb_PreviewMouseLeftButtonDown" />
I added this:
<Style.Triggers>
<Trigger Property="IsSelected" Value="True" >
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="Black" />
</Trigger>
</Style.Triggers>
Did it work? No, it didn't. It didn't get rid of the big blue box. It did make my fonts bold though, so I know it at least had that effect.
Your Thumb is still generated inside of a ListBoxItem, Which has ControlTemplate of It's own.
You need to put in your ListBoxItem Style, a Setter for Template, and put in there only:
<ControlTemplate TargetType="ListBoxItem">
<ContentPresenter ContentSource="Content"/>
</ControlTemplate>
I think this is a "standard" thing, wpf does for you in ListBoxes. It highlights the currently selected Item for you. I found a similar thread here: WPF ListBox, how to hide border and change selected item background color?
They fixed it by adding this code to the listbox and setting the HighlightBrushKey to Transparent for this ListBox, did you try that in your case?
<ListBox BorderThickness="0" HorizontalContentAlignment="Stretch" >
<ListBox.Resources>
<Style TargetType="ListBoxItem">
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent"/>
</Style.Resources>
</Style>
</ListBox.Resources>
</ListBox>

Listbox.ItemContainerStyle selected item

I have a list box with an ItemContainerStyle which describes the style of each element in the listbox. Like looks something like this:
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem" BasedOn="{StaticResource MyStyle}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border BorderThickness="0,0,0,1" BorderBrush="#1f000000" Padding="16 8">
<Button Command={Binding MyCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=lists:MyControl}}}" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
The thing is that, when I click this button I want to know the SelectedItem in the listbox, which is bound in my ViewModel. This selection doesnt trigger unless I select the item first.
Any Ideas?
You need to force the IsSelected with a trigger:
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="IsSelected" Value="True"/>
</Trigger>
</Style.Triggers>
Here is a full working example:
<ListBox x:Name="ListBox" ItemsSource="{Binding SomeList}" SelectedItem="{Binding SelectedListElement, Mode=TwoWay}" IsSynchronizedWithCurrentItem="True" >
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem" >
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="IsSelected" Value="True"/>
</Trigger>
</Style.Triggers>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border BorderThickness="0,0,0,1" BorderBrush="#1f000000" Padding="16 8">
<Button Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListBox}},
Path=DataContext.Run}" CommandParameter="{Binding}" Height="30" Width="100"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>

Dock Ability arrows editable

Hi I was wondering is there a way to only edit the Dock Ability Arrows on the Dock Guide for a docking manager using syncfusion for wpf? I can edit the complete style of the docking manager but I only want the ability to edit the arrows as I have the theme in a specific way but need the arrows only to be different is this possible at all? And if so how can I access it?
To do this you need to edit the Drag Provider Templates below is some XAML example for anyone who has a similar problem or was curious as to how to do it
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Syncfusion="http://schemas.syncfusion.com/wpf"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<!-- Control template for TopDragProvider -->
<ControlTemplate x:Key="TopDragProviderTemplate" TargetType="{x:Type ContentControl}">
<Image Name="Img" Width="29" Height="32" Syncfusion:DockPreviewManagerVS2005.ProviderAction="GlobalTop" Source="pack://application:,,,/Syncfusion.Tools.WPF;component/Framework/DockingManager/Resources/DockPreviewVS2005GlobalTop.png" />
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding Path=IsSideButtonActive, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Syncfusion:DockPreviewMainButtonVS2005}}}" Value="true">
<Setter TargetName="Img" Property="Source" Value="pack://application:,,,/Syncfusion.Tools.WPF;component/Framework/DockingManager/Resources/DockPreviewVS2005GlobalTopOver.png"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<!-- Control template for LeftDragProvider -->
<ControlTemplate x:Key="LeftDragProviderTemplate" TargetType="{x:Type ContentControl}">
<Image Name="Img" Width="32" Height="29" Syncfusion:DockPreviewManagerVS2005.ProviderAction="GlobalLeft" Source="pack://application:,,,/Syncfusion.Tools.WPF;component/Framework/DockingManager/Resources/DockPreviewVS2005GlobalLeft.png" />
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding Path=IsSideButtonActive, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Syncfusion:DockPreviewMainButtonVS2005}}}" Value="true">
<Setter TargetName="Img" Property="Source" Value="pack://application:,,,/Syncfusion.Tools.WPF;component/Framework/DockingManager/Resources/DockPreviewVS2005GlobalLeftOver.png"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<!-- Control template for RightDragProvider -->
<ControlTemplate x:Key="RightDragProviderTemplate" TargetType="{x:Type ContentControl}">
<Image Name="Img" Width="32" Height="29" Syncfusion:DockPreviewManagerVS2005.ProviderAction="GlobalRight" Source="pack://application:,,,/Syncfusion.Tools.WPF;component/Framework/DockingManager/Resources/DockPreviewVS2005GlobalRight.png" />
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding Path=IsSideButtonActive, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Syncfusion:DockPreviewMainButtonVS2005}}}" Value="true">
<Setter TargetName="Img" Property="Source" Value="pack://application:,,,/Syncfusion.Tools.WPF;component/Framework/DockingManager/Resources/DockPreviewVS2005GlobalRightOver.png"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<!-- Control template for BottomDragProvider -->
<ControlTemplate x:Key="BottomDragProviderTemplate" TargetType="{x:Type ContentControl}">
<Image Name="Img" Width="29" Height="32" Syncfusion:DockPreviewManagerVS2005.ProviderAction="GlobalBottom" Source="pack://application:,,,/Syncfusion.Tools.WPF;component/Framework/DockingManager/Resources/DockPreviewVS2005GlobalBottom.png" />
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding Path=IsSideButtonActive, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Syncfusion:DockPreviewMainButtonVS2005}}}" Value="true">
<Setter TargetName="Img" Property="Source" Value="pack://application:,,,/Syncfusion.Tools.WPF;component/Framework/DockingManager/Resources/DockPreviewVS2005GlobalBottomOver.png"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>

Changing the background of a button in a DataGrid if the row is selected - WPF

I have a datagrid which contains a template column which holds a few buttons.
I need the color of these buttons to change from black to white when the row is selected.
Although I am not sure how can I reach "DataGridRow.IsSelected" from the buttons setters.
Here is what I tried and didn't work:
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self}, Path=DataContext}"
Command="{Binding ViewModel.OnRemoveDirectoryClick, ElementName=Root}">
<Button.Style>
<Style>
<Setter Property="Button.Background">
<Setter.Value>
<ImageBrush ImageSource="../../Images/menu_delete.png"/>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="DataGridRow.IsSelected" Value="True">
<Setter Property="Button.Background">
<Setter.Value>
<ImageBrush ImageSource="../../Images/menu_delete_white.png"/>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Thanks ahead,
Yotam
Eventually I found that I needed to use DatatTrigger with a relative source to DataGridRow
<Button.Style>
<Style>
<Setter Property="Button.Background">
<Setter.Value>
<ImageBrush ImageSource="../../Images/menu_delete.png"/>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsSelected, RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}}" Value="True">
<Setter Property="Button.Background">
<Setter.Value>
<ImageBrush ImageSource="../../Images/menu_delete_white.png"/>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
An additional and perhaps more elegant way I found to tackle this problem was using a template which contains rectangle with white filling which takes the button's background as the opacity mask.
<ControlTemplate x:Key="DataGridButtonTemplate" TargetType="{x:Type Button}">
<Grid Style="{x:Null}">
<Rectangle x:Name="Mask" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" Opacity="{StaticResource NormalOpacity}" Style="{x:Null}"
OpacityMask="{TemplateBinding Background}" Fill="White" Visibility="{Binding Path=IsSelected, RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}, Converter={StaticResource BoolToVisibilityConverter}}"/>
</Grid>
</ControlTemplate>
Try putting this style as Resource
<Style TargetType="{x:Type DataGridCell}">
<Style.Triggers>
<Trigger Property="DataGridCell.IsSelected"
Value="True">
<Setter Property="Foreground" TargetName="YourButtonName"
Value="YourColor" />
</Trigger>
</Style.Triggers>
</Style>

XAML Style Won't Apply Until EventTrigger Command Completes

I have a ListBox which defines a custom ControlTemplate. The selected item has a style that changes the background and foreground, and the style basically works. However, I want to introduce a behaviour that displays a modal message box on selection changed, asking the user if they really want to select a different item. I've implemented an ICommand to do this which in the code below is shown as AreYouSureCommand.
The problem is whilst that modal message box is shown, the background style for the selected item is changed but the foreground is not. As soon as I dismiss the modal message box, the foreground colour changes. I haven't included the code for the ICommand because it's a bit convoluted but hopefully it is sufficient to say that a Window is opened with ShowDialog when it is executed.
Can anybody shed any light on why my background colour changes but not my foreground colour?
<ListBox x:Name="SubMenu" ItemsSource="{Binding MyItems}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=DisplayName}"
Foreground="{Binding Foreground, RelativeSource={RelativeSource AncestorType=ContentControl}}" />
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.Resources>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border x:Name="MainBorder">
<ContentControl x:Name="Presenter">
<ContentPresenter />
</ContentControl>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<!-- Setter on MainBorder applies before AreYouSureCommand completes -->
<Setter TargetName="MainBorder" Property="Background" Value="Red" />
<!-- Setter on Presenter applies after AreYouSureCommand completes -->
<Setter TargetName="Presenter" Property="Foreground" Value="Green" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.Resources>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding AreYouSureCommand}"
CommandParameter="{Binding SelectedItem, ElementName=SubMenu}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
I eventually came to a solution for this. Basically I use another setter for the IsSelected property to push the value of IsSelected for the ListViewItem onto the view model. Then I use a DataTrigger instead of a regular Trigger to set the selected styles, and bind the trigger to the IsSelected property on the view model rather than the property of the ListViewItem itself. I don't really know why this works - it shouldn't be any different really, but it does work.
Thanks Juan and Ben for your comments.
<ListBox x:Name="SubMenu" ItemsSource="{Binding MyItems}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=DisplayName}"
Foreground="{Binding Foreground, RelativeSource={RelativeSource AncestorType=ContentControl}}" />
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.Resources>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border x:Name="MainBorder">
<ContentControl x:Name="Presenter">
<ContentPresenter />
</ContentControl>
</Border>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter TargetName="MainBorder" Property="Background" Value="Red" />
<Setter TargetName="Presenter" Property="Foreground" Value="Green" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.Resources>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding AreYouSureCommand}"
CommandParameter="{Binding SelectedItem, ElementName=SubMenu}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>

Categories

Resources