The following code binds a GradientStop to the Background.Color property of TemplatedParent. Everything works but I am getting a binding error in the output window:
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=Background.Color; DataItem=null; target element is 'GradientStop' (HashCode=6944299); target property is 'Color' (type 'Color')
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="WpfBindingTest.MainWindow"
x:Name="Window"
Title="MainWindow"
Width="100" Height="100">
<Window.Resources>
<ControlTemplate x:Key="GradientTemplate" TargetType="{x:Type ContentControl}">
<Border BorderThickness="1" BorderBrush="{TemplateBinding Background}">
<Border.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="{Binding Path=Background.Color,
RelativeSource={RelativeSource TemplatedParent}}" Offset="1"/>
<GradientStop Color="White" Offset="0"/>
</LinearGradientBrush>
</Border.Background>
<ContentPresenter/>
</Border>
</ControlTemplate>
</Window.Resources>
<Grid x:Name="LayoutRoot">
<ContentControl Background="Green" Template="{StaticResource GradientTemplate}" >
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" Text="X" />
</ContentControl>
</Grid>
</Window>
I also had the same error in the Visual Studio console output.
A possible explanation and workaround for this is reported here
Basically if you use a Converter that returns a LinearGradientBrush then you don't get the error
The code is something like this
[ValueConversion(typeof(System.Windows.Media.Color), typeof(LinearGradientBrush))]
class GradientConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var brush = new LinearGradientBrush();
var color = (Color)value;
brush.StartPoint = new Point(0.5, 0);
brush.EndPoint = new Point(0.5, 1);
brush.GradientStops.Add(new GradientStop(Colors.White, 0));
brush.GradientStops.Add(new GradientStop((Color)value, 1));
return brush;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
And in the XAML
<Border BorderThickness="1" BorderBrush="{TemplateBinding Background}" Background="{Binding Path=Background.Color, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource gradConv}}">
Related
I have a problem attempting to follow the advice in:
How to bind inverse boolean properties in WPF?
When I use with ResourceDictionary, it give run-time error. InverseBooleanConverter not found.
XMAL as follows:
<UserControl x:Class="SMTF.MasterDataView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SMTF" mc:Ignorable="d" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" d:DesignHeight="466" d:DesignWidth="483">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../AppResource.xaml" />
<ResourceDictionary Source="../DefaultStyle.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<StackPanel HorizontalAlignment="Left" Margin="200,12,0,0" Name="stkMain" VerticalAlignment="Top" >
<Grid Margin="4">
<ContentControl Visibility="{Binding IsChecked, ElementName=VisibilityToggle, Converter={StaticResource InverseBooleanConverter}}" >
<Border Grid.Column="2" Style="{StaticResource MainBorderStyle}">
<HeaderedContentControl Content="{Binding Path=WorkspaceView}" ContentTemplate="{StaticResource WorkspacesTemplate}" Header="View" Style="{StaticResource MainHCCStyle}"/>
</Border>
</ContentControl>
</Grid>
<Grid DockPanel.Dock="Bottom" Margin="0,2,4,2">
<TextBlock HorizontalAlignment="Right">
<ToggleButton x:Name="VisibilityToggle" Focusable="False" Style="{StaticResource SMToggle}" Command ="{Binding ShowNew}" >
</ToggleButton>
<!--<ToggleButton x:Name="VisibilityToggle" Background="Transparent" Command ="{Binding ShowNew}" >
<Image Source="/Image/Add.png" Width="24" />
</ToggleButton>-->
</TextBlock>
</Grid>
<Grid Margin="4">
<ContentControl Visibility="{Binding IsChecked, ElementName=VisibilityToggle, Converter={StaticResource BoolToVisibility}}" >
<Border Grid.Column="2" Style="{StaticResource MainBorderStyle}">
<HeaderedContentControl Content="{Binding Path=WorkspaceEdit}" ContentTemplate="{StaticResource WorkspacesTemplate}" Header="Configure" Style="{StaticResource MainHCCStyle}"/>
</Border>
</ContentControl>
</Grid>
</StackPanel>
</Grid>
</UserControl>
I'm using the same code provided in the link.
ie:
[ValueConversion(typeof(bool), typeof(bool))]
public class InverseBooleanConverter: IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if (targetType != typeof(bool))
throw new InvalidOperationException("The target must be a boolean");
return !(bool)value;
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
#endregion
}
in the AppResource XML
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:SMTF">
<vm:InverseBooleanConverter x:Key="InverseBoolToVisibility" />
.....
.....
</ResourceDictionary>
Thanks in advance
NS
An alternative to converters for style related binding is to use Style.Triggers, the following shows a canvas when checkbox IsChecked = false, which would otherwise require an InverseBooleanConverter.
<Canvas x:Name="Overlay">
<Canvas.Style>
<Style TargetType="Canvas">
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, ElementName=MyCheckbox}" Value="True">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
<DataTrigger Binding="{Binding IsChecked, ElementName=MyCheckbox}" Value="False">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Canvas.Style>
<Rectangle Canvas.ZIndex="3" Fill="#99333333" Height="25" Stroke="Transparent" Width="293" Canvas.Left="10" Canvas.Top="-25"/>
</Canvas>
The key you are using is not correct. You resource key is InverseBoolToVisibility, while you have used InverseBooleanConverter as key.
Change the resource key to refer to correct resource as
<ContentControl Visibility="{Binding IsChecked, ElementName=VisibilityToggle, Converter={StaticResource InverseBoolToVisibility}}" >
Also your implementation for convereter is wrong. If you want to change the Visibility based on Boolean inverse value update your converter code as:
public class InverseBooleanConverter: IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if (targetType != typeof(Visibility))
throw new InvalidOperationException("The target must be a boolean");
if(!(bool)value)
{
return Visibility.Visible;
}
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
#endregion
}
I'm trying to build a Chart, with Columns. The columns will have different colors by value.
I'm using MVVM with WPF and Databinding.
How i'm trying to do (Into my ViewModel):
private Int16 colorValue;
public Int16 ColorValue
{
get { return colorValue; }
set
{
colorValue = value;
if (ColorValue < 20)
ColorType = new SolidColorBrush { Color = Colors.Aqua };
if (ColorValue < 40)
ColorType = new SolidColorBrush { Color = Colors.Gray };
if (ColorValue >= 41)
ColorType = new SolidColorBrush { Color = Colors.Black };
}
}
private Brush colorType;
public Brush ColorType
{
get { return colorType; }
set
{
if (value != null)
{
colorType = value;
OnPropertyChanged("ColorType");
}
}
}
Into My Xaml (This is the Static Resource to change the Column Color Attribute):
<Style x:Key="ColorByGradeColumn" TargetType="DVC:ColumnDataPoint">
<Setter Property="Background" Value="DarkGray"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate
TargetType="DVC:ColumnDataPoint">
<Border
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid Background="{Binding ColorType}">
<Rectangle>
<Rectangle.Fill>
<LinearGradientBrush>
<GradientStop Color="#77ffffff" Offset="0"/>
<GradientStop Color="#00ffffff" Offset="1"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<Border BorderBrush="#ccffffff" BorderThickness="1">
<Border BorderBrush="#77ffffff" BorderThickness="1"/>
</Border>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
My Chart into Xaml:
<Grid Grid.Column="2" Height="368" HorizontalAlignment="Left" Name="grid1" VerticalAlignment="Bottom" Width="1009" Grid.ColumnSpan="4" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="198*" />
<ColumnDefinition Width="191*" />
</Grid.ColumnDefinitions>
<DVC:Chart x:Name="ColumnChart"
Grid.ColumnSpan="2">
<DVC:ColumnSeries
AnimationSequence="FirstToLast"
FlowDirection="LeftToRight"
Title="Largura"
ItemsSource="{Binding Path=Placas, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ToolTip="{Binding Path=Slab.InfThick}"
DependentValueBinding="{Binding Path=Slab.InfThick}"
IndependentValueBinding="{Binding Path=Slab.SlabId}"
DataPointStyle="{StaticResource ColorByGradeColumn}">
</DVC:ColumnSeries>
</DVC:Chart>
</Grid>
So... My Chart using ColumnSeries get it's attribute by the Static Resource defined into DataPointStyle. StaticResource 'ColorByGradeColumn' i've made a binding to my property ColorType.
Here's the question... Why isn't working? I've followed the steps explained in this link:
Columns of a different color [Customizing the appearance of Silverlight charts with re-templating and MVVM]
And I really don't know what i'm missing.
Thanks in advance.
Maybe I'm misunderstanding, but aren't you covering your grid background colour with a fixed gradient fill?
Ok, we've figured out how to fix that annoying thing:
We've created a converter than will receive the value, and returns the color than we wanted. Before, I was trying to do with a Property:
#region Converters
/// <summary>
/// Retorna a cor do estado da placa
/// </summary>
public class RetornaCorEstadoBarra : DependencyObject, IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
try
{
var ColorValue = (Int32)value;
if (ColorValue < 800)
return "Aqua";
else if (ColorValue < 1000)
return "Gray";
else //if (ColorValue > 1001)
return "Black";
}
catch
{
return "Black";
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
#endregion
Created a Resource inside xaml file:
<vm:RetornaCorEstadoBarra x:Key="RetornaCorEstadoBarra" />
And created a Style a little below that Resource:
<Style x:Key="ColorByGradeColumn" TargetType="DVC:ColumnDataPoint">
<Setter Property="Template" >
<Setter.Value>
<ControlTemplate TargetType="{x:Type DVC:ColumnDataPoint}">
<Border
BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"
Background="{Binding Slab.InfThick,Converter={StaticResource RetornaCorEstadoBarra}}"
>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Ok, now's the catch:
<DVC:Chart x:Name="ColumnChart" Grid.ColumnSpan="2" Width="{Binding Path=GridWidthSize}" >
<DVC:ColumnSeries
AnimationSequence="FirstToLast" FlowDirection="LeftToRight" Title="Largura" ItemsSource="{Binding Path=Placas, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ToolTip="{Binding Path=Slab.SlabId}" DependentValueBinding="{Binding Path=Slab.InfThick}" IndependentValueBinding="{Binding Path=Slab.SlabId}"
DataPointStyle="{StaticResource ColorByGradeColumn}">
<DVC:ColumnSeries.IndependentAxis>
<DVC:CategoryAxis Orientation="X" Visibility="Visible" Foreground="Transparent"/>
</DVC:ColumnSeries.IndependentAxis>
</DVC:ColumnSeries>
</DVC:Chart>
</ScrollViewer>
Well, the problem is, the color into the Chart is a Static Resource. It doesn't change a second time. So, with a 'improvised' dynamic resource, the problem is fixed, right here:
Background="{Binding Slab.InfThick,Converter={StaticResource RetornaCorEstadoBarra}}"
We pass the parameter to that Converter RetornaCorEstadoBarra. It will receive the parameter, and return a color value. Then, inside my Chart, than Binds to my Resource, will populate the Chart with the value received by my Converter. But the Chart only gets the value once. However, my converter will always return a value when he receive a value. That's the catch.
Thanks for the help :)
I have a project in which i would like to be able to have a tooltip on some control that would incoporate some controls like textbox and datepicker. The idea would be to have some sort of a popup window with limited graphic but some control wo interact.
I know how to add a 'normal' tooltip to a control, but when you move, the tooltip disapear so I can't interact with it.
is this possible? If so how and if not, is there any alternative to this ?
Thanks
You should use a Popup instead of a ToolTip
Example. A Popup is opened when the mouse moves over the TextBox and stays open as long as the mouse is over the TextBox or the Popup
<TextBox Name="textBox"
Text="Popup On Mouse Over"
HorizontalAlignment="Left"/>
<Popup PlacementTarget="{Binding ElementName=textBox}"
Placement="Bottom">
<Popup.IsOpen>
<MultiBinding Mode="OneWay" Converter="{StaticResource BooleanOrConverter}">
<Binding Mode="OneWay" ElementName="textBox" Path="IsMouseOver"/>
<Binding RelativeSource="{RelativeSource Self}" Path="IsMouseOver" />
</MultiBinding>
</Popup.IsOpen>
<StackPanel>
<TextBox Text="Some Text.."/>
<DatePicker/>
</StackPanel>
</Popup>
Is uses a BooleanOrConverter
public class BooleanOrConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
foreach (object booleanValue in values)
{
if (booleanValue is bool == false)
{
throw new ApplicationException("BooleanOrConverter only accepts boolean as datatype");
}
if ((bool)booleanValue == true)
{
return true;
}
}
return false;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}
Update
To do this for a cell in DataGrid you have a few options. Two of them are to add a Popup inside the DataTemplates for DataGridTemplateColumn, or you can add it to the DataGridCell Template. Here is an example of the later. It will require you to set SelectionMode="Single" and SelectionUnit="Cell" on the DataGrid
<DataGrid SelectionMode="Single"
SelectionUnit="Cell"
...>
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridCell}">
<Grid>
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
<ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
<Popup PlacementTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}"
Placement="Right"
IsOpen="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsSelected}">
<StackPanel>
<TextBox Text="Some Text.."/>
<DatePicker/>
</StackPanel>
</Popup>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.CellStyle>
<!--...-->
</DataGrid>
<UserControl x:Class="WpfApplication2.ProgressBar"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<ProgressBar Minimum="0" Maximum="1" Value="0.5" LargeChange="0.1" SmallChange="0.01" Margin="2,2,12,2" Height="22">
<ProgressBar.Template>
<ControlTemplate>
<Border BorderThickness="2" BorderBrush="Black">
<Rectangle>
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0">
<LinearGradientBrush.EndPoint>
<Point Y="0" X="{Binding RelativeSource={RelativeSource AncestorType={x:Type ProgressBar}}, Path=ProgressBar.Value}"/>
</LinearGradientBrush.EndPoint>
<GradientStop Color="Transparent" Offset="1.01"/>
<GradientStop Color="#FF0000" Offset="1.0"/>
<GradientStop Color="#FFFF00" Offset="0.50"/>
<GradientStop Color="#00FF00" Offset="0.0"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
</Border>
</ControlTemplate>
</ProgressBar.Template>
</ProgressBar>
<TextBlock Text="50%" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</UserControl>
I get error: "A 'Binding' cannot be set on the 'X' property of type 'Point'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject."
Is there any clean workaround?
Since Point.X isn't a Dependency Property you can't bind it to something. You could bind the EndPointProperty though, and use a Converter that creates the Point for you. It could take the Y value as parameter for example
Xaml
<LinearGradientBrush.EndPoint>
<Binding RelativeSource="{RelativeSource AncestorType={x:Type ProgressBar}}"
Path="Value"
Converter="{StaticResource PointXConverter}"
ConverterParameter="0"/>
</LinearGradientBrush.EndPoint>
PointXConverter
public class PointXConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double progressBarValue = (double)value;
double yValue = System.Convert.ToDouble(parameter);
return new Point(progressBarValue, yValue);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Note: Probably not related to your question but if you would need to bind Y as well, you can use a MultiBinding like this
<LinearGradientBrush.EndPoint>
<MultiBinding Converter="{StaticResource PointConverter}">
<Binding RelativeSource="{RelativeSource AncestorType={x:Type ProgressBar}}"
Path="Value"/>
<Binding RelativeSource="{RelativeSource AncestorType={x:Type ProgressBar}}"
Path="Value"/>
</MultiBinding>
</LinearGradientBrush.EndPoint>
PointConverter
public class PointConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double xValue = (double)values[0];
double yValue = (double)values[1];
return new Point(xValue, yValue);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
I've defined a control Template for a progressbar to make it look like a thermometer ... now i want it to change its color when reaching a certain value (for example .. when the progressbar has the value 70, its color should change to yellow)
Currently the color of PART_Indicator is bound to the background color of the progressbar .. the backgroundcolor is changed in the ValueChanged Eventhandler, and so the color of the indicator changes too... is there a possibility to do this within the template only, so i dont need to use the ValueChanged Eventhandler?
<ControlTemplate x:Key="myThermometer" TargetType="{x:Type ProgressBar}">
<ControlTemplate.Resources>
<RadialGradientBrush x:Key="brushBowl" GradientOrigin="0.5 0.5">
<GradientStop Offset="0" Color="Pink" />
<GradientStop Offset="1" Color="Red" />
</RadialGradientBrush>
</ControlTemplate.Resources>
<Canvas>
<Path Name="PART_Track" Stroke="Black" StrokeThickness="5" Grid.Column="0">
<Path.Data>
<CombinedGeometry GeometryCombineMode="Union">
<CombinedGeometry.Geometry1>
<RectangleGeometry Rect="10,40,130,20" RadiusX="5" RadiusY="5"/>
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<EllipseGeometry Center="10,50" RadiusX="20" RadiusY="20"/>
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</Path.Data>
</Path>
<Path Fill="{TemplateBinding Background}">
<Path.Data>
<EllipseGeometry Center="10,50" RadiusX="17" RadiusY="17"/>
</Path.Data>
</Path>
<Path Name="PART_Indicator" Fill="{TemplateBinding Background}" Grid.Column="0">
<Path.Data>
<RectangleGeometry Rect="22,43,115,15" RadiusX="5" RadiusY="5"/>
</Path.Data>
</Path>
<Canvas Canvas.Top="35" Canvas.Right="375">
<Canvas.RenderTransform>
<RotateTransform CenterX="120" CenterY="120" Angle="-270" />
</Canvas.RenderTransform>
<TextBlock FontWeight="Bold" FontSize="16" Foreground="Black" Text="{TemplateBinding Tag}"/>
</Canvas>
</Canvas>
</ControlTemplate>
<ProgressBar x:Name="progressBar" Background="{Binding RelativeSource={RelativeSource Self},Path=Value,Converter={StaticResource IntToBrushConverter}}" />
public class IntToBrushConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double val = (double)value;
if (val > 50)
return Brushes.Blue;
else
return Brushes.Green;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new Exception("Not Implemented");
}
#endregion
}
more or less - I'd write a custom ValueConverter that converts your progress value to a Color and bind the fill of the progress bar using this converter.
XAML:
Fill="{TemplateBinding Progress,
Converter={StaticResource ProgressToColorConverter}}"
Code:
[ValueConversion(typeof(int), typeof(Color))]
public class ProgressToColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
int progress = (int)value;
if (progress < 60)
return Color.Green;
else
return Color.Red;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
This code is just from the top of my head and hasn't been tested, but it should show the principles.