A different take on Deep Copy
Posted by xcalibur37 in .Net, C#, Silverlight, WPF on May 15, 2012
I have seen several examples of different methods to perform a deep copy in C#. The most common one is with a Stream, implemented like this:
public static T DeepClone<T>(T obj)
{
using (var ms = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(ms, obj);
ms.Position = 0;
return (T) formatter.Deserialize(ms);
}
}
That is certainly one way to do it. But I like using a different approach. That is with my good friend Reflection:
/// <summary>
/// Copies all public properties from one object to another.
/// </summary>
/// <param name="source"></param>
/// <param name="destination"></param>
public static void DeepCopy(object source, object destination)
{
// Get properties
var propertyInfos = source.GetType().GetProperties();
// Evaluate
if (propertyInfos.Length > 0)
{
foreach (var propInfo in propertyInfos)
{
// Process only public properties
if (propInfo.CanWrite)
{
object value = propInfo.GetValue(source, null);
propInfo.SetValue(destination, value, null);
// Evaluate
if (value != null)
{
var newPropInfo = value.GetType().GetProperties();
if (newPropInfo.Length > 0)
{
// Copy properties for each child where necessary
DeepCopy(
source.GetType().GetProperty(propInfo.Name),
destination.GetType().GetProperty(propInfo.Name));
}
}
}
}
}
}
This will work in Silverlight because of the if block for propInfo.CanWrite. This is added because based on Silverlights rigid security constraints we can only copy public properties.
That’s all for now.
Creating a ViewModel Base – Part III: Subscribing can make all the difference.
Posted by xcalibur37 in .Net, C# on February 6, 2012
In this post I am going to introduce the idea of subscribing to property change events.
Note: Please read Part I before continuing here.
Note: Please read Part II before continuing here.
The purpose of this is to cover a potentially annoying situation:
Scenario: Let’s say you have a ViewModel, and inside that you have another ViewModel acting as a property like this:
private MyCustomObject _customObjectInstance;
public MyCustomObject CustomObjectInstance
{
get
{
return _customObjectInstance;
}
set
{
SetValue("CustomObjectInstance", ref _customObjectInstance, ref value);
}
}
Okay, now you want your UI bound to the current ViewModel to change every time CustomObjectInstance.MeterReading changes.
Q: (Panic moment) So, how do I do that without breaking my wonderful abstraction?
A: Implementing an ability to Subscribe to a nested property. All it takes is a little reflection and patience.
The idea is that we tell that property to fire a specific Action whenever it is changed in our ViewModelBase.
The first thing we will need to add is a private list of subscriptions:
/// <summary> /// Subscription list. /// </summary> private readonly List<Tuple<string, Action<object>>> _subscriptions;
Instead of creating yet another custom object, I decided to use a Tuple because they are convenient.
- The string value will serve as the name of the property you want to subscribe to.
- The Action will serve as the Action you wish to call when the property is changed. The object parameter allows you to return an object if needed.
Next, we make sure to create a new instance of _subscriptions in the Constructor:
/// <summary>
/// Default constructor.
/// </summary>
protected ViewModelBase()
{
_subscriptions = new List<Tuple<string, Action<object>>>();
}
Now we will expose a Subscribe method.
/// <summary>
/// Subscribes an action to a specific property that will be called
/// during that property's OnPropertyChanged event.
/// </summary>
/// <param name="propertyName"></param>
/// <param name="onChange"></param>
public void Subscribe(string propertyName, Action<object> onChange)
{
// Verify property
var propInfo = this.GetType().GetProperty(propertyName);
// If valid, add to subscription pool.
if (propInfo != null)
{
_subscriptions.Add(
new Tuple<string, Action<object>>(propertyName, onChange));
}
else
{
// Invalid property name provided.
throw new Exception(
"Property \"" + propertyName + "\" could not be " +
"found for type \"" + this.GetType().ToString() + "\"!");
}
}
This idea here is fairly simple:
- We pass our property name and intended Action that will fire OnPropertyChanged.
- If the property name is not valid, we will throw an exception to ensure we didn’t pass invalid information into our Subscribe method.
Q: Alright, now we have a nice Tuple-list full of property names and Actions. Now what?
A: Glad you asked. Here comes the hard part:
/// <summary>
/// Processes existing subscriptions matching the provided property name.
/// </summary>
/// <param name="propertyName"></param>
private void ProcessSubscriptions(string propertyName)
{
// Get matching subscriptions
var subList =
(from p in _subscriptions
where p.Item1 == propertyName
select p).ToList();
// Check if any matches were found.
if (subList.Any())
{
// Process actions
foreach (var sub in subList)
{
// Evaluate action
var onChange = sub.Item2;
if (onChange != null)
{
// Get property value by name
var propInfo = this.GetType().GetProperty(propertyName);
var propValue = propInfo.GetValue(this, null);
// Invoke action
onChange(propValue);
}
}
}
}
ProcessSubscriptions does the following:
- Looks up a specific property by name in _subscriptions and gets a list of all entries that are registered for that property.
- Loop: If a specific entry has a valid Action assigned to it, it will use reflection to get that property value and pass it to the action (as out object parameter mentioned earlier).
So, the last piece is making sure ProcessSubscriptions is fired when the property has been changed. And that is as easy as augmenting our trusted OnPropertyChanged method:
/// <summary>
/// Calls the PropertyChanged event
/// </summary>
/// <param name="propertyName"></param>
/// <param name="onChanged"></param>
protected void OnPropertyChanged(string propertyName, Action onChanged = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
// Call handler
handler(this, new PropertyChangedEventArgs(propertyName));
// Subscriptions
ProcessSubscriptions(propertyName);
// On changed
if (onChanged != null)
{
onChanged();
}
}
}
No worries if your specific property being changed is without entries. ProcessSubscriptions only acts on what is present in _subscriptions, so no entries means it just moves on.
Here is how you would use it in your parent ViewModel:
CustomObjectInstance.Subscribe("MeterReading", obj => MyActionThatDoesStuff());
Now, every time CustomObjectInstance.MeterReading is updated, the MyActionThatDoesStuff Action will be called allowing you to always have the latest values from your nested properties.
Here is our new ViewModelBase in it’s entirety:
/// <summary>
/// Extends the INotifyPropertyChanged interface to the class properties.
/// </summary>
public abstract class ViewModelBase : INotifyPropertyChanged
{
#region Members
/// <summary>
/// Subscription list.
/// </summary>
private readonly List<Tuple<string, Action<object>>> _subscriptions;
#endregion
#region Constructors
/// <summary>
/// Default constructor.
/// </summary>
protected ViewModelBase()
{
_subscriptions = new List<Tuple<string, Action<object>>>();
}
#endregion
#region Methods
/// <summary>
/// To be used within the "set" accessor in each property.
/// This invokes the OnPropertyChanged method.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="name"></param>
/// <param name="value"></param>
/// <param name="newValue"></param>
/// <param name="onChanged"></param>
protected void SetValue<T>(string name, ref T value, ref T newValue,
Action onChanged = null)
{
if (newValue != null)
{
if (!newValue.Equals(value))
{
value = newValue;
OnPropertyChanged(name, onChanged);
}
}
else
{
value = default(T);
}
}
#endregion
#region INotifyPropertyChanged
/// <summary>
/// The PropertyChanged event handler.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Calls the PropertyChanged event
/// </summary>
/// <param name="propertyName"></param>
/// <param name="onChanged"></param>
protected void OnPropertyChanged(string propertyName, Action onChanged = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
// Call handler
handler(this, new PropertyChangedEventArgs(propertyName));
// Subscriptions
ProcessSubscriptions(propertyName);
// On changed
if (onChanged != null)
{
onChanged();
}
}
}
/// <summary>
/// Subscribes an action to a specific property that will be called
/// during that property's OnPropertyChanged event.
/// </summary>
/// <param name="propertyName"></param>
/// <param name="onChange"></param>
public void Subscribe(string propertyName, Action<object> onChange)
{
// Verify property
var propInfo = this.GetType().GetProperty(propertyName);
// If valid, add to subscription pool.
if (propInfo != null)
{
_subscriptions.Add(
new Tuple<string, Action<object>>(propertyName, onChange));
}
else
{
// Invalid property name provided.
throw new Exception(
"Property \"" + propertyName + "\" could not be " +
"found for type \"" + this.GetType().ToString() + "\"!");
}
}
/// <summary>
/// Processes existing subscriptions matching the provided property name.
/// </summary>
/// <param name="propertyName"></param>
private void ProcessSubscriptions(string propertyName)
{
// Get matching subscriptions
var subList =
(from p in _subscriptions
where p.Item1 == propertyName
select p).ToList();
// Check if any matches were found.
if (subList.Any())
{
// Process actions
foreach (var sub in subList)
{
// Evaluate action
var onChange = sub.Item2;
if (onChange != null)
{
// Get property value by name
var propInfo = this.GetType().GetProperty(propertyName);
var propValue = propInfo.GetValue(this, null);
// Invoke action
onChange(propValue);
}
}
}
}
#endregion
}
I hope this has been helpful for you.
Creating a ViewModel Base – Part II: Let’s call an Action when the property changes.
Posted by xcalibur37 in .Net, C# on February 6, 2012
Yes, I am going to do exactly what the title suggests.
Note: Please read Part I before continuing here.
Alright, so let’s look at our ViewModelBase from Part I:
/// <summary>
/// Extends the INotifyPropertyChanged interface to the class properties.
/// </summary>
public abstract class ViewModelBase : INotifyPropertyChanged
{
#region Methods
/// <summary>
/// To be used within the "set" accessor in each property.
/// This invokes the OnPropertyChanged method.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="name"></param>
/// <param name="value"></param>
/// <param name="newValue"></param>
protected void SetValue<T>(string name, ref T value, ref T newValue)
{
if (newValue != null)
{
if (!newValue.Equals(value))
{
value = newValue;
OnPropertyChanged(name);
}
}
else
{
value = default(T);
}
}
#endregion
#region INotifyPropertyChanged
/// <summary>
/// The PropertyChanged event handler.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Calls the PropertyChanged event
/// </summary>
/// <param name="propertyName"></param>
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
Q: Okay, so what now?
A: Glad you asked. Let’s augment the above class:
/// <summary>
/// Extends the INotifyPropertyChanged interface to the class properties.
/// </summary>
public abstract class ViewModelBase : INotifyPropertyChanged
{
#region Methods
/// <summary>
/// To be used within the "set" accessor in each property.
/// This invokes the OnPropertyChanged method.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="name"></param>
/// <param name="value"></param>
/// <param name="newValue"></param>
protected void SetValue<T>(string name, ref T value, ref T newValue, Action onChanged = null)
{
if (newValue != null)
{
if (!newValue.Equals(value))
{
value = newValue;
OnPropertyChanged(name, onChanged);
}
}
else
{
value = default(T);
}
}
#endregion
#region INotifyPropertyChanged
/// <summary>
/// The PropertyChanged event handler.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Calls the PropertyChanged event
/// </summary>
/// <param name="propertyName"></param>
protected void OnPropertyChanged(string propertyName, Action onChanged = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
// On changed
if (onChanged != null)
{
onChanged();
}
}
}
#endregion
}
Essentially, we added an Action as a parameter to the SetValue method. This is then passed whenever the OnPropertyChanged method is called and executed after the event fires (if it is not null).
Using it is as easy as this:
private string phoneNumberValue = String.Empty;
public string PhoneNumber
{
get
{
return this.phoneNumberValue;
}
set
{
SetValue("PhoneNumber", ref this.phoneNumberValue, ref value, () => { UpdateSomeOtherUISection(); });
}
}
It’s not a common need, but sometimes something like this can really get you out of a bind when you need to update other areas of your application when this property changes.
Try it sometime.
Creating a ViewModel Base – Part I: Cleaning up your custom objects
Posted by xcalibur37 in .Net, C# on February 6, 2012
Having used properties in an observable collection, you are probably familiar with implementing INotifyPropertyChanged in your custom objects.
A property utilizing this implementation would look like this:
private string phoneNumberValue = String.Empty;
public string PhoneNumber
{
get
{
return this.phoneNumberValue;
}
set
{
if (value != this.phoneNumberValue)
{
this.phoneNumberValue = value;
NotifyPropertyChanged("PhoneNumber");
}
}
}
To remedy this repetitive implementation, I created this:
/// <summary>
/// Extends the INotifyPropertyChanged interface to the class properties.
/// </summary>
public abstract class ViewModelBase : INotifyPropertyChanged
{
#region Methods
/// <summary>
/// To be used within the "set" accessor in each property.
/// This invokes the OnPropertyChanged method.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="name"></param>
/// <param name="value"></param>
/// <param name="newValue"></param>
protected void SetValue<T>(string name, ref T value, ref T newValue)
{
if (newValue != null)
{
if (!newValue.Equals(value))
{
value = newValue;
OnPropertyChanged(name);
}
}
else
{
value = default(T);
}
}
#endregion
#region INotifyPropertyChanged
/// <summary>
/// The PropertyChanged event handler.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Calls the PropertyChanged event
/// </summary>
/// <param name="propertyName"></param>
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
Then, all we need to do is inherit ViewModelBase in our custom object.
Now, our property would look something like this:
private string phoneNumberValue = String.Empty;
public string PhoneNumber
{
get
{
return this.phoneNumberValue;
}
set
{
SetValue("PhoneNumber", ref this.phoneNumberValue, ref value);
}
}
So, what was the reason for doing this?
- It avoids directly implementing INotifyPropertyChanged. This allows the ability to start up objects with this implementation much faster.
- Less coding and potential messes involving the set operator.
Customizing the WPF MenuItem in XAML
Posted by xcalibur37 in WPF on December 17, 2011
So, I was spending some time trying to figure out how to make a menu in WPF scrollable before it maxed out on my screen height. Well, unfortunately, the only way to do it is to override the default Aero template for the MenuItem in WPF. How do we do that?
Like this:
- Get the default WPF themes here.
- Override the Aero MenuItem template and components. Here’s the section to change. Notice the addition of “MaxHeight” on the “SubMenuScrollViewer” object. It’s really that simple.
<Popup x:Name="PART_Popup"
AllowsTransparency="true"
Placement="Right"
VerticalOffset="-3"
HorizontalOffset="-2"
IsOpen="{Binding Path=IsSubmenuOpen,RelativeSource={RelativeSource TemplatedParent}}"
Focusable="false"
PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}">
<theme:SystemDropShadowChrome Name="Shdw" Color="Transparent">
<ContentControl Name="SubMenuBorder"
Template="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type FrameworkElement}, ResourceId=SubmenuContent}}"
IsTabStop="false">
<ScrollViewer Name="SubMenuScrollViewer" CanContentScroll="true" MaxHeight="400"
Style="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type FrameworkElement}, ResourceId=MenuScrollViewer}}">
<Grid RenderOptions.ClearTypeHint="Enabled">
<Canvas Height="0" Width="0" HorizontalAlignment="Left" VerticalAlignment="Top">
<Rectangle
Height="{Binding ElementName=SubMenuBorder,Path=ActualHeight}"
Width="{Binding ElementName=SubMenuBorder,Path=ActualWidth}"
Fill="{StaticResource SubMenuBackgroundBrush}" />
</Canvas>
<ItemsPresenter Name="ItemsPresenter" Margin="2"
KeyboardNavigation.TabNavigation="Cycle"
KeyboardNavigation.DirectionalNavigation="Cycle"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
Grid.IsSharedSizeScope="true"/>
</Grid>
</ScrollViewer>
</ContentControl>
</theme:SystemDropShadowChrome>
</Popup>
Here is a screenshot of the desired effect:
Here is the full Resource Dictionary xaml:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:theme="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero">
<MenuScrollingVisibilityConverter x:Key="MenuScrollingVisibilityConverter"/>
<Geometry x:Key="DownArrow">M 0,0 L 3.5,4 L 7,0 Z</Geometry>
<Geometry x:Key="UpArrow">M 0,4 L 3.5,0 L 7,4 Z</Geometry>
<Geometry x:Key="RightArrow">M 0,0 L 4,3.5 L 0,7 Z</Geometry>
<Geometry x:Key="Checkmark">M 0,5.1 L 1.7,5.2 L 3.4,7.1 L 8,0.4 L 9.2,0 L 3.3,10.8 Z</Geometry>
<SolidColorBrush x:Key="SubMenuBackgroundBrush" Color="#FFF5F5F5" />
<LinearGradientBrush x:Key="MenuItemSelectionFill"
StartPoint="0,0"
EndPoint="0,1">
<LinearGradientBrush.GradientStops>
<GradientStop Color="#34C5EBFF"
Offset="0"/>
<GradientStop Color="#3481D8FF"
Offset="1"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="MenuItemPressedFill"
StartPoint="0,0"
EndPoint="0,1">
<LinearGradientBrush.GradientStops>
<GradientStop Color="#28717070"
Offset="0"/>
<GradientStop Color="#50717070"
Offset="0.75"/>
<GradientStop Color="#90717070"
Offset="1"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
<Style x:Key="{x:Static MenuItem.SeparatorStyleKey}"
TargetType="{x:Type Separator}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Separator}">
<Grid SnapsToDevicePixels="true" Margin="0,6,0,4">
<Rectangle Height="1"
Margin="30,0,1,1"
Fill="#E0E0E0"/>
<Rectangle Height="1"
Margin="30,1,1,0"
Fill="White"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<ControlTemplate x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type MenuItem}, ResourceId=TopLevelItemTemplateKey}"
TargetType="{x:Type MenuItem}">
<Grid SnapsToDevicePixels="true">
<Rectangle x:Name="OuterBorder"
RadiusX="2"
RadiusY="2"/>
<Rectangle Name="Bg"
Margin="1"
Fill="{TemplateBinding MenuItem.Background}"
Stroke="{TemplateBinding MenuItem.BorderBrush}"
StrokeThickness="1"
RadiusX="1"
RadiusY="1"/>
<Rectangle x:Name="InnerBorder"
Margin="2"/>
<DockPanel>
<ContentPresenter x:Name="Icon"
Margin="4,0,6,0"
VerticalAlignment="Center"
ContentSource="Icon"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
<Path x:Name="GlyphPanel"
Margin="7,0,0,0"
Visibility="Collapsed"
VerticalAlignment="Center"
Fill="{TemplateBinding MenuItem.Foreground}"
FlowDirection="LeftToRight"
Data="{StaticResource Checkmark}"/>
<ContentPresenter ContentSource="Header"
Margin="{TemplateBinding MenuItem.Padding}"
RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</DockPanel>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="Icon"
Value="{x:Null}">
<Setter TargetName="Icon"
Property="Visibility"
Value="Collapsed"/>
</Trigger>
<Trigger Property="IsChecked"
Value="true">
<Setter TargetName="GlyphPanel"
Property="Visibility"
Value="Visible"/>
<Setter TargetName="Icon"
Property="Visibility"
Value="Collapsed"/>
</Trigger>
<Trigger Property="IsHighlighted"
Value="true">
<Setter TargetName="Bg"
Property="Stroke"
Value="#90717070"/>
<Setter TargetName="OuterBorder"
Property="Stroke"
Value="#50FFFFFF"/>
<Setter TargetName="InnerBorder"
Property="Stroke"
Value="#50FFFFFF"/>
</Trigger>
<Trigger Property="IsKeyboardFocused"
Value="true">
<Setter TargetName="Bg"
Property="Stroke"
Value="#E0717070"/>
<Setter TargetName="Bg"
Property="Fill"
Value="{StaticResource MenuItemPressedFill}"/>
<Setter TargetName="InnerBorder"
Property="Stroke"
Value="#50747272"/>
</Trigger>
<Trigger Property="IsEnabled"
Value="false">
<Setter Property="Foreground"
Value="#FF9A9A9A"/>
<Setter TargetName="GlyphPanel"
Property="Fill"
Value="#848589"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<ControlTemplate x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type MenuItem}, ResourceId=TopLevelHeaderTemplateKey}"
TargetType="{x:Type MenuItem}">
<Grid SnapsToDevicePixels="true">
<Rectangle x:Name="OuterBorder"
RadiusX="2"
RadiusY="2"/>
<Rectangle Name="Bg"
Margin="1"
Fill="{TemplateBinding MenuItem.Background}"
Stroke="{TemplateBinding MenuItem.BorderBrush}"
StrokeThickness="1"
RadiusX="1"
RadiusY="1"/>
<Rectangle x:Name="InnerBorder"
Margin="2"/>
<DockPanel>
<ContentPresenter x:Name="Icon"
Margin="4,0,6,0"
VerticalAlignment="Center"
ContentSource="Icon"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
<Path x:Name="GlyphPanel"
Margin="7,0,0,0"
Visibility="Collapsed"
VerticalAlignment="Center"
Fill="{TemplateBinding MenuItem.Foreground}"
FlowDirection="LeftToRight"
Data="{StaticResource Checkmark}"/>
<ContentPresenter ContentSource="Header"
Margin="{TemplateBinding MenuItem.Padding}"
RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</DockPanel>
<Popup x:Name="PART_Popup"
HorizontalOffset="1"
VerticalOffset="-1"
AllowsTransparency="true"
Placement="Bottom"
IsOpen="{Binding Path=IsSubmenuOpen,RelativeSource={RelativeSource TemplatedParent}}"
Focusable="false"
PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}">
<theme:SystemDropShadowChrome Name="Shdw"
Color="Transparent">
<ContentControl Name="SubMenuBorder"
Template="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type FrameworkElement}, ResourceId=SubmenuContent}}"
IsTabStop="false">
<ScrollViewer Name="SubMenuScrollViewer" CanContentScroll="true"
Style="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type FrameworkElement}, ResourceId=MenuScrollViewer}}">
<Grid RenderOptions.ClearTypeHint="Enabled">
<Canvas Height="0" Width="0" HorizontalAlignment="Left" VerticalAlignment="Top">
<Rectangle
Height="{Binding ElementName=SubMenuBorder,Path=ActualHeight}"
Width="{Binding ElementName=SubMenuBorder,Path=ActualWidth}"
Fill="{StaticResource SubMenuBackgroundBrush}" />
</Canvas>
<ItemsPresenter Name="ItemsPresenter" Margin="2"
KeyboardNavigation.TabNavigation="Cycle"
KeyboardNavigation.DirectionalNavigation="Cycle"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
Grid.IsSharedSizeScope="true"/>
</Grid>
</ScrollViewer>
</ContentControl>
</theme:SystemDropShadowChrome>
</Popup>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSuspendingPopupAnimation"
Value="true">
<Setter TargetName="PART_Popup"
Property="PopupAnimation"
Value="None"/>
</Trigger>
<Trigger Property="Icon"
Value="{x:Null}">
<Setter TargetName="Icon"
Property="Visibility"
Value="Collapsed"/>
</Trigger>
<Trigger Property="IsChecked"
Value="true">
<Setter TargetName="GlyphPanel"
Property="Visibility"
Value="Visible"/>
<Setter TargetName="Icon"
Property="Visibility"
Value="Collapsed"/>
</Trigger>
<Trigger SourceName="PART_Popup"
Property="Popup.HasDropShadow"
Value="true">
<Setter TargetName="Shdw"
Property="Margin"
Value="0,0,5,5"/>
<Setter TargetName="Shdw"
Property="Color"
Value="#71000000"/>
</Trigger>
<Trigger Property="IsHighlighted"
Value="true">
<Setter TargetName="Bg"
Property="Stroke"
Value="#90717070"/>
<Setter TargetName="OuterBorder"
Property="Stroke"
Value="#50FFFFFF"/>
<Setter TargetName="InnerBorder"
Property="Stroke"
Value="#50FFFFFF"/>
</Trigger>
<Trigger Property="IsKeyboardFocused"
Value="true">
<Setter TargetName="Bg"
Property="Stroke"
Value="#E0717070"/>
<Setter TargetName="Bg"
Property="Fill"
Value="{StaticResource MenuItemPressedFill}"/>
<Setter TargetName="InnerBorder"
Property="Stroke"
Value="#50747272"/>
</Trigger>
<Trigger Property="IsSubmenuOpen"
Value="true">
<Setter TargetName="Bg"
Property="Stroke"
Value="#E0717070"/>
<Setter TargetName="Bg"
Property="Fill"
Value="{StaticResource MenuItemPressedFill}"/>
<Setter TargetName="InnerBorder"
Property="Stroke"
Value="#50747272"/>
</Trigger>
<Trigger Property="IsEnabled"
Value="false">
<Setter Property="Foreground"
Value="#FF9A9A9A"/>
<Setter TargetName="GlyphPanel"
Property="Fill"
Value="#848589"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<!-- Submenu -->
<ControlTemplate x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type MenuItem}, ResourceId=SubmenuItemTemplateKey}"
TargetType="{x:Type MenuItem}">
<Grid SnapsToDevicePixels="true">
<Rectangle Name="Bg"
Fill="{TemplateBinding MenuItem.Background}"
Stroke="{TemplateBinding MenuItem.BorderBrush}"
StrokeThickness="1"
RadiusX="2"
RadiusY="2"/>
<Rectangle x:Name="InnerBorder"
Margin="1"
RadiusX="2"
RadiusY="2"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="24"
Width="Auto"
SharedSizeGroup="MenuItemIconColumnGroup"/>
<ColumnDefinition Width="4"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="37"/>
<ColumnDefinition Width="Auto"
SharedSizeGroup="MenuItemIGTColumnGroup"/>
<ColumnDefinition Width="17"/>
</Grid.ColumnDefinitions>
<ContentPresenter x:Name="Icon"
Margin="1"
VerticalAlignment="Center"
ContentSource="Icon"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
<Border x:Name="GlyphPanel"
Background="#E6EFF4"
BorderBrush="#CDD3E6"
BorderThickness="1"
CornerRadius="3"
Margin="1"
Visibility="Hidden"
Width="22"
Height="22">
<Path Name="Glyph"
Width="9"
Height="11"
Fill="#0C12A1"
FlowDirection="LeftToRight"
Data="{StaticResource Checkmark}"/>
</Border>
<ContentPresenter Grid.Column="2"
ContentSource="Header"
Margin="{TemplateBinding MenuItem.Padding}"
RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
<TextBlock Grid.Column="4"
Text="{TemplateBinding MenuItem.InputGestureText}"
Margin="{TemplateBinding MenuItem.Padding}"/>
</Grid>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="Icon"
Value="{x:Null}">
<Setter TargetName="Icon"
Property="Visibility"
Value="Collapsed"/>
</Trigger>
<Trigger Property="IsChecked"
Value="true">
<Setter TargetName="GlyphPanel"
Property="Visibility"
Value="Visible"/>
<Setter TargetName="Icon"
Property="Visibility"
Value="Collapsed"/>
</Trigger>
<Trigger Property="IsHighlighted"
Value="true">
<Setter TargetName="Bg"
Property="Fill"
Value="{StaticResource MenuItemSelectionFill}"/>
<Setter TargetName="Bg"
Property="Stroke"
Value="#8071CBF1"/>
<Setter TargetName="InnerBorder"
Property="Stroke"
Value="#40FFFFFF"/>
</Trigger>
<Trigger Property="IsEnabled"
Value="false">
<Setter Property="Foreground"
Value="#FF9A9A9A"/>
<Setter TargetName="GlyphPanel"
Property="Background"
Value="#EEE9E9"/>
<Setter TargetName="GlyphPanel"
Property="BorderBrush"
Value="#DBD6D6"/>
<Setter TargetName="Glyph"
Property="Fill"
Value="#848589"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<ControlTemplate x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type MenuItem}, ResourceId=SubmenuHeaderTemplateKey}"
TargetType="{x:Type MenuItem}">
<Grid SnapsToDevicePixels="true">
<Rectangle Name="Bg"
Fill="{TemplateBinding MenuItem.Background}"
Stroke="{TemplateBinding MenuItem.BorderBrush}"
StrokeThickness="1"
RadiusX="2"
RadiusY="2"/>
<Rectangle x:Name="InnerBorder"
Margin="1"
Stroke="Transparent"
StrokeThickness="1"
RadiusX="2"
RadiusY="2"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="24"
Width="Auto"
SharedSizeGroup="MenuItemIconColumnGroup"/>
<ColumnDefinition Width="4"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="37"/>
<ColumnDefinition Width="Auto"
SharedSizeGroup="MenuItemIGTColumnGroup"/>
<ColumnDefinition Width="17"/>
</Grid.ColumnDefinitions>
<ContentPresenter x:Name="Icon"
Margin="1"
VerticalAlignment="Center"
ContentSource="Icon"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
<Border x:Name="GlyphPanel"
Background="#E6EFF4"
BorderBrush="#CDD3E6"
BorderThickness="1"
CornerRadius="3"
Margin="1"
Visibility="Hidden"
Width="22"
Height="22">
<Path Name="Glyph"
Width="9"
Height="11"
Fill="#0C12A1"
FlowDirection="LeftToRight"
Data="{StaticResource Checkmark}"/>
</Border>
<ContentPresenter Grid.Column="2"
ContentSource="Header"
Margin="{TemplateBinding MenuItem.Padding}"
RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
<TextBlock Grid.Column="4"
Text="{TemplateBinding MenuItem.InputGestureText}"
Margin="{TemplateBinding MenuItem.Padding}"
Visibility="Collapsed"/>
<Path Grid.Column="5"
VerticalAlignment="Center"
Margin="4,0,0,0"
Fill="{TemplateBinding MenuItem.Foreground}"
Data="{StaticResource RightArrow}"/>
</Grid>
<Popup x:Name="PART_Popup"
AllowsTransparency="true"
Placement="Right"
VerticalOffset="-3"
HorizontalOffset="-2"
IsOpen="{Binding Path=IsSubmenuOpen,RelativeSource={RelativeSource TemplatedParent}}"
Focusable="false"
PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}">
<theme:SystemDropShadowChrome Name="Shdw"
Color="Transparent">
<ContentControl Name="SubMenuBorder"
Template="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type FrameworkElement}, ResourceId=SubmenuContent}}"
IsTabStop="false">
<ScrollViewer Name="SubMenuScrollViewer" CanContentScroll="true" MaxHeight="400"
Style="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type FrameworkElement}, ResourceId=MenuScrollViewer}}">
<Grid RenderOptions.ClearTypeHint="Enabled">
<Canvas Height="0" Width="0" HorizontalAlignment="Left" VerticalAlignment="Top">
<Rectangle
Height="{Binding ElementName=SubMenuBorder,Path=ActualHeight}"
Width="{Binding ElementName=SubMenuBorder,Path=ActualWidth}"
Fill="{StaticResource SubMenuBackgroundBrush}" />
</Canvas>
<ItemsPresenter Name="ItemsPresenter" Margin="2"
KeyboardNavigation.TabNavigation="Cycle"
KeyboardNavigation.DirectionalNavigation="Cycle"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
Grid.IsSharedSizeScope="true"/>
</Grid>
</ScrollViewer>
</ContentControl>
</theme:SystemDropShadowChrome>
</Popup>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSuspendingPopupAnimation"
Value="true">
<Setter TargetName="PART_Popup"
Property="PopupAnimation"
Value="None"/>
</Trigger>
<Trigger Property="IsHighlighted"
Value="true">
<Setter TargetName="InnerBorder"
Property="Stroke"
Value="#D1DBF4FF"/>
</Trigger>
<Trigger Property="Icon"
Value="{x:Null}">
<Setter TargetName="Icon"
Property="Visibility"
Value="Collapsed"/>
</Trigger>
<Trigger Property="IsChecked"
Value="true">
<Setter TargetName="GlyphPanel"
Property="Visibility"
Value="Visible"/>
<Setter TargetName="Icon"
Property="Visibility"
Value="Collapsed"/>
</Trigger>
<Trigger SourceName="PART_Popup"
Property="Popup.HasDropShadow"
Value="true">
<Setter TargetName="Shdw"
Property="Margin"
Value="0,0,5,5"/>
<Setter TargetName="Shdw"
Property="Color"
Value="#71000000"/>
</Trigger>
<Trigger Property="IsHighlighted"
Value="true">
<Setter TargetName="Bg"
Property="Fill"
Value="{StaticResource MenuItemSelectionFill}"/>
<Setter TargetName="Bg"
Property="Stroke"
Value="#8571CBF1"/>
</Trigger>
<Trigger Property="IsEnabled"
Value="false">
<Setter Property="Foreground"
Value="#FF9A9A9A"/>
<Setter TargetName="GlyphPanel"
Property="Background"
Value="#EEE9E9"/>
<Setter TargetName="GlyphPanel"
Property="BorderBrush"
Value="#DBD6D6"/>
<Setter TargetName="Glyph"
Property="Fill"
Value="#848589"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<Style x:Key="{x:Type MenuItem}"
TargetType="{x:Type MenuItem}">
<Setter Property="HorizontalContentAlignment"
Value="{Binding Path=HorizontalContentAlignment,RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="VerticalContentAlignment"
Value="{Binding Path=VerticalContentAlignment,RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="Background"
Value="Transparent"/>
<Setter Property="ScrollViewer.PanningMode"
Value="Both"/>
<Setter Property="Stylus.IsFlicksEnabled"
Value="False"/>
<Setter Property="Template"
Value="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type MenuItem}, ResourceId=SubmenuItemTemplateKey}}"/>
<Style.Triggers>
<Trigger Property="Role"
Value="TopLevelHeader">
<Setter Property="Padding"
Value="7,2,8,3"/>
<Setter Property="Template"
Value="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type MenuItem}, ResourceId=TopLevelHeaderTemplateKey}}"/>
</Trigger>
<Trigger Property="Role"
Value="TopLevelItem">
<Setter Property="Padding"
Value="7,2,8,3"/>
<Setter Property="Template"
Value="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type MenuItem}, ResourceId=TopLevelItemTemplateKey}}"/>
</Trigger>
<Trigger Property="Role"
Value="SubmenuHeader">
<Setter Property="Padding"
Value="2,3,2,3"/>
<Setter Property="Template"
Value="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type MenuItem}, ResourceId=SubmenuHeaderTemplateKey}}"/>
</Trigger>
<Trigger Property="Role"
Value="SubmenuItem">
<Setter Property="Padding"
Value="2,3,2,3"/>
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
It’s not pretty. I hope MS adds the ability to change the height in future versions as I would rather not write a separate extended control just for this.
Until next time…
Getting accurate processor information in Windows 2003
Posted by xcalibur37 in C#, WPF on December 12, 2011
This is a bit dated, but the question is about how you get (near) accurate processor data in Windows 2003 with WMI and no external executables.
I wrote this a long time ago for VBS and translated it into C# for my upcoming application: Astronomy. I added some snazzy XAML in there to pimp it out a bit. There is a lot of code here, but that is because a lot needs to be done to format the data into something we want to see.
The desired result:
So, let’s see all that code:
The XAML:
<Window x:Class="GetProcessCountLegacy.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="200" Width="525">
<Window.Resources>
<!-- Text Styles -->
<Style x:Key="ListHeader" TargetType="{x:Type TextBlock}">
<Setter Property="FontWeight" Value="Bold" />
</Style>
<Style x:Key="ListLabel" TargetType="{x:Type TextBlock}">
<Setter Property="FontWeight" Value="Normal" />
</Style>
<Style x:Key="ListValue" TargetType="{x:Type TextBlock}">
<Setter Property="FontWeight" Value="DemiBold" />
</Style>
<!-- Border -->
<Style x:Key="BorderStyle" TargetType="Border">
<Setter Property="BorderBrush" Value="#999" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="6" />
<Setter Property="Padding" Value="16,14,16,16" />
<Setter Property="Margin" Value="12,6,12,6" />
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush StartPoint="0.5, 0" EndPoint="0.5, 1" Opacity="1.0">
<GradientStop Color="#FFFFFF" Offset="0" />
<GradientStop Color="#FFFFFF" Offset="0.5" />
<GradientStop Color="#EDEDED" Offset="1" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<!-- Processor Information -->
<Border Style="{StaticResource BorderStyle}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="28"/>
<RowDefinition Height="22"/>
<RowDefinition Height="22"/>
<RowDefinition Height="22"/>
<RowDefinition Height="22"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="600"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"
VerticalAlignment="Top"
Text="Processor Information"
Style="{StaticResource ListHeader}" />
<TextBlock Grid.Row="1" Grid.Column="0"
Text="Description:"
Style="{StaticResource ListLabel}" />
<TextBlock Grid.Row="1" Grid.Column="1"
x:Name="txtDescription"
Style="{StaticResource ListValue}" />
<TextBlock Grid.Row="2" Grid.Column="0"
Text="Core Speed:"
Style="{StaticResource ListLabel}" />
<TextBlock Grid.Row="2" Grid.Column="1"
x:Name="txtSpeed"
Style="{StaticResource ListValue}" />
<TextBlock Grid.Row="3" Grid.Column="0"
Text="Count:"
Style="{StaticResource ListLabel}" />
<TextBlock Grid.Row="3" Grid.Column="1"
x:Name="txtCount"
Style="{StaticResource ListValue}" />
<TextBlock Grid.Row="4" Grid.Column="0"
Text="Width:"
Style="{StaticResource ListLabel}" />
<TextBlock Grid.Row="4" Grid.Column="1"
x:Name="txtWidth"
Style="{StaticResource ListValue}" />
</Grid>
</Border>
</Grid>
</Window>
C#
[Main Code]
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
#region Contructors
/// <summary>
/// Main constructor.
/// </summary>
public MainWindow()
{
// Initialize UI
InitializeComponent();
// Get processor information
GetProcessorInformation();
}
#endregion
/// <summary>
/// Retrieve the processor information and add to UI.
/// </summary>
private void GetProcessorInformation()
{
// Build an options object for the remote connection
var options = new ConnectionOptions
{
Impersonation = ImpersonationLevel.Impersonate,
EnablePrivileges = true
};
// Make a connection to a remote computer.
var ms = new ManagementScope(@"\\" + "." + @"\root\cimv2", options);
// Query
string query = "SELECT Manufacturer, MaxClockSpeed, AddressWidth, Name, " +
"SocketDesignation, ProcessorID, UniqueID FROM Win32_Processor";
// Searcher
var searcher =
new ManagementObjectSearcher(ms, new WqlObjectQuery(query));
// Get legacy info
var processor = GetProcessorInfoLegacy(searcher);
// Processor information
// Description
txtDescription.Text = RemoveExcessSpaces(processor.Name);
if (String.IsNullOrEmpty(txtDescription.Text))
{
txtDescription.Text = "{Unknown}";
}
// Speed
txtSpeed.Text = GetProcessorSpeed(processor.Speed);
if (String.IsNullOrEmpty(txtSpeed.Text))
{
txtSpeed.Text = "{Unknown}";
}
// Count
txtCount.Text = GetProcessorCount(5.2, processor);
if (String.IsNullOrEmpty(txtCount.Text))
{
txtCount.Text = "{Unknown}";
}
// Width
txtWidth.Text = "x86-" + processor.Architecture;
if (String.IsNullOrEmpty(txtWidth.Text))
{
txtWidth.Text = "{Unknown}";
}
}
/// <summary>
/// Gets processor information for Windows version 5.x.
/// </summary>
/// <param name="searcher"></param>
/// <returns></returns>
private static ProcessorInfo GetProcessorInfoLegacy(
ManagementObjectSearcher searcher)
{
// Processor object
var processor = new ProcessorInfo();
// Descriptors
var socket = new List<string>();
var procId = new List<string>();
var uniqueId = new List<string>();
// Get data
ManagementObjectCollection colItems = searcher.Get();
try
{
// Evaluate data
foreach (ManagementBaseObject objItem in colItems)
{
// Manufacturer
if (objItem["Manufacturer"] != null)
{
processor.Manufacturer = objItem["Manufacturer"].ToString();
}
// Speed
if (objItem["MaxClockSpeed"] != null)
{
processor.Speed = Convert.ToInt32(objItem["MaxClockSpeed"]);
}
// Architecture
if (objItem["AddressWidth"] != null)
{
processor.Architecture = objItem["AddressWidth"].ToString();
}
// Socket Designation
if (objItem["SocketDesignation"] != null)
{
processor.SocketDesignation =
objItem["SocketDesignation"].ToString();
socket.Add(processor.SocketDesignation);
}
// Name
if (objItem["Name"] != null)
{
processor.Name = objItem["Name"].ToString();
}
// ProcessorID
if (objItem["ProcessorID"] != null)
{
processor.ProcessorID = objItem["ProcessorID"].ToString();
procId.Add(processor.ProcessorID);
}
else
{
procId.Add("");
}
// UniqueID
if (objItem["UniqueID"] != null)
{
processor.UniqueID = objItem["UniqueID"].ToString();
uniqueId.Add(processor.UniqueID);
}
else
{
uniqueId.Add("");
}
}
// Logical count
int totalProcCount = colItems.Count;
processor.LogicalProcessors = totalProcCount;
// Cores
GetLegacyCoreCount(socket, procId, uniqueId, ref processor);
// Get #of possible sockets
if ((processor.Cores > 0) && (processor.LogicalProcessors > 0))
{
int result = (processor.LogicalProcessors / processor.Cores);
processor.Count = result;
}
}
catch
{
}
finally
{
colItems.Dispose();
}
// return
return processor;
}
/// <summary>
/// Gets the number of processor cores for Windows version 5.x.
/// </summary>
/// <param name="socket"></param>
/// <param name="procId"></param>
/// <param name="uniqueId"></param>
/// <param name="processor"></param>
private static void GetLegacyCoreCount(List<string> socket, List<string> procId,
List<string> uniqueId, ref ProcessorInfo processor)
{
int totalProcessors = 0;
// Processor marker
// Check Socket Designation
for (int i = 0; i < socket.Count; i++)
{
// Start with the assumption this is unique.
bool isUnique = true;
// Check for Redundancies
for (int j = i + 1; j < socket.Count; j++)
{
if (socket[i] == socket[j])
{
isUnique = false;
break;
}
}
// If Redundant Unique ID's Exist
for (int j = i + 1; j < socket.Count; j++)
{
if ((uniqueId[i] != "") && (uniqueId[j] != "") &&
(uniqueId[i] != uniqueId[j]))
{
isUnique = true;
break;
}
}
// Check for NULL ProcessorID
if (procId[i].Trim() == "0000000000000000")
{
isUnique = false;
}
// Calculate Total
if (isUnique)
{
totalProcessors++;
}
}
// Get Cores
int result = (processor.LogicalProcessors / totalProcessors);
processor.Cores = result;
}
/// <summary>
/// Retrieves the Processor speed in human-readable format.
/// </summary>
/// <param name="speed"></param>
/// <returns></returns>
public static string GetProcessorSpeed(int speed)
{
string result = string.Empty;
if (speed.ToString().Length >= 4)
{
double dSpeed = Convert.ToDouble(speed) / 1000;
result = dSpeed.ToString("0.00") + " GHz";
}
else
{
result = speed.ToString() + " MHz";
}
// Return
return result;
}
/// <summary>
/// Returns the processor count in a human-readable format.
/// </summary>
/// <param name="oSver"></param>
/// <param name="proc"></param>
/// <returns></returns>
public static string GetProcessorCount(double oSver, ProcessorInfo proc)
{
string result = "";
int physical = proc.Count;
int cores = proc.Cores;
// Manufacturer
string hyper = "HyperThreading";
if (proc.Manufacturer.ToLower().IndexOf("intel") == -1)
{
hyper = "HyperTransport";
}
// Processor count
string physDesc = physical.ToString() + " processor(s)";
// Cores
string coreDesc = string.Empty;
// Current
if (oSver >= 6)
{
coreDesc = cores.ToString() + " core(s)";
if (proc.IsHyperThreaded)
{
coreDesc += " w/ HyperThreading";
}
}
// Legacy
else
{
coreDesc = cores.ToString() + " core(s)";
if ((cores / physical) == 2)
{
// Intel
if (proc.Manufacturer.ToLower().IndexOf("intel") != -1)
{
coreDesc += " (or " + physical + " core(s) w/ " + hyper + ")";
}
}
}
result = physDesc + " (" + coreDesc + ")";
// Return
return result;
}
/// <summary>
/// Removes extra spaces between words.
/// </summary>
/// <param name="procDesc"></param>
/// <returns></returns>
public static string RemoveExcessSpaces(string procDesc)
{
string result = "";
// Evaluate
string[] desc = procDesc.Split((char)32);
// Name
for (int i = 0; i < desc.Length; i++)
{
if (desc[i].Trim() != "")
{
result += desc[i] + " ";
}
}
// Return
return result;
}
}
[ProcessorInfo class]
/// <summary>
/// Information relevant to the physical processors.
/// </summary>
public class ProcessorInfo
{
#region Properties
/// <summary>
/// Win32_Processor: AddressWidth: On a 32-bit operating system, the value is 32
/// and on a 64-bit operating system it is 64.
/// </summary>
public string Architecture { get; set; }
/// <summary>
/// Win32_Processor: NumberOfCores: Number of cores for the current instance of
/// the processor.
/// </summary>
public int Cores { get; set; }
/// <summary>
/// Total processor count.
/// </summary>
public int Count { get; set; }
/// <summary>
/// Determines whether processors support Hyper-Threading or Hyper-Transport.
/// </summary>
public bool IsHyperThreaded { get; set; }
/// <summary>
/// Win32_Processor: NumberOfLogicalProcessors: Number of logical processors for
/// the current instance of the processor.
/// </summary>
public int LogicalProcessors { get; set; }
/// <summary>
/// Win32_Processor: Manufacturer: Name of the processor manufacturer.
/// </summary>
public string Manufacturer { get; set; }
/// <summary>
/// Win32_Processor: Name: Label by which the object is known.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Win32_Processor: ProcessorID: Processor information that describes the
/// processor features.
/// </summary>
public string ProcessorID { get; set; }
/// <summary>
/// Win32_Processor: SocketDesignation: Type of chip socket used on the circuit.
/// </summary>
public string SocketDesignation { get; set; }
/// <summary>
/// Win32_Processor: MaxClockSpeed: Detected processor speed.
/// </summary>
public int Speed { get; set; }
/// <summary>
/// Win32_Processor: UniqueID: Globally unique identifier for the processor.
/// </summary>
public string UniqueID { get; set; }
#endregion
#region Constructors
public ProcessorInfo()
{
Architecture = "x86";
}
#endregion
}
WPF: Drag and Drop Textbox for Windows Explorer files
Posted by xcalibur37 in C#, WPF on December 10, 2011
There are a lot of places on the web that talk about this concept and it’s clear there are a few ways to accomplish the task.
Here is the code I recommend:
Given:
Your TextBox name is “TextBox1″
C#
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
// Initialize UI
InitializeComponent();
// Loaded event
this.Loaded += delegate
{
TextBox1.AllowDrop = true;
TextBox1.PreviewDragEnter += TextBox1PreviewDragEnter;
TextBox1.PreviewDragOver += TextBox1PreviewDragOver;
TextBox1.Drop += TextBox1DragDrop;
};
}
/// <summary>
/// We have to override this to allow drop functionality.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void TextBox1PreviewDragOver(object sender, DragEventArgs e)
{
e.Handled = true;
}
/// <summary>
/// Evaluates the Data and performs the DragDropEffect
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void TextBox1PreviewDragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
e.Effects = DragDropEffects.Copy;
}
else
{
e.Effects = DragDropEffects.None;
}
}
/// <summary>
/// The drop activity on the textbox.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void TextBox1DragDrop(object sender, DragEventArgs e)
{
// Get data object
var dataObject = e.Data as DataObject;
// Check for file list
if (dataObject.ContainsFileDropList())
{
// Clear values
TextBox1.Text = string.Empty;
// Process file names
StringCollection fileNames = dataObject.GetFileDropList();
StringBuilder bd = new StringBuilder();
foreach (var fileName in fileNames)
{
bd.Append(fileName + "\n");
}
// Set text
TextBox1.Text = bd.ToString();
}
}
}
Now, let’s look at each event and cover what is going on:
/// <summary>
/// We have to override this to allow drop functionality.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void TextBox1PreviewDragOver(object sender, DragEventArgs e)
{
e.Handled = true;
}
We need to add this to override the default PreviewDrag behavior. If we don’t, then the Drop Event will not fire.
/// <summary>
/// Evaluates the Data and performs the DragDropEffect
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void TextBox1PreviewDragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
e.Effects = DragDropEffects.Copy;
}
else
{
e.Effects = DragDropEffects.None;
}
}
This piece deals with the source being dragged over the textbox. The data is evaluated at this point, and the DragDropEffects are executed accordingly. Notice that the data is checked for the correct type.
MSDN breaks down the DragDropEffects here.
/// <summary>
/// The drop activity on the textbox.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void TextBox1DragDrop(object sender, DragEventArgs e)
{
// Get data object
var dataObject = e.Data as DataObject;
// Check for file list
if (dataObject.ContainsFileDropList())
{
// Clear values
TextBox1.Text = string.Empty;
// Process file names
StringCollection fileNames = dataObject.GetFileDropList();
StringBuilder bd = new StringBuilder();
foreach (var fileName in fileNames)
{
bd.Append(fileName + "\n");
}
// Set text
TextBox1.Text = bd.ToString();
}
}
In the drop event, a few things occur:
- We grab the DataObject from “e”.
- We evaluate the DataObject to ensure it has a StringCollection using GetFileDropList.
- We grab the StringCollection.
- We enumerate through the collection and append each filename to a StringBuilder object.
- Finally, we add the StringBuilder content to the TextBox Text field.
.Net Framework Versions – Framework vs. CLR
Posted by xcalibur37 in .Net on October 30, 2011
Quite often I see people either at work or on forums asking why they can’t see .Net Framework 3.5 in IIS. Then, someone of much experience will tell them to use 2.0 with no further explanation since in most cases the person asking the question just wants it to work. Well, I want you to know the answer, so let me explain that.
In .Net there are 2 versions of consideration: the CLR and the Framework. A .NET CLR can contain several .Net frameworks, since .Net CLR’s are incremental versions (contain content from the earlier version and build upon them like a foundation). To get a visual idea of how this is laid out:
.NET Framework Versions and Dependencies
As you can see, there is not necessarily a new CLR every time a new Framework version is released. In many cases, you can consider the 3.5 framework to be a massive service release update to 2.0.
When configuring IIS, remember to look to the CLR version number and you will be okay. If you can’t find those numbers then you need to check that the right frameworks are installed.
The link posted above from Microsoft lays it all out in nice charts which include the versions of Windows these frameworks are included by default.
That’s all for now…
C#: How to get a return value from an Action
Posted by xcalibur37 in C# on October 26, 2011
The Question:
Can I actually get a return value from an Action?
In my travels during research and development efforts on exciting coding projects I have seen this question get asked time and time again. The answers I usually see are:
- No, or
- Create your own Action object.
I think the point is missed here. The issue with passing a simple object into an Action and then setting your method result to that object, is that the object gets overwritten. Why? Because it’s an object. Once you assign something else to that object, the reference is changed. Therefore the original object is no longer in context.
How can we resolve this? We can easily do this with a complex object.
Our object:
public class MyComplexObject
{
/// <summary>
/// Name provided for the result.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Value of the result.
/// </summary>
public object Value { get; set; }
}
Now, let’s add this to a new Action of type MyComplexObject:
Action<MyComplexObject> myAction = (MyComplexObject result) =>
{
result.Value = MyMethodThatReturnsSomething();
};
If you reference your MyComplexObject, you will be able to use the value assigned to it’s “Value” property.
I know this isn’t Earth-shaking, but I hope this tip helps you along the way.
Happy Coding.
The value of a good graphic artist
Posted by xcalibur37 in Business on July 24, 2011
In the world of web design, we are constantly facing the challenge of making a site as good looking as it is functional. And though many of us have attempted to do graphic design work on our own, it is usually proven that our scope is limited resulting in the pumping out of the same old templates to our customers.
With that in mind, we usually have to hire a graphic artist to assist in the web design process. It is well known that this can be a troublesome if not completely undesirable with the wrong people on board.
Here are the basic dilemma’s web designers face when dealing with graphic artists:
The client provides one to help supplement the job: The issue here is trust and collaboration. Many times when two contractors are working together because the client forces the relationship, competitive interests come to the surface making the relationship difficult. If the chemistry is not perfect, one of the contractors may abandon the project entirely. Even if you are not the one that left and finished the site, it still leaves a bad note with the client since you didn’t “work it out”.
The artist does good work, but is a diva: This one is pretty common. I have dealt with many artists who work on “their” schedule. This translates to “when I get my lazy butt in gear and care enough”. These people are usually very difficult to deal with, and don’t like to revise their work to meet client expectations should the planning phase drag on long enough.
The artist does poor work but refuses to accept it: Ever have an artist on contract that doesn’t meet the client expectations because the quality of the mocks and design assets are not good enough within realistic requirements? Then trying to convince them to update their stuff without hurting their feelings is near impossible.
Now, all that being said we are not without hope. I personally have found an awesome graphic artist who I will call “Rebecca” in this context, so as to not publish her real name on this blog. Xcalibur Systems asked Rebecca to design mocks for our new site, and I have to say; we are thrilled at what she has come up with. Take a look at the below image to see what I mean:
In summary, working with a graphic artist can be a huge challenge. However, there are plenty of Rebecca’s out there that will make the experience worthwhile. With patience and continued research, you’ll find one too.





