I have custom control with some text in content template:
<ControlTemplate TargetType="{x:Type local:TouchScreenKey}">
<TextBlock><ContentPresenter Content="{TemplateBinding Title, Converter={StaticResource CaseConverter}}" /></TextBlock>
</ControlTemplate>
and custom IValueConverter CaseConverter - with property UpperCase. So, when UpperCase property of converter set to true it converts text to upper case on binding. Everything goes fine if I change UpperCase in markup. But if i change property in runtime - nothing happens - because changing converter property not force my control to rebind.
How can I rebind control which uses converter on converter's property change?
As far as I know there is no way to tell converter to update all targets. Converter knows nothing about targets. It's just a stateless function, F(x), takes one value and returns another.
To update property you should ask WPF to do so. For example, if property is bound to some source property, you can implement INotifyPropertyChanged, and trigger PropertyChanged event. Or you can ask BindingOperations to get binding expression, and invoke UpdateTarget() manually.
Maybe converter isn't the best choice here? You may also want to consider using Attached Properties to change capitalization.
It may help someone - I found solution - using multibinding
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center">
<ContentPresenter>
<ContentPresenter.Content>
<MultiBinding Converter="{StaticResource MultiCaseConverter}">
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Title" />
<Binding ElementName="TouchKeyboard" Path="UpperCase" />
</MultiBinding>
</ContentPresenter.Content>
</ContentPresenter>
and wrote MultiCaseConverter - which convert first parameter depending from second (UpperCase)
Related
In my WPF application (.Net 4.5) I want to provide extended visual feedback about validation results to the UI. The data layer's validation engine returns warnings and errors via INotifyDataErrorInfo interface.
I have the following XAML to display red or orange border depending on the error type and the list of error messages. Here errorToColor is a resource key of the value converter which returns red brush if there is at least one error in the Validation.Errors collection and orange brush if there are only warnings.
<TextBox Name="MappingName" Text="{Binding Path=Mapping.Name, NotifyOnValidationError=True}" >
<Validation.ErrorTemplate>
<ControlTemplate>
<DockPanel>
<Border BorderBrush="{Binding Converter={StaticResource errorsToColor}}" BorderThickness="1">
<AdornedElementPlaceholder />
</Border>
<ListView DisplayMemberPath="ErrorContent" ItemsSource="{Binding}" />
</DockPanel>
</ControlTemplate>
</Validation.ErrorTemplate>
</TextBox>
Now let's see what happens when I type some 'not valid' text in the TextBox.
Typed 'Text1' and changed focus.Debugger stepped into the converter and both validators resulting in two items in the ListView (1 error and 1 warning) and a red border. [OK]
Typed 'Text' to correct the error, changed focus.
The value converter wasn't even hit! Of course, the same red border. But the ListView has changed and shows only one warning.
Can somebody explain what's going on? Why the ListView receives collection change notification and the Border not? Is it because ListView is an ItemsControl and the Validation.Errors is wrapped into the CollectionView?
For those who are interested. The errorsToColor converter wasn't fired because the Validation.Errors collection didn't raise PropertyChanged event (needed trigger binding coverter) when errors were added or removed.
In order to raise PropertyChanged event we need to bind to a property which is changed on each error is added, for example Count. I still need the Errors collection itself in the converter, so I used a multi-binding here.
<Border BorderThickness="1">
<Border.BorderBrush>
<MultiBinding Converter="{StaticResource errorsToColor}">
<Binding Path="." />
<Binding Path=".Count" />
</MultiBinding>
</Border.BorderBrush>
<AdornedElementPlaceholder Name="adornedElement" />
</Border>
Now the errorsToColor converter (which is now implements IMultiValueConverter) is executed every time new error is added / removed.
I have a question that is connected with setting path when binding in XAML, using WPF.
Imagine that my DataContext is of PropertyInfo type. PropertyInfo contains data about Property Name.
And in that object I nest (for example) TextBox which Text property I would like to bind to property with that name of another's element DataContext.
Something like that [it's pseudocode because it's not possible that way]:
<DataTemplate>
<TextBox Text={Binding ElementName=someElement, Path=DataContext. + {Binding Path=Name}}/>
</DataTemplate>
I want to create flexible view that's why I need to solve that problem.
Is there a way to achieve this without code behind?
Considering all the above, I think that I can make my question short and simply ask whether there is a way to concatenate string while setting binding's path.
I'm not sure if I understand correctly but is this something that multibinding would assist with?
<TextBlock Grid.Row="3" Grid.Column="1" Padding="5"><TextBlock.Text>
<MultiBinding StringFormat="[{0}, {1}]">
<Binding Path="LastName"></Binding>
<Binding Path="FirstName"></Binding>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
With all the styling information around controls, and what's nested, control template, and triggers all around, I'm trying to figure out the following.
Take a combobox control. it has a control template for the toggle button component which shows the normal display in standard display (not drop-down mode) which shows the display value and toggle button to activate the drop-down.
<ControlTemplate TargetType="ToggleButton" x:Key="baseComboBoxToggleButton" >
<!-- overall border covering left side display value and the actual toggle button -->
<Border x:Name="Border" Grid.ColumnSpan="2" />
<!-- area in left side (column=0) that shows the DISPLAY value -->
<Border x:Name="ShowDisplayValueArea" Grid.Column="0" />
<!-- second column using a path to draw the glyph down arrow -->
<Path Grid.Column="1" />
<Triggers for the toggle button ... />
</ControlTemplate>
Then, you have the main combobox control that uses the template of the toggle button above
<ControlTemplate TargetType="ComboBox" x:Key="ComboBoxGridControlTemplate" >
<Grid>
<ToggleButton Name="ToggleButton"
Template="{StaticResource baseComboBoxToggleButton}"
... />
</Grid>
</ControlTemplate>
So, I'm trying to ULTIMATELY change the background color of the "ShowDisplayValueArea" based on the results of a MultiBinding converter. If I place the multibinding converter in the toggleButton control template area such as..
<MultiBinding Converter="{StaticResource myMultiParmConverter}">
<Binding Path="." RelativeSource="{RelativeSource Self}" />
</MultiBinding>
The first "value" in the values object array is correctly passing the instance of the toggle button control template. The entire object reference, not just the name.
public object Convert(object[] values,
Type targetType,
object parameter,
CultureInfo culture)
So, how I tell the Binding parameter to pass the actual Combobox that the toggle button came from (ie: the parent to the toggle button) so I get the actual entire combobox control passed as the parameter.
Found it... completely by accident...
While in the debugger of my converter class, I was looking at the object reference in the debugger window. Then, since it was an object, I clicked on the magnifying glass to the right side of the "Value" column. Doing so, brought up the "WPF Visualizer" (never used/seen that before). So, seeing it from that perspective, and saw that it's "TemplatedParent" was actually the object I wanted. There were obviously other properties I could consider playing with at some other time, but the TemplatedParent was the one I wanted.
So, I changed the binding from
<Binding Path="." RelativeSource="{RelativeSource Self}" />
to
<Binding Path=".TemplatedParent" RelativeSource="{RelativeSource Self}" />
and correctly got the actual Combobox control passed as the argument to my converter. Now, I can utilize any/all properties of the combobox directly in the converter without having to explicitly pass them all individually.
I have designed an analog clock control. It uses the stroke from two ellipses to represent an outer border and an inner border to the clock face.
I have exposed properties in the UserControl that allow a user to alter the thickness of these two borders. The Ellipse.StrokeThickness properties are then bound to these UserControl properties. At the moment, I am binding the UserControl property for the outer border thickness to the margins of the inner elements so that they are not hidden when the border size is increased.
<Ellipse Name="OuterBorder" Panel.ZIndex="1" StrokeThickness="{Binding OuterBorderThickness,
ElementName=This}" Stroke="{StaticResource OuterBorderBrush}" />
<Ellipse Name="InnerBorder" Panel.ZIndex="5" StrokeThickness="{Binding InnerBorderThickness,
ElementName=This}" Margin="{Binding OuterBorderThickness, ElementName=This}"
Stroke="{StaticResource InnerBorderBrush}">
...
<Ellipse Name="Face" Panel.ZIndex="1" Margin="{Binding OuterBorderThickness, ElementName=This}"
Fill="{StaticResource FaceBackgroundBrush}" />
...
The problem is that if the inner border thickness is increased, this does not affect the margins and so the hour ticks and numbers can become partially obscured or hidden. So what I really need is to be able to bind the margin properties of the inner controls to the sum of the inner and outer border thickness values (they are of type double).
I have done this successfully using 'DataContext = this;', but am trying to rewrite the control without this as I hear it is not recommended. I also thought about using a converter and passing the second value as the ConverterParameter, but didn't know how to bind to the ConverterParameter. Any tips would be greatly appreciated.
EDIT>>
Thanks to Kent's suggestion, I've created a simple MultiConverter to add the input values and return the result. I've hooked the SAME multibinding with converter XAML to both a TextBlock.Text property and the TextBlock.Margin property to test it.
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource SumConverter}" ConverterParameter="Add">
<Binding Path="OuterBorderThickness" ElementName="This" />
<Binding Path="InnerBorderThickness" ElementName="This" />
</MultiBinding>
</TextBlock.Text>
<TextBlock.Margin>
<MultiBinding Converter="{StaticResource SumConverter}" ConverterParameter="Add">
<Binding Path="OuterBorderThickness" ElementName="This" />
<Binding Path="InnerBorderThickness" ElementName="This" />
</MultiBinding>
</TextBlock.Margin>
</TextBlock>
I can see the correct value displayed in the TexBlock, but the Margin is not set. Any ideas?
EDIT >> >>
Interestingly, the Margin property can be bound to a data property of type double, but this does not seem to apply within a MultiBinding. As advised by Kent, I changed the Converter to return the value as a Thickness object and now it works. Thanks Kent.
Sounds like you're looking for a MultiBinding with a converter capable of evaluating an expression. My ExpressionConverter permits exactly this. Of course, if you don't want a dependency on a third party library just for this, you could write your own multi-value-converter that adds two values together.
I have done this successfully using 'DataContext = this;', but am trying to rewrite the control without this as I hear it is not recommended.
You can bind to properties of a user control without changing its DataContext; just add an x:Name attribute to its root element and then bind using {Binding ElementName=Root, Path=MyProperty}. It's much simpler to do this than it is to use converters to inject code into the binding.
So I have an object that implements INotifyPropertyChanged, and I have a property that when it changes, it call the PropertyChanged event all right, but when I use a converter like this:
<Image Grid.Column="0">
<Image.Source>
<Binding Path="IsInstrumentStatusOk" UpdateSourceTrigger="PropertyChanged">
<Binding.Converter>
<converters:BooleanToImageConverter
ImagePathIfFalse="/Images/InstrumentStatusBar/Instrument_Status_Alarm.png"
ImagePathIfTrue="/Images/InstrumentStatusBar/Instrument_Status_OK.png" />
</Binding.Converter>
</Binding>
</Image.Source>
</Image>
For some reason it doesn't update it, and it doesn't call the converter. If I use it like normally
Source="{Binding MyProperty, Converter={StaticResource MyConverter}}"
It works, but I don't want to use it like that because I have a bunch of converters that I want to use with different images. Any idea why it doesn't update?
Thanks.
You're setting UpdateSourceTrigger="PropertyChanged" in your XAML. This means when the target property changes the value should update back to the source. Obviously nothing is ever changing the Image::Source property.
Just remove the UpdateSourceTrigger setting altogether and you should be fine.