How to change property Fill of icon of current selected element in checkbox depending on IsChecked property?
My resource dictionary:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- (...) -->
</ResourceDictionary.MergedDictionaries>
<DataTemplate x:Key="FooTemplate">
<Icons:ExIcon Fill="Red"
Width="12"
Height="Auto">
<!--
<Icons:ExIcon.Triggers>
<DataTrigger Binding="{Binding RelativeSource={???}}, Path=???}" Maybe here?
Value="???"/>
</Icons:ExIcon.Triggers>
-->
</Icons:ExIcon>
</DataTemplate>
<!-- (...) -->
<styles:IconSelector x:Key="IconSelector"
FooTemplate="{StaticResource FooTemplate}"
FooTemplateSSecond="{StaticResource FooTemaplteSecond}"/>
And listbox:
<ListBox ItemsSource="{Binding DataSources}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<!-- (...) -->
<CheckBox IsChecked="{Binding IsSelected, Mode=TwoWay}">
<CheckBox.Template>
<!-- (...) -->
<ContentControl Name="Icon"
Content="{Binding}"
ContentTemplateSelector="{StaticResource IconSelector}"
HorizontalAlignment="Right"
Grid.Column="1"/>
<!-- (...) -->
</CheckBox.Template>
</CheckBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Is it possible?
It's not completely clear to me how your CheckBox.Template looks like. Also the Icons:ExIcon Control is a mystery to me. Anyway here is a small working example. It uses a Rectangle instead of your ExIcon. But you can easily replace it ;-) I've bound directly to IsSelected via a DataTrigger instead of searching by ElementName or RelativeSource.
XAML:
<ListBox ItemsSource="{Binding}">
<ListBox.Resources>
<DataTemplate x:Key="template1">
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Path=IsSelected}" />
<Rectangle Height="20"
Width="20">
<Rectangle.Style>
<Style TargetType="Rectangle">
<Setter Property="Fill"
Value="Blue" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsSelected}"
Value="True">
<Setter Property="Fill"
Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
</StackPanel>
</DataTemplate>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<ContentControl ContentTemplate="{StaticResource template1}" Content="{Binding}">
</ContentControl>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
NOTE Content="{Binding}". This sets the DataContext of the ContentPresenter to the "actual" DataContext of the ListBoxItem. If you use VS2015 you can investigate this with the VisualTreeViewer otherwise you can use https://snoopwpf.codeplex.com/
You should also consider to rename IsSelectedto IsChecked. Noramlly you use the IsSelected property for indicating if the row is selected or not.
Related
This question is a follow-up to one I posted on SO yesterday. I've followed Andy's suggestion to use INotifyDataErrorInfo. I've used it in the Class model class I referred yesterday. And I found an article on C-SharpCorner.com which I also adapted to my situation. After putting these in place and testing it, I found that it does catch the range restriction. However, it only circled the cell with the problem in the DataGrid in red. It didn't show me the error message next to the cell, which is what my boss wants. I'm wondering if what the boss wants isn't possible in a WPF DataGrid? Or have I botched it?
I set a breakpoint in the HandleValidationResults method from the second link above. It properly assigns the error message to the _errors collection. Here's how I modified the DataGridTextColumn:
<DataGridTextColumn Header="Start" Binding="{Binding StartTime, StringFormat={}{0:0000}, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnNotifyDataErrors=True, UpdateSourceTrigger=PropertyChanged}">
<Validation.ErrorTemplate>
<ControlTemplate>
<StackPanel>
<AdornedElementPlaceholder x:Name="dataGridTextColumn" />
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ErrorContent}" Foreground="Red" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ControlTemplate>
</Validation.ErrorTemplate>
</DataGridTextColumn>
Do not set the validation error template directly, set it in the ElementStyle or EditingElementStyle:
<DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}" BasedOn="{StaticResource {x:Type TextBlock}}">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<StackPanel>
<AdornedElementPlaceholder x:Name="dataGridTextColumn" />
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ErrorContent}" Foreground="Red" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGridTextColumn.ElementStyle>
However, you will notice that your current approach does not work well, as the error template will occlude other cells below the cell that is in error. A better alternative is to set the error template as ToolTip. This way, the errors are shown as popup if you hover over a cell in error.
<DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}" BasedOn="{StaticResource {x:Type TextBlock}}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip DataContext="{Binding PlacementTarget.(Validation.Errors), RelativeSource={RelativeSource Self}}">
<ItemsControl ItemsSource="{Binding }">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ErrorContent}" Foreground="Red" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ToolTip>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</DataGridTextColumn.ElementStyle>
I am using the code from stack overflow question:
RadioButton and image
This is working fine, but since I activated the style in my radio buttons, the grouping does not work correctly. It should group the radio buttons per row, but instead it is grouped by the whole datagrid:
Grouping by whole grid
The Binding of the Id ensures that the groupname should be unique of each row, which does work when I remove the style attribute of my radio buttons.
Code of my radio buttons, which are included in a wpf datagrid:
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<RadioButton
x:Name="radioPlayNext"
MinWidth="50"
Command="{Binding PlayNextClipCommand}"
GroupName="{Binding Id}"
IsChecked="{Binding Handling, Converter={StaticResource EnumBooleanConverter}, ConverterParameter={x:Static local:Handling_e.Handling_PlayNextClipInList}}"
Style="{StaticResource StyleRadioPlayNext}" />
<RadioButton
x:Name="radioLoopClip"
MinWidth="50"
Command="{Binding LoopClipCommand}"
GroupName="{Binding Id}"
IsChecked="{Binding Handling, Converter={StaticResource EnumBooleanConverter}, ConverterParameter={x:Static local:Handling_e.Handling_LoopClip}}"
Style="{StaticResource StyleRadioLoopClip}" />
<RadioButton
x:Name="radioPlayAndHold"
MinWidth="50"
Command="{Binding PlayAndHoldLastFrameCommand}"
GroupName="{Binding Id}"
IsChecked="{Binding Handling, Converter={StaticResource EnumBooleanConverter}, ConverterParameter={x:Static local:Handling_e.Handling_PlayAndHoldLastFrameOfClip}}"
Style="{StaticResource StyleRadioPlayHold}" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Style is as follows:
<Image x:Key="imgOnNext" Source="../../Images/NEXT_on.png" />
<Image x:Key="imgOffNext" Source="../../Images/NEXT_off.png" />
<!-- Style for radio button play next clip in list -->
<Style x:Key="StyleRadioPlayNext" TargetType="RadioButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RadioButton}">
<Grid>
<Image
Width="32"
Height="32"
Source="../../Images/NEXT_off.png" />
<ContentPresenter />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Content" Value="{DynamicResource imgOnNext}" />
</Trigger>
<Trigger Property="IsChecked" Value="False">
<Setter Property="Content" Value="{DynamicResource imgOffNext}" />
</Trigger>
</Style.Triggers>
</Style>
The example you have linked is a bad one.
Better use a BulletDecorator instead of the Content to show the check state image. This way you are still able to use the Content property to show a button label.
Generally, if you don't know how a control is properly styled then lookup the corresponding control's style and template: Microsoft Docs: Control Styles and Templates.
The GroupName must be unique within the current scope, which is the DataTemplate. Each DataTemplate is its own scope. Therefore you don't have to bind GroupName to a unique ID, which very likely introduces some issues. It's redundant.
Simply set the GroupName to individual names like you would do with the x:Name directive.
For performance reasons, you should always choose StaticResource over DynamicResource.
<Window>
<Window.Resources>
<Image x:Key="imgOnNext" Source="../../Images/NEXT_on.png" />
<Image x:Key="imgOffNext" Source="../../Images/NEXT_off.png" />
<Style x:Key="RadioButtonStyle" TargetType="RadioButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RadioButton">
<BulletDecorator>
<BulletDecorator.Bullet>
<ContentPresenter x:Name="BulletContent"
Content="{StaticResource imgOffNext}" />
</BulletDecorator.Bullet>
<ContentPresenter />
</BulletDecorator>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="BulletContent"
Property="Content"
Value="{StaticResource imgOnNext}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<DataGrid>
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<RadioButton GroupName="A"
Style="{StaticResource RadioButtonStyle}"
Content="{Binding SomeValue}" />
<RadioButton GroupName="A"
Style="{StaticResource RadioButtonStyle}"
Content="{Binding SomeOtherValue}" />
<RadioButton GroupName="B"
Style="{StaticResource RadioButtonStyle}"
Content="{Binding SomeValue}" />
<RadioButton GroupName="B"
Style="{StaticResource RadioButtonStyle}"
Content="{Binding SomeOtherValue}" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Window>
I am trying to sort this out and cannot see light in the dark.
I have worksheets (tables) in workspace. All dynamic (columns, mount of tables, ...). Based on object for worksheet I would like to set datagrid rotation.
VM structure:
ControlVM :
{ Workspace: [
{
Worksheet: [
{
DataViewBindingData: [DataView type],
isRotated: rue | false
},
{...}
]
}
<ScrollViewer Grid.Column="0" Grid.Row="0" >
<ItemsControl ItemsSource="{Binding Path=Workspace}" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Expander Header="{Binding Path=WorksheetName}" ExpandDirection="Down" IsExpanded="True" >
<StackPanel>
<DataGrid CanUserResizeColumns="False" HeadersVisibility="Column" CanUserSortColumns="False" HorizontalAlignment="Stretch" CanUserAddRows="False" Margin="5"
SelectionUnit="Cell" SelectionMode="Single" ItemsSource="{Binding Path=DataViewBindingData}" Loaded="DataGrid_Loaded"
BeginningEdit="DataGrid_BeginningEdit" />
</StackPanel>
</Expander>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
And inside custom styles resources in my base style for control (header, cells), I am trying to access isRotated property of worksheet object
<Style x:Key="DataGridBase" TargetType="Control">
<Style.Triggers>
<!-- TODO cannot find an ancestor for data binding -->
<DataTrigger Binding="{Binding IsRotated, Converter={StaticResource DebugConverter}, Mode=OneWay}" Value="True">
<Setter Property="LayoutTransform">
<Setter.Value>
<TransformGroup>
<RotateTransform Angle="-90" />
<ScaleTransform ScaleX="-1" ScaleY="1" />
</TransformGroup>
</Setter.Value>
</Setter>
<Setter Property="TextOptions.TextFormattingMode" Value="Display" />
</DataTrigger>
</Style.Triggers>
</Style >
<!--DataGrid Cells-->
<Style TargetType="{x:Type DataGridCell}" BasedOn="{StaticResource DataGridBase}" />
And thats the problem, I dont know how to access parent control's data to bind the property. Thanks for suggestions.
The implicit DataContext inside DataGridCell is actually the current DataGridRow's bound item. So in this case you have to use RelativeSource to find the DataGrid and set Path to DataContext.IsRotated (the DataGrid's DataContext is a Worksheet):
<DataTrigger Binding="{Binding DataContext.IsRotated,
RelativeSource={RelativeSource AncestorType=DataGrid}
Converter={StaticResource DebugConverter}, Mode=OneWay}"
Value="True">
<!-- ... -->
I am working on a C# WPF project and I have a list view which uses data binding and a collection view.
Below is how my ListView is currently populated.
<ListView HorizontalAlignment="Left" Margin="547,12,0,41" Name="lstCallLogInformation" Width="320">
<ListView.Style>
<Style TargetType="ListView">
<Style.Triggers>
<Trigger Property="HasItems" Value="False">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListView">
<TextBlock Text="No items in your devices call log" FontWeight="Bold" HorizontalAlignment="Center" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</ListView.Style>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Expander IsExpanded="True">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock FontWeight="Bold" Foreground="Gray" Text="{Binding Name}" VerticalAlignment="Bottom" />
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Control.HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<DockPanel Style="{StaticResource onmouseover}">
<StackPanel HorizontalAlignment="Stretch" Orientation="Horizontal" Tag="{Binding logID}">
<Image Height="30" Source="{Binding base64ImageString, Converter={StaticResource ImageConverter}}" Width="30" />
<StackPanel Margin="10" Orientation="Vertical">
<TextBlock FontWeight="Bold" Text="{Binding contactNameOrPhoneNumber}" />
<StackPanel HorizontalAlignment="Stretch" Orientation="Horizontal">
<TextBlock HorizontalAlignment="Left" Text="{Binding dateString}" />
<TextBlock Margin="15, 0, 0, 0" Text="Duration: " />
<TextBlock HorizontalAlignment="Right" Text="{Binding humanReadableCallDuration}" />
</StackPanel>
</StackPanel>
<StackPanel Orientation="Vertical">
<Button Background="Transparent" BorderThickness="0" BorderBrush="Transparent" Tag="{Binding logID}" Name="btnAddAsContact" Click="btnAddAsContact_Click">
<Image Width="15" Height="15" Source="/BoardiesSMSServer;component/images/add.png" Tag="{Binding logID}" />
</Button>
<Button Background="Transparent" BorderThickness="0" BorderBrush="Transparent" Tag="{Binding logID}" Name="btnDeleteContactFromLog" Click="btnDeleteContactFromLog_Click">
<Image Width="15" Height="15" Source="/BoardiesSMSServer;component/images/delete.png" Margin="0,0,0,5" Tag="{Binding logID}" />
</Button>
</StackPanel>
</StackPanel>
</DockPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Within my DockPanel within the DataTemplate I have a StackPanel which contains various TextBlocks from the array that is passed into the binding.
In one of the StackPanels you will see you will see a TextBlock that binds to the element of contactNameOrPhoneNumber.
What I want to be able to do is if another value from the binding is null then I add a TextBlock for the contactNameOrPhoneNumber` element and alongside it have another TextBlock which contains another element of number``, if the other binding value is not null then this extra textblock of number is not added.
So in simple terms, what I want to be able to do is within the DataTemplate have a conditional if statement of if binding value == null add textblock to stackpanel else don't add textblock.
You can do that with Style + Triggers something like this:
<TextBlock>
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding PropertyName}" Value="{x:Null}">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
Also you can create IValueConverter say ObjectToVisibilityConverter which will take input your property and if property is null return Visibility.Collapsed else return Visibility.Visible.
You can then bind property with Visibility value of TextBlock via Converter.
<TextBlock Visibility="{Binding PropertyName,
Converter={StaticResource ObjectToVisibilityConverter}}"/>
Hi I have the following UserControl.
Can notice that in the second HierarchicalDataTemplate contains also a ListBox.
I would like the ListBox ItemsSource to bind to the a field in the viewModel taht is CurrentPropertyValues .
When I try to bind it in the mainwindow or just outside the tree, it works, (just ItemsSource="{Binding CurrentPropertyValues}")
I tried various things to make it work inside the HierarchicalDataTemplate (it has a different data context) but I keep on failing.
Thanks for any help!
<UserControl x:Class="RidaDiff2.Views.LeftTreeControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:converters="clr-namespace:RidaDiff2.Converters"
xmlns:model="clr-namespace:RidaDiff2.Model"
xmlns:viewModel="clr-namespace:RidaDiff2.ViewModels"
xmlns:views="clr-namespace:RidaDiff2.Views"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<converters:EnumToPicConverter x:Key="Converter" />
<HierarchicalDataTemplate DataType="{x:Type model:TreeNode}" ItemsSource="{Binding ChildListNodes}" x:Key="NotSelected">
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Path=EntityType,Converter={StaticResource Converter}}" />
<TextBlock Margin="5,0" Text="{Binding Name1}" />
<!--"{Binding ItemName, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" -->
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type model:TreeNode}" ItemsSource="{Binding ChildListNodes}" x:Key="Selected">
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Path=EntityType,Converter={StaticResource Converter}}" />
<TextBlock Margin="5,0" Text="{Binding Name1}" />
</StackPanel>
<ListBox DataContext="viewModel:TreeViewModel" Height="300" Width="100" ItemsSource="{Binding Path=(viewModel:TreeViewModel.CurrentPropertyValues), Mode=TwoWay}" ></ListBox>
</StackPanel>
</HierarchicalDataTemplate>
</UserControl.Resources>
<views:ExtendedTreeView x:Name="Tree" ScrollViewer.VerticalScrollBarVisibility="Auto" ItemsSource="{Binding RootNode}" SelectedItem_="{Binding CurrentCouple,Mode=TwoWay}">
<views:ExtendedTreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding Path=(model:TreeNode.IsExpanded), Mode=TwoWay}" />
<Setter Property="Background" Value="{Binding Path=(model:TreeNode.BackgroundColor), Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding Path=(model:TreeNode.IsSelected), Mode=TwoWay}"></Setter>
<Setter Property="HeaderTemplate" Value="{StaticResource NotSelected}"/>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="HeaderTemplate" Value="{StaticResource Selected}"/>
</Trigger>
</Style.Triggers>
</Style>
</views:ExtendedTreeView.ItemContainerStyle>
</views:ExtendedTreeView>
If CurrentPropertyValues is a property of TreeViewModel then you can use RelativeSource binding and go up the visual tree to find TreeView and bind ItemsSource to property of its DataContext:
<ListBox
Height="300"
Width="100"
ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TreeView}}, Path=DataContext.CurrentPropertyValues}" />