| Oleg 的个人资料Oleg V. Polikarpotchkin ...日志网络 | 帮助 |
|
|
6月14日 ColorChooser ControlThis element is the one more control from the ColorPicker constituent controls bug in addition to the published in my recent posts. The That is the sample. The On the base of the The codeYou can download the code and the samples here: The archive contains my WPFGears library with samples, tests, etc. It’s the Visual Studio 2008 SP1 solution targeted to .NET Framework 3.5. You’ll find ColorChooserSample and ColorDialogSample projects there. The Regards, AllInOneColorChooser ControlThis element is the one more control from the ColorPicker constituent controls bug in addition to the published in my recent posts. The The pictures below show you different color pickers in action. Sincerely I don't think that it would be very good idea to use The The codeYou can download the code and the samples here: The archive contains my WPFGears library with samples, tests, etc. It’s the Visual Studio 2008 SP1 solution targeted to .NET Framework 3.5. You’ll find AllInOneColorChooserSample project there. Regards, KnownColorPicker ControlThis element is the one more control from the ColorPicker constituent controls bug in addition to the published in my recent posts. The The That is the sample: At the figure above the Bisque border is given to the The The codeYou can download the code and the samples here: The archive contains my WPFGears library with samples, tests, etc. It’s the Visual Studio 2008 SP1 solution targeted to .NET Framework 3.5. You’ll find KnownColorPickerSample project there. Regards, 6月11日 RainbowColorPicker2 ControlThe That is the sample: At the figure above the Bisque border is given to the The The codeYou can download the code and the samples here: The archive contains my WPFGears library with samples, tests, etc. It’s the Visual Studio 2008 SP1 solution targeted to .NET Framework 3.5. You’ll find RainbowColorPicker2Sample project there. Regards, RainbowColorPicker ControlThis element is the one more control from the ColorPicker constituent controls bug in addition to the published in my recent posts. The That is the sample: At the figure above the Bisque border is given to the The The codeYou can download the code and the samples here: The archive contains my WPFGears library with samples, tests, etc. It’s the Visual Studio 2008 SP1 solution targeted to .NET Framework 3.5. You’ll find RainbowColorPickerSample project there. Regards, 6月10日 LuminositySlider ControlThis element is one more control from the ColorPicker constituent controls bug in addition to the published in my recent posts.
That is the sample: At the top is the RainbowSlider allowing to select the BaseColor for two
Value = Luminance * (Maximum - Minimum) + Minimum;
When the The direction the Luminosity value changes depends on the value of The codeYou can download the code and the samples here: The archive contains my WPFGears library with samples, tests, etc. It’s the Visual Studio 2008 SP1 solution targeted to .NET Framework 3.5. You’ll find LuminositySliderSample project there. Regards, 6月9日 RainbowBox ElementThis element is the one more control from the ColorPicker constituent controls bug in addition to the published in my recent posts.
The horizontal color spectrum is overlaid by the vertical gradient ranging from transparent color to Gray color. That is the sample. You select the color with the color selector (small white circle with black border). You can drag it with the mouse or just click anywhere on the Read-only The The codeYou can download the code and the samples here: The archive contains my WPFGears library with samples, tests, etc. It’s the Visual Studio 2008 SP1 solution targeted to .NET Framework 3.5. You’ll find RainbowBoxSample project there. Regards, 6月8日 ColorSaturationBox ElementThis element is one more control from the ColorPicker constituent controls bug in addition to ones published in my recent posts.
The color surface to select color from is the superposition of some color gradients. Roughly it consists of
BaseColor is the variable reference color you can get or set with the That is the sample. The You select the color with the color selector (small white circle with black border). You can drag it with the mouse or just click anywhere on the Read-only The codeYou can download the code and the samples here: The archive contains my WPFGears library with samples, tests, etc. It’s the Visual Studio 2008 SP1 solution targeted to .NET Framework 3.5. You’ll find ColorSaturationSample project there. Regards, 6月7日 RainbowSlider ControlYesterday I’ve posted the article on the OpacitySlider Control. The RainbowSlider is one else control to the bug of Color Picker related components.
i.e. its style redefines the slider background and the constituent As you can see at the figure above the Read-only The The direction the The codeYou can download the code and the samples here: The archive contains my WPFGears library with samples, tests, etc. It’s the Visual Studio 2008 SP1 solution targeted to .NET Framework 3.5. You’ll find RainbowSliderSample project there. Regards, 6月6日 OpacitySlider Control
Read-write
SelectedOpacity = 1.0 - (Value - Minimum) / (Maximum - Minimum);
i.e. its style redefines the slider background and the constituent As you can see at the figure above the The direction the Opacity value changes depends on the value of The codeYou can download the code and the samples here: The archive contains my WPFGears library with samples, tests, etc. It’s the Visual Studio 2008 SP1 solution targeted to .NET Framework 3.5. You’ll find OpacitySliderSample project there.Opacity Regards, 6月4日 Math Value ConvertersYesterday I’ve posted the topic on Math Expression Interpreter. It’s handy to use for Math calculations with WPF Value Converters. See the code below. /// <summary> /// Converts the input value to other value using the string /// parameter value as the math expression. /// </summary> public class MathConverter : IValueConverter { #region IValueConverter Members /// <summary> /// Converts the input <paramref name="value"/> to other value using the string /// <paramref name="parameter"/> value as the math expression. /// </summary> /// <param name="value">The value produced by the binding source.</param> /// <param name="targetType">Output value type.</param> /// <param name="parameter">Math expression string.</param> /// <param name="culture">The culture used to convert to/form double values.</param> /// <returns>Either the value calculated by the expression given in <paramref name="parameter"/> /// or <see cref="DependencyProperty.UnsetValue"/>.</returns> public object Convert(object value, Type targetType, object parameter , CultureInfo culture) { if (value == null || parameter == null || !(parameter is string)) return DependencyProperty.UnsetValue; try { Expression interpreter = new Expression(parameter as string); // Expression parameter. double expressionParameter; if (value is string) expressionParameter = double.Parse(value as string, culture.NumberFormat); else expressionParameter = (double)System.Convert.ChangeType(value, typeof(double) , culture.NumberFormat); double result = interpreter.Evaluate(new double[] { expressionParameter }); // The result to the target type if (double.IsNaN(result) || double.IsInfinity(result)) { if (targetType == typeof(double)) return result; else if (targetType == typeof(float)) { if (double.IsNaN(result)) return float.NaN; else if (double.IsPositiveInfinity(result)) return float.PositiveInfinity; else if (double.IsNegativeInfinity(result)) return float.NegativeInfinity; } return DependencyProperty.UnsetValue; } return System.Convert.ChangeType(result, targetType, culture.NumberFormat); } catch (Exception) { return DependencyProperty.UnsetValue; } } public object ConvertBack(object value, Type targetType, object parameter , CultureInfo culture) { throw new NotImplementedException(); } #endregion IValueConverter Members } /// <summary> /// Converts the input array of values to other value using the string /// parameter value as the math expression. /// </summary> public class MathMultiConverter : IMultiValueConverter { #region IMultiValueConverter Members /// <summary> /// Converts the input <paramref name="values"/> to other value using the string /// <paramref name="parameter"/> value as the math expression. /// </summary> /// <param name="values">The value array produced by the binding source.</param> /// <param name="targetType">Output value type.</param> /// <param name="parameter">Math expression string.</param> /// <param name="culture">The culture used to convert to/form double values.</param> /// <returns>Either the value calculated by the expression given in <paramref name="parameter"/> /// or <see cref="DependencyProperty.UnsetValue"/>.</returns> public object Convert(object[] values, Type targetType, object parameter , CultureInfo culture) { if (values == null || parameter == null || !(parameter is string)) return DependencyProperty.UnsetValue; try { Expression interpreter = new Expression(parameter as string); // Expression parameters array. double[] expressionParameters = new double[values.Length]; for (int i = 0; i < values.Length; i++) { if (values[i] is string) expressionParameters[i] = double.Parse(values[i] as string, culture.NumberFormat); else expressionParameters[i] = (double)System.Convert.ChangeType(values[i] , typeof(double), culture.NumberFormat); } double result = interpreter.Evaluate(expressionParameters); // The result to the target type if (double.IsNaN(result) || double.IsInfinity(result)) { if (targetType == typeof(double)) return result; else if (targetType == typeof(float)) { if (double.IsNaN(result)) return float.NaN; else if (double.IsPositiveInfinity(result)) return float.PositiveInfinity; else if (double.IsNegativeInfinity(result)) return float.NegativeInfinity; } return DependencyProperty.UnsetValue; } return System.Convert.ChangeType(result, targetType, culture.NumberFormat); } catch (Exception) { return DependencyProperty.UnsetValue; } } public object[] ConvertBack(object value, Type[] targetTypes, object parameter , CultureInfo culture) { throw new NotImplementedException(); } #endregion IMultiValueConverter Members }
The first one is for use with the regular Binding class, and the second is for the MultiBinding. Regards, 6月3日 Math Expressions InterpreterIntroductionI’ve ported from C++ to C# my old expression interpreter. It’s written from scratch without any parser generators like, say, ANTLR. Maybe it isn’t the modern approach but I’m using this code during last twenty years or so. BasicsAn expression contains parameters, numeric and predefined symbolic constants, predefined functions calls. All elements type is treated as double. Evaluation result is also double (having, possibly, special values NaN, INF, -INF). Samples:
UsageExpression class constructor takes an expression string as a parameter and parses it into the tree of operations. Exceptions of some types could be thrown during parsing. Then the expression could be evaluated multiple times with different parameters set with the Evaluate method. Evaluation is always successful (doesn't throws exceptions) although the result could be one of the double type special values mentioned above. ConstantsAn expression can contain numeric literals and predefined symbolic constants (like "PI"). The decimal point is used as the separator in literals. Scientific notation 'e' or 'E' can be used. Predefined ConstantsThe following symbolic constant names are defined:
Constant names are case-insensitive. ParametersExpression parameters (variables) are denoted with the placeholder syntax {index} like in the string.Format method format string. The 'index' is the parameter position in the array passed to the Evaluate method call. If the parameter at the specified index is missed the NaN value used.OperationsOperations priorities are as follows (the top is highest).
ParenthesesAny level parentheses used to group the operations. Function callsAll functions return double value and can have one or more arguments. Syntax: function(arg1, arg2, …). Argument itself also could be an expression. Some functions can have variable argument count. Unary plus and minusUnary plus is ignored. Unary minus is right associative, i.e. --x means (-(-x)). When Unary minus is applied to an infinity its sign changes to the opposite (e.g. Inf becomes NInf and vice versa). ExponentiationPow function analog. The operation is right associative, i.e. x^y^z means (x^(y^z)). Any values except NaN powered to 0 equal to 1. NaN at any power equal to NaN. Multiplication, division, modulusThese operations are left associative, i.e. x*y/z means ((x*y)/z). Addition and subtractionThese operations are left associative, i.e. x+y-z means ((x+y)-z). FunctionsFunction names are case-insensitive. Conditional FunctionsConditional functions allow emulate a branching in the intrinsically linear expression syntax. First argument value of any conditional function is compared to the branching condition defined by that function. Depending on the comparison result one of the other arguments is calculated. Branching by sign (If)If function has 4 or 5 arguments. If(cond, var1, var2, var3) or If(cond, var1, var2, var3, spec) If function checks the cond argument has a special value. If so, two variants are possible:
If the cond argument doesn't have a special value, If function calculates and returns the value of one of three arguments.
Branching by special value (IfNotFinite)IfNotFinite function has 2 or 3 arguments. IfNotFinite(cond, spec) or IfNotFinite(cond, spec, var) If the cond argument has a special value the function calculates and returns the spec argument value. Otherwise, if var argument exists the function calculates and returns it. Else it returns the cond value. Branching by NaN (IfNaN)IfNaN function has 2 or 3 arguments. IfNaN(cond, spec) or IfNaN(cond, spec, var) If the cond argument value is NaN the function calculates and returns the spec argument value. Otherwise, if var argument exists the function calculates and returns it. Else it returns the cond value. Branching by Infinity (IfInf)IfInf function has 2 or 3 arguments. IfInf(cond, spec) or IfInf(cond, spec, var) If the cond argument value is Inf or NInf the function calculates and returns the spec argument value. Otherwise, if var argument exists the function calculates and returns it. Else it returns the cond value. Branching by Positive Infinity (IfPInf)IfPInf function has 2 or 3 arguments. IfPInf(cond, spec) or IfPInf(cond, spec, var) If the cond argument value is Inf the function calculates and returns the spec argument value. Otherwise, if var argument exists the function calculates and returns it. Else it returns the cond value. Branching by Negative Infinity (IfNInf)IfNInf function has 2 or 3 arguments. IfNInf(cond, spec) or IfNInf(cond, spec, var) If the cond argument value is NInf the function calculates and returns the spec argument value. Otherwise, if var argument exists the function calculates and returns it. Else it returns the cond value. Math FunctionsAbsolute value AbsAbs has one argument and returns its absolute value. The same is applied to the negative infinity: Inf is returned in place of NInf. Square root SqrtSqrt has one argument and returns its square root. The square root of negative number, including negative infinity, and NaN, is NaN. The square root of Inf is Inf. The Sqrt(x) is the analog of Pow(x, 0.5). Exponentiation PowPow has two arguments and returns the first argument raised to the power of the second argument. It's the analog of ^ operation. Any value except NaN at the power of 0 is equal to 1. NaN at any power is equal to NaN. LogarithmsLog and Log10 functions has one argument. Logarithm of 0 is equal to NInf. Logarithm of negative argument is NaN. Thrigonometric functionsSin, Cos and Tan functions has one argument. If the argument has any special value the result will be NaN. Reverse thrigonometric functionsAsin, Acos and Atan functions has one argument and return the angle value in radians or a special value. If Asin or Acos argument is outside of [-1,1] or has a special value the result will be NaN. If Atan argument has a special value the result will be:
Hyperbolic functionsSinh, Cosh and Tanh functions has one argument. The codeYou can download the code here:
Regards, 5月4日 WPF Choose Font DialogWPF still doesn’t provide out of the box common Font Chooser dialog, and I can’t realize if Microsoft is going to provide such the dialog in the forthcoming WPF 4.0 version. Currently we have the following alternatives:
The former two items are full-fledged Font Chooser dialog implementations, and the latter one is the sample of how to implement the basic Font Chooser in pure XAML. Maybe there are other implementations but I could not find them. From full-fledged options I prefer the one of Niklas Borson: it has more compact design and, as much as I tested it, all its features work fine. The only drawback is that the dialog initialization takes too much time. There are things I don’t like in both the first and the second implementations: they are written rather in the Windows Forms style than in the WPF one. The dialog controls and layout are defined in the XAML. All the logic is implemented in the dialog code behind file, and this file is long and complicated. There is no one Binding object used! I have rewrote the Font Chooser dialog basing on the approach close to the MVC pattern: there is the Font properties class, the Model, and the dialog controls binds to the properties of this model class in XAML. The code behind file contains as little code as possible. The implementation consists of the following main parts:
You can download the dialog source code here: Regards, 4月25日 Represent an enumeration as a sequenceThere is a nice way to represent an arbitrary enumeration as a sequence of its names, values or name/value pairs. To do this we have to use extension methods: using System; using System.Linq; using System.Collections.Generic; /// <summary> /// Contains extension method on the <see cref="System.Type"/> allowing to represent /// an enumeration members as a sequence. /// </summary> public static class EnumExtensions { /// <summary> /// Get values of the specified enumeration type. /// </summary> /// <param name="type">Extendee type.</param> /// <returns>The sequence of values of the enumeration specified.</returns> public static IEnumerable<object> Values(this Type type) { Type enumType = Nullable.GetUnderlyingType(type) ?? type; if (!enumType.IsEnum) yield break; foreach (object item in Enum.GetValues(enumType)) yield return Convert.ChangeType(item, Enum.GetUnderlyingType(enumType)); } /// <summary> /// Get names of the specified enumeration type. /// </summary> /// <param name="type">Extendee type.</param> /// <returns>The sequence of names of the enumeration specified.</returns> public static IEnumerable<string> Names(this Type type) { Type enumType = Nullable.GetUnderlyingType(type) ?? type; if (!enumType.IsEnum) yield break; foreach (string item in Enum.GetNames(enumType)) yield return item; } /// <summary> /// Get name/value pairs of the specified enumeration type. /// </summary> /// <param name="type">Extendee type.</param> /// <returns>The sequence of name/value pairs of the enumeration specified.</returns> public static IEnumerable<KeyValuePair<string, object>> Dictionary(this Type type) { Type enumType = Nullable.GetUnderlyingType(type) ?? type; if (!enumType.IsEnum) yield break; IEnumerable<KeyValuePair<string, object>> dictionary = from value in Enum.GetValues(enumType).Cast<object>() select new KeyValuePair<string, object>(Enum.GetName(enumType, value) , Convert.ChangeType(value, Enum.GetUnderlyingType(enumType))); foreach (KeyValuePair<string, object> item in dictionary) yield return item; } } This could be useful when, for example, the task is to fill the contents of the ListBox or an another such control with the list of enumeration values. In WPF, for example, if we have three list boxes which we’re going to fill with the values of Type enumType = typeof(UriPartial); lbxValues.ItemsSource = enumType.Values(); lbxNames.ItemsSource = enumType.Names(); lbxDictionary.ItemsSource = enumType.Dictionary(); and we'll get the following picture: I’m aware on other approaches to this task (see, for example, Accessing Enum members in Xaml by Andrew Smith or Binding and using Friendly enums in WPF by Sasha Barber), this is just the another one. Cheers, Generic Data Points Series XML format, Version 2New version of this service is published yesterday at the CodeProject article. It contains the following additions:
Cheers, 4月7日 How to express series of generic data points in XML and read them without a painForewordIt’s often desirable to provide generic data point series data in some XML format. XML gives the ability to decorate the series with attributes, nest them in complex data objects, mix different data series/data objects in one data file and load them with concise Generic data point is a structure with just two required {X, Y} properties expressing the point position in 2D space. Each point dimension has its own "base" type, e.g. numeric, Generic Data Points Series XML formatData point series XML format has to be developed in a way providing for ability of validation with XML schema. First, we have to define the root element. Suppose it is called <?xml version="1.0" encoding="utf-8"?> <Items xmlns="urn:PointSeries-schema"> … </Items>
The root element contains unrestricted number of data point series. First try is to define point series elements as follows: <Items xmlns="urn:PointSeries-schema"> <Points …> </Points> <Points …> </Points> … </Items>
That won’t work because different point series elements could contain points of different base types and, so, the point series elements themselves could be of different types. XML schema rules don’t allow elements of different types have the same name in the same scope. Hence we must assign different names to point series elements of different types. It’s decided to name point series elements according to the following patterns:
BaseType, XBaseType and YBaseType point series element name parts are called collectively "type strings". It's necessary to draw an agreement on how to define these type strings, establish the mapping between the type strings, XSD-defined data types and .NET data types.
This table contains the partial list of XSD simple types. You can extend it by including other XSD types and, moreover, any types you derived from XSD simple types with XML schema derivation rules. According the mapping above, for example, the Point element itself is something like Below is the excerpt from the example input XML data file: <?xml version="1.0" encoding="utf-8"?> <Items xmlns="urn:PointSeries-schema"> <Points.Int.Double YName="y=x^2"> <Point x="0" y="0"/> <Point x="1" y="0.01"/> … </Points.Int.Double> <Points.Date.Int YName="temperature" XName="Date"> <Point x="2008-01-01" y="-20"/> <Point x="2008-02-01" y="-25"/> … </Points.Date.Int> <Points.Month.Double YName="2008 year month temperatures" XName="Month"> <Point x="--01--" y="-20.8"/> <Point x="--02--" y="-25.2"/> … </Points.Month.Double> … </Items> Note that point series elements are decorated with optional Generic data point series data XML format is defined by the XML schema which excerpt follows: <?xml version="1.0" encoding="utf-8"?> <xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <!-- Root element --> <xs:element name="Items" type="itemsType"/> <!-- Root element type --> <xs:complexType name="itemsType"> <xs:choice maxOccurs="unbounded"> <xs:element name="Points.Int" type="pointsIntIntType"/> <xs:element name="Points.Int.DateTime" type="pointsIntDttmType"/> … <xs:element name="Points.Double" type="pointsDblDblType"/> <xs:element name="Points.Double.Int" type="pointsDblIntType"/> … </xs:choice> </xs:complexType> <!-- Point Series Type attributes --> <xs:attributeGroup name="pointSetAttributes"> <xs:attribute name="YName" type="xs:string" use="optional" /> <xs:attribute name="XName" type="xs:string" use="optional" /> </xs:attributeGroup> <!-- Point Series Types --> <xs:complexType name="pointsIntIntType"> <xs:sequence> <xs:element minOccurs="1" maxOccurs="unbounded" name="Point"> <xs:complexType> <xs:attribute name="x" type="xs:int" use="required" /> <xs:attribute name="y" type="xs:int" use="required" /> </xs:complexType> </xs:element> </xs:sequence> <xs:attributeGroup ref="pointSetAttributes"/> </xs:complexType> <xs:complexType name="pointsIntDttmType"> <xs:sequence> <xs:element minOccurs="1" maxOccurs="unbounded" name="Point"> <xs:complexType> <xs:attribute name="x" type="xs:int" use="required" /> <xs:attribute name="y" type="xs:dateTime" use="required" /> </xs:complexType> </xs:element> </xs:sequence> <xs:attributeGroup ref="pointSetAttributes"/> </xs:complexType> … <xs:complexType name="pointsDblIntType"> <xs:sequence> <xs:element minOccurs="1" maxOccurs="unbounded" name="Point"> <xs:complexType> <xs:attribute name="x" type="xs:double" use="required" /> <xs:attribute name="y" type="xs:int" use="required" /> </xs:complexType> </xs:element> </xs:sequence> <xs:attributeGroup ref="pointSetAttributes"/> </xs:complexType> <xs:complexType name="pointsDblDblType"> <xs:sequence> <xs:element minOccurs="1" maxOccurs="unbounded" name="Point"> <xs:complexType> <xs:attribute name="x" type="xs:double" use="required" /> <xs:attribute name="y" type="xs:double" use="required" /> </xs:complexType> </xs:element> </xs:sequence> <xs:attributeGroup ref="pointSetAttributes"/> </xs:complexType> … </xs:schema>
That schema defines In the code attached to the article the The rest of the schema contains the long list of element type definitions. Each of these types defines the point series with specific x, y base types. Now the data format is completely defined and we can proceed to the data reading. Reading the DataIn the code attached to this post (see below) all data reading code is placed into That is the full /// <summary> /// Class to load DataPointSeries collection from a file. /// </summary> /// <remarks> /// On error contains the string indicating what wrong is happen while loading. /// </remarks> public class Loader { // XML Schema resource name. const string schemaFileName = "schema.xsd"; // XML namespace must be used in XML data files. const string namespaceName = "urn:PointSeries-schema"; /// <summary> /// Gets the error string. /// </summary> /// <remarks> /// List of error descritions happen on the input file loading or validation. /// </remarks> /// <value>The error string or null.</value> public string Errors { get; private set; } /// <summary> /// Loads DataPointSeries collection from the file specified. /// </summary> /// <param name="fileName">DataPointSeries collection XML file Name.</param> /// <returns>DataPointSeries collection on success; null on error.</returns> public DataPointSeries[] Load(string fileName) { Errors = null; XmlReader reader = null; try { // Get xml schema stream from the "schemaFileName" resource. Assembly assembly = Assembly.GetAssembly(this.GetType()); ResourceManager rm = new ResourceManager(assembly.GetName().Name + ".g", assembly); using (XmlTextReader schemaReader = new XmlTextReader(rm.GetStream(schemaFileName))) { // Prepaire XmlReaderSettings for input file validation. XmlReaderSettings settings = new XmlReaderSettings(); settings.ValidationType = ValidationType.Schema; settings.Schemas.Add(namespaceName, schemaReader); StringBuilder sbErrors = null; settings.ValidationEventHandler += (sender, e) => { if (sbErrors == null) sbErrors = new StringBuilder(); sbErrors.AppendFormat("Validation error: {1}{0}Line={2}, position={3}{0}" , System.Environment.NewLine, e.Exception.Message , e.Exception.LineNumber, e.Exception.LinePosition); }; // Load and validate the file. reader = XmlReader.Create(fileName, settings); XDocument xml = XDocument.Load(reader); if (sbErrors != null) { // Validation error(s) occured. Errors = sbErrors.ToString(); return null; } XNamespace xns = namespaceName; // Check the root element (i.e. Items in "urn:PointSeries-schema" xmlns). XElement rootElement = xml.Element(xns + "Items"); if (rootElement == null) { Errors = string.Format("Root element {0} missed", xns + "Items"); return null; } // Parse the Point.XXX elements. return rootElement.Elements().Select<XElement, DataPointSeries>( (item) => { // Parse item tag name for X/Y type strings. string xType, yType; getXYTypeStrings(item.Name.ToString(), out xType, out yType); // Optional attributes. var yName = item.Attribute("YName"); var xName = item.Attribute("XName"); DataPointSeries series = new DataPointSeries() { YName = yName == null ? "" : yName.Value, XName = xName == null ? "" : xName.Value, XTypeString = xType, YTypeString = yType }; foreach (var pt in from pt in item.Elements(xns + "Point") select new { X = pt.Attribute("x").Value, Y = pt.Attribute("y").Value }) { series.AddPoint(pt.X, pt.Y); } return series; }).ToArray(); } } catch (Exception ex) { Errors = ex.Message; } finally { if (reader != null) reader.Close(); } return null; } /// <summary> /// Parses the point series element tag and returns x,y type strings. /// </summary> /// <param name="tagName">Tag name.</param> /// <param name="xType">Output Type of the x-dimension.</param> /// <param name="yType">Output Type of the y-dimension.</param> static void getXYTypeStrings(string tagName, out string xType, out string yType) { int n = tagName.IndexOf('}'); Debug.Assert(n > 0, "n > 0"); const string pointsTagPrefix = "Points"; int pointsTagPrefixLength = pointsTagPrefix.Length; Debug.Assert(tagName.Length > n + pointsTagPrefixLength + 1, "tagName.Length > n + pointsTagPrefixLength + 1"); string xyTypes = tagName.Substring(n + pointsTagPrefixLength + 2); n = xyTypes.IndexOf('.'); if (n < 0) { xType = xyTypes; yType = xyTypes; } else { xType = xyTypes.Substring(0, n); yType = xyTypes.Substring(n + 1); } } }
This class isn’t static just because it stores the
This approach make the By design the The schema file is stored as the resource in the
XNamespace xns = namespaceName; XElement rootElement = xml.Element(xns + "Items");
Note the return rootElement.Elements().Select<XElement, DataPointSeries>(…)
statement.
Using the CodeYou can download this post source code here:
This code contains the Visual Studio 2008 SP1 solution targeted to .NET Framework 3.5 with three projects. Main part is the One other project is the simple Console Application which loads the XML data from the file pointed by the first command line argument and either reports errors or displays the results of XML data parsing. Another project is more important. It’s the Unit Test project containing the tests for every data point series type supported by the 3月24日 OpenWPFChart library Beta 02 release uploadedThis release contains the following OpenWPFChart library project updates:
Regards, 3月18日 OpenWPFChart library publishedI’ve published OpenWPFChart library project at CodePlex. The library defines the object model and the set of base components (parts) to assemble Charts in Windows Presentation Foundation. Parts set is extensible so the developer can add its own new components. Chart controls composed from the these parts could have different look and feel. It also contains some Chart controls prebuilt from the parts. Regards, Oleg V. Polikarpotchkin 3月1日 Draw Closed Smooth Curve with Bezier SplineDecember 17, 2008 I posted an article on how to draw the smooth curve through the set of 2D points with Bezier drawing primitives. Yesterday I got a question on how to draw a closed curve in the same manner. Indeed, there is a difference to open-ended curve. EquationsNote: to make a sequence of individual Bezier curves to be a spline we should calculate Bezier control points so that the spline curve has two continuous derivatives at the knot points. Bezier curve at the single interval is expressed as B(t)=(1-t)3P0+3(1-t)2tP1+3(1-t)t2P2+t3P3 where t is in [0,1] and 1. P0 – first knot point 2. P1 – first control point (at first knot) 3. P2 – second control point (at second knot) 4. P3 – second knot point First derivative is: B’(t) = -3 (1 - t)2 P0 + 3 (3 t2 – 4t + 1) P1 + 3 (2t – 3t2) P2 + 3t2 P3 Second derivative is: B’’(t) = 6 (1 - t) P0 + 3 (6t - 4) P1 + 3 (2 – 6t) P2+ 6 t P3 Let’s consider piece-wise Bezier curve on the interval with n (n > 2) points Pi (0,..,n-1) and n subintervals Si (0,..,n-1). Si = (Pi, Pi+1) for (i=0,..,n-2) Sn-1 = (Pn-1, P0) for (i=n-1) Bezier curve at Si (i=0,..,n-2) will be Bi(t) = (1 - t)3 Pi + 3 (1-t)2 t P1i + 3 (1-t) t2 P2i+1 + t3 Pi+1; (i=0,..,n-2) and the closing Bezier curve at Sn-1 Bi(t) = (1 - t)3 Pn-1 + 3 (1-t)2 t P1n-1 + 3 (1-t) t2 P20 + t3 P0 First derivative at Si (i=0,..,n-2) will be B’i(t) = -3 (1-t)2 Pi + 3(3t2 - 4t + 1) P1i + 3(2t - 3t2) P2i+1+3t2 Pi+1 and at Sn-1 B’n-1(t) = -3 (1-t)2 Pn-1 + 3(3t2 - 4t + 1) P1n-1 + 3(2t - 3t2) P20+3t2 P0 First derivative continuity condition gives: P1i + P2i = 2 Pi (i=0,..,n-1) (1) Second derivative at Si (i=0,..,n-2) will be B’’i (t) = 6 (1-t) Pi +6 (3t - 2) P1i + 6 (1 - 3t) P2i+1 + 6t Pi+1 and at Sn-1 B’’n-1 (t) = 6 (1-t) Pn-1 +6 (3t - 2) P1n-1 + 6 (1 - 3t) P20 + 6t P0 Second derivative continuity condition gives: at P0 2 P10 + P1n-1 = 2 P20 + P21 (2.1) at Pi (i=1,..,n-2) 2 P1i + P1i-1 = 2 P2i + P2i+1 (2.2) and at Pn-1 2 P1n-1 + P1n-2 = 2 P2n-1 + P20 (2.3) Excluding P2 form (2.1-3) with (1) we get the set of equations for P1 4 P10 + P11 + P1n-1 = 4 P0 + 2 P1 for P0 P1i-1 + 4 P1i + P1i+1 = 4 Pi + 2 Pi+1 for Pi (i=1,..,n-2) P10 + P1n-2 + 4 P1n-1 = 4 Pn-1 + 2 P0 for Pn-1 We got the system with the matrix which looks like 4 1 0 0 ... 0 1 1 4 1 0 ... 0 0 0 1 4 1 ... 0 0 ............... 1 0 0 0 ... 1 4 It's so-called "cyclic" system which can be solved as effectively as a tridiagonal system with the trick which you can find in the "Numerical Recipes", for example. After P1's found it's easy to get P2's from (1). Code and sampleYou can download the code and sample application here: It is Visual Studio 2008 solution targeted to .NET 3.5. It contains WPF Windows Application project designed to demonstrate sampled circle drawn with Bezier spline above. Although I compiled this code in C# 3.0 I don’t see why it can’t be used without any modification in C# 2.0 and even in C# 1.0 if you remove keyword “static” from the class declarations. |
|
|