A Tale Of Three Widgets
For this program, I created three custom widgets, each with some sort of measurement unit attached to it as well as other properties and methods. The first Widget has three Lengths as properties and the user needs the units to be in inches. Well easy enough, slap a text tag "Inch(es)" after the values the user enters and done. Well, not really. What if the user needs to see what the values are in Metric or what if the person entering the values only has the values in feet, and is very bad at multiplication...
Now, we have a challenge - how to assign a unit of measurement to something that will automatically update when the user switches measurement units, like inches to feet; or even toggling between English (US) and Metric (SI) measurement systems.
In the following screenshot, we have updated Widget One's State Of Mind and Color which are both reflected in the Widget One TreeView Item. The Length units have also been changed from the default inches - "in (US)" to feet - "ft (US)".
Length will be the first measurement unit to incorporate into our application. For Length, inches, feet - for English units; centimeters, millimeters, and meters will be supported for Metric units. We need an object, a container for our units. We will call this container a Bucket. The Bucket will hold any type of measurement that we need, it will become our class to deal with the Measurement Systems. Looking forward we also want to support Area with square inches, centimeters, and millimeters as values; and Volume with gallons, liters, cubic - feet, meters, centimeters, and millimeters.
In order to support changing units, we need to maintain a "control" value. The control value will be stored in the default units - for Length that would be inches. Using the control value, a display value of any unit is created and maintained. We will call the control value the "RealBucket" and the value the user sees in the selected units is the "DisplayBucket". So we know we need two values for Length - LengthRealBucket and LengthDisplayBucket, same for Volume and Area - VolumeRealBucket, VolumeDisplayBucket, AreaRealBucket, and finally AreaDisplayBucket.
There is a relationship between the units of measurement. For example, the default Length unit will be inches; from this, we know that there are .08333 inches per foot, 2.54 centimeters per inch, 25.4 millimeters per inch and so on. From this, we can define our Bucket as a formulaic relation to one another. The Bucket will have the following properties to encapsulate a measurement unit,
- Units it represents - Length, Area, Volume
- Measurement system of the Bucket - (US or SI)
- Conversion value - numeric conversion from the default unit - (Inches = 1.00, Feet = .08333)
- Shift value if needed that will through addition or subtraction will adjust the overall quantity
An example of a measurement unit that would need the shift would be Temperature. Degrees Celcius would be defined as a new Bucket ("Celcius", "Degrees Celcius", 1.0, 0.0, BucketType.Temperature) and Fahrenheit relation to Celcius is Temp(celsius) * 1.8 + 32. So the formula would be as follows: new Bucket ("Fahrenheit", "Degrees Fahrenheit", 1.8, 32.0, BucketType.Temperature).
So our measurement units class is taking shape. Below is the definition for Length Units. Area and Volume are very similar, they can be viewed in the source. Note however; that any type and any number of measurement types can be added - such as Mass, Velocity, Temperature, etc...
-
- LengthBuckets = new[]
- {
- new Bucket("in", "US", 1.0, 0.0, BucketType.Length),
- new Bucket("ft", "US", 0.0833333, 0.0, BucketType.Length),
- new Bucket("cm", "SI", 2.54, 0.0, BucketType.Length),
- new Bucket("mm", "SI", 25.4, 0.0, BucketType.Length),
- new Bucket("m", "SI", 0.0254, 0.0, BucketType.Length)
- };
-
- LengthDisplayBucket = LengthBuckets[0];
- LengthRealBucket = LengthBuckets[0];
Note the Bucket Type, since we have Area, Length, and Volume we need to k now which bucket we are dealing with. Definition of our Buckets is only one step, now we have to convert these when the user changes them so we need an event mechanism - the Property Changed Event Handler. Each unit property will have a name associated with it, for Length we assigned the property names of "LengthRealBucket" and "LengthDisplayBucket". The name of the property gets pushed via the property changed event,
-
-
-
- public event PropertyChangedEventHandler PropertyChanged;
- internal void Changed(string id)
- {
- PropertyChangedEventHandler eventhandler = PropertyChanged;
- if (null != eventhandler)
- eventhandler(this, new PropertyChangedEventArgs(id));
- }
Using the Bucket definition we can create a calculate routine that will handle conversion between compatible types of units. In other words, the routine can switch between all length units, all volume units, and all area units by using the conversion values we associated with each Bucket.
- internal static double
- Calculate(Bucket bucket_before, Bucket bucket_after, double value)
- {
- if (bucket_before.BType != bucket_after.BType) throw
- new InvalidOperationException("Conversion Between Incorrect Types!");
- if ((bucket_before.BucketShift == bucket_after.BucketShift) &&
- (bucket_before.BucketScale == bucket_after.BucketScale)) return value;
- double retVal = (value - bucket_before.BucketShift) / bucket_before.BucketScale;
- return (retVal * bucket_after.BucketScale + bucket_after.BucketShift);
- }
Two routines are needed that will call the Calculate method. The first is to calculate the display bucket from the real bucket as shown here. Note that the other units of Area and Volume have been included and are controlled by the Bucket Type. Don't worry about copying these code fragments, just follow along. I'll make the code for the complete classes available at the end of the article. I can't give out the whole source since I'm using the Property Grid from the Xceed Library. It's a license issue.
- internal double
- CalcFromRealBucket(BucketType btype, double value)
- {
- switch(btype)
- {
- case BucketType.Area:
- return Calculate(AreaRealBucket, AreaDisplayBucket, value);
- case BucketType.Length:
- return Calculate(LengthRealBucket, LengthDisplayBucket, value);
- case BucketType.Volume:
- return Calculate(VolumeRealBucket, VolumeDisplayBucket, value);
- default:
- throw new
- InvalidOperationException("Error! Bucket Types Supported Are Area, Length, And Volume");
- }
- }
A quick tidbit on WPF before we look at the actual Widget that will use the Units class. As you can see from the screen shot WPF is really great for making the UI look like something more than the plain old app. Even applying a linear gradient to the combo boxes makes them look like something special, something different from the same old plain combo box. Yeah, it is great, until the user puts the focus on the control - then YUK!!
If you do any customization of the GUI, then be prepared for having to deal with GotFocus / LostFocus events to force things to look pretty. In the case of the button and the window background I used a LinearGradient to define the color scheme. I won't go into how to use a LinearGradient since there is a lot on the web about it. The GotFocus event is used to override the highlight colors by defining a new foreground and background. We also capture the LinearGradient of the current background before switching. Define a public variable to hold the LinearGradient background. Then in the GotFocus event we save the LinearGradient background before setting it to Gold with a foreground of black for visibility.
-
- public static LinearGradientBrush lgb_ComboBackground;
- .
- .
- .
-
-
-
-
-
- private void
- cmbBucketSystem_GotFocus(object sender, RoutedEventArgs e)
- {
-
- lgb_ComboBackground = (LinearGradientBrush) cmbBucketSystem.Background;
- cmbBucketSystem.Background = Brushes.Gold;
- cmbBucketSystem.Foreground = Brushes.Black;
- }
- private void cmbBucketSystem_LostFocus
- (object sender, RoutedEventArgs e)
- {
- cmbBucketSystem.Foreground = Brushes.Gold;
- cmbBucketSystem.Background = lgb_ComboBackground;
- }
The Measurement Unit class Buckets is done, now it needs to be utilized. A custom Widget will be created that will need Length measurements. The Widget will contain three lengths associated with it, why three - just decided at random to have three different lengths. Each will update when the units or the measurement system changes. Below are the protected property variables that Widget One will have. The Properties are Name, Color, State Of Mind, Length A, Length B, Length C, and Date Created.
There are two other Widgets, one has Area as a measurement unit and the other uses Volume. The set up for each is very similar to Widget One, but I included them in the code to show some more complexity and versatility as well.
-
-
- private string _widget_name = string.Empty;
-
- private string _widget_color = string.Empty;
-
- private string _widget_state = string.Empty;
-
- private DateTime _widget_created = DateTime.Now;
-
- private double _widget_length_A = 0.0;
-
- private double _widget_length_B = 0.0;
-
- private double _widget_length_C = 0.0;
-
- private string _widget_length_bucket = string.Empty;
For each length the Widget will need to maintain two values as discussed earlier.
-
- public double RealLengthA
- {
- get { return _widget_length_A; }
- set
- {
- if (value != _widget_length_A)
- {
- _widget_length_A = value;
- PropChanged("RealLengthA");
- PropChanged("DisplayLengthA");
- }
- }
- }
-
-
- public double DisplayLengthA
- {
- get { return
- Buckets.Instance.CalcFromRealBucket(BucketType.Length, _widget_length_A); }
- set
- {
- double len_val = Buckets.Instance.CalcToRealBucket(BucketType.Length, value);
- if (len_val != _widget_length_A)
- {
- _widget_length_A = len_val;
- PropChanged("RealLengthA");
- PropChanged("DisplayLengthA");
- }
- }
- }
For each, real and display; if the value changes we want both to be in sync so we raise the Property Changed on both values: "RealLengthA" and "DisplayLengthA". We need to attach the PropertyChanged event to the Widget as well as the routine that will handle the event. The Buckets class will take care of knowing how to update the values via routines just mentioned Buckets.Instance.CalcFromRealBucket and Buckets.Instance.CalcFromDisplayBucket.
-
-
-
- public class widgetOne
- {
- public widgetOne()
- {
- Buckets.Instance.PropertyChanged += BucketsChanged;
- EnumBinding = SystemType.UnitedStates;
- }
- void BucketsChanged(object sender, PropertyChangedEventArgs e)
- {
- PropChanged("RealLengthA");
- PropChanged("DisplayLengthA");
- PropChanged("RealLengthB");
- PropChanged("DisplayLengthB");
- PropChanged("RealLengthC");
- PropChanged("DisplayLengthC");
- PropChanged("LengthBuckets");
- }
- .
- .
- .
Now the code for PropChanged which declares the Property Changed Event Handler and passes the changed Property Name to the handler. The other portion needed for this mechanism is the Enumeration Binding which assigns the correct units when the system of measurement changes from English to Metric and vice versa. It will call the support routine TypeChanged to assign the correct default value.
-
-
-
- private SystemType _enumBinding;
-
- public SystemType EnumBinding
- {
- get
- {
- return _enumBinding;
- }
- set
- {
- if (_enumBinding != value)
- {
- if (value == SystemType.UnitedStates)
- {
- _enumBinding = SystemType.UnitedStates;
- TypeChanged();
- }
- else
- {
- _enumBinding = SystemType.InternationalSystem;
- TypeChanged();
- }
- if (PropertyChanged != null)
- PropertyChanged(this, new
- PropertyChangedEventArgs("EnumBinding"));
- }
- else
- {
- if (value == SystemType.UnitedStates)
- {
-
- _enumBinding = SystemType.UnitedStates;
- TypeChanged();
- }
- else
- {
-
- _enumBinding = SystemType.InternationalSystem;
- TypeChanged();
- }
- if (PropertyChanged != null)
- PropertyChanged(this, new
- PropertyChangedEventArgs("EnumBinding"));
-
- }
- }
- }
-
-
-
-
- public void TypeChanged()
- {
-
- if (EnumBinding.Equals(SystemType.UnitedStates))
- {
-
- Buckets.Instance.LengthDisplayBucket = Buckets.Instance.LengthBuckets[0];
- }
-
- else
- {
-
- Buckets.Instance.LengthDisplayBucket = Buckets.Instance.LengthBuckets[2];
- }
- }
Now that we have all the code we need for Widget One, be sure to add using System.Collections.ObjectModel so that we can use the ObservableCollection to define our list of Widget Ones(s).
-
-
-
- public class widgetOneList : ObservableCollection<widgetOne>
- {
- public widgetOneList()
- : base()
- { }
- }
In the main program we can utilize our new Widget.
-
- public static widgetOneList list_WidgetOne = new widgetOneList();
The following routine creates a new Widget and adds it to the TreeView. In the XAML we define a Data Template for Widget One so the Treeview knows how we want the widget to be displayed as well as what data to use. A label will be used for the name, text boxes for Color and State of Mind as well as a button. The button will be for the user to select the Widget from the list of Widgets in the TreeView. Once selected the Xceed PropertyGrid will be assigned the data context of the selected Widget. The Data Template will be defined under the TreeView.Resources tag.
-
-
-
-
-
- private void CreateOne_Click(object sender, RoutedEventArgs e)
- {
- string str_NewWidgetName = "Widget One - " + list_WidgetOne.Count.ToString();
- var newWidget = new widgetOne();
- newWidget.WidgetName = str_NewWidgetName;
- newWidget.WidgetState = "Happy";
-
- newWidget.WidgetColor = "Black";
-
- newWidget.EnumBinding = (SystemType) systype_ProgramMeasurement;
- list_WidgetOne.Add(newWidget);
-
- UpdateTreeView();
- LblStatus.Content = "New Widget One Created: " + newWidget.WidgetCreated;
- }
-
- <!--Widget One Data Template-->
- <DataTemplate DataType="{x:Type local:widgetOne}">
- <StackPanel Orientation="Vertical"
- Margin="1,1,1,1" MinWidth="200"
- Width="Auto" MinHeight="90"
- Height="Auto">
- <Border BorderBrush="Black"
- BorderThickness="1"
- Padding="2" Margin="2">
- <Grid>
- <Grid.RowDefinitions>
- <RowDefinition Height="30" />
- <RowDefinition Height="25" />
- <RowDefinition Height="25" />
- <RowDefinition Height="25" />
- </Grid.RowDefinitions>
- <Grid.ColumnDefinitions>
- <ColumnDefinition Width="Auto" />
- <ColumnDefinition Width="Auto" />
- </Grid.ColumnDefinitions>
- <Label Grid.Row="0"
- Grid.ColumnSpan="2" Foreground="Yellow"
- Background="Transparent"
- FontStyle="Italic" FontFamily="Copperplate Gothic"
- FontSize="14">Widget One</Label>
- <Button Grid.Row="1"
- Grid.ColumnSpan="2" Name="WidgetName"
- Content="{Binding Path=WidgetName}"
- Height="22" Width="Auto"
- Foreground="Yellow" FontFamily="Copperplate Gothic"
- Click="WidgetName_Click">
- <Button.Background>
- <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
- <GradientStop Color="#FFff0099" Offset="0" />
- <GradientStop Color="#FF993366" Offset="1" />
- </LinearGradientBrush>
- </Button.Background>
- </Button>
- <TextBlock Grid.Row="2"
- Grid.Column="0" FontWeight="Bold"
- Text="Selected Color:" Foreground="Yellow"
- Background="Transparent" FontFamily="Copperplate Gothic" />
- <TextBlock Grid.Row="2"
- Grid.Column="1" Text="{Binding Path=WidgetColor}"
- Margin="5,0,0,0" Foreground="Yellow"
- Background="Transparent" FontFamily="Copperplate Gothic" />
- <TextBlock Grid.Row="3"
- Grid.Column="0" FontWeight="Bold"
- Text="State Of Mind:" Foreground="White"
- Background="Transparent" FontFamily="Copperplate Gothic" />
- <TextBlock Grid.Row="3"
- Grid.Column="1" Text="{Binding Path=WidgetState}"
- Margin="5,0,0,0" Foreground="White"
- Background="Transparent" FontFamily="Copperplate Gothic" />
- </Grid>
- </Border>
- </StackPanel>
- </DataTemplate>
So far we have only discussed Widget One, yet the program has three different Widgets, and there can be any number of each Widget. So here is the complete UpdateTreeView routine. It creates three branch nodes, one for each Widget and populates the TreeView Node with the corresponding list of Widgets. Of course Widget's are non-sensical, it is just for demonstration purposes. In real life, this technique could be applied to many things, for example a Lumber Yard Inventory program or even an Auto Parts Database. The possibilities are limitless.
-
-
-
-
-
-
- private void UpdateTreeView()
- {
- TreeViewItem tvi_Widget = null;
-
- _treeView.ItemsSource = null;
- _treeView.Items.Clear();
-
-
- if (list_WidgetOne.Count > 0)
-
- {
- tvi_Widget = new TreeViewItem();
- tvi_Widget.Header = "Created Widget One(s)";
- _treeView.Items.Add(tvi_Widget);
- foreach (var w_Widget in list_WidgetOne)
- {
- tvi_Widget.Items.Add(w_Widget);
- }
- tvi_Widget.IsExpanded = true;
- }
-
-
- if (list_WidgetTwo.Count > 0)
-
- {
- tvi_Widget = new TreeViewItem();
- tvi_Widget.Header = "Created Widget Two(s)";
- _treeView.Items.Add(tvi_Widget);
- foreach (var w_Widget in list_WidgetTwo)
- {
- tvi_Widget.Items.Add(w_Widget);
- }
- tvi_Widget.IsExpanded = true;
- }
-
-
- if (list_WidgetThree.Count > 0)
-
- {
- tvi_Widget = new TreeViewItem();
- tvi_Widget.Header = "Created Widget Three(s)";
- _treeView.Items.Add(tvi_Widget);
- foreach (var w_Widget in list_WidgetThree)
- {
- tvi_Widget.Items.Add(w_Widget);
- }
- tvi_Widget.IsExpanded = true;
- }
-
- _treeView.Items.Refresh();
- _treeView.UpdateLayout();
-
- }
In the case of this program where I am using the Xceed PropertyGrid, there is a routine to update the grid using two tracking variables. One to hold the current index of the Widget selected, of course, we don't know which Widget that index belongs to; so the other variable is a tracker of which Widget is the active Widget. All Widgets have a button associated with each which call the same routine:
WidgetName_Click() Each Widget Name is unique, it is simply a matter of matching the name of the selected Widget. Once matched the index of the Widget and which Widget type are saved to the tracking variables.
-
- public enum WidgetTypes
- {
- WidgetOne = 1,
- WidgetTwo,
- WidgetThree
- }
-
- public static WidgetTypes widgtype_Widget = WidgetTypes.WidgetOne;
- public int int_CurrentWidgetIndex = 0;
- .
- .
- .
-
-
-
-
-
-
-
- private void WidgetName_Click(object sender, RoutedEventArgs e)
- {
-
- int int_widget_index = -1;
-
- string string_Widget = e.Source.ToString();
-
- string_Widget = string_Widget.Remove(0, 32);
-
-
-
- if (string_Widget.Contains("One"))
- {
- for (int kLoop = 0; kLoop < list_WidgetOne.Count(); kLoop++)
- {
- if (null != list_WidgetOne[kLoop])
- {
- if (list_WidgetOne[kLoop].WidgetName == string_Widget)
- { int_widget_index = kLoop; break; }
- }
- }
-
- if (int_widget_index > -1) void_ShowWidgetOneProperties(int_widget_index);
- }
-
- else if (string_Widget.Contains("Two"))
- {
-
- for (int kLoop = 0; kLoop < list_WidgetTwo.Count(); kLoop++)
- {
- if (null != list_WidgetTwo[kLoop])
- {
- if (list_WidgetTwo[kLoop].WidgetName == string_Widget)
- { int_widget_index = kLoop; break; }
- }
- }
-
- if (int_widget_index > -1) void_ShowWidgetTwoProperties(int_widget_index);
- }
-
- else if (string_Widget.Contains("Three"))
- {
-
- for (int kLoop = 0; kLoop < list_WidgetThree.Count(); kLoop++)
- {
- if (null != list_WidgetThree[kLoop])
- {
- if (list_WidgetThree[kLoop].WidgetName == string_Widget)
- { int_widget_index = kLoop; break; }
- }
- }
- if (int_widget_index > -1) void_ShowWidgetThreeProperties(int_widget_index);
- }
- else
- {
-
- }
- }
-
-
-
-
-
- public void
- void_ShowWidgetOneProperties(int intWidgetIndex)
- {
- this.DataContext = list_WidgetOne[intWidgetIndex];
-
- int_CurrentWidgetIndex = intWidgetIndex;
-
- widgtype_Widget = WidgetTypes.WidgetOne;
- }
-
- public void
- void_ShowWidetTwoProperties(int intWidgetIndex)...
- public void
- void_ShowWidgetThreeProperties(int intWidgetIndex)...
One last thing I need to talk about before I just dump the code out there. How do you change Measurement Systems and measurement units within a system? There are four combo boxes defined in the program, one for switching between English (US) and Metric (SI) and one combo box for Length, Area, and Volume respectively. Below is listed the complete XAML and event code for the Bucket System - i.e. changing Measurement Systems.
We keep track of the selected Measurement System with systype_ProgramMeasurement then the Widgets are updated by setting each Widget to the selected measurement system, and fire the TypeChanged event for each Widget which will update all measurements within the Widgets. The PropertyGrid is updated for the current Widget assigned to it's DataContext. Finally, the combos are updated to show the default measurement unit for Length, Area, and Volume in the selected measurement system.
For example, if the Metric System is selected the Length combo is set to the default of "centimeters (SI)". Each of the other combos are updated respectively to their default value as the selected item. All Length Values of Widget One(s) will be converted to the current system.
- <!--Buckets Control-->
- <ComboBox Name="cmbBucketSystem"
- Grid.Row="1"
- Grid.Column="2"
- Height="30"
- Width="Auto"
- Margin="2,2,2,0"
- VerticalAlignment="Top"
- FontFamily="Copperplate Gothic"
- SelectionChanged="cmbBucketSystem_SelectionChanged"
- Foreground="Yellow"
- GotFocus="cmbBucketSystem_GotFocus"
- LostFocus="cmbBucketSystem_LostFocus">
- <ComboBox.Background>
- <LinearGradientBrush
- EndPoint="0.5,1"
- StartPoint="0.5,0">
- <GradientStop Color="#FFff0099"
- Offset="0" />
- <GradientStop Color="#FF993366"
- Offset="1" />
- </LinearGradientBrush>
- </ComboBox.Background>
- <ComboBoxItem Content="English System"
- IsSelected="True"></ComboBoxItem>
- <ComboBoxItem Content="Metric System"></ComboBoxItem>
- </ComboBox>
-
-
-
-
-
-
- private void cmbBucketSystem_SelectionChanged
- (object sender, SelectionChangedEventArgs e)
- {
- ComboBoxItem cbi_SelectedUnit = (ComboBoxItem)cmbBucketSystem.SelectedItem;
- string str_Content = cbi_SelectedUnit.Content.ToString();
- if (str_Content.Contains("English System"))
- systype_ProgramMeasurement = SystemType.UnitedStates;
- else
- systype_ProgramMeasurement = SystemType.InternationalSystem;
-
- UpdateAllWidgets();
- UpdatePropertyGrid();
- UpdateMeasurementCombos();
- }
For each of the Widget collections it is just a matter of cycling through each and set EnumBinding to the selected system and firing the TypeChanged() method.
-
-
-
- private void UpdateAllWidgets()
- {
-
- for (int kLoop = 0; kLoop < list_WidgetOne.Count(); kLoop++)
- {
- list_WidgetOne[kLoop].EnumBinding = (SystemType)systype_ProgramMeasurement;
- list_WidgetOne[kLoop].TypeChanged();
- }
-
- for (int kLoop = 0; kLoop < list_WidgetTwo.Count(); kLoop++)
- {
- list_WidgetTwo[kLoop].EnumBinding = (SystemType)systype_ProgramMeasurement;
- list_WidgetTwo[kLoop].TypeChanged();
- }
-
- for (int kLoop = 0; kLoop < list_WidgetThree.Count(); kLoop++)
- {
- list_WidgetThree[kLoop].EnumBinding = (SystemType)systype_ProgramMeasurement;
- list_WidgetThree[kLoop].TypeChanged();
- }
- }
Updating the combo boxes to the new units. To avoid null instances the code is wrapped with a flag that is set to true when the program launches. The WindowLoaded() event sets the flag to false.
-
-
-
- private void UpdateMeasurementCombos()
- {
- if (!bool_StartingUp)
- {
-
- widgetOne widgetone_Temp = new widgetOne();
- widgetone_Temp.EnumBinding = (SystemType)systype_ProgramMeasurement;
- cmbLengthBuckets.SelectedItem = widgetone_Temp.WidgetLengthBucket;
-
-
- widgetTwo widgettwo_Temp = new widgetTwo();
- widgettwo_Temp.EnumBinding = (SystemType)systype_ProgramMeasurement;
- cmbAreaBuckets.SelectedItem = widgettwo_Temp.WidgetAreaBucket;
-
-
- widgetThree widgetthree_Temp = new widgetThree();
- widgetthree_Temp.EnumBinding = (SystemType)systype_ProgramMeasurement;
- cmbVolumeBuckets.SelectedItem = widgetthree_Temp.WidgetVolumeBucket;
- }
- }
For the Area, Length, and Volume combo boxes, those we will bind in the XAML and use a Selection Change event to provide updates for the new unit selected. We bind the combo boxes directly to the Buckets.Instance and the appropriate unit for the bucket - in the case of Length, it is the LengthBuckets as the ItemsSource and the selected item is the LengthDisplayBucket, the default is "inches (US)". I'll only show one here since the code is the same for each of the other unit selection combo boxes and will be included in the source download.
- <ComboBox Name="cmbLengthBuckets" Grid.Row="0"
- Grid.Column="1" VerticalAlignment="Center"
- Margin="2,2,2,2" DataContext="{Binding Source={x:Static local:Buckets.Instance}}"
- ItemsSource="{Binding LengthBuckets}" SelectedItem="{Binding LengthDisplayBucket}"
- Foreground="Yellow" FontFamily="Copperplate Gothic"
- GotFocus="cmbLengthBuckets_GotFocus"
- LostFocus="cmbLengthBuckets_LostFocus"
- SelectionChanged="cmbLengthBuckets_SelectionChanged">
- <ComboBox.Background>
- <LinearGradientBrush
- EndPoint="0.5,1"
- StartPoint="0.5,0">
- <GradientStop Color="#FFff0099"
- Offset="0" />
- <GradientStop Color="#FF993366"
- Offset="1" />
- </LinearGradientBrush>
- </ComboBox.Background>
- </ComboBox>
-
-
-
-
-
-
- private
- void cmbLengthBuckets_SelectionChanged(object sender, SelectionChangedEventArgs e)
- {
-
- if (bool_StartingUp) return;
-
- bool bool_Update = false;
-
-
-
- if (systype_ProgramMeasurement == SystemType.UnitedStates)
- {
- if (cmbLengthBuckets.SelectedIndex >= 0 && cmbLengthBuckets.SelectedIndex <= 1)
- {
- bool_Update = true;
- }
- else
- {
- cmbLengthBuckets.SelectedIndex = 0;
- }
- }
- else
- {
- if (cmbLengthBuckets.SelectedIndex >= 2 && cmbLengthBuckets.SelectedIndex <= 4)
- {
- bool_Update = true;
- }
- else
- {
- cmbLengthBuckets.SelectedIndex = 2;
- }
- }
-
- if (bool_Update)
- {
- lb_LengthBucket = (Buckets.Bucket)cmbLengthBuckets.SelectedItem;
- Buckets.Instance.LengthDisplayBucket =
- Buckets.Instance.LengthBuckets[cmbLengthBuckets.Items.IndexOf(lb_LengthBucket)];
- UpdatePropertyGrid();
- }
- }
Thanks for bearing with me, I know it was a long article but hopefully I've made some sense out of complex classes and a bit more on WPF. The source code is located here:
Widget Manager
End Of Line Man!