Right now, I'm learning WPF. Can we change another WPF object's property when an WPF object's property is changed?
Below is simplified scenario.
I have a Window with a TextBox named m_Text and a ToggleButton named m_Button. I want to change the m_Text.Background property if m_Button is pressed, that is m_Button.IsChecked = true. I think it's possible using a Trigger but I don't know how to do it.
P.S. If possible, I want to do it only in XAML.
WPF makes this really easy - you can databind the TextBox's Background property directly to the IsChecked property on the ToggleButton. Of course, you will need to convert the IsChecked (boolean) to be a Brush, but WPF allows you to specify a Converter object right in the binding...
In code, you create an object that implements IValueConverter, and implement the Convert method, like
public class BoolToBrushConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool isChecked = (bool)value;
string[] colours = parameter.ToString().Split(':');
if (isChecked)
return new BrushConverter().ConvertFromString(colours[0]);
return new BrushConverter().ConvertFromString(colours[1]);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
then in xaml you need to add the namespace containing this class, declare an instance of the converter as a resource within the window, then use it in the Binding... it should look something like this:
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<local:BoolToBrushConverter x:Key="boolToBrushConverter" />
</Window.Resources>
<StackPanel Height="250">
<ToggleButton Name="toggleButton" Height="32" Content="Green" />
<TextBox
Background="{Binding ElementName=toggleButton, Path=IsChecked, Converter={StaticResource boolToBrushConverter}, ConverterParameter=Green:White}" />
</StackPanel>
</Window>
UPDATE: As per Ivan's excellent suggestion - have updated to show how you can pass parameters through to the Converter from the XAML...
Related
This question already has answers here:
Binding ConverterParameter
(3 answers)
Closed 27 days ago.
In the following xaml sample source, I am trying to bind the A property in the SampleViewModel to the B property, which is a DependencyProperty in the SampleConverter.
However, when I do this, I get a XAML bind error and the Data Context is displayed as null.
I know it is possible to get the Data Context using x:Name, but is it possible to get the Data Context without using x:Name?
<Window
x:Class="WpfApp1.BindPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApp1"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Window.DataContext>
<local:SampleViewModel />
</Window.DataContext>
<StackPanel>
<StackPanel.Height>
<Binding Path="ActualWidth" RelativeSource="{RelativeSource Mode=Self}">
<Binding.Converter>
<local:SampleConverter B="{Binding A}" />
</Binding.Converter>
</Binding>
</StackPanel.Height>
</StackPanel>
</Window>
I should mention that with RelativeSource I could not get other than myself (in this case, other than the SampleConverter).
That isn't how you use converters.
I don't know what local:SampleConverter is exactly but it has a property B
B="{Binding A}"
The binding provides the value, you may also bind a command parameter
Here's an example
<Image Name="GridImage"
Visibility="{Binding AppSettings.ShowGrid
, Converter={StaticResource BooleanToVisibilityConverter}}"
/>
This is going to set the Visibility property, the binding is to AppSetting.ShowGrid which is in the datacontext rather than converter. The BooleanToVibilityConverter is taking a bool from ShowGrid and converts it to a Visibility.
If you wanted to bind multiple properties then you can use a multiconverter with a multibinding.
<MultiBinding Converter="{ui:AllMultiplyMultiConverter}" StringFormat="{}{0:n0}">
<Binding Path="Value" ElementName="turnTime"/>
<Binding Path="MoveRate"/>
</MultiBinding>
A multiconverter receives an array object[] for those values.
Since markup extension and ivalueconverter are not dependency objects you would need to reference the parent object to use a dependency property.
You could add dependency properties to your window and reference them in a markup extension that's also a valueconverter.
Consider this value converter that's also a markup extension.
public class AddConverter : MarkupExtension, IValueConverter
{
public double ValueToAdd { get; set; }
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double bound = (Double)value;
return bound + ValueToAdd;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
ValueToAdd is just a property, you can't bind it.
You can get a reference in that ProvideValue to the parent dependency object. Hence window or usercontrol.
IProvideValueTarget target = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
That target is your control and you can grab dependency properties off that. You could (say) set ValueToAdd in there and use it in the converter. You could cast the datacontext of the targetobject and read values off properties directly.
This is a very complicated approach. I've never had the need for this myself and I would recommend multibinding and multiconverter instead.
I need to access the Logical Tree inside a converter. Actually this is inside a UserControl, which is (I think) only relevant insofar as this UserControl can live multiple times in my application.
I found a solution, but a rather crude one and my question is: Is there a better, more elegant solution.
Here is what I did. I added an arbitrary control ("Anchor") as a property to my converter. With that control I access the logical tree. In the example I grab the Tag property from the enclosing Grid and convert the value accordingly.
public class SomeConverter : IValueConverter
{
public System.Windows.Controls.Control Anchor { get; set; }
public object Convert(object value, Type t, object parameter, CultureInfo culture)
{
return toUpper() ? value.ToString().ToUpper() : value;
}
public object ConvertBack(object value, Type t, object parameter, CultureInfo culture)
{
return value;
}
private bool toUpper()
{
string tag = (Anchor.Parent as Grid).Tag as string;
return ! String.IsNullOrEmpty(tag);
}
}
So far so good. The real ugly part is how I assign the control to the property. I create an empty ContentControl and assign it to the converter definition. In order for the ContontControl to be in the logical tree I also need to instantiate is somewhere, which I do with Visibility=hidden. Here's the XAML:
<Window x:Class="WpfApp4__Various_Tests_.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp4__Various_Tests_"
Title="MainWindow" Height="100" Width="300">
<Window.Resources>
<ContentControl x:Key="anchor" Visibility="Hidden"/>
</Window.Resources>
<Grid Tag="toUpper">
<Grid.Resources>
<local:SomeConverter x:Key="SomeConverter" Anchor="{StaticResource anchor}"/>
</Grid.Resources>
<StaticResource ResourceKey="anchor" />
<TextBox
x:Name="textBox"
Text="{Binding SomeProperty, Converter={StaticResource SomeConverter}}"
/>
</Grid>
You can try using MultiBinding with IMultiValueConverter. There you can pass the target element using a binding.
I want to put a IValueConverter on a binding to the title of a window, so that changes when the active project changes. The problem is that the value converter is a static resource, which is only loaded a few lines later:
<Window x:Class="MyProject.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyProject"
Height="600" Width="800" VerticalAlignment="Stretch"
Title="{Binding ActiveProject, Converter={StaticResource windowTitleConverter}}, UpdateSourceTrigger=PropertyChanged">
<Window.Resources>
<local:MainWindowTitleConverter x:Key="windowTitleConverter"/>
</Window.Resources>
<!-- Rest of the design -->
</Window>
And then the definition of the converter:
public class MainWindowTitleConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null) return "Programme"; else return "Programme: " + (value as string);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
This crashes, presumably because the StaticResource hasn't been loaded yet (I can't think of any other reason), cause without the converter it works fine. However, I can't change the order. I tried to put it in a <Window.Title> tag, but anything that I put within that tag gives a compilation error. What is the proper way of doing this?
Just use the more verbose definitions
xmlns:System="clr-namespace:System;assembly=mscorlib"
...
<Window.Resources>
<local:MainWindowTitleConverter x:Key="windowTitleConverter"/>
...
</Window.Resources>
<Window.Title>
<Binding Path="ActiveProject">
<Binding.Converter>
<StaticResource ResourceKey="windowTitleConverter" />
</Binding.Converter>
</Binding>
</Window.Title>
I can't test this at the moment, but it should work.
The proper way would be to put the converter in your app.xaml.
I need to develop a Label control in WPF, on .NET 3.5 and VisualStudio 2010, in which the FontSize will automatically make the text fill the control area.
I don't know if I should create a CustomControl inheriting from Label or if I should create a UserControl which contains a Label control.
I've seen an example here using a ValueConverter, but I'm not understanding its behavior, here: change font size dynamically.
Can anyone give me a clue about that?
Update:
I found the solutiion using the DoubleConverter from the example I've posted before:
The soultion is using a ValueConverter, which I extracted from the example, but added the NumerFormat IFormatProvider to correctly parse "0.1" value, I found that at Decimal d1 = Decimal.Parse("0.1"); // = 1?!?:
[ValueConversion(typeof(object), typeof(double))]
public class DoubleConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
double dblValue = (double)value;
double scale = Double.Parse(((string)parameter), System.Globalization.CultureInfo.InvariantCulture.NumberFormat);
return dblValue * scale;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Then, you have to instantiate in XAML the DoubleConverter, and specify the binding in the FonSize Property:
<UserControl x:Class="<Namespace>.LabelAutoFontSize"
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:me="clr-namespace:<Namespace>"
mc:Ignorable="d"
d:DesignHeight="60" d:DesignWidth="278">
<UserControl.Resources>
<me:DoubleConverter x:Key="doubleConverter" />
</UserControl.Resources>
<Grid>
<Label
x:Name="lbl"
FontSize="{
Binding Path=Width,
RelativeSource={RelativeSource AncestorType={x:Type UserControl}},
Converter={StaticResource doubleConverter},
ConverterParameter=0.116}"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
Content="LabelAutoFontSize"
d:LayoutOverrides="Width"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" />
</Grid>
</UserControl>
An important point is that the value for ConverterParameter depends absolutely from the font assigned. Each font may need a different value and you have to "play around" to get the correct value to fit exactly.
<Viewbox>
<TextBlock>asd</TextBlock>
</Viewbox>
Also does the job.
i got some xaml here and what i m trying to do it's simply bind a property call Property (not the real name) on the width of a rectangle and to convert the value of this property with the converter name Conv and it's working perfectly with {TemplateBinding Property} or DataContext={TemplateBinding Property} or with a relative source (like in the code sample).
My problem is that the converterParameter should also be a binding property, but i m not able to bind any property in the converterParameter. So the 30 in the sample should be something like {Binding Path=SecondProperty}. If anyone got that problem or maybe if anyone got some other way to bind stuff in custom control thanks a lot ;)
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:RatingControl">
<Style TargetType="controls:Ctr">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:Ctr">
<Grid>
<Grid.Resources>
<controls:Converter x:Name="Conv" />
</Grid.Resources>
<Rectangle x:Name="rect" Width="{Binding Path=Property, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource Conv}, ConverterParameter=30}" Height="20" />
It doesn't look like that's possible: http://msdn.microsoft.com/en-us/library/system.windows.data.binding.converterparameter(VS.95).aspx
You can add a property to the Converter class and bind to that.
You can't bind to a property of the Binding object, since it isn't a DependencyProperty in fact Binding isn't a DependencyObject. This is understandable can you imagine the complexity of managing dependency trees and the possiblity of recursive or circular bindings in bindings.
However you could use a Specialised converter for the task:-
public class MySpecialConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Ctr obj = (Ctr)value;
var val = obj.Property;
var param = obj.SecondProperty;
// Do your intended code with val and param here.
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException("This converter only works for one way binding");
}
}
now your Xaml looks like:-
<Rectangle x:Name="rect" Height="20"
Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource Conv}" />
It's a really good solution but it's not working bcs my first property must be bind (twoWay) because if i got any change on it the converter must convert again the value so i get the result back and show the real result.