question

MERUNKUMARMAITY-4120 avatar image
0 Votes"
MERUNKUMARMAITY-4120 asked HuiLiu-MSFT commented

How Can I Bind ItemsSource of a ComboBox in a ControlTemplate?

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; }
    
         }


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="ItemStyleOne" 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="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 FontSize="10">MP4 A</Run><Run FontSize="9">udio</Run>
                                         </TextBlock>
                                     </DataTemplate>
                                 </ContentPresenter.ContentTemplate>
                                 </ContentPresenter>
                             </Border>
                             <Rectangle x:Name="Border1" Width="12" Height="10" Margin="-220,-7,0,0"          RenderOptions.BitmapScalingMode="HighQuality" RenderOptions.ClearTypeHint="Auto" SnapsToDevicePixels="True" RenderOptions.EdgeMode="Unspecified">
                                 <Rectangle.Fill>
                                     <DrawingBrush>
                                         <DrawingBrush.Drawing>
                                             <DrawingGroup>
                                                 <DrawingGroup.Children>
                                                     <GeometryDrawing Brush="#d0021b" Geometry="M95.118,21.399L86.601,12.882C85.422,11.703,83.523,11.703,82.344,12.882L38.799,58.854 13.839,33.882C12.648,32.691,10.716,32.691,9.52499999999999,33.882L0.896999999999991,42.516C-0.294000000000009,43.704,-0.294000000000009,45.636,0.896999999999991,46.83L36.396,83.154C37.083,83.844 38.016,84.081 38.913,83.964 39.84,84.102 40.806,83.868 41.517,83.154L95.118,25.659C96.294,24.483,96.294,22.575,95.118,21.399z">
                                                         <GeometryDrawing.Pen>
                                                             <Pen LineJoin="Round" Brush="#d0021b" Thickness="2.5"/>
                                                         </GeometryDrawing.Pen>
                                                     </GeometryDrawing>
                                                 </DrawingGroup.Children>
                                             </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="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="Hidden"/>
                             </Trigger>
                             <Trigger Property="ComboBoxItem.IsSelected" Value="True">
                                 <Setter TargetName="Border1" Property="Visibility" Value="Visible"/>
                                 <!--<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"/>-->
                             </Trigger>
                         </ControlTemplate.Triggers>
                     </ControlTemplate>
                 </Setter.Value>
             </Setter>
         </Style>



After adding this controltemplate in my ComboBoxitem my comboBox looks amazing. This is how I add this ComboBoxItem Style="{StaticResource ItemStyle}" ,

But in this case my ComboBox content is added from a List<> not from a ComboBox item, My question is then How can I add these controltemplate
visual effect to my ComboBox content, because we know ControlTemplate contains the tree of elements that define the desired look.





dotnet-csharpwindows-wpf
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

1 Answer

HuiLiu-MSFT avatar image
0 Votes"
HuiLiu-MSFT answered HuiLiu-MSFT commented

I changed the code as follows and populated the data. You could try to refer to it.

 <Window.Resources>
    
         <Style x:Key="{x:Type ComboBoxItem}" TargetType="{x:Type ComboBoxItem}">
          ...
             <Setter Property="Template">
                 <Setter.Value>
                     <ControlTemplate TargetType="{x:Type ComboBoxItem}">
                         <Grid>
                          ...
                                         <DataTemplate>
                                             <TextBlock SnapsToDevicePixels="True" UseLayoutRounding="True" RenderOptions.BitmapScalingMode="HighQuality" RenderOptions.EdgeMode="Aliased">
                                              
                                                 <Run Text="{Binding Name}"/>
                                             </TextBlock>
                                         </DataTemplate>
                                     ...
                         </Grid>
                         <ControlTemplate.Triggers>
                            ....
                         </ControlTemplate.Triggers>
                     </ControlTemplate>
                 </Setter.Value>
             </Setter>
         </Style>
           
        
     </Window.Resources>
     <Grid>
         <ComboBox  x:Name="cb" Width="200" Height="40">
         </ComboBox>
     </Grid>

Codebehind:
243166-2222.txt


Update:

     <Application.Resources>

     <local:FirstValueConverter x:Key="FirstValueConverter"/>

     <local:RestValueConverter x:Key="RestValueConverter"/>

             <Style x:Key="MyItemStyle" TargetType="{x:Type ComboBoxItem}">
               ...
                 <Setter Property="Template">
                     <Setter.Value>
                                 ...
                                             <DataTemplate>
                                                 <TextBlock SnapsToDevicePixels="True" UseLayoutRounding="True" RenderOptions.BitmapScalingMode="HighQuality" RenderOptions.EdgeMode="Aliased">
                                                     <Run x:Name="first" FontSize="18" Text="{Binding Name,Converter={StaticResource FirstValueConverter }}" />
                                                     <Run x:Name="rest" FontSize="10" Text="{Binding Name, Converter={StaticResource RestValueConverter}}" />
                                                 </TextBlock>
                                             </DataTemplate>
                            ...
                     </Setter.Value>
                 </Setter>
             </Style>   
  </Application.Resources>

     <ComboBox  x:Name="cb" Width="200" Height="40" ItemContainerStyle="{StaticResource MyItemStyle}">
          
     </ComboBox>

Converter:

  public class FirstValueConverter : IValueConverter
     {
         public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
         {
             var p=value as string;
    
             var first="";
             if (!string.IsNullOrWhiteSpace(p))
             {
                 first = p.Substring(0, 1);
             }
             return first;
         }
         public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
         {
             throw new NotImplementedException();
         }
     }
    
     public class RestValueConverter : IValueConverter
     {
         public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
         {
             var p = value as string;
             var rest = "";
             if (!string.IsNullOrWhiteSpace(p))
             {
                 rest = p.Substring(1, p.Length - 1);
             }
             return  rest;
                
         }
         public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
         {
             throw new NotImplementedException();
         }
     }

The result:

243834-image.png


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.



image.png (19.4 KiB)
2222.txt (1.1 KiB)
image.png (28.9 KiB)
· 4
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Thank you very much sir, as usual you are always present here to help me. I apply your code in my project and it works flawlessly there no issue on it. I also understand each and everything from your code. Now I can successfully apply Control template to my ComboBox ItemsSource. Your talent is Amazing the way you bind the name with Run is completely on another level. I completely speechless by seeing this code first time.

But I want some more modification on your code, as I say previously my ComboBox ItemsSource is bind to a List<> , by using that, I can retrieve the all available audio devices. Now my question is How Can I give different Font Size to that Audio device name? Relax, I give you an example Suppose one of my audio device name is "Microphone Array (Conexant SmartAudio HD)", This device name is a string which I get from the List<>,

I want to give Font Size 10 to the letter M and rest of the letter Font Size 9, How Can I do that? In XAML I can easily do that by <Run FontSize="10">M</Run><Run FontSize="9">icrophone</Run>...rest of the string.

My Second question is Can I put that ComboBoxItem Control template in my App.xaml and later called it as a Static resource because most of my Control templates are declared in App.xaml with a proper x:Key value and later I called it on my control style as Static resource.

That's it.

By the way, thank you again for helping me in my hard times.

0 Votes 0 ·
HuiLiu-MSFT avatar image HuiLiu-MSFT MERUNKUMARMAITY-4120 ·

Hi,@ MERUNKUMARMAITY-4120. I updated my answer, you could check it out.

1 Vote 1 ·

Thank you very much sir, for the effort you put in to answer my questions and queries. Your code is Amazing, and now I can successfully change the Font Size I also understand that code and it's your remarkable skill, the way you beautifully crafted the code, only a genius can do that. But I want some more advance Font Sizing customization. By using your code, I can successfully change the Font Size with in a same string that's really amazing. But instead of we deciding which letter has bigger Font Size and which letter has smaller Font Size, we do this according to the Capital letter (Upper case) and small letter (lowercase).

I want to give bigger Font Size to the Capital letter (Upper case) and smaller Font Size to the small letter (lowercase). I give you an example suppose I get the Audio device name from that List<>, "Microphone Array (Conexant SmartAudio HD)", if you carefully see this string then you can see the M is in Capital letter (Upper case), A is also capital and so on and rest have small letter (lowercase). I want to give bigger Font Size to M, A etc and comparatively smaller Font size to the rest of the lower case letter.

To give your answer just update your answer don't delete that previous one, because all your answer is GOLD like you sir.

0 Votes 0 ·
Show more comments