This example shows how that is achieved with three examples.
The main trick here is to use a MultiValueConverter and a MultiBinding, but where it is defined in resources is very important.
Example 1
The first example passes in a bound value from the code-behind and the control itself through a MultiBinding, to the MultiValueConverter.
<
TextBlock
x:Name
=
"tb1"
Margin
=
"20"
>
<
TextBlock.Text
>
<
MultiBinding
Converter
=
"{StaticResource MyConverter}"
>
<
Binding
RelativeSource
=
"{RelativeSource Self}"
/>
<
Binding
Path
=
"MyValue"
/>
</
MultiBinding
>
</
TextBlock.Text
>
</
TextBlock
>
The main thing to note here is the RelativeSource of the first binding, being {RelativeSource Self}.
This will pass the control itself into the converter.
Here is the converter:
public
class
MyConverter : IMultiValueConverter
{
FrameworkElement myControl;
object
theValue;
public
object
Convert(
object
[] values, System.Type targetType,
object
parameter, System.Globalization.CultureInfo culture)
{
myControl = values[0]
as
FrameworkElement;
theValue = values[1];
return
myControl.Name +
" : "
+ values[1];
}
public
object
[] ConvertBack(
object
value, System.Type[] targetTypes,
object
parameter, System.Globalization.CultureInfo culture)
{
return
new
object
[] {
null
, myControl.Name +
" : "
+ theValue };
}
}
The Convert method just takes the first value and converts it into a FrameworkElement, so it can take the control's name (proving the control was passed in correctly) which it joins to the second parameter. It also keeps a local copy of the values for the ConvertBack method discussed later in example 3.
Notice the values array in MultiValueConverters order the bidings by the same order they are shown in the markup code.
Example 2
The second example is similar in that it sets a property of UserControl1, but this example includes a couple of extras.
Firstly you will notice the DependancyPropertyChangedCallback parameter of the
DependancyProperty constructor is used. Any changes to the MyUserControlvalue property will cause a call to MyUserControlValueChanged. In the example, this method takes the new value (e.NewValue) and adds it to a datestamp into the third textbox of UserControl1.
Also notice the control itself was passed in, this is where you can act on the specific object instance.
public
static
readonly
DependencyProperty MyUserControlValueProperty =
DependencyProperty.Register(
"MyUserControlValue"
,
typeof
(
string
),
typeof
(UserControl1),
new
UIPropertyMetadata(
null
, MyUserControlValueChanged));
static
void
MyUserControlValueChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
{
var uc1 = dp
as
UserControl1;
uc1.MyChangedDateString = e.NewValue +
" - "
+ DateTime.Now.ToString();
}
This user control is used just the same as the previous example:This user control is used just the same as the previous example:This user control is used just the same as the previous example:
<local:UserControl1 x:Name=
"uc1"
Background=
"Yellow"
>
<local:UserControl1.MyUserControlValue>
<MultiBinding Converter=
"{StaticResource MyConverter2}"
>
<Binding RelativeSource=
"{RelativeSource Self}"
/>
<Binding Path=
"MyValue"
/>
</MultiBinding>
</local:UserControl1.MyUserControlValue>
</local:UserControl1>
However the converter in this example shows how once you have the control, the converter can safely do stuff on it like calling it's methods.
public
class
MyConverter2 : IMultiValueConverter
{
public
object
Convert(
object
[] values, System.Type targetType,
object
parameter, System.Globalization.CultureInfo culture)
{
var uc = values[0]
as
UserControl1;
uc.MyShowTextMethod(
"Set from converter"
);
return
uc.Name +
" : "
+ values[1];
}
public
object
[] ConvertBack(
object
value, System.Type[] targetTypes,
object
parameter, System.Globalization.CultureInfo culture)
{
throw
new
System.NotImplementedException();
}
}
Example 3
This is in response to a follow-up question where the poster wanted to use the passed in control in the ConvertBack method. As shown above, the passed in values are kept for use with the ConvertBack method.
It is important to understand how and WHERE you refer to the Converter, not as one StaticResource, but as several localised resources. If it is defined in the top Window/Page level resources, all controls will use the same converter instance, so any passed in values will be written over by the next control. When you try to convert back, you will get whatever the last control to use Convert was.
Instead, you have to define the static resource at CONTROL LEVEL, so only that control uses THAT instance of the converter.This is an important concept to know.
<TextBox x:Name=
"tb1"
Margin=
"20"
>
<TextBox.Resources>
<local:MyConverter x:Key=
"MyConverter"
/>
</TextBox.Resources>
<TextBox.Text>
<MultiBinding Converter=
"{StaticResource MyConverter}"
UpdateSourceTrigger=
"PropertyChanged"
>
<Binding RelativeSource=
"{RelativeSource Self}"
Mode=
"OneTime"
/>
<Binding Path=
"MyValue"
/>
</MultiBinding>
</TextBox.Text>
</TextBox>
<TextBox x:Name=
"tb2"
Margin=
"20"
>
<TextBox.Resources>
<local:MyConverter x:Key=
"MyConverter"
/>
</TextBox.Resources>
<TextBox.Text>
<MultiBinding Converter=
"{StaticResource MyConverter}"
UpdateSourceTrigger=
"PropertyChanged"
>
<Binding RelativeSource=
"{RelativeSource Self}"
Mode=
"OneTime"
/>
<Binding Path=
"MyValue2"
/>
</MultiBinding>
</TextBox.Text>
</TextBox>
This available in a demo project here.
This small article is part of a series of WPF "How To" articles, in response to real user questions on the MSDN WPF Forum.