Call other element inside WPF Tree in Style - wpf

My point is this. For test i need when user check chk1 the chk2 element changed the property IsEnabled to False, but i can't do reference to chk2 element.
This is Style XAML.
<Style x:Key="styleCheckBox" TargetType="{x:Type CheckBox}">
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
</Style.Triggers>
</Style
Call to Style..
<StackPanel>
<CheckBox x:Name="chk1" Content="CheckBox1" Style="{StaticResource styleCheckBox}"/>
<CheckBox x:Name="chk2" Content="CheckBox2"/>
</StackPanel>

You cannot set TargetProperty in Style Trigger. This basically means that you should create a custom control derived from StackPanel which contains two checkboxes and these checkboxes are exposed as properties. Then you'll be able to define a style for that control (not the CheckBox) and set the properties you want.
Much easier way (if only needed for testing) would be this:
<StackPanel>
<StackPanel.Resources>
<local:InverseBoolConverter x:Key="InverseBoolConverter"/>
</StackPanel.Resources>
<CheckBox x:Name="chk1" Content="CheckBox1"/>
<CheckBox x:Name="chk2" Content="CheckBox2" IsEnabled="{Binding ElementName=chk1, Path=IsChecked, Converter={StaticResource InverseBoolConverter}}"/>
</StackPanel>
Where InverseBoolConverter is defined as follows:
[ValueConversion(typeof(bool), typeof(bool))]
public class InverseBoolConverter: IValueConverter {
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
if(value is bool)
return !(bool)value;
else
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
if(value is bool)
return !(bool)value;
else
return null;
}
}

Related

Change a Path's Fill color depending on a bool property

I have a DataTemplate with a Path in a ResourceDictionary and I want to change the Fill color for the Path (would choose between two colors) depending on a bool property from the viewmodel.
<DataTemplate x:Key="FileIcon">
<Path Data="M20.8573547,8.0085467..." Fill="#F0F1F3" Width="30" Height="30"/>
</DataTemplate>
I presume, I need to use some converter but not sure how to write the XAML code for it. Something like this?
<Path Fill="{Binding MyBoolProperty, RelativeSource={RelativeSource FindAncestor, AncestorType=Path}, Converter={StaticResource BoolToColorConverter}}"/>
The Path isn't an ancestor of itself. If the viewmodel is the Path's DataContext, a conventional binding should suffice:
<Path
Fill="{Binding MyBoolProperty, Converter={StaticResource BoolToColorHiddenConverter}}"
/>
You could also skip the converter and use a Style trigger. Note that the default Fill is no longer set as an attribute in this version; if it is, then it'll override anything the Style does.
<Path
Data="M20.8573547,8.0085467..."
Width="30"
Height="30"
>
<Path.Style>
<Style TargetType="Path">
<Setter Property="Fill" Value="#F0F1F3" />
<Style.Triggers>
<DataTrigger
Binding="{Binding MyBoolProperty}"
Value="True"
>
<Setter Property="Fill" Value="FluorescentBeige" />
</DataTrigger>
</Style.Triggers>
</Style>
</Path.Style>
</Path>
If you want to use a converter, you can follow this example code for making one:
1. Make a new class
2. Use the following namespaces:
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
3. Inherit and implement the IValueConverter Interface
4. In the Convert function, evaluate the value parameter and return the corresponding color you want
Example Code
class BoolToColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if ((bool)value == true)
{
// return the color you want
}
else
{
// return the color you want
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

Setting style based on existence of an ancestor type

I have 2 sets of TextBlocks some of them are in an ItemControl and some of them are not.
I want to make a style (just based on type) which sets the background of the TextBlock if its ancestor is an ItemControl.
I can do it using the following code but my problem is that on the log (and output window) a data biding error message is displayed because of the TextBlocks which do not have ItemControl as their ancestor.
Is there a better way to do this task and avoid this error message?
<Grid>
<Grid.Resources>
<local:HasAncestorConverter x:Key="HasAncestorConverter" />
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger
Binding="{Binding RelativeSource={RelativeSource
AncestorType={x:Type ItemsControl}},
Converter={StaticResource HasAncestorConverter}}" Value="True">
<Setter Property="Background"
Value="{Binding Tag,
RelativeSource={RelativeSource
AncestorType={x:Type ItemsControl}}}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<StackPanel>
<TextBlock Text="Out of ItemControl" />
<ItemsControl Tag="Blue" >
<TextBlock Text="Inside of ItemControl" />
</ItemsControl>
</StackPanel>
</Grid>
Convertor:
class HasAncestorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
return value != null;
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Error message:
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.ItemsControl', AncestorLevel='1''. BindingExpression:Path=; DataItem=null; target element is 'TextBlock' (Name=''); target property is 'NoTarget' (type 'Object')
I think #Xameli solution is what you are actually looking for...
but if you simply must do it in a style then you can achieve it using VisualTreeHelper like that:
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}, Converter={StaticResource HasAncestorConverter}}" Value="True">
<Setter Property="Background"
Value="{Binding Tag,RelativeSource={RelativeSource Self}}" />
</DataTrigger>
</Style.Triggers>
the converter:
class HasAncestorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
//you probably will have to look a few levels up
var parent = VisualTreeHelper.GetParent(value) as ItemsControl;
return item != null;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
According to #makc's response I solved the problem this way:
<Grid>
<Grid.Resources>
<local:HasAncestorConverter x:Key="HasAncestorConverter" />
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger
Binding="{Binding RelativeSource={RelativeSource
AncestorType={x:Type ItemsControl}},
Converter={StaticResource HasAncestorConverter}}" Value="True">
<Setter Property="Background"
Value="{Binding Tag,
RelativeSource={RelativeSource
AncestorType={x:Type ItemsControl}}}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<StackPanel>
<TextBlock Text="Out of ItemControl" />
<ItemsControl Tag="Blue" >
<TextBlock Text="Inside of ItemControl" />
</ItemsControl>
</StackPanel>
</Grid>
Converter:
class HasAncestorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter
, System.Globalization.CultureInfo culture)
{
object parent = null;
if (value != null && parameter != null &&
parameter is Type && value is DependencyObject)
{
var control = value as DependencyObject;
Type t = parameter as Type;
parent = ParentFinder.FindParent(control, t);
}
return parent != null;
}
public object ConvertBack(object value, Type targetType, object parameter
, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Helper class for finding the parent of specific type:
Note: This helper find any kind of parent in logical or visual tree. for example in my case ItemsControl is a parent in the logical tree, and it can be a grandparent.
class ParentFinder
{
public static object FindParent(DependencyObject child, Type parentType)
{
object parent = null;
var logicalParent = LogicalTreeHelper.GetParent(child);
var visualParent = VisualTreeHelper.GetParent(child);
if (!(logicalParent == null && visualParent == null))
{
if (logicalParent != null && logicalParent.GetType() == parentType)
parent = logicalParent;
else if (visualParent != null && visualParent.GetType() == parentType)
parent = visualParent;
else
{
if (visualParent != null)
parent = FindParent(visualParent, parentType);
if (parent == null && logicalParent != null)
parent = FindParent(logicalParent, parentType);
}
}
return parent;
}
}
Use DataTemplate for the items in ItemsControl.
<ItemsControl ....
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding }"
Background="{Binding Tag,
RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<sys:String>Inside of ItemControl</String>
</ItemsControl>
Keep the style that you have if you need it for other setters, just remove the trigger.
You can work with FallbackValue or TargetNullValue
Check this link out:
http://dontcodetired.com/blog/post/FallbackValue-TargetNullValue-StringFormat-in-Silverlight-4.aspx

DataGrid with templated RowHeader and ColumnHeader in Silverlight

How to create a grid with a templated RowHeader, ColumnHeader, and Cells?
The ViewModel can have a list of objects for RowHeader item display, a list of objects for ColumnHeader item display. Basically think of it like a matrix display.
Probably a ControlTemplate needs to be written, but running out of ideas. There's not much of a documentation on this functionality.
Any Ideas?
You can create a style for a DataGridColumnHeader or a DataGridRowHeader and set the ContentTemplate to a DataTemplate which allows the Header property to be bound. For this you need an IValueConverter which enables the binding.
The headers are in the Controls.Primitives namespace:
xmlns:dp="clr-namespace:System.Windows.Controls.Primitives;assembly=System.Windows.Controls.Data"
Style:
<Style TargetType="dp:DataGridColumnHeader" >
<Setter Property="ContentTemplate" >
<Setter.Value>
<DataTemplate>
<ContentPresenter Content="{Binding Converter={StaticResource vcBC}}" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
Converter:
public class BindingConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value.GetType().Name == "Binding")
{
ContentControl cc = new ContentControl();
cc.SetBinding(ContentControl.ContentProperty, value as Binding);
return cc;
}
else return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
Converter instance:
<yourassembly:BindingConverter x:Key="vcBC"/>

Turn off and on column visibility

I have a DataGrid with many columns and I'd like to provide the users with a drop down that allows them to select which columns they can see. I'm using the .Net 4 WPF DataGrid in a desktop application.
Does anyone know of an easy way to accomplish what I am trying to do.
I do this as follows.
I derive from the grid an add an ICommand called HideShowColumnCommand that takes as its parameter a DataGridColumn (the one I want to hide or show) and hides the column if it is visible, and shows it if it is not.
Then I use a tricky context menu that I attach to the column header that has a tick that shows the column visible/hidden state..
The context menu looks like so
<ContextMenu
ItemsSource="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Column.DataGridOwner.Columns}">
<ContextMenu.Resources>
<local:DataGridHeaderVisibilityToBooleanConverter
x:Key="visibilityConverter" />
<BooleanToVisibilityConverter
x:Key="VisibilityOfBool" />
<DataTemplate
DataType="{x:Type DataGridColumn}">
<ContentPresenter
Content="{Binding Path=Header}"
RecognizesAccessKey="True" />
</DataTemplate>
</ContextMenu.Resources>
<ContextMenu.ItemContainerStyle>
<Style
TargetType="MenuItem">
<!--Warning dont change the order of the following two setters
otherwise the command parameter gets set after the command fires,
not much use eh?-->
<Setter
Property="CommandParameter"
Value="{Binding Path=.}" />
<Setter
Property="Command"
Value="{Binding Path=DataGridOwner.HideShowColumnCommand}" />
<Setter
Property="IsChecked"
Value="{Binding Path=Visibility, Converter={StaticResource visibilityConverter}}" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
The converter like this
public class DataGridHeaderVisibilityToBooleanConverter :IValueConverter{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
try {
Visibility visibility = (Visibility)value;
if (visibility == Visibility.Visible) {
return true;
}
else {
return false;
}
}
catch { }
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
throw new NotImplementedException();
}
#endregion
}

WPF TextBlock Negative Number In Red

I am trying to figure out the best way to create a style/trigger to set foreground to Red, when value is < 0. what is the best way to do this? I'm assuming DataTrigger, but how can I check for negative value, do i have to create my own IValueConverter?
If you are not using an MVVM model (where you may have a ForegroundColor property), then the easiest thing to do is to create a new IValueConverter, binding your background to your value.
In MyWindow.xaml:
<Window ...
xmlns:local="clr-namespace:MyLocalNamespace">
<Window.Resources>
<local:ValueToForegroundColorConverter x:Key="valueToForeground" />
<Window.Resources>
<TextBlock Text="{Binding MyValue}"
Foreground="{Binding MyValue, Converter={StaticResource valueToForeground}}" />
</Window>
ValueToForegroundColorConverter.cs
using System;
using System.Windows.Media;
using System.Windows.Data;
namespace MyLocalNamespace
{
class ValueToForegroundColorConverter: IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
SolidColorBrush brush = new SolidColorBrush(Colors.Black);
Double doubleValue = 0.0;
Double.TryParse(value.ToString(), out doubleValue);
if (doubleValue < 0)
brush = new SolidColorBrush(Colors.Red);
return brush;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
}
You should have your view specific information in your ViewModel. But you can get rid of the Style specific information in the ViewModel.
Hence create a property in the ViewModel which would return a bool value
public bool IsMyValueNegative { get { return (MyValue < 0); } }
And use it in a DataTrigger so that you can eliminate the ValueConverter and its boxing/unboxing.
<TextBlock Text="{Binding MyValue}">
<TextBlock.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding IsMyValueNegative}" Value="True">
<Setter Property="Foreground" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
For Amsakanna's solution I had to add a class name to the Property Setter:
<Setter Property="TextBlock.Foreground" Value="Red" />

Resources