The strings are not fix length and the caps are in different positions so you'll need generate dynamic markup using C# not XAML. Do a Google search for dynamic inline style in WPF. However, I'm not sure if it's possible for a combobox to have an inline style. This is not possible in the web markup world. On web we have to use code (JavaScript) to create a custom component. I guess you'll need to do some research.
Let us know what you find.
How Can I give Different Font size to the Uppercase characters of a string that binded to A List in WPF?
Hi there, I have a WPF application where I use a ComboBox. My ComboBox ItemSource is bind to a List <>
which I populate from C# in background.
Here is the C# code of that List<> :
// Main code
List<AudioDevice> devices = new List<AudioDevice>();
HRESULT hr = HRESULT.E_FAIL;
Guid CLSID_MMDeviceEnumerator = new Guid("{BCDE0395-E52F-467C-8E3D-C4579291692E}");
Type MMDeviceEnumeratorType = Type.GetTypeFromCLSID(CLSID_MMDeviceEnumerator, true);
object MMDeviceEnumerator = Activator.CreateInstance(MMDeviceEnumeratorType);
IMMDeviceEnumerator pMMDeviceEnumerator = (IMMDeviceEnumerator)MMDeviceEnumerator;
if (pMMDeviceEnumerator != null)
{
string sIdDefaultRender = null;
string sIdDefaultCapture = null;
IMMDevice pDefaultDevice = null;
hr = pMMDeviceEnumerator.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eConsole, out pDefaultDevice);
if (hr == HRESULT.S_OK)
{
IntPtr hGlobal = Marshal.AllocHGlobal(260);
hr = pDefaultDevice.GetId(out hGlobal);
sIdDefaultRender = Marshal.PtrToStringUni(hGlobal);
Marshal.FreeHGlobal(hGlobal);
Marshal.ReleaseComObject(pDefaultDevice);
}
hr = pMMDeviceEnumerator.GetDefaultAudioEndpoint(EDataFlow.eCapture, ERole.eConsole, out pDefaultDevice);
if (hr == HRESULT.S_OK)
{
IntPtr hGlobal = Marshal.AllocHGlobal(260);
hr = pDefaultDevice.GetId(out hGlobal);
sIdDefaultCapture = Marshal.PtrToStringUni(hGlobal);
Marshal.FreeHGlobal(hGlobal);
Marshal.ReleaseComObject(pDefaultDevice);
}
IMMDeviceCollection pDeviceCollection = null;
hr = pMMDeviceEnumerator.EnumAudioEndpoints(EDataFlow.eAll, DEVICE_STATE_ACTIVE | DEVICE_STATE_UNPLUGGED, out pDeviceCollection);
if (hr == HRESULT.S_OK)
{
uint nDevices = 0;
hr = pDeviceCollection.GetCount(out nDevices);
devices.Add(new AudioDevice() { Name = "System default", Direction = "Playback", Id = sIdDefaultRender, Default = true });
for (uint i = 0; i < nDevices; i++)
{
IMMDevice pDevice = null;
hr = pDeviceCollection.Item(i, out pDevice);
if (hr == HRESULT.S_OK)
{
IPropertyStore pPropertyStore = null;
hr = pDevice.OpenPropertyStore(STGM_READ, out pPropertyStore);
if (hr == HRESULT.S_OK)
{
string sFriendlyName = null;
string sDesc = null;
PROPVARIANT pv = new PROPVARIANT();
hr = pPropertyStore.GetValue(ref PKEY_Device_FriendlyName, out pv);
if (hr == HRESULT.S_OK)
{
sFriendlyName = Marshal.PtrToStringUni(pv.pwszVal);
}
hr = pPropertyStore.GetValue(ref PKEY_Device_DeviceDesc, out pv);
if (hr == HRESULT.S_OK)
{
sDesc = Marshal.PtrToStringUni(pv.pwszVal);
}
IntPtr hGlobal = Marshal.AllocHGlobal(260);
hr = pDevice.GetId(out hGlobal);
string sId = Marshal.PtrToStringUni(hGlobal);
Marshal.FreeHGlobal(hGlobal);
IMMEndpoint pEndpoint = null;
pEndpoint = (IMMEndpoint)pDevice;
EDataFlow eDirection = EDataFlow.eAll;
hr = pEndpoint.GetDataFlow(out eDirection);
//System.Diagnostics.Trace.WriteLine("\tDirection : " + eDirection.ToString());
string sDirection = "";
if (eDirection == EDataFlow.eRender)
sDirection = "Playback";
else if (eDirection == EDataFlow.eCapture)
sDirection = "Recording";
int nState = 0;
hr = pDevice.GetState(out nState);
if ((nState == DEVICE_STATE_ACTIVE))
{
devices.Add(new AudioDevice() { Name = sFriendlyName, Direction = sDirection, Id = sId, Default = (sId == sIdDefaultRender || (sId == sIdDefaultCapture)) });
//sFriendlyName += (sId == sIdDefaultRender || sId == sIdDefaultCapture) ? " (System default)" : "";
//devices.Add(new AudioDevice() { Name = sFriendlyName, Direction = sDirection, Default = (sId == sIdDefaultRender || (sId == sIdDefaultCapture)) });
////devices.Add(new AudioDevice() { Name = sFriendlyName, Direction = sDirection, Default = (sId == sIdDefaultRender || (sId == sIdDefaultCapture)) });
}
Marshal.ReleaseComObject(pPropertyStore);
}
Marshal.ReleaseComObject(pDevice);
}
}
devices.Insert(devices.Count - 0, new AudioDevice() { Name = "Selected application ...", Direction = "Recording", Id = "Idlast", Default = false });
}
Marshal.ReleaseComObject(pDeviceCollection);
ListCollectionView lcv = new ListCollectionView(devices);
lcv.GroupDescriptions.Add(new PropertyGroupDescription("Direction"));
this.cmb1.ItemsSource = lcv;
}
}
public class AudioDevice
{
public string Name { get; set; }
public string Direction { get; set; }
public string Id { get; set; }
public bool Default { get; set; }
public override string ToString()
{
return Name;
}
}
If you carefully see the code then You see this line this.cmb1.ItemsSource = lcv;
That means the List<>
is added as ItemSource of the ComboBox, Now, I have a control template for comboBoxItem, my ComboBoxitem control template have lots of visual customisation and effects according to my choice.
Here is my ComboBoxItem controltemplate :
<Style x:Key="{x:Type ComboBoxItem}" TargetType="{x:Type ComboBoxItem}">
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="UseLayoutRounding" Value="True"/>
<Setter Property="RenderOptions.BitmapScalingMode" Value="NearestNeighbor"/>
<Setter Property="RenderOptions.ClearTypeHint" Value="Enabled"/>
<Setter Property="FontFamily" Value="Segoe UI"/>
<Setter Property="FontSize" Value="9"/>
<Setter Property="TextOptions.TextFormattingMode" Value="Display"/>
<Setter Property="Foreground" Value="#939393"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBoxItem}">
<Grid>
<Border x:Name="gd" Background="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ComboBox}},Path=Background}" BorderThickness="0" SnapsToDevicePixels="True" UseLayoutRounding="True" RenderOptions.BitmapScalingMode="NearestNeighbor" RenderOptions.ClearTypeHint="Enabled">
<ContentPresenter
Name="ContentSite" Margin="25, 3, 0, 11" VerticalAlignment="Center">
<ContentPresenter.ContentTemplate>
<DataTemplate>
<TextBlock SnapsToDevicePixels="True" UseLayoutRounding="True" RenderOptions.BitmapScalingMode="HighQuality" RenderOptions.EdgeMode="Aliased">
<Run Text="{Binding Name}"/>
</TextBlock>
</DataTemplate>
</ContentPresenter.ContentTemplate>
</ContentPresenter>
</Border>
<Rectangle x:Name="Border1" Width="12.2" Height="11" Margin="-220,-7,0,0" RenderOptions.BitmapScalingMode="HighQuality" RenderOptions.ClearTypeHint="Auto" SnapsToDevicePixels="True" RenderOptions.EdgeMode="Unspecified">
<Rectangle.Fill>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup x:Name="DrawingLayer">
<DrawingGroup.ClipGeometry>
<RectangleGeometry Rect="0,0,512,512" />
</DrawingGroup.ClipGeometry>
<DrawingGroup Transform="0.1,0,0,-0.1,0,512">
<GeometryDrawing Brush="#d0021b">
<GeometryDrawing.Geometry>
<PathGeometry FillRule="Nonzero" Figures="M4670,3950L2560,1350 1560,2570 830,2190 2590,170 5140,3260 4670,3950z" />
</GeometryDrawing.Geometry>
<GeometryDrawing.Pen>
<Pen Brush="#d0021b" Thickness="0" StartLineCap="Flat" EndLineCap="Flat" LineJoin="Miter" />
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingGroup>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="ComboBoxItem.IsMouseOver" Value="True">
<Setter TargetName="gd" Property="Background" Value="#3c3c3c"></Setter>
<Setter TargetName="gd" Property="TextElement.Foreground" Value="#ffffff"></Setter>
<Setter TargetName="gd" Property="SnapsToDevicePixels" Value="True"/>
<Setter TargetName="gd" Property="UseLayoutRounding" Value="True"/>
<Setter TargetName="gd" Property="RenderOptions.BitmapScalingMode" Value="NearestNeighbor"/>
<Setter TargetName="gd" Property="RenderOptions.ClearTypeHint" Value="Enabled"/>
<Setter TargetName="Border1" Property="Visibility" Value="Collapsed"/>
</Trigger>
<Trigger Property="ComboBoxItem.IsMouseOver" Value="False">
<Setter TargetName="Border1" Property="Visibility" Value="Hidden"/>
</Trigger>
<Trigger Property="ComboBoxItem.IsSelected" Value="True">
<Setter TargetName="Border1" Property="Visibility" Value="Visible"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
After adding this controltemplate in my ComboBoxitem, my comboBox looks amazing. My ComboBox content is added from a List<> not from a ComboBox item. If you carefully see this controltemplate, you can see this line <Run Text="{Binding Name}"/>
, this is How I bind the List<>
into my ComboBoxitem controltemplate.
All of my code run perfectly But my Main question is How Can I give different Font Size to the Uppercase characters and Lowercase characters of that string (The string is the Name which I get from the Class Audio device).
My approach is to separate Uppercase and Lowercase characters from that string "Name" using a IValueConverter. And then give bigger Font Size to the Uppercase converter and lower Font Size to the Lowercase converter.
Something like this :
<DataTemplate>
<TextBlock SnapsToDevicePixels="True" UseLayoutRounding="True" RenderOptions.BitmapScalingMode="HighQuality" RenderOptions.EdgeMode="Aliased">
<Run x:Name="first" FontSize="18" Text="{Binding Name,Converter={StaticResource UppercaseConverter }}" />
<Run x:Name="rest" FontSize="10" Text="{Binding Name, Converter={StaticResource LowercaseConverter}}" />
</TextBlock>
</DataTemplate>
I tried so many things like string builder, string replace but nothing works. Any other simple code suggestion is also welcome.
3 answers
Sort by: Most helpful
-
AgaveJoe 26,136 Reputation points
2022-10-06T13:49:40.393+00:00 -
Hui Liu-MSFT 40,266 Reputation points Microsoft Vendor
2022-10-11T02:41:31.817+00:00 Hi,@MERUN KUMAR MAITY . AgaveJoe's answer is great. Based on my research, it is difficult to return a specific font size for characters in variable length and variable content strings bound to controls.
A string does not have a font. If you are displaying a string as part of a control (such as a ComboBox) then how you format it depends on the functionality available within that control. If you are drawing that string as text on a display or in a printout, then you control the font that is used as part of the drawing commands.----------------------------------------------------------------------------
If the response is helpful, please click "Accept Answer" and upvote it.
Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread. -
MERUN KUMAR MAITY 511 Reputation points
2022-10-11T19:43:49.967+00:00 I can create a custom Text Block and override its text property.
using System.Windows; using System.Windows.Controls; using System.Windows.Documents; namespace WpfApp1; public class CustomTextBlock : TextBlock { public new string Text { get => (string)GetValue(TextProperty); set => SetValue(TextProperty, value); } // Using a DependencyProperty as the backing store for Text. This enables animation, styling, binding, etc... public static new readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(CustomTextBlock), new PropertyMetadata(null, (d, ea) => { if (d is TextBlock textBlock) { textBlock.Inlines.Clear(); if (ea.NewValue is string value) { foreach (char c in value) { textBlock.Inlines.Add(new Run { FontSize = GetFontSize(c), Text = c.ToString() }); } } } })); private static double GetFontSize(char c) { // You can add your other logic here /*if (c == '#') { return 5; } if (char.IsNumber(c)) { return 22; }*/ if (char.IsUpper(c)) { return 18; } return 10; } }
I can use it in my XAML this way. I used the CustomTextBlock in the ComboBox ItemTemplate but I think it should also work in the ComboBoxItem ControlTemplate.
<Window x:Class="WpfApp1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp1" xmlns:sys="clr-namespace:System;assembly=mscorlib" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Grid> <ComboBox VerticalAlignment="Top"> <ComboBox.ItemsSource> <x:Array Type="sys:String"> <sys:String>Some Item #0</sys:String> <sys:String>Some Item #1</sys:String> <sys:String>Some Item #2</sys:String> </x:Array> </ComboBox.ItemsSource> <ComboBox.ItemTemplate> <DataTemplate> <local:CustomTextBlock Text="{Binding}"/> </DataTemplate> </ComboBox.ItemTemplate> </ComboBox> </Grid> </Window>
In my ComboBoxItem ControlTemplate it can be used like below.
<ContentPresenter Name="ContentSite" Margin="25, 6.5, 0, 6.5" VerticalAlignment="Center"> <ContentPresenter.ContentTemplate> <DataTemplate> <local:CustomTextBlock Text="{Binding Name}" SnapsToDevicePixels="True" UseLayoutRounding="True" RenderOptions.BitmapScalingMode="HighQuality" RenderOptions.EdgeMode="Aliased" /> </DataTemplate> </ContentPresenter.ContentTemplate> </ContentPresenter>
This is a part of my ComboBoxItem ControlTemplate code where I do the Binding with Name and apply the CustomTextBlock.