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<> :
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);
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)) });
}
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 Text="{Binding Name}"/>
</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}" ,
The code I given, is to show available audio devices in a computer systems.
But in this case my ComboBox content is added from a List<> not from a ComboBox item, all of my code is perfect and I can run successfully.
But my problem is, my ComboBox can not refresh at run time. I give an example, suppose I have three audio devices and I run my Application, my application successfully detect the three devices but If I add more devices at that time, suppose I add two more devices that means total five devices but still my application is showing three devices but it only showing five devices if I restart my application.
How Can I refresh that? I know that, updating and refreshing ComboBox is too much easy, if my Combobox Item source is binded from a ViewModel and if I implement INotifyPropertyChanged event there, but in my case the ComboBox item source is from a List<>. That's why it's too much difficult.
What I have tried:
I tried to implement
INotifyPropertyChanged
but there only error.