Problem with binding in converter in silverlight custom control - silverlight

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.

Related

How Binding Converter to a control's property can access other properties of the control

I am trying to bind a value "MaxLines" to the TextBlock's Height property in WP7 app. There is a converter to the binding which is supposed to multiple the LineHeight with the MaxLines and return the expected height. What I am trying to say is I want to control the number of lines being shown in the TextBlock. How will I be able to access the TextBlock's LineHeight property from the converter.
To make this generic I did not want maintain the LineHeights separately or access them from viewModel
Check out this article, Silverlight data binding and value converters, where he explains how to Databind in Silverlight. In the example he uses a ValueConverter with parametervalue.
I think that is what you need, just bind your LineHeight to the parameter. (You can use Blend for that)
You can use the ConverterParameter:
<TextBlock x:Name="MyTextBlock" Height="{Binding ConverterParameter=Height, ElementName=MyTextBlock, Converter={StaticResource SomeConverter}}" Text="{Binding SomeLongText}" />
or pass the whole textblock:
<TextBlock x:Name="MyTextBlock" Height="{Binding Converter={StaticResource ImageFileConverter}, ElementName=DropdownImage}" Text="{Binding SomeLongText}" />
Then inside the controller:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var image = value as TextBlock;
/*do your magic here*/
}

Set a property in XAML to a function

I need to set the property of cotrol which is a dependent of another property of its parent.
I try to explain better my problem with an example. I want to create a toggle switch button that animates a "slider" element into it. The dimensions of the toggle switch is being defined when the usercontrol is inserted into the application window. I want the slider be sized half larger than the switch case. So if the control is large 100, the slider should be 50, or if large 250, the slider should be 125. Then I need a sort of call to a function or something similar:
<UserControl>
<Border Name="switchCase" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Border Name="slider" Width="**Container.Width/2**" ></Border>
</Border>
</UserControl>
Is there any possibilities to achieve this ??
Thanks in advance
Paolo
Yes you need databinding with a converter, such as the following example
xmlns:conv="clr-namespace:MyConverters.Converters"
.......
<UserControl.Resources>
<conv:WidthConvertercs x:Key="widthConv"></conv:WidthConvertercs>
</UserControl.Resources>
<Border Name="switchCase" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" >
<Border Name="slider" Width="{Binding ElementName=switchCase, Path=ActualWidth, Converter={StaticResource widthConv}}" Background="DarkMagenta"></Border>
</Border>
Your converter class would be
[ValueConversion(typeof(double), typeof(double))]
class WidthConvertercs : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double withPar = (double)value;
return withPar/2.0;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
I hope this helps
Out of the box it's not supported by XAML. You can only bind to Properties.
You can write a converter which do the calculation (or you can use
MathConverter)
You can do the calculation in the code behind in event
handlers
If you are following the MVVM pattern you can do the
calculation in the ViewModel (altough it will introduce view
related concepts to the ViewModels which is not always good...)
You can write your own Binding extension

WPF: multiple controls binding to same property

Hello
I'm trying to change several controls' property according to some environment variables and i want to avoid creating a property for each control in the datacontext, so i thought using a converter which sets the property according to control name. Goal is to use one property for all controls:
<Grid.Resources>
<local:NameToStringConverter x:Key="conv" />
</Grid.Resources>
<TextBlock Name="FordPerfect"
Text="{Binding ElementName="FordPerfect" Path=Name, Converter={StaticResource conv}, Mode=OneWay}"/>
<TextBlock Name="Arthur"
Text="{Binding ElementName="Arthur" Path=Name, Converter={StaticResource conv}, Mode=OneWay}"/>
<TextBlock Name="ZaphodBeeblebrox"
Text="{Binding ElementName="ZaphodBeeblebrox" Path=Name, Converter={StaticResource conv}, Mode=OneWay}"/>
and ...
public class NameToStringConverter : IValueConverter
{
public object Convert(
object value, Type targetType,
object parameter, CultureInfo culture)
{
if (MyGlobalEnv.IsFlavor1 && ((string)value).Equals("ZaphodBeeblebrox")) return "42"
if (MyGlobalEnv.IsFlavor2 && ((string)value).Equals("ZaphodBeeblebrox")) return "43"
if (MyGlobalEnv.IsFlavor1 && ((string)value).Equals("Arthur")) return "44"
return "?";
}
public object ConvertBack(
object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotSupportedException("Cannot convert back");
}
}
I'm sure there's a better and more elegant way... Any ideas?
The point of oneway databinding is just to decouple UI (XAML) from code (CS). Here, your code and UI are tied so tightly together that trying to do this through databinding is really not buying you anything. You might simplify things by writing a method that takes the data value and applies it correctly to each control - still tightly coupled (bad) but at least the code is condensed and easy to follow (less bad).
What you should probably do though is not rely on the control name but define a ConverterParameter. See the bottom 1/3 of this article http://www.switchonthecode.com/tutorials/wpf-tutorial-binding-converters
You may bind directly to environment variable in your situation :
<Window xmlns:system="clr-namespace:System;assembly=mscorlib" ...>
<TextBlock Text="{Binding Source={x:Static system:Environment.OSVersion}}"/>

WPF Label adapt FontSize to it's Width and Height

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.

Change other's property

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...

Resources