From 86bf5620c1258878c7e2c93ef701db15f134658a Mon Sep 17 00:00:00 2001 From: Tim Kennedy Date: Thu, 23 Jul 2026 20:22:56 -0500 Subject: [PATCH 1/3] Refactor GPU info retrieval and UI binding Refactored video controller (GPU) data retrieval to fetch all details in a single call, replacing per-DeviceID queries. Updated ViewModel to store controller info as a list of dictionaries and adjusted UI binding and ComboBox logic to match. Improved helper methods for null handling and localization, and added new resource strings for "Unknown" and "billion". Enhanced layout and resource usage for better maintainability and localization support. --- TimVer/Helpers/VideoHelpers.cs | 139 ++++++++++++++-------------- TimVer/Languages/Strings.en-US.xaml | 2 + TimVer/ViewModels/VideoViewModel.cs | 16 +++- TimVer/Views/VideoPage.xaml | 4 +- 4 files changed, 85 insertions(+), 76 deletions(-) diff --git a/TimVer/Helpers/VideoHelpers.cs b/TimVer/Helpers/VideoHelpers.cs index f1d278d..dc14fc9 100644 --- a/TimVer/Helpers/VideoHelpers.cs +++ b/TimVer/Helpers/VideoHelpers.cs @@ -7,54 +7,32 @@ namespace TimVer.Helpers; /// internal static class VideoHelpers { - #region Get list of video controllers + #region Get all video controllers with full info /// - /// Gets the DeviceID for each video controller. + /// Gets all video controllers with their complete information. /// - /// A List of controller DeviceID's - internal static List GetGPUList() + /// A list of dictionaries containing video controller information + internal static List> GetAllVideoControllers() { const string scope = @"\\.\root\CIMV2"; const string dialect = "WQL"; - const string query = "SELECT DeviceID From Win32_VideoController"; - - try - { - List devices = []; - using CimSession cimSession = CimSession.Create(null); - devices.AddRange(cimSession.QueryInstances(scope, dialect, query) - .Select(drive => drive.CimInstanceProperties["DeviceID"]?.Value!.ToString())); - return devices; - } - catch (Exception ex) - { - _log.Error(ex, "Win32_VideoController call failed."); - return []; - } - } - #endregion Get list of video controllers - - #region Get WMI info for video controller(s) - /// - /// Get CIM value from Win32_VideoController - /// - /// DeviceID of the controller - /// Video controller information as a Dictionary - public static Dictionary GetVideoInfo(string controller) - { - const string scope = @"\\.\root\CIMV2"; - const string dialect = "WQL"; - string query = "SELECT DeviceID, Name, Description, CurrentHorizontalResolution, CurrentVerticalResolution, " + - "CurrentRefreshRate, CurrentBitsPerPixel, VideoProcessor, " + - "AdapterRam, CurrentNumberOfColors " + - $"From Win32_VideoController Where DeviceID = '{controller}'"; + const string query = "SELECT DeviceID, Name, Description, CurrentHorizontalResolution, CurrentVerticalResolution, " + + "CurrentRefreshRate, CurrentBitsPerPixel, VideoProcessor, " + + "AdapterRam, CurrentNumberOfColors " + + "FROM Win32_VideoController"; try { + List> controllers = []; using CimSession cimSession = CimSession.Create(null); - IEnumerable instance = cimSession.QueryInstances(scope, dialect, query); Stopwatch sw = Stopwatch.StartNew(); - Dictionary info = instance - .Select(gpu => new Dictionary + + var instances = cimSession.QueryInstances(scope, dialect, query); + + int displayCount = GetDisplayCount(); + + foreach (var gpu in instances) + { + Dictionary controllerInfo = new() { [GetStringResource("GraphicsInfo_GraphicsAdapter")] = CimStringProperty(gpu, "Name"), [GetStringResource("GraphicsInfo_AdapterType")] = CimStringProperty(gpu, "VideoProcessor"), @@ -65,27 +43,24 @@ public static Dictionary GetVideoInfo(string controller) //[GetStringResource("GraphicsInfo_AdapterRAM")] = FormatAdapterRamInfo(gpu), [GetStringResource("GraphicsInfo_BitsPerPixel")] = CimStringProperty(gpu, "CurrentBitsPerPixel"), [GetStringResource("GraphicsInfo_NumberOfColors")] = FormatColorsInfo(gpu), - [GetStringResource("GraphicsInfo_NumberOfDisplays")] = GetDisplayCount() - .ToString(CultureInfo.InvariantCulture), - }) - .FirstOrDefault()!; + [GetStringResource("GraphicsInfo_NumberOfDisplays")] = displayCount.ToString(CultureInfo.InvariantCulture), + }; + controllers.Add(controllerInfo); + _log.Debug($"Added video controller: {controllerInfo[GetStringResource("GraphicsInfo_GraphicsAdapter")]} Device ID is " + + $"{controllerInfo[GetStringResource("GraphicsInfo_DeviceID")]}"); + } + sw.Stop(); - _log.Debug($"Getting video controller info took {sw.Elapsed.TotalMilliseconds:N2} ms"); - return info ?? new Dictionary - { - [GetStringResource("MsgText_NotAvailable")] = string.Empty - }; + _log.Debug($"Getting all video controller info took {sw.Elapsed.TotalMilliseconds:N2} ms. Found {controllers.Count} controller(s)"); + return controllers; } catch (Exception ex) { _log.Error(ex, "Win32_VideoController call failed."); - return new Dictionary - { - [GetStringResource("MsgText_NotAvailable")] = string.Empty - }; + return []; } } - #endregion Get WMI info for video controller(s) + #endregion Get all video controllers with full info #region Get WMI information formatted as string /// @@ -99,7 +74,12 @@ private static string CimStringProperty(CimInstance instance, string name) if (instance.CimInstanceProperties[name] == null) { _log.Debug($"Value for {name} was null"); - return "Not Available"; + return GetStringResource("MsgText_NotAvailable"); + } + if (instance.CimInstanceProperties[name].Value == null) + { + _log.Debug($"Value for {name} was null"); + return GetStringResource("MsgText_NotAvailable"); } return instance.CimInstanceProperties[name].Value.ToString()!; } @@ -113,8 +93,13 @@ private static string CimStringProperty(CimInstance instance, string name) /// A formatted string. private static string FormatCurrentRefresh(CimInstance instance) { - string? rate = instance.CimInstanceProperties["CurrentRefreshRate"].Value.ToString(); - return $"{rate} Hz"; + var rateValue = instance.CimInstanceProperties["CurrentRefreshRate"]?.Value; + if (rateValue == null) + { + _log.Debug("CurrentRefreshRate was null"); + return GetStringResource("MsgText_NotAvailable"); + } + return $"{rateValue} Hz"; } #endregion Get current refresh rate @@ -126,9 +111,15 @@ private static string FormatCurrentRefresh(CimInstance instance) /// A formatted string. private static string FormatResolution(CimInstance instance) { - string? horz = instance.CimInstanceProperties["CurrentHorizontalResolution"].Value.ToString(); - string? vert = instance.CimInstanceProperties["CurrentVerticalResolution"].Value.ToString(); - return $"{horz} x {vert}"; + object? horzValue = instance.CimInstanceProperties["CurrentHorizontalResolution"]?.Value; + object? vertValue = instance.CimInstanceProperties["CurrentVerticalResolution"]?.Value; + + if (horzValue == null || vertValue == null) + { + _log.Debug("Resolution properties were null"); + return GetStringResource("MsgText_NotAvailable"); + } + return $"{horzValue} x {vertValue}"; } #endregion Get current resolution @@ -139,14 +130,14 @@ private static string FormatResolution(CimInstance instance) /// The CimInstance /// A formatted string. #pragma warning disable RCS1213 // Remove unused member declaration - // Leaving this method in place for now as I may add it back in the future. It is currently commented out in the GetVideoInfo method. + // Leaving this method in place for now as I may add it back in the future. It is currently commented out in the GetAllVideoControllers method. private static string FormatAdapterRamInfo(CimInstance instance) #pragma warning restore RCS1213 // Remove unused member declaration { if (instance.CimInstanceProperties["AdapterRam"] == null) { _log.Debug("Value for AdapterRam was null"); - return "Not Available"; + return GetStringResource("MsgText_NotAvailable"); } double ram = Convert.ToDouble(instance.CimInstanceProperties["AdapterRam"].Value, CultureInfo.InvariantCulture); if (ram >= Math.Pow(1024, 3)) @@ -197,20 +188,30 @@ private static string FormatColorsInfo(CimInstance instance) return GetStringResource("MsgText_NotAvailable"); } - ulong colors = Convert.ToUInt64(instance.CimInstanceProperties["CurrentNumberOfColors"].Value, CultureInfo.InvariantCulture); - if (colors >= (ulong)Math.Pow(1024, 2)) + double colors = Convert.ToDouble(instance.CimInstanceProperties["CurrentNumberOfColors"].Value, CultureInfo.InvariantCulture); + if (colors == 0) + { + _log.Debug("Value for CurrentNumberOfColors was 0"); + return GetStringResource("MsgText_NotAvailable"); + } + else if (colors >= Math.Pow(1024, 3)) + { + colors /= (double)Math.Pow(1024, 3); + return $"{colors:N2} {GetStringResource("MsgText_Billion")}"; + } + else if (colors >= Math.Pow(1024, 2)) { - colors /= (ulong)Math.Pow(1024, 3); - return string.Format(CultureInfo.CurrentCulture, $"{colors:N2} {GetStringResource("MsgText_Million")}"); + colors /= (double)Math.Pow(1024, 2); + return $"{colors:N2} {GetStringResource("MsgText_Million")}"; } - else if (colors >= (ulong)Math.Pow(1024, 1)) + else if (colors >= Math.Pow(1024, 1)) { - colors /= (ulong)Math.Pow(1024, 2); - return string.Format(CultureInfo.CurrentCulture, $"{colors:N2} {GetStringResource("MsgText_Thousand")}"); + colors /= (double)Math.Pow(1024, 1); + return $"{colors:N2} {GetStringResource("MsgText_Thousand")}"; } else { - return string.Format(CultureInfo.CurrentCulture, $"{colors:N0}"); + return $"{colors:N0}"; } } #endregion Total Colors diff --git a/TimVer/Languages/Strings.en-US.xaml b/TimVer/Languages/Strings.en-US.xaml index 45f71a5..a930258 100644 --- a/TimVer/Languages/Strings.en-US.xaml +++ b/TimVer/Languages/Strings.en-US.xaml @@ -152,6 +152,7 @@ Select Adapter If there is more than one video adapter installed use the drop-down to select the adapter to display. + Unknown BIOS BIOS Manufacturer @@ -190,6 +191,7 @@ Do you want to go to the release page? A newer release ({0}) has been found. No newer releases were found. + billion Current page was copied to the clipboard Item was copied to the clipboard Copy to clipboard failed diff --git a/TimVer/ViewModels/VideoViewModel.cs b/TimVer/ViewModels/VideoViewModel.cs index a0b0498..605ce55 100644 --- a/TimVer/ViewModels/VideoViewModel.cs +++ b/TimVer/ViewModels/VideoViewModel.cs @@ -7,7 +7,7 @@ internal sealed partial class VideoViewModel : ObservableObject #region Constructor public VideoViewModel() { - if (VideoInfoCollection == null) + if (AllControllers == null || AllControllers.Count == 0) { LoadData(); } @@ -17,8 +17,10 @@ public VideoViewModel() #region Load data for video properties private static void LoadData() { - ControllerList = VideoHelpers.GetGPUList()!; - VideoInfoCollection = VideoHelpers.GetVideoInfo(ControllerList[0]); + AllControllers = VideoHelpers.GetAllVideoControllers(); + ControllerList = [.. AllControllers.Select(c => + c.GetValueOrDefault( GetStringResource("GraphicsInfo_GraphicsAdapter"), GetStringResource("GraphicsInfo_Unknown")))]; + VideoInfoCollection = AllControllers.FirstOrDefault(); } #endregion Load data for video properties @@ -30,15 +32,19 @@ private static void LoadData() [RelayCommand] private static void SelectVideoController(SelectionChangedEventArgs e) { - if (e.Source is ComboBox box) + if (e.Source is ComboBox box && AllControllers?.Count > 0 + && box.SelectedIndex >= 0 && box.SelectedIndex < AllControllers.Count) { - VideoInfoCollection = VideoHelpers.GetVideoInfo(ControllerList![box.SelectedIndex]); + VideoInfoCollection = AllControllers[box.SelectedIndex]; VideoPage.Instance!.VideoGrid.ItemsSource = VideoInfoCollection; + _log.Debug($"Selected video controller index: {box.SelectedIndex}"); } } #endregion Relay command #region Collections + private static List>? AllControllers { get; set; } + public static List? ControllerList { get; private set; } public static Dictionary? VideoInfoCollection { get; private set; } diff --git a/TimVer/Views/VideoPage.xaml b/TimVer/Views/VideoPage.xaml index 0dc9b19..1e421cc 100644 --- a/TimVer/Views/VideoPage.xaml +++ b/TimVer/Views/VideoPage.xaml @@ -63,9 +63,9 @@ - - Date: Thu, 23 Jul 2026 20:27:19 -0500 Subject: [PATCH 2/3] Bump version number --- TimVer/TimVer.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TimVer/TimVer.csproj b/TimVer/TimVer.csproj index 7f44c58..f0276ea 100644 --- a/TimVer/TimVer.csproj +++ b/TimVer/TimVer.csproj @@ -26,8 +26,8 @@ true - 0.14.0 - + 0.14.1 + Beta From 53f9727585ecd3aae77bfffe9884d6ab39adff2c Mon Sep 17 00:00:00 2001 From: Tim Kennedy Date: Fri, 24 Jul 2026 11:58:47 -0500 Subject: [PATCH 3/3] Additional fixes for Graphics page --- TimVer/Helpers/VideoHelpers.cs | 37 ++++++----------------------- TimVer/Languages/Strings.en-US.xaml | 5 +++- TimVer/ViewModels/VideoViewModel.cs | 1 - 3 files changed, 11 insertions(+), 32 deletions(-) diff --git a/TimVer/Helpers/VideoHelpers.cs b/TimVer/Helpers/VideoHelpers.cs index dc14fc9..0c677dc 100644 --- a/TimVer/Helpers/VideoHelpers.cs +++ b/TimVer/Helpers/VideoHelpers.cs @@ -40,14 +40,14 @@ internal static List> GetAllVideoControllers() [GetStringResource("GraphicsInfo_DeviceID")] = CimStringProperty(gpu, "DeviceID"), [GetStringResource("GraphicsInfo_CurrentResolution")] = FormatResolution(gpu), [GetStringResource("GraphicsInfo_CurrentRefreshRate")] = FormatCurrentRefresh(gpu), - //[GetStringResource("GraphicsInfo_AdapterRAM")] = FormatAdapterRamInfo(gpu), + [GetStringResource("GraphicsInfo_AdapterRAM")] = FormatAdapterRamInfo(gpu), [GetStringResource("GraphicsInfo_BitsPerPixel")] = CimStringProperty(gpu, "CurrentBitsPerPixel"), [GetStringResource("GraphicsInfo_NumberOfColors")] = FormatColorsInfo(gpu), [GetStringResource("GraphicsInfo_NumberOfDisplays")] = displayCount.ToString(CultureInfo.InvariantCulture), }; controllers.Add(controllerInfo); - _log.Debug($"Added video controller: {controllerInfo[GetStringResource("GraphicsInfo_GraphicsAdapter")]} Device ID is " + - $"{controllerInfo[GetStringResource("GraphicsInfo_DeviceID")]}"); + _log.Debug($"Found video controller: {controllerInfo[GetStringResource("GraphicsInfo_GraphicsAdapter")]} (Device ID is " + + $"{controllerInfo[GetStringResource("GraphicsInfo_DeviceID")]})"); } sw.Stop(); @@ -56,7 +56,7 @@ internal static List> GetAllVideoControllers() } catch (Exception ex) { - _log.Error(ex, "Win32_VideoController call failed."); + _log.Error(ex, $"Win32_VideoController call failed. {ex.Message} - {ex.InnerException}"); return []; } } @@ -179,7 +179,7 @@ private static int GetDisplayCount() /// Gets to number of colors. /// /// The CimInstance - /// A formatted string. + /// A string formatted for the current culture. private static string FormatColorsInfo(CimInstance instance) { if (instance.CimInstanceProperties["CurrentNumberOfColors"] == null) @@ -188,31 +188,8 @@ private static string FormatColorsInfo(CimInstance instance) return GetStringResource("MsgText_NotAvailable"); } - double colors = Convert.ToDouble(instance.CimInstanceProperties["CurrentNumberOfColors"].Value, CultureInfo.InvariantCulture); - if (colors == 0) - { - _log.Debug("Value for CurrentNumberOfColors was 0"); - return GetStringResource("MsgText_NotAvailable"); - } - else if (colors >= Math.Pow(1024, 3)) - { - colors /= (double)Math.Pow(1024, 3); - return $"{colors:N2} {GetStringResource("MsgText_Billion")}"; - } - else if (colors >= Math.Pow(1024, 2)) - { - colors /= (double)Math.Pow(1024, 2); - return $"{colors:N2} {GetStringResource("MsgText_Million")}"; - } - else if (colors >= Math.Pow(1024, 1)) - { - colors /= (double)Math.Pow(1024, 1); - return $"{colors:N2} {GetStringResource("MsgText_Thousand")}"; - } - else - { - return $"{colors:N0}"; - } + ulong colors = Convert.ToUInt64(instance.CimInstanceProperties["CurrentNumberOfColors"].Value, CultureInfo.CurrentCulture); + return $"{colors:N0}"; } #endregion Total Colors } diff --git a/TimVer/Languages/Strings.en-US.xaml b/TimVer/Languages/Strings.en-US.xaml index a930258..c68a28d 100644 --- a/TimVer/Languages/Strings.en-US.xaml +++ b/TimVer/Languages/Strings.en-US.xaml @@ -191,7 +191,6 @@ Do you want to go to the release page? A newer release ({0}) has been found. No newer releases were found. - billion Current page was copied to the clipboard Item was copied to the clipboard Copy to clipboard failed @@ -536,4 +535,8 @@ + + + + \ No newline at end of file diff --git a/TimVer/ViewModels/VideoViewModel.cs b/TimVer/ViewModels/VideoViewModel.cs index 605ce55..6fe4cc3 100644 --- a/TimVer/ViewModels/VideoViewModel.cs +++ b/TimVer/ViewModels/VideoViewModel.cs @@ -37,7 +37,6 @@ private static void SelectVideoController(SelectionChangedEventArgs e) { VideoInfoCollection = AllControllers[box.SelectedIndex]; VideoPage.Instance!.VideoGrid.ItemsSource = VideoInfoCollection; - _log.Debug($"Selected video controller index: {box.SelectedIndex}"); } } #endregion Relay command