I want all characters in a TextBlock to be displayed in uppercase
<TextBlock Name="tbAbc"
FontSize="12"
TextAlignment="Center"
Text="Channel Name"
Foreground="{DynamicResource {x:Static r:RibbonSkinResources.RibbonGroupLabelFontColorBrushKey}}" />
The strings are taken through Binding. I don't want to make the strings uppercase in the dictionary itself.
Or use
Typography.Capitals="AllSmallCaps"
in your TextBlock definition.
See here: MSDN - Typography.Capitals
EDIT:
This does not work in Windows Phone 8.1, only in Windows 8.1 ...
Implement a custom converter.
using System.Globalization;
using System.Windows.Data;
// ...
public class StringToUpperConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null && value is string )
{
return ((string)value).ToUpper();
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
Then include that in your XAML as a resource:
<local:StringToUpperConverter x:Key="StringToUpperConverter"/>
And add it to your binding:
Converter={StaticResource StringToUpperConverter}
You can use an attached property like this:
public static class TextBlock
{
public static readonly DependencyProperty CharacterCasingProperty = DependencyProperty.RegisterAttached(
"CharacterCasing",
typeof(CharacterCasing),
typeof(TextBlock),
new FrameworkPropertyMetadata(
CharacterCasing.Normal,
FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.NotDataBindable,
OnCharacterCasingChanged));
private static readonly DependencyProperty TextProxyProperty = DependencyProperty.RegisterAttached(
"TextProxy",
typeof(string),
typeof(TextBlock),
new PropertyMetadata(default(string), OnTextProxyChanged));
private static readonly PropertyPath TextPropertyPath = new PropertyPath("Text");
public static void SetCharacterCasing(DependencyObject element, CharacterCasing value)
{
element.SetValue(CharacterCasingProperty, value);
}
public static CharacterCasing GetCharacterCasing(DependencyObject element)
{
return (CharacterCasing)element.GetValue(CharacterCasingProperty);
}
private static void OnCharacterCasingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is System.Windows.Controls.TextBlock textBlock)
{
if (BindingOperations.GetBinding(textBlock, TextProxyProperty) == null)
{
BindingOperations.SetBinding(
textBlock,
TextProxyProperty,
new Binding
{
Path = TextPropertyPath,
RelativeSource = RelativeSource.Self,
Mode = BindingMode.OneWay,
});
}
}
}
private static void OnTextProxyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
d.SetCurrentValue(System.Windows.Controls.TextBlock.TextProperty, Format((string)e.NewValue, GetCharacterCasing(d)));
string Format(string text, CharacterCasing casing)
{
if (string.IsNullOrEmpty(text))
{
return text;
}
switch (casing)
{
case CharacterCasing.Normal:
return text;
case CharacterCasing.Lower:
return text.ToLower();
case CharacterCasing.Upper:
return text.ToUpper();
default:
throw new ArgumentOutOfRangeException(nameof(casing), casing, null);
}
}
}
}
Then usage in xaml will look like:
<StackPanel>
<TextBox x:Name="TextBox" Text="abc" />
<TextBlock local:TextBlock.CharacterCasing="Upper" Text="abc" />
<TextBlock local:TextBlock.CharacterCasing="Upper" Text="{Binding ElementName=TextBox, Path=Text}" />
<Button local:TextBlock.CharacterCasing="Upper" Content="abc" />
<Button local:TextBlock.CharacterCasing="Upper" Content="{Binding ElementName=TextBox, Path=Text}" />
</StackPanel>
If it's not a big deal you could use TextBox instead of TextBlock like this:
<TextBox CharacterCasing="Upper" IsReadOnly="True" />
While there's already a great answer here that uses a converter, I'm providing an alternative implementation that simplifies the conversion to a single line (thanks to null coalescing), as well as making it a subclass of MarkupExtension so it's easier to use in XAML.
Here's the converter...
using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Markup;
namespace IntuoSoft.Wpf.Converters {
[ValueConversion(typeof(string), typeof(string))]
public class CapitalizationConverter : MarkupExtension, IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
=> (value as string)?.ToUpper() ?? value; // If it's a string, call ToUpper(), otherwise, pass it through as-is.
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
=> throw new NotSupportedException();
public override object ProvideValue(IServiceProvider serviceProvider) => this;
}
}
And here's how you use it (Note: This assumes the above namespace is prefixed with is in your XAML):
<TextBlock Text={Binding SomeValue, Converter={is:CapitalizationConverter}}" />
Because it's a MarkupExtension subclass, you can simply use it right where/when it's needed. No need to define it in the resources first.
I use a character casing value converter:
class CharacterCasingConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var s = value as string;
if (s == null)
return value;
CharacterCasing casing;
if (!Enum.TryParse(parameter as string, out casing))
casing = CharacterCasing.Upper;
switch (casing)
{
case CharacterCasing.Lower:
return s.ToLower(culture);
case CharacterCasing.Upper:
return s.ToUpper(culture);
default:
return s;
}
}
object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
Related
I have a enum to string converter
public class EnumToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
CultureInfo culture)
{
MailSettingsStateEnum enumValue = (MailSettingsStateEnum)value;
// extension method on the enum, to return a string based on enum.
return enumValue.Description();
}
// ConvertBack not relevant here.
}
I am using this in wpf xaml easily as follows to set the Content property of a label.
<Label Content="{Binding MailSettingState, Converter={StaticResource
EnumConverterString}}"
BorderBrush="{Binding MailSettingState, Converter={StaticResource
EnumConverterBorderBrush}}" />
Now as you can see, I have another property BorderBrush. I also have to set this based on the same enum. And so I had to write another converter EnumConverterBorderBrush
So is there a way by which I have only one converter, and it return an object which has two properties and i can use these properties in the xaml? I can create the converter, its easy, but I dont know how to use it in xaml. Say the converter returned an object and has tow property called MessageString(of type string), and another BorderBrush of the type Brush, how do I use it the xaml?
You can switch the output based on the targetType you receive in your converter.
So you could do something like this:
public class EnumToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
CultureInfo culture)
{
var enumValue = (MailSettingsStateEnum)value;
switch(targetType)
{
case typeof(string)
return enumValue.Description();
case typeof(Brush)
return enumValue.GetBrush();
default:
throw new NotSupportedException("Type not supported")
}
}
// ConvertBack not relevant here.
}
Now you'll have one converter to rule them all!
converter should return object which match requested targetType. converter can return different values for input enum value depending on parameter. I think it is more flexible than relying on targetType only.
public class SpecEnumConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Enum)
{
if ((string) parameter == "brush")
return "Red"; // return brush here!
// if not pre-defined parameter (null or any other), return description
return (int) value; // return enum description here!
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
usage:
<Label Content="{Binding MailSettingState, Converter={StaticResource
EnumConverterSpec}}"
BorderBrush="{Binding MailSettingState, Converter={StaticResource
EnumConverterSpec}, ConverterParameter='brush'}" />
I already commented above, but here's the solution.
<Label DataContext="{Binding MailSettingState, Converter={converters:EnumConverter}}" Content="{Binding Label}" BorderBrush="{Binding BorderBrush}"/>
public class EnumConverter: MarkupExtension, IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var enumValue = (MailSettingsStateEnum) value;
return new ConvertedEnum { Label = enumValue.Description(),
BorderBrush = new BorderBrush()};
}
// ConvertBack not relevant here.
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
public class ConvertedEnum
{
public string Label {get; set;}
public BorderBrush {get; set;}
}
Separate converters still look prettier to me.
I want to make part of the text of a textblock bold. This is what i tried in the IValueConverter but it does not seem to work.
public class Highlighter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
{
return null;
}
return "Question1:<Bold>Answer1</Bold>, Question2:<Bold>Answer2</Bold>, Question3:<Bold>Answer3</Bold>";
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
This does not make the Answer bold.
This is how i am using it in XAML.
<TextBlock Height="Auto" Width="Auto" MaxHeight="64" Text="{Binding Path=QuestionAnswer, Mode=OneWay, Converter={x:Static Highlighter}}" />
Is there a way i can achieve this by formatting the text or by sending the TextBlock to the converter?
It is definitely possible to do with TextBlock control, but considering all the efforts you might want to switch to other control (ItemsControl for example).
Anyway, here is a solution. There are actually several problems to solve:
TextBlock.Text property is string, and you can't assign preformatted text to it
TextBlock.Inlines can accept formatted text, but it is read-only property
You'll have to format text yourself (probably there are easy ways to parse text with tags and produce formatted output as a collection of Inline objects, but I don't know any)
You can create an attached property to deal with the first 2 problems:
public static class TextBlockEx
{
public static Inline GetFormattedText(DependencyObject obj)
{
return (Inline)obj.GetValue(FormattedTextProperty);
}
public static void SetFormattedText(DependencyObject obj, Inline value)
{
obj.SetValue(FormattedTextProperty, value);
}
public static readonly DependencyProperty FormattedTextProperty =
DependencyProperty.RegisterAttached(
"FormattedText",
typeof(Inline),
typeof(TextBlockEx),
new PropertyMetadata(null, OnFormattedTextChanged));
private static void OnFormattedTextChanged(
DependencyObject o,
DependencyPropertyChangedEventArgs e)
{
var textBlock = o as TextBlock;
if(textBlock == null) return;
var inline = (Inline)e.NewValue;
textBlock.Inlines.Clear();
if(inline != null)
{
textBlock.Inlines.Add(inline);
}
}
}
XAML would change just a bit:
<TextBlock local:TextBlockEx.FormattedText="{Binding Path=QuestionAnswer,
Mode=OneWay,
Converter={x:Static Highlighter}}" />
Note that you'll need to map you namespace where TextBlockEx is declared in xmlns:local="clr-namepace:<namespace_name>" in XAML.
Now you need to construct formatted text in converter instead of plain text to solve the last problem:
public object Convert(object value, Type targetType, object parameter,
CultureInfo culture)
{
if(value == null)
{
return null;
}
var span = new Span();
span.Inlines.Add(new Run("Question1: "));
span.Inlines.Add(new Run("Answer1") { FontWeight = FontWeights.Bold });
span.Inlines.Add(new Run(", "));
span.Inlines.Add(new Run("Question2: "));
span.Inlines.Add(new Run("Answer2") { FontWeight = FontWeights.Bold });
span.Inlines.Add(new Run(", "));
span.Inlines.Add(new Run("Question3: "));
span.Inlines.Add(new Run("Answer3") { FontWeight = FontWeights.Bold });
return span;
}
Ya, something like this should put ya on track;
<TextBlock>
<Run Text="Question / Binding / Whatever..."/>
<Run Text="Answer / Binding / Whatever..." FontWeight="Bold"/>
</TextBlock>
I wanted to experiment with being able to have a converter whose arguments can be bound with the current data context. Can anyone tell me why when reaching the Convert() function, the Source property is always null?
namespace WpfApplication32
{
public class ConverterTest : DependencyObject, IValueConverter
{
public static readonly DependencyProperty SourceProperty =
DependencyProperty.Register("Source", typeof(DependencyObject), typeof(ConverterTest));
public DependencyObject Source
{
get { return (DependencyObject)this.GetValue(SourceProperty); }
set { this.SetValue(SourceProperty, value); }
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
Value = 7;
InitializeComponent();
DataContext = this;
}
public float Value
{
get;
set;
}
}
}
<Window x:Class="WpfApplication32.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication32">
<Slider>
<Slider.Value>
<Binding Path="Value">
<Binding.Converter>
<local:ConverterTest Source="{Binding}"/>
</Binding.Converter>
</Binding>
</Slider.Value>
</Slider>
</Window>
One possible solution is to make your Converter inherit from Freezable instead (Hillberg Freezable trick). Then you can even define your Converter in your Resources and reference it in your binding as an attribute instead of an extra child element.
I have a DataTemplate that is used by a listbox:
<local:BooleanToFontColorConverter x:Key="boolToFontColor" />
<DataTemplate x:Key="ListBox_DataTemplateSpeakStatus">
<Label Width="Auto">
<TextBlock Name="MY_TextBlock" Text="Hello!" Foreground="{Binding Path=MY_COLOR, Converter={StaticResource boolToFontColor}}" />
</Label>
</DataTemplate>
MY_COLOR is the following bit of code:
public class Packet_Class : INotifyPropertyChanged
{
private bool _my_color = false;
public bool MY_COLOR { get { return _my_color; }
set { _my_color = value; RaisePropertyChanged("MY_COLOR"); } }
}
and then when appropriate I set the property, which I think would fire the RaisePropertyChanged function
myPacketClass.MY_COLOR = true;
while boolToFontColor is "trying" to use this bit:
public class BooleanToFontColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
CultureInfo culture)
{
if (value is Boolean)
{
return ((bool)value) ? new SolidColorBrush(Colors.Red) : new SolidColorBrush(Colors.Black);
}
return new SolidColorBrush(Colors.Black);
}
public object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture)
{
throw new NotImplementedException();
}
}
When I change the value of MY_COLOR from true to false, or vice versa, I see no visible changes in my text foreground color during runtime. Is anyone able to give advice as to where I am going wrong? Much appreciated and thank you in advance.
EDIT:
Some additional information to attempt to provide more clarity. I am using my DataTemplate in a ListBox like this:
<ListBox x:Name="MyUserList" ItemTemplate="{StaticResource ListBox_DataTemplateSpeakStatus}" SelectionMode="Extended" />
And in my WPF Window element I set my local namespace to the namespace that my mainwindow.xaml.cs is encapsulated in:
xmlns:local ="clr-namespace:My_NameSpace"
the RaisePropertyChanged method should raise the PropertyChanged event define in the interface and look like:
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged (string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
the converter:
public class BooleanToFontColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
CultureInfo culture)
{
if (value is Boolean)
{
return ((bool)value) ? new SolidColorBrush(Colors.Red) : new SolidColorBrush(Colors.Black);
}
return new SolidColorBrush(Colors.Black);
}
public object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture)
{
throw new NotImplementedException();
}
}
You have to use a SolidColorBrush to make it work.
It works on my environment, let me know if you encounter any trouble.
In Silverlight, I have a standard IValueConverter where I want to define the possible parameters as const strings.
public class BooleanConverter : IValueConverter
{
public const string PARAMETER_ONE = "one";
public const string PARAMETER_TWO = "two";
...
I would then like to use the constant within the Converter Parameter, something like:
<TextBlock Text="{Binding Name, Converter={StaticResource BooleanConverter},
ConverterParameter={StaticResource BooleanConvereter.PARAMETER_TWO}" />
Is this possible? If yes, what is the correct syntax?
You should be able to use the Static markup extension:
<TextBlock Text="{Binding Name,Converter={StaticResource BooleanConverter},ConverterParameter={x:Static cnv:BooleanConverter.PARAMETER_ONE}}" />
Note that the x:Static needs a reference to the class itself, not your resource. Therefore you'll need to prefix it with the appropriate XML namespace prefix.
You can use both parameter and argument. Sample:
<converters:VisibilityConverter x:Key="inversVisibConverter" Inverse="True"/>
Visibility="{Binding ABoolValue, Mode=OneWay, Converter={StaticResource inversVisibConverter},ConverterParameter=abc}"
The Converter (contains both parameter and argument):
public sealed class VisibilityConverter : IValueConverter {
public bool Inverse { get; set; }
#region Implementation of IValueConverter
object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Visibility visibility = Visibility.Visible;
if (value is bool)
{
visibility = (bool) value ? Visibility.Visible : Visibility.Collapsed;
}
if( parameter != null )
{
//handle parameter here:
}
if (Inverse)
{
visibility = (visibility == Visibility.Visible) ? Visibility.Collapsed : Visibility.Visible;
}
return visibility;
}
object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
if (!(value is Visibility)) {
throw new ArgumentOutOfRangeException("value", "VisibilityConverter can only convert from Visibility");
}
if (targetType == typeof(bool)) {
return ((Visibility)value == Visibility.Visible) ? true : false;
}
throw new ArgumentOutOfRangeException("targetType", "VisibilityConverter can only convert to Boolean");
}
#endregion
}